diff --git a/src/agent/input/human-input.ts b/src/agent/input/human-input.ts index 82d1f2a1cf..e008ace850 100644 --- a/src/agent/input/human-input.ts +++ b/src/agent/input/human-input.ts @@ -1,5 +1,6 @@ import { defineSchema } from "#veryfront/schemas/index.ts"; import { AGENT_ERROR, AGENT_TIMEOUT } from "#veryfront/errors"; +import { sleep } from "#veryfront/utils"; import type { InferInput, InferSchema, @@ -314,7 +315,7 @@ export async function waitForDurableHumanInputResolution( return result; } - await delay(options.pollIntervalMs); + await sleep(options.pollIntervalMs); } throw AGENT_TIMEOUT.create({ @@ -371,9 +372,3 @@ function submitHumanInputResumeValue( // or the ephemeral session has been finalized. } } - -function delay(ms: number): Promise { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} diff --git a/src/agent/service/detached-run-tracker.ts b/src/agent/service/detached-run-tracker.ts index 98f3be3616..9ee8fe4c8a 100644 --- a/src/agent/service/detached-run-tracker.ts +++ b/src/agent/service/detached-run-tracker.ts @@ -1,5 +1,6 @@ import { RunResumeSessionManager } from "../runtime/resume-session.ts"; import { AGENT_TIMEOUT } from "#veryfront/errors"; +import { sleep } from "#veryfront/utils"; /** Result returned from detached run drain. */ export interface DetachedRunDrainResult { @@ -51,12 +52,6 @@ const DEFAULT_DETACHED_RUN_DRAIN_TIMEOUT_MS = 15_000; const DETACHED_RUN_DRAIN_TIMEOUT_MESSAGE = "Detached durable runs did not drain before shutdown timeout"; -function sleep(ms: number): Promise { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} - /** Create detached run tracker. */ export function createDetachedRunTracker( options: DetachedRunTrackerOptions = {}, diff --git a/src/agent/service/registration.ts b/src/agent/service/registration.ts index bd9fefac78..78d7e5e257 100644 --- a/src/agent/service/registration.ts +++ b/src/agent/service/registration.ts @@ -2,6 +2,7 @@ import type { Schema, SchemaValidator } from "#veryfront/extensions/schema/index import { defineSchema } from "../../schemas/define.ts"; import { lazySchema } from "../../schemas/lazy.ts"; import { CONFIG_INVALID, NETWORK_ERROR } from "#veryfront/errors"; +import { computeHash } from "#veryfront/utils"; /** Public API contract for agent service registration mode. */ export type AgentServiceRegistrationMode = "auto" | "enabled" | "disabled"; @@ -242,11 +243,7 @@ async function stableServiceKey(input: { input.baseUrl, input.podIdentity ?? "no-pod-identity", ].join("|"); - const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(keySource)); - const hash = Array.from(new Uint8Array(digest)) - .map((byte) => byte.toString(16).padStart(2, "0")) - .join("") - .slice(0, 32); + const hash = (await computeHash(keySource)).slice(0, 32); return `${input.serviceName}:${hash}`.slice(0, 128); } diff --git a/src/build/bundler/code-splitter/manifest-builder.ts b/src/build/bundler/code-splitter/manifest-builder.ts index b6dddaa3df..beaf62a232 100644 --- a/src/build/bundler/code-splitter/manifest-builder.ts +++ b/src/build/bundler/code-splitter/manifest-builder.ts @@ -8,6 +8,7 @@ import { join, relative } from "#veryfront/compat/path/index.ts"; import { createFileSystem } from "#veryfront/platform/compat/fs.ts"; import type { ChunkInfo, ChunkManifest, MetafileOutput } from "./types.ts"; import { createError, toError } from "#veryfront/errors"; +import { computeHashBytes } from "#veryfront/utils"; const fs = createFileSystem(); @@ -43,10 +44,7 @@ export function extractChunkName(file: string): string { /** Calculates SHA-256 hash of file content (returns first 8 hex chars) */ export async function calculateFileHash(content: Uint8Array): Promise { - const hashBuffer = await crypto.subtle.digest("SHA-256", content.slice()); - return Array.from(new Uint8Array(hashBuffer), (b) => b.toString(16).padStart(2, "0")) - .join("") - .slice(0, 8); + return (await computeHashBytes(content.slice())).slice(0, 8); } /** Determines which imports are critical and should be preloaded */ diff --git a/src/build/production-build/local-release-assets.ts b/src/build/production-build/local-release-assets.ts index be2e7f72f5..089ffb7afd 100644 --- a/src/build/production-build/local-release-assets.ts +++ b/src/build/production-build/local-release-assets.ts @@ -16,7 +16,7 @@ import { type ReleaseAssetTransform, } from "#veryfront/release-assets/build-executor.ts"; import type { ReleaseAssetManifest } from "#veryfront/release-assets/manifest-schema.ts"; -import { sha256HexBytes } from "#veryfront/release-assets/hash.ts"; +import { computeHashBytes } from "#veryfront/utils"; import { resolveProjectReactVersion } from "#veryfront/transforms/esm/package-registry.ts"; import { VERSION } from "#veryfront/utils/version.ts"; import type { VeryfrontConfig } from "#veryfront/config"; @@ -90,7 +90,7 @@ export async function generateLocalReleaseAssetManifest( assetsByHash.set(asset.contentHash, asset); } const gaps = [...cached.gaps, ...built.gaps, ...framework.gaps]; - const sourceContentHash = await sha256HexBytes( + const sourceContentHash = await computeHashBytes( new TextEncoder().encode( [ options.projectDir, diff --git a/src/build/vendor-cache.ts b/src/build/vendor-cache.ts index a29fda90d3..a2e088a9be 100644 --- a/src/build/vendor-cache.ts +++ b/src/build/vendor-cache.ts @@ -5,6 +5,7 @@ * Provides per-project vendor bundle management with automatic invalidation. **************************************************/ +import { computeHash } from "#veryfront/utils"; import { createCacheNamespace } from "#veryfront/utils/cache-namespace.ts"; function buildVendorCacheConfig( @@ -38,12 +39,7 @@ export async function generateVendorCacheKey( ...buildVendorCacheConfig(reactVersion, dependencies), }); - const data = new TextEncoder().encode(configStr); - const hashBuffer = await crypto.subtle.digest("SHA-256", data); - const hash = Array.from(new Uint8Array(hashBuffer)) - .map((b) => b.toString(16).padStart(2, "0")) - .join("") - .slice(0, 16); + const hash = (await computeHash(configStr)).slice(0, 16); return `vendor:${projectId}:${hash}`; } diff --git a/src/discovery/transpiler.ts b/src/discovery/transpiler.ts index 1ef389ef91..4e5846f062 100644 --- a/src/discovery/transpiler.ts +++ b/src/discovery/transpiler.ts @@ -10,6 +10,7 @@ import { ensureDefaultBundlerContracts } from "#veryfront/extensions/bundler/def import { isDeno, isDenoCompiled } from "#veryfront/platform/compat/runtime.ts"; import { createFileSystem } from "#veryfront/platform/compat/fs.ts"; import * as pathHelper from "#veryfront/compat/path"; +import { computeHash } from "#veryfront/utils"; import { getEsbuildLoader } from "#veryfront/utils/path-utils.ts"; import type { FileSystemAdapter } from "#veryfront/platform/adapters/base.ts"; import type { FileDiscoveryContext } from "./types.ts"; @@ -29,19 +30,6 @@ type TranspileCacheEntry = { // still match (see findCachedModuleWithFreshDeps). const transpileCache = new Map(); -const textEncoder = new TextEncoder(); - -async function hashSource(source: string): Promise { - const digest = await crypto.subtle.digest( - "SHA-256", - textEncoder.encode(source), - ); - return Array.from( - new Uint8Array(digest), - (byte) => byte.toString(16).padStart(2, "0"), - ).join(""); -} - /** * Returns the first cached module whose recorded bundled-dependency contents * still match what the current adapter serves. esbuild inlines relative @@ -63,7 +51,7 @@ async function findCachedModuleWithFreshDeps( const content = context.fsAdapter ? await context.fsAdapter.readFile(dep.path) : await createFileSystem().readTextFile(dep.path); - hash = await hashSource(content); + hash = await computeHash(content); } catch { hash = undefined; } @@ -214,7 +202,7 @@ export async function importModule( // another project's discovery. The entry hash alone is still not enough — // bundled relative imports are inlined into the module — so cached entries // are only served after their recorded dependency contents re-verify. - const cacheKey = `${file} ${await hashSource(source)}`; + const cacheKey = `${file} ${await computeHash(source)}`; const cachedEntries = transpileCache.get(cacheKey); if (cachedEntries) { const cached = await findCachedModuleWithFreshDeps(cachedEntries, context); @@ -308,7 +296,7 @@ export async function importModule( moduleUrl.searchParams.set("v", String(Date.now())); const module = await import(moduleUrl.href); const deps = await Promise.all( - bundledDeps.map(async ({ path, content }) => ({ path, hash: await hashSource(content) })), + bundledDeps.map(async ({ path, content }) => ({ path, hash: await computeHash(content) })), ); const entries = transpileCache.get(cacheKey) ?? []; entries.push({ deps, module }); diff --git a/src/eval/agent-service/durable-run-canaries/runner.ts b/src/eval/agent-service/durable-run-canaries/runner.ts index 813b7e35b4..ffd08e32f4 100644 --- a/src/eval/agent-service/durable-run-canaries/runner.ts +++ b/src/eval/agent-service/durable-run-canaries/runner.ts @@ -1,5 +1,6 @@ import { defineSchema } from "#veryfront/schemas/index.ts"; import { API_CLIENT_ERROR, TIMEOUT_ERROR } from "#veryfront/errors"; +import { sleep } from "#veryfront/utils"; import { ensureBuiltinSchemaValidator } from "#veryfront/extensions/builtin-extensions.ts"; import type { InferSchema } from "#veryfront/extensions/schema/index.ts"; @@ -462,12 +463,6 @@ function collectReferencedChildConversationIds(messages: DurableRunCanaryMessage return [...childConversationIds]; } -function sleep(ms: number): Promise { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} - function isTerminalRunStatus(status: string): boolean { return status === "completed" || status === "failed" || status === "cancelled"; } diff --git a/src/eval/agent-service/live-evals/api-client.ts b/src/eval/agent-service/live-evals/api-client.ts index f5348784e1..8cb567e638 100644 --- a/src/eval/agent-service/live-evals/api-client.ts +++ b/src/eval/agent-service/live-evals/api-client.ts @@ -1,5 +1,6 @@ import { ensureBuiltinSchemaValidator } from "#veryfront/extensions/builtin-extensions.ts"; import { API_CLIENT_ERROR, INVALID_ARGUMENT, TIMEOUT_ERROR } from "#veryfront/errors"; +import { sleep } from "#veryfront/utils"; import type { InferSchema } from "#veryfront/extensions/schema/index.ts"; import { defineSchema } from "#veryfront/schemas/index.ts"; import type { LiveEvalProjectFile } from "./runner.ts"; @@ -218,10 +219,8 @@ function getResponseText(response: Response): Promise { return response.text(); } -async function wait(input: { ms: number }): Promise { - await new Promise((resolve) => { - setTimeout(resolve, input.ms); - }); +function wait(input: { ms: number }): Promise { + return sleep(input.ms); } async function waitForProjectUploadFixture( diff --git a/src/eval/provenance.ts b/src/eval/provenance.ts index 3138b20916..f73c7aaccd 100644 --- a/src/eval/provenance.ts +++ b/src/eval/provenance.ts @@ -1,4 +1,5 @@ import { join } from "@std/path"; +import { computeHash, computeHashBytes } from "#veryfront/utils"; import { VERSION } from "#veryfront/utils/version-constant.ts"; import type { EvalRunProvenance } from "./types.ts"; @@ -185,19 +186,6 @@ async function runGit( } } -async function sha256Hex(value: string): Promise { - return sha256Bytes(new TextEncoder().encode(value)); -} - -async function sha256Bytes(value: Uint8Array): Promise { - const bytes = new Uint8Array(value.byteLength); - bytes.set(value); - const hash = await crypto.subtle.digest("SHA-256", bytes.buffer); - return Array.from(new Uint8Array(hash)) - .map((byte) => byte.toString(16).padStart(2, "0")) - .join(""); -} - function parseNullSeparated(value: string | undefined): string[] { if (!value) return []; return value.split("\0").filter(Boolean).sort(); @@ -212,7 +200,7 @@ async function hashUntrackedFiles( for (const relativePath of paths) { try { const content = await readFile(join(projectDir, relativePath)); - entries.push(`${relativePath}\0${await sha256Bytes(content)}`); + entries.push(`${relativePath}\0${await computeHashBytes(new Uint8Array(content))}`); } catch { entries.push(`${relativePath}\0unreadable`); } @@ -250,7 +238,7 @@ async function resolveGitProvenance( ...(branch ? { branch } : {}), ...(dirty !== undefined ? { dirty } : {}), ...(dirty - ? { dirtyHash: await sha256Hex(`${status ?? ""}\n${diff ?? ""}\n${untrackedHashInput}`) } + ? { dirtyHash: await computeHash(`${status ?? ""}\n${diff ?? ""}\n${untrackedHashInput}`) } : {}), }; } diff --git a/src/eval/report.ts b/src/eval/report.ts index e53f5e5344..68ec0241bc 100644 --- a/src/eval/report.ts +++ b/src/eval/report.ts @@ -15,6 +15,7 @@ import type { EvalReportSummary, EvalUsageSummary, } from "./types.ts"; +import { computeHash } from "#veryfront/utils"; /** Additive eval report contract version written by new reports and summary artifacts. */ export const EVAL_REPORT_SCHEMA_VERSION = 2; @@ -54,13 +55,6 @@ function stableStringify(value: unknown): string { return JSON.stringify(sortJsonValue(value)); } -async function sha256Hex(value: string): Promise { - const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)); - return Array.from(new Uint8Array(digest)) - .map((byte) => byte.toString(16).padStart(2, "0")) - .join(""); -} - function createDatasetHashInput(dataset: EvalDataset, examples: EvalExample[]) { return { kind: dataset.kind, @@ -83,7 +77,7 @@ export async function createEvalDatasetMetadata( kind: dataset.kind, ...(dataset.path ? { path: dataset.path } : {}), examples: examples.length, - hash: `sha256:${await sha256Hex(stableStringify(hashInput))}`, + hash: `sha256:${await computeHash(stableStringify(hashInput))}`, }; } diff --git a/src/html/styles-builder/plugin-loader.ts b/src/html/styles-builder/plugin-loader.ts index 496ed3de7f..df85199b4d 100644 --- a/src/html/styles-builder/plugin-loader.ts +++ b/src/html/styles-builder/plugin-loader.ts @@ -7,7 +7,7 @@ * @module html/styles-builder/plugin-loader */ -import { serverLogger } from "#veryfront/utils"; +import { encodeBase64, serverLogger } from "#veryfront/utils"; import { type ErrorSlug, getErrorBySlug, @@ -73,23 +73,6 @@ try { // by the `@veryfront/ext-css-tailwind` extension's `setup()` hook — they depend on // tailwindcss imports that live in the extension package, not in core. -function encodeToBase64(source: string): string { - const bufferCtor = (globalThis as { - Buffer?: { - from: (input: string, encoding: string) => { toString: (encoding: string) => string }; - }; - }).Buffer; - - if (bufferCtor?.from) { - return bufferCtor.from(source, "utf8").toString("base64"); - } - - const bytes = new TextEncoder().encode(source); - let binary = ""; - for (const byte of bytes) binary += String.fromCharCode(byte); - return btoa(binary); -} - /** * esm.sh bundles can contain root-relative nested imports. Once the bundle is * written to a temp file, Deno resolves those as local file paths unless they @@ -116,7 +99,7 @@ export function rewriteEsmShRootRelativeImports(code: string): string { async function importBundledModule(code: string): Promise { if (!isDeno) { - const dataUrl = `data:text/javascript;base64,${encodeToBase64(code)}`; + const dataUrl = `data:text/javascript;base64,${encodeBase64(code)}`; return await import(dataUrl); } diff --git a/src/knowledge/index.ts b/src/knowledge/index.ts index 47db557f2b..530fa212b0 100644 --- a/src/knowledge/index.ts +++ b/src/knowledge/index.ts @@ -15,6 +15,7 @@ import { ragStore } from "#veryfront/embedding/index.ts"; import { INPUT_VALIDATION_FAILED } from "#veryfront/errors"; +import { base64urlEncodeBytes } from "#veryfront/utils"; import type { RagSearchOptions, RagSearchResult, @@ -469,10 +470,7 @@ function createLookupItem( } function encodeCursor(state: ProjectKnowledgeLookupCursorState): string { - const bytes = new TextEncoder().encode(JSON.stringify(state)); - let binary = ""; - for (const byte of bytes) binary += String.fromCharCode(byte); - return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); + return base64urlEncodeBytes(new TextEncoder().encode(JSON.stringify(state))); } function decodeCursor(cursor: string): ProjectKnowledgeLookupCursorState { diff --git a/src/metrics/index.ts b/src/metrics/index.ts index 83a7bcc3a2..6f6b4601f2 100644 --- a/src/metrics/index.ts +++ b/src/metrics/index.ts @@ -22,6 +22,7 @@ import { } from "#veryfront/observability"; import { getCurrentRequestContext } from "#veryfront/platform/adapters/fs/veryfront/request-context.ts"; import { getEnv, getHostEnv } from "#veryfront/platform/compat/process.ts"; +import { encodeBase64 } from "#veryfront/utils"; import { isProjectEnvActive } from "#veryfront/server/project-env/storage.ts"; export type MetricAttributeValue = string | number | boolean | null | undefined; @@ -196,7 +197,7 @@ function resolveInternalMetricsUrl(): string | null { function buildBasicAuth(username: string, password: string): string { const credentials = `${username}:${password}`; - return `Basic ${globalThis.btoa(credentials)}`; + return `Basic ${encodeBase64(credentials)}`; } function parseHeaders(headerInput: string | undefined): Record { diff --git a/src/oauth/providers/base.ts b/src/oauth/providers/base.ts index b5aaa4342a..8c9c68c7e4 100644 --- a/src/oauth/providers/base.ts +++ b/src/oauth/providers/base.ts @@ -10,7 +10,7 @@ import type { } from "../types.ts"; import { getEnv } from "#veryfront/platform/compat/process.ts"; import { INVALID_ARGUMENT, TOKEN_STORAGE_ERROR } from "#veryfront/errors"; -import { logger as baseLogger } from "#veryfront/utils"; +import { base64urlEncodeBytes, logger as baseLogger } from "#veryfront/utils"; const logger = baseLogger.component("o-auth"); @@ -36,11 +36,7 @@ function generateCodeVerifier(): string { async function generateCodeChallenge(verifier: string): Promise { const data = new TextEncoder().encode(verifier); - const hash = await crypto.subtle.digest("SHA-256", data); - return btoa(String.fromCharCode(...new Uint8Array(hash))) - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=+$/, ""); + return base64urlEncodeBytes(new Uint8Array(await crypto.subtle.digest("SHA-256", data))); } /** Implement oauth provider. */ diff --git a/src/proxy/routing-invalidation-redis.ts b/src/proxy/routing-invalidation-redis.ts index 95a3632d95..9164b062be 100644 --- a/src/proxy/routing-invalidation-redis.ts +++ b/src/proxy/routing-invalidation-redis.ts @@ -1,5 +1,6 @@ import { getRedisModule } from "#veryfront/platform/adapters/redis/modules.ts"; import { getEnv } from "#veryfront/platform/compat/process.ts"; +import { base64urlEncodeBytes } from "#veryfront/utils"; import type { ProxyRoutingInvalidationEvent, ProxyRoutingInvalidationPublisher, @@ -115,11 +116,6 @@ function signatureInput(domain: SignatureDomain, issuedAtMs: number, payload: st return encoded.buffer.slice(encoded.byteOffset, encoded.byteOffset + encoded.byteLength); } -function base64UrlEncode(bytes: ArrayBuffer): string { - const binary = Array.from(new Uint8Array(bytes), (byte) => String.fromCharCode(byte)).join(""); - return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, ""); -} - function base64UrlDecode(value: string): ArrayBuffer | null { if (!/^[A-Za-z0-9_-]+$/u.test(value)) return null; const padded = value.replaceAll("-", "+").replaceAll("_", "/").padEnd( @@ -149,8 +145,10 @@ async function signPayload( issuedAtMs: number, payload: string, ): Promise { - return base64UrlEncode( - await crypto.subtle.sign("HMAC", key, signatureInput(domain, issuedAtMs, payload)), + return base64urlEncodeBytes( + new Uint8Array( + await crypto.subtle.sign("HMAC", key, signatureInput(domain, issuedAtMs, payload)), + ), ); } diff --git a/src/proxy/server-resolver.ts b/src/proxy/server-resolver.ts index 96b8fc3c05..da9f4164bc 100644 --- a/src/proxy/server-resolver.ts +++ b/src/proxy/server-resolver.ts @@ -10,6 +10,7 @@ import { proxyLogger } from "./logger.ts"; import { unrefTimer } from "#veryfront/platform/compat/process.ts"; +import { encodeBase64 } from "#veryfront/utils"; const logger = proxyLogger.child({ module: "server-resolver" }); @@ -112,7 +113,7 @@ export class ServerResolver { const headers: Record = { Accept: "application/json" }; if (this.apiUser && this.apiPass) { - headers.Authorization = `Basic ${btoa(`${this.apiUser}:${this.apiPass}`)}`; + headers.Authorization = `Basic ${encodeBase64(`${this.apiUser}:${this.apiPass}`)}`; } let response: Response; diff --git a/src/release-assets/build-executor.ts b/src/release-assets/build-executor.ts index a639b47942..a5e1f1b6bf 100644 --- a/src/release-assets/build-executor.ts +++ b/src/release-assets/build-executor.ts @@ -52,7 +52,7 @@ import { collectCssImportPaths, CSS_IMPORTING_SOURCE_EXTENSIONS, } from "#veryfront/html/styles-builder/css-import-extraction.ts"; -import { sha256HexBytes } from "./hash.ts"; +import { computeHashBytes } from "#veryfront/utils"; import { RELEASE_ASSET_BASE_PATH, RELEASE_ASSET_CONTENT_TYPES, @@ -1163,7 +1163,7 @@ async function addPreparedJavaScriptAsset( const bytes = new TextEncoder().encode(code) as Uint8Array; if (bytes.byteLength > RELEASE_ASSET_MAX_SIZE_BYTES) return null; - const contentHash = await sha256HexBytes(bytes); + const contentHash = await computeHashBytes(bytes); const entry: PreparedAsset = { logicalPath, contentHash, @@ -1712,7 +1712,7 @@ async function runBuildInner( }); if (compiled && compiled.css) { const bytes = new TextEncoder().encode(compiled.css) as Uint8Array; - const contentHash = await sha256HexBytes(bytes); + const contentHash = await computeHashBytes(bytes); css.push({ contentHash, size: bytes.byteLength, @@ -1796,7 +1796,7 @@ async function runBuildInner( } // 6. Assemble and PUT the manifest. - const sourceContentHash = await sha256HexBytes( + const sourceContentHash = await computeHashBytes( new TextEncoder().encode([...sourceByPath.keys()].sort().join("\n")) as Uint8Array, ); const manifest: ReleaseAssetManifest = { @@ -1965,7 +1965,7 @@ async function releaseFileSetSignature(sourceByPath: Map): Promi .sort(([left], [right]) => left.localeCompare(right)) .map(([path, content]) => `${path}${separator}${content}`) .join(separator); - return await sha256HexBytes(new TextEncoder().encode(serialized) as Uint8Array); + return await computeHashBytes(new TextEncoder().encode(serialized) as Uint8Array); } async function resolveReleaseConfigFromSourceFiles( diff --git a/src/release-assets/hash.ts b/src/release-assets/hash.ts deleted file mode 100644 index b44b3a3b57..0000000000 --- a/src/release-assets/hash.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Release Asset Manifest — SHA-256 helper. - * - * Produces lowercase hex SHA-256 digests of raw asset bytes using the Web - * Crypto API (`crypto.subtle`), matching the content-addressing contract. - * - * @module release-assets/hash - */ - -function toHex(buffer: ArrayBuffer): string { - return Array.from(new Uint8Array(buffer), (b) => b.toString(16).padStart(2, "0")).join(""); -} - -/** Compute the lowercase hex SHA-256 digest of raw bytes. */ -export async function sha256HexBytes(bytes: BufferSource): Promise { - const digest = await crypto.subtle.digest("SHA-256", bytes); - return toHex(digest); -} - -/** Compute the lowercase hex SHA-256 digest of a UTF-8 string. */ -export async function sha256Hex(input: string): Promise { - const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input)); - return toHex(digest); -} diff --git a/src/release-assets/index.ts b/src/release-assets/index.ts index 60260859aa..48491e3b1a 100644 --- a/src/release-assets/index.ts +++ b/src/release-assets/index.ts @@ -20,7 +20,6 @@ export { type ReleaseAssetExtension, releaseAssetUrl, } from "./constants.ts"; -export { sha256Hex, sha256HexBytes } from "./hash.ts"; export { getReleaseAssetManifestSchema, parseReleaseAssetManifest, diff --git a/src/rendering/orchestrator/module-loader/cache.ts b/src/rendering/orchestrator/module-loader/cache.ts index cab69f50eb..ff595b93a4 100644 --- a/src/rendering/orchestrator/module-loader/cache.ts +++ b/src/rendering/orchestrator/module-loader/cache.ts @@ -9,19 +9,8 @@ */ export { createEsmCache, createModuleCache } from "#veryfront/cache/module-cache.ts"; - -const HEX_CHARS = "0123456789abcdef"; +import { computeHash } from "#veryfront/utils"; export async function generateHash(str: string): Promise { - const data = new TextEncoder().encode(str); - const hashBuffer = await crypto.subtle.digest("SHA-256", data); - const bytes = new Uint8Array(hashBuffer); - - let hex = ""; - for (let i = 0; i < 8; i++) { - const byte = bytes[i] ?? 0; - hex += (HEX_CHARS[byte >> 4] ?? "0") + (HEX_CHARS[byte & 0xf] ?? "0"); - } - - return hex; + return (await computeHash(str)).slice(0, 16); } diff --git a/src/routing/api/module-loader/esbuild-plugin.ts b/src/routing/api/module-loader/esbuild-plugin.ts index 8ad53cce39..0508024e87 100644 --- a/src/routing/api/module-loader/esbuild-plugin.ts +++ b/src/routing/api/module-loader/esbuild-plugin.ts @@ -6,6 +6,7 @@ import { HTTP_NETWORK_CONNECT_TIMEOUT, type LockfileManager, serverLogger, + sleep, } from "#veryfront/utils"; import { createFileSystem, type FileSystem } from "#veryfront/platform/compat/fs.ts"; import * as pathHelper from "#veryfront/compat/path"; @@ -165,10 +166,6 @@ export function createHTTPPlugin(options: HTTPPluginOptions | string[]): Plugin return status === 429 || status >= 500 || status === HTTP_NETWORK_CONNECT_TIMEOUT; } - function delay(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - function describePersistenceError(error: unknown): string { if (!(error instanceof Error)) return typeof error; @@ -228,7 +225,7 @@ export function createHTTPPlugin(options: HTTPPluginOptions | string[]): Plugin logger.warn( `[http] fetch attempt ${attempt} failed ${url} ${response.status}; retrying`, ); - await delay(HTTP_MODULE_FETCH_RETRY_DELAY_MS * attempt); + await sleep(HTTP_MODULE_FETCH_RETRY_DELAY_MS * attempt); } return new Response("Remote module fetch failed", { diff --git a/src/sandbox/lazy-sandbox.ts b/src/sandbox/lazy-sandbox.ts index 7aa1ec7630..23f16c750d 100644 --- a/src/sandbox/lazy-sandbox.ts +++ b/src/sandbox/lazy-sandbox.ts @@ -1,6 +1,6 @@ import { REQUEST_ERROR } from "#veryfront/errors"; import { getHostEnv } from "#veryfront/platform/compat/process.ts"; -import { logger } from "#veryfront/utils"; +import { logger, sleep } from "#veryfront/utils"; import { resolveSandboxApiUrl, resolveSandboxAuthToken } from "./config.ts"; import { readSandboxFileContent, sandboxSessionRoute } from "./proxy-routes.ts"; import { @@ -607,7 +607,7 @@ export class LazySandbox { const start = Date.now(); while (Date.now() - start < this.startupTimeoutMs) { - await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs)); + await sleep(this.pollIntervalMs); const res = await this.fetchControl( `${this.apiUrl}/sandbox-sessions/${encodeURIComponent(sessionId)}`, @@ -659,7 +659,7 @@ export class LazySandbox { lastFailure = error instanceof Error ? error.message : String(error); } - await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs)); + await sleep(this.pollIntervalMs); } throw REQUEST_ERROR.create({ @@ -838,7 +838,7 @@ export class LazySandbox { } private waitForExecStartRetry(): Promise { - return new Promise((resolve) => setTimeout(resolve, this.execStartRetryDelayMs)); + return sleep(this.execStartRetryDelayMs); } private async reprovisionAfterExecStartFailure(): Promise { diff --git a/src/security/http/auth.ts b/src/security/http/auth.ts index 134923adf1..de06480dfe 100644 --- a/src/security/http/auth.ts +++ b/src/security/http/auth.ts @@ -1,5 +1,4 @@ import { BaseHandler } from "./base-handler.ts"; -import { createError, toError } from "#veryfront/errors"; import type { HandlerContext, HandlerMetadata, @@ -7,35 +6,10 @@ import type { HandlerResult, } from "#veryfront/types"; import type { AuthConfig } from "./middleware/types.ts"; -import { Buffer } from "node:buffer"; +import { encodeBase64 } from "#veryfront/utils"; import { constantTimeEqual } from "../utils/constant-time.ts"; import { isProduction } from "#veryfront/platform/environment.ts"; -function encodeBase64(value: string): string { - if (typeof globalThis.btoa === "function") { - try { - return globalThis.btoa(value); - } catch (_) { - /* expected: non-Latin1 string — fall back to TextEncoder */ - const bytes = new TextEncoder().encode(value); - let binary = ""; - for (const byte of bytes) binary += String.fromCharCode(byte); - return globalThis.btoa(binary); - } - } - - const bufferCtor = (globalThis as { Buffer?: typeof Buffer }).Buffer; - if (bufferCtor) return bufferCtor.from(value, "utf8").toString("base64"); - - throw toError( - createError({ - type: "not_supported", - message: "Base64 encoding is not supported in this runtime", - feature: "Base64 encoding", - }), - ); -} - function sanitizeRealm(realm: unknown): string { // deno-lint-ignore no-control-regex -- intentional: strips control chars and special chars from HTTP realm header return String(realm).replace(/[\x00-\x1f\x7f"\\]/g, ""); diff --git a/src/server/handlers/studio/bridge-modules.handler.ts b/src/server/handlers/studio/bridge-modules.handler.ts index 169bee14fa..ff19e92d9c 100644 --- a/src/server/handlers/studio/bridge-modules.handler.ts +++ b/src/server/handlers/studio/bridge-modules.handler.ts @@ -18,7 +18,7 @@ import type { } from "../../handlers/types.ts"; import { HTTP_OK, PRIORITY_HIGH_DEV } from "#veryfront/utils/constants/index.ts"; import { STUDIO_BRIDGE_BUNDLE } from "#veryfront/studio/bridge/bridge-bundle.generated.ts"; -import { serverLogger } from "#veryfront/utils"; +import { computeHash, serverLogger } from "#veryfront/utils"; const logger = serverLogger.component("studio-bridge-handler"); @@ -32,10 +32,7 @@ const BRIDGE_DIR = new URL("../../../studio/bridge/", import.meta.url).pathname; * Compute a content hash for ETag. */ async function computeEtag(content: string): Promise { - const data = new TextEncoder().encode(content); - const hashBuffer = await crypto.subtle.digest("SHA-256", data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - return hashArray.slice(0, 16).map((b) => b.toString(16).padStart(2, "0")).join(""); + return (await computeHash(content)).slice(0, 32); } /** diff --git a/src/server/project-env/fetcher.ts b/src/server/project-env/fetcher.ts index 58c23b2805..82ed1090ab 100644 --- a/src/server/project-env/fetcher.ts +++ b/src/server/project-env/fetcher.ts @@ -4,7 +4,7 @@ * @module server/project-env/fetcher */ -import { getBaseLogger } from "#veryfront/utils"; +import { encodeBase64, getBaseLogger } from "#veryfront/utils"; import { NETWORK_ERROR } from "#veryfront/errors"; import { getHostEnv } from "#veryfront/platform/compat/process.ts"; @@ -24,7 +24,7 @@ function getInternalAuthorization(): string | undefined { const username = getHostEnv("VERYFRONT_API_INTERNAL_USER"); const password = getHostEnv("VERYFRONT_API_INTERNAL_PASS"); if (!username || !password) return undefined; - return `Basic ${globalThis.btoa(`${username}:${password}`)}`; + return `Basic ${encodeBase64(`${username}:${password}`)}`; } async function fetchEnvironmentVariables( diff --git a/src/server/services/rsc/endpoints/rsc-bundles.generated.ts b/src/server/services/rsc/endpoints/rsc-bundles.generated.ts index f68a9d060b..95c9fabc81 100644 --- a/src/server/services/rsc/endpoints/rsc-bundles.generated.ts +++ b/src/server/services/rsc/endpoints/rsc-bundles.generated.ts @@ -7,7 +7,7 @@ */ export const CLIENT_BOOT_BUNDLE: string = - 'var It=Object.defineProperty;var St=(t,e,r)=>e in t?It(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var p=(t,e,r)=>St(t,typeof e!="symbol"?e+"":e,r);var Ot="3.2.3";function Nt(t,e,r,n){let s=[];if(n?.external?.length&&s.push(`external=${n.external.join(",")}`),s.push(`target=${n?.target??"es2022"}`),n?.deps){let a=Object.entries(n.deps).map(([d,f])=>`${d}@${f}`).join(",");s.push(`deps=${a}`)}let u=e?`@${e}`:"",c=r??"",g=s.length?`?${s.join("&")}`:"";return`https://esm.sh/${t}${u}${c}${g}`}function m(t,e,r,n=!1){return Nt(t,e,r,{external:n?["react"]:void 0,deps:{csstype:Ot}})}var Dt="19.2.4",_=Dt;function z(t=_){return{react:m("react",t),"react-dom":m("react-dom",t,void 0,!0),"react-dom/client":m("react-dom",t,"/client",!0),"react-dom/server":m("react-dom",t,"/server",!0),"react/jsx-runtime":m("react",t,"/jsx-runtime",!0),"react/jsx-dev-runtime":m("react",t,"/jsx-dev-runtime",!0)}}function J(t=_){return z(t).react}function X(t=_){return z(t)["react-dom/client"]}function vt(t){return t.replaceAll("+","-").replaceAll("/","_").replaceAll("=","")}function H(t){return vt(btoa(t))}var no=Object.freeze({react:"","react-dom":"","react-dom/client":"","react-dom/server":"","react/jsx-runtime":"","react/jsx-dev-runtime":""});function O(t,e){if(!e)return!1;if(Object.prototype.hasOwnProperty.call(e,t))return!0;for(let r of Object.keys(e))if(r.endsWith("/")&&t.startsWith(r))return!0;return!1}function bt(t){try{return JSON.parse(t)?.imports??{}}catch(e){return console.warn("Failed to parse import map JSON; treating as empty",{errorName:e instanceof Error?e.name:typeof e,inputLength:t.length}),{}}}function V(t=document){let e=t.querySelector(\'script[type="importmap"]\');return e?.textContent?bt(e.textContent):{}}var l="/_veryfront",F={RSC:`${l}/rsc/`,FS:`${l}/fs/`,MODULES:`${l}/modules/`,PAGES:`${l}/pages/`,DATA:`${l}/data/`,LIB:`${l}/lib/`,CHUNKS:`${l}/chunks/`,CLIENT:`${l}/client/`},K={HMR_RUNTIME:`${l}/hmr-runtime.js`,HMR:`${l}/hmr.js`,ERROR_OVERLAY:`${l}/error-overlay.js`,DEV_LOADER:`${l}/dev-loader.js`,CLIENT_LOG:`${l}/log`,CLIENT_JS:`${l}/client.js`,ROUTER_JS:`${l}/router.js`,PREFETCH_JS:`${l}/prefetch.js`,MANIFEST_JSON:`${l}/manifest.json`,APP_JS:`${l}/app.js`,RSC_CLIENT:`${l}/rsc/client.js`,RSC_MANIFEST:`${l}/rsc/manifest`,RSC_STREAM:`${l}/rsc/stream`,RSC_PAYLOAD:`${l}/rsc/payload`,RSC_RENDER:`${l}/rsc/render`,RSC_PAGE:`${l}/rsc/page`,RSC_MODULE:`${l}/rsc/module`,RSC_DOM:`${l}/rsc/dom.js`,LIB_CHAT_REACT:`${l}/lib/chat/react.js`,LIB_CHAT_COMPONENTS:`${l}/lib/chat/components.js`,LIB_CHAT_PRIMITIVES:`${l}/lib/chat/primitives.js`};var Lt={ROOT:".veryfront",CACHE:".veryfront/cache",KV:".veryfront/kv",LOGS:".veryfront/logs",TMP:".veryfront/tmp"},lo=Lt.CACHE;var uo={HMR_RUNTIME:K.HMR_RUNTIME,ERROR_OVERLAY:K.ERROR_OVERLAY};var h=F.RSC,Z=F.FS,Q="veryfront-hydration-data",C="rsc-root";var R=class{constructor(e,r){p(this,"prefix",e);p(this,"level",r)}log(e,r,n,...s){this.level>e||r?.(n,...s)}debug(e,...r){this.log(0,console.debug,`[${this.prefix}] DEBUG: ${e}`,...r)}info(e,...r){this.log(1,console.log,`[${this.prefix}] ${e}`,...r)}warn(e,...r){this.log(2,console.warn,`[${this.prefix}] WARN: ${e}`,...r)}error(e,...r){this.log(3,console.error,`[${this.prefix}] ERROR: ${e}`,...r)}};function Ut(){if(typeof window>"u")return 2;let t=globalThis;return t.__VERYFRONT_DEV__||t.__RSC_DEV__?t.__VERYFRONT_DEBUG__||t.__RSC_DEBUG__?0:1:2}var N=Ut(),i=new R("RSC",N),Ro=new R("PREFETCH",N),Eo=new R("HYDRATE",N),yo=new R("VERYFRONT",N);function E(t=document){try{let e=t.getElementById(Q);return e?JSON.parse(e.textContent||"{}"):null}catch(e){return i.debug("hydration data parse failed",e),null}}function A(t){return t?.clientModuleStrategy?t.clientModuleStrategy:t?.dev?"fs":"rsc-module"}function wt(t,e){if(!e)return t;let r=t.includes("?")?"&":"?";return`${t}${r}v=${encodeURIComponent(e)}`}function Pt(t,e){return wt(`${Z}${H(t)}.js`,e)}function Ht(t,e){let r=e?`&v=${encodeURIComponent(e)}`:"";return`${h}module?rel=${encodeURIComponent(t)}${r}`}function D(t){if(t.strategy==="fs"){let e=t.absPath??t.rel;return e?Pt(e,t.version):null}return Ht(t.rel,t.version)}function v(t=document,e=_){let r=V(t);return{react:O("react",r)?"react":J(e),reactDomClient:O("react-dom/client",r)?"react-dom/client":X(e)}}function tt(t=document){let e=V(t);return O("veryfront/router",e)?"veryfront/router":null}function o(t){return{...t,create(e){return new k(e?.message||e?.detail||t.title,{slug:t.slug,category:t.category,status:e?.status??t.status,title:t.title,suggestion:t.suggestion,detail:e?.detail,cause:e?.cause,instance:e?.instance,context:e?.context})}}}var k=class extends Error{constructor(r,n){super(r);p(this,"slug");p(this,"category");p(this,"status");p(this,"title");p(this,"suggestion");p(this,"detail");p(this,"cause");p(this,"instance");p(this,"context");this.name="VeryfrontError",this.slug=n.slug,this.category=n.category,this.status=n.status,this.title=n.title,this.suggestion=n.suggestion,this.detail=n.detail,this.cause=n.cause,this.instance=n.instance,this.context=n.context}toRFC9457(){return{type:`https://veryfront.com/docs/errors/${this.slug}`,title:this.title,status:this.status,detail:this.detail,instance:this.instance,category:this.category,suggestion:this.suggestion,cause:typeof this.cause=="string"?this.cause:void 0}}getDocsUrl(){return`https://veryfront.com/docs/errors/${this.slug}`}};var Vt=o({slug:"config-not-found",category:"CONFIG",status:404,title:"Configuration file not found",suggestion:"Run \'vf init\' to create a configuration file"}),Ft=o({slug:"config-invalid",category:"CONFIG",status:400,title:"Invalid configuration format",suggestion:"Check your veryfront.config.ts for syntax errors"}),kt=o({slug:"config-parse-error",category:"CONFIG",status:400,title:"Failed to parse configuration",suggestion:"Ensure your configuration file is valid TypeScript/JSON"}),Gt=o({slug:"config-validation-error",category:"CONFIG",status:422,title:"Configuration validation failed",suggestion:"Check the configuration against the schema requirements"}),$t=o({slug:"config-type-error",category:"CONFIG",status:400,title:"Configuration type mismatch",suggestion:"Ensure configuration values match expected types"}),Bt=o({slug:"import-map-invalid",category:"CONFIG",status:400,title:"Invalid import map configuration",suggestion:"Check your import map syntax and paths"}),Yt=o({slug:"cors-config-invalid",category:"CONFIG",status:400,title:"Invalid CORS configuration",suggestion:"Review CORS settings in your configuration"}),jt=o({slug:"config-validation-failed",category:"CONFIG",status:400,title:"Configuration validation failed",suggestion:"Check configuration values against requirements"}),Wt=o({slug:"webhook-config-invalid",category:"CONFIG",status:400,title:"Invalid webhook configuration",suggestion:"Check webhook definition fields, target settings, and eventFilter conditions"}),qt=o({slug:"schedule-config-invalid",category:"CONFIG",status:400,title:"Invalid schedule configuration",suggestion:"Check schedule definition fields, cron expression, target settings, and positive-integer limits"}),zt=o({slug:"trigger-config-invalid",category:"CONFIG",status:400,title:"Invalid trigger configuration",suggestion:"Check trigger ID format (lowercase, alphanumeric, dots/slashes/hyphens) and ensure all input values are JSON-serializable"}),et={"config-not-found":Vt,"config-invalid":Ft,"config-parse-error":kt,"config-validation-error":Gt,"config-type-error":$t,"import-map-invalid":Bt,"cors-config-invalid":Yt,"config-validation-failed":jt,"webhook-config-invalid":Wt,"schedule-config-invalid":qt,"trigger-config-invalid":zt};var Jt=o({slug:"build-failed",category:"BUILD",status:500,title:"Build process failed",suggestion:"Check the build output for specific errors"}),Xt=o({slug:"bundle-error",category:"BUILD",status:500,title:"Bundle generation failed",suggestion:"Review bundler output for details"}),Kt=o({slug:"typescript-error",category:"BUILD",status:500,title:"TypeScript compilation error",suggestion:"Fix TypeScript errors shown in the output"}),Zt=o({slug:"mdx-compile-error",category:"BUILD",status:500,title:"MDX compilation failed",suggestion:"Check your MDX file syntax"}),Qt=o({slug:"asset-optimization-error",category:"BUILD",status:500,title:"Asset optimization failed",suggestion:"Check asset file formats and paths"}),te=o({slug:"ssg-generation-error",category:"BUILD",status:500,title:"Static site generation failed",suggestion:"Review SSG configuration and data fetching"}),ee=o({slug:"sourcemap-error",category:"BUILD",status:500,title:"Source map generation failed",suggestion:"Check source map configuration"}),re=o({slug:"compilation-error",category:"BUILD",status:500,title:"Compilation failed",suggestion:"Review compiler output for specific errors"}),rt={"build-failed":Jt,"bundle-error":Xt,"typescript-error":Kt,"mdx-compile-error":Zt,"asset-optimization-error":Qt,"ssg-generation-error":te,"sourcemap-error":ee,"compilation-error":re};var oe=o({slug:"hydration-mismatch",category:"RUNTIME",status:500,title:"Client/server hydration mismatch",suggestion:"Ensure server and client render the same content"}),ne=o({slug:"render-error",category:"RUNTIME",status:500,title:"Component render failed",suggestion:"Check component for runtime errors"}),se=o({slug:"component-error",category:"RUNTIME",status:500,title:"Component execution error",suggestion:"Review component logic and props"}),ie=o({slug:"layout-not-found",category:"RUNTIME",status:404,title:"Layout component not found",suggestion:"Ensure layout file exists at the expected path"}),ae=o({slug:"page-not-found",category:"RUNTIME",status:404,title:"Page component not found",suggestion:"Check that the page file exists in the routes directory"}),ce=o({slug:"api-error",category:"RUNTIME",status:500,title:"API route handler error",suggestion:"Review API route handler for errors"}),le=o({slug:"middleware-error",category:"RUNTIME",status:500,title:"Middleware execution error",suggestion:"Check middleware function for errors"}),ue=o({slug:"trigger-target-not-found",category:"RUNTIME",status:404,title:"Trigger target not found",suggestion:"Ensure the referenced task or workflow ID is registered in the project"}),de=o({slug:"trigger-execution-failed",category:"RUNTIME",status:500,title:"Trigger target execution failed",suggestion:"Check the task or workflow for errors and review the trigger input"}),ge=o({slug:"trigger-not-supported",category:"RUNTIME",status:501,title:"Trigger target type not supported in local runtime",suggestion:"Use a workflow or task target for local trigger runs; agent targets require the Cloud runtime"}),ot={"hydration-mismatch":oe,"render-error":ne,"component-error":se,"layout-not-found":ie,"page-not-found":ae,"api-error":ce,"middleware-error":le,"trigger-target-not-found":ue,"trigger-execution-failed":de,"trigger-not-supported":ge};var pe=o({slug:"route-conflict",category:"ROUTE",status:409,title:"Conflicting route definitions",suggestion:"Rename or reorganize conflicting route files"}),fe=o({slug:"invalid-route-file",category:"ROUTE",status:400,title:"Invalid route file structure",suggestion:"Ensure route file exports required functions"}),me=o({slug:"route-handler-invalid",category:"ROUTE",status:400,title:"Invalid route handler export",suggestion:"Export a valid handler function from the route file"}),Re=o({slug:"dynamic-route-error",category:"ROUTE",status:500,title:"Dynamic route parsing failed",suggestion:"Check dynamic route segment syntax"}),Ee=o({slug:"route-params-error",category:"ROUTE",status:400,title:"Route parameters invalid",suggestion:"Validate route parameter values"}),ye=o({slug:"api-route-error",category:"ROUTE",status:500,title:"API route definition error",suggestion:"Review API route configuration"}),nt={"route-conflict":pe,"invalid-route-file":fe,"route-handler-invalid":me,"dynamic-route-error":Re,"route-params-error":Ee,"api-route-error":ye};var _e=o({slug:"module-not-found",category:"MODULE",status:404,title:"Module could not be resolved",suggestion:"Check the import path and ensure the module is installed"}),he=o({slug:"import-resolution-error",category:"MODULE",status:500,title:"Import path resolution failed",suggestion:"Verify import paths and module configuration"}),Ce=o({slug:"circular-dependency",category:"MODULE",status:500,title:"Circular dependency detected",suggestion:"Refactor imports to break the circular dependency"}),Te=o({slug:"invalid-import",category:"MODULE",status:400,title:"Invalid import statement",suggestion:"Fix import syntax or path"}),xe=o({slug:"dependency-missing",category:"MODULE",status:404,title:"Required dependency not installed",suggestion:"Install the missing dependency with your package manager"}),Ie=o({slug:"version-mismatch",category:"MODULE",status:409,title:"Dependency version mismatch",suggestion:"Update dependencies to compatible versions"}),st={"module-not-found":_e,"import-resolution-error":he,"circular-dependency":Ce,"invalid-import":Te,"dependency-missing":xe,"version-mismatch":Ie};var Se=o({slug:"port-in-use",category:"SERVER",status:409,title:"Server port already in use",suggestion:"Use a different port or stop the process using this port"}),Oe=o({slug:"server-start-error",category:"SERVER",status:500,title:"Server failed to start",suggestion:"Check server configuration and port availability"}),Ne=o({slug:"cache-error",category:"SERVER",status:500,title:"Cache operation failed",suggestion:"Clear the cache and try again"}),Ae=o({slug:"file-watch-error",category:"SERVER",status:500,title:"File watcher error",suggestion:"Restart the development server"}),De=o({slug:"request-error",category:"SERVER",status:500,title:"HTTP request handling error",suggestion:"Check request handler and middleware"}),ve=o({slug:"service-overloaded",category:"SERVER",status:503,title:"Service overloaded",suggestion:"Reduce load or scale up resources"}),be=o({slug:"semaphore-timeout",category:"SERVER",status:503,title:"Semaphore acquire timeout",suggestion:"Reduce concurrency or increase the semaphore acquire timeout"}),Me=o({slug:"circuit-breaker-open",category:"SERVER",status:503,title:"Circuit breaker is open",suggestion:"Wait for the breaker reset timeout before retrying"}),Le=o({slug:"cache-path-mismatch",category:"SERVER",status:500,title:"Cache path mismatch",suggestion:"Clear the cache directory and rebuild"}),Ue=o({slug:"network-error",category:"SERVER",status:502,title:"Network operation failed",suggestion:"Check network connectivity and retry"}),we=o({slug:"api-client-error",category:"SERVER",status:500,title:"API client request failed",suggestion:"Check API connectivity and authentication"}),Pe=o({slug:"token-storage-error",category:"SERVER",status:500,title:"Token storage operation failed",suggestion:"Check token storage backend and credentials"}),He=o({slug:"cache-invariant-violation",category:"SERVER",status:500,title:"Cache path invariant violated",suggestion:"Clear the cache and rebuild"}),Ve=o({slug:"release-not-found",category:"SERVER",status:404,title:"No active release found",suggestion:"Deploy the project to create a release for this environment"}),Fe=o({slug:"fallback-exhausted",category:"SERVER",status:500,title:"Primary and fallback operations both failed",suggestion:"Check service availability and connectivity"}),it={"port-in-use":Se,"server-start-error":Oe,"cache-error":Ne,"file-watch-error":Ae,"request-error":De,"service-overloaded":ve,"semaphore-timeout":be,"circuit-breaker-open":Me,"cache-path-mismatch":Le,"network-error":Ue,"api-client-error":we,"token-storage-error":Pe,"cache-invariant-violation":He,"release-not-found":Ve,"fallback-exhausted":Fe};var ke=o({slug:"client-boundary-violation",category:"BOUNDARY",status:400,title:"Client boundary rule violation",suggestion:"Add \'use client\' directive or move code to a client component"}),Ge=o({slug:"server-only-in-client",category:"BOUNDARY",status:400,title:"Server-only code in client component",suggestion:"Move server-only code to a server component"}),$e=o({slug:"client-only-in-server",category:"BOUNDARY",status:400,title:"Client-only code in server component",suggestion:"Move client-only code to a client component"}),Be=o({slug:"invalid-use-client",category:"BOUNDARY",status:400,title:"Invalid \'use client\' directive",suggestion:"Place \'use client\' at the top of the file"}),Ye=o({slug:"invalid-use-server",category:"BOUNDARY",status:400,title:"Invalid \'use server\' directive",suggestion:"Place \'use server\' at the top of the file or function"}),je=o({slug:"rsc-payload-error",category:"BOUNDARY",status:500,title:"RSC payload serialization error",suggestion:"Ensure props are serializable (no functions, symbols, etc.)"}),at={"client-boundary-violation":ke,"server-only-in-client":Ge,"client-only-in-server":$e,"invalid-use-client":Be,"invalid-use-server":Ye,"rsc-payload-error":je};var We=o({slug:"hmr-error",category:"DEV",status:500,title:"Hot module replacement error",suggestion:"Restart the development server"}),qe=o({slug:"dev-server-error",category:"DEV",status:500,title:"Development server error",suggestion:"Check the dev server logs and restart"}),ze=o({slug:"fast-refresh-error",category:"DEV",status:500,title:"Fast refresh failed",suggestion:"Save the file again or restart the dev server"}),Je=o({slug:"error-overlay-error",category:"DEV",status:500,title:"Error overlay failed",suggestion:"Check browser console for details"}),Xe=o({slug:"source-map-error",category:"DEV",status:500,title:"Source map loading error",suggestion:"Rebuild or clear cache"}),ct={"hmr-error":We,"dev-server-error":qe,"fast-refresh-error":ze,"error-overlay-error":Je,"source-map-error":Xe};var Ke=o({slug:"deployment-error",category:"DEPLOY",status:500,title:"Deployment process failed",suggestion:"Check deployment logs for details"}),Ze=o({slug:"platform-error",category:"DEPLOY",status:500,title:"Platform-specific error",suggestion:"Check platform documentation and requirements"}),Qe=o({slug:"env-var-missing",category:"DEPLOY",status:500,title:"Required environment variable missing",suggestion:"Set the required environment variable"}),tr=o({slug:"production-build-required",category:"DEPLOY",status:400,title:"Production build required",suggestion:"Run \'vf build\' before deploying"}),lt={"deployment-error":Ke,"platform-error":Ze,"env-var-missing":Qe,"production-build-required":tr};var er=o({slug:"agent-error",category:"AGENT",status:500,title:"Agent operation error",suggestion:"Check agent configuration and logs"}),rr=o({slug:"agent-not-found",category:"AGENT",status:404,title:"Agent not found",suggestion:"Verify the agent ID exists"}),or=o({slug:"agent-timeout",category:"AGENT",status:408,title:"Agent operation timed out",suggestion:"Increase timeout or simplify the request"}),nr=o({slug:"agent-intent-error",category:"AGENT",status:400,title:"Agent intent parsing error",suggestion:"Rephrase the request more clearly"}),sr=o({slug:"orchestration-error",category:"AGENT",status:500,title:"Multi-agent orchestration error",suggestion:"Check agent coordination logic"}),ir=o({slug:"cost-limit-exceeded",category:"AGENT",status:429,title:"Cost limit exceeded",suggestion:"Wait for the budget period to reset or increase the limit"}),ar=o({slug:"tool-id-conflict",category:"AGENT",status:409,title:"Tool ID conflict",suggestion:"Use a unique tool ID or rename one of the conflicting tools"}),ut={"agent-error":er,"agent-not-found":rr,"agent-timeout":or,"agent-intent-error":nr,"orchestration-error":sr,"cost-limit-exceeded":ir,"tool-id-conflict":ar};var cr=o({slug:"unknown-error",category:"GENERAL",status:500,title:"Unknown/unclassified error",suggestion:"Check logs for more details"}),lr=o({slug:"permission-denied",category:"GENERAL",status:403,title:"File/resource permission denied",suggestion:"Check file permissions and access rights"}),ur=o({slug:"file-not-found",category:"GENERAL",status:404,title:"File not found",suggestion:"Verify the file path exists"}),dr=o({slug:"resource-not-found",category:"GENERAL",status:404,title:"Requested resource not found",suggestion:"Verify the referenced resource ID or name exists"}),gr=o({slug:"invalid-argument",category:"GENERAL",status:400,title:"Invalid function argument",suggestion:"Check argument types and values"}),pr=o({slug:"timeout-error",category:"GENERAL",status:408,title:"Operation timed out",suggestion:"Increase timeout or optimize the operation"}),fr=o({slug:"initialization-error",category:"GENERAL",status:500,title:"Initialization failed",suggestion:"Check initialization requirements and dependencies"}),mr=o({slug:"not-supported",category:"GENERAL",status:501,title:"Feature not supported",suggestion:"Check documentation for supported features"}),G=o({slug:"security-violation",category:"GENERAL",status:403,title:"Security violation detected",suggestion:"Check for path traversal or unauthorized access attempts"}),Rr=o({slug:"input-validation-failed",category:"GENERAL",status:400,title:"Input validation failed",suggestion:"Check request input against validation rules"}),dt={"unknown-error":cr,"permission-denied":lr,"file-not-found":ur,"resource-not-found":dr,"invalid-argument":gr,"timeout-error":pr,"initialization-error":fr,"not-supported":mr,"security-violation":G,"input-validation-failed":Rr};var mn={...et,...rt,...ot,...nt,...st,...it,...at,...ct,...lt,...ut,...dt};var Er=[{source:String.raw`]*>[\\s\\S]*?<\\/script>`,flags:"gi",name:"inline script"},{source:String.raw`javascript:`,flags:"gi",name:"javascript: URL"},{source:String.raw`\\bon\\w+\\s*=`,flags:"gi",name:"event handler attribute"},{source:String.raw`data:\\s*text\\/html`,flags:"gi",name:"data: HTML URL"}];function yr(){return Er.map(({source:t,flags:e,name:r})=>({pattern:new RegExp(t,e),name:r}))}function _r(){let t=globalThis;return t.__VERYFRONT_DEV__===!0||t.Deno?.env?.get?.("VERYFRONT_ENV")==="development"}function T(t,e={}){let{allowInlineScripts:r=!1,strict:n=!1,warn:s=!0}=e;for(let{pattern:u,name:c}of yr())if(!(r&&c==="inline script")&&(u.lastIndex=0,!!u.test(t)&&(s&&console.warn(`[Security] Suspicious ${c} detected in server HTML`),n||!_r())))throw G.create({detail:`Potentially unsafe HTML: ${c} detected`});return t}function x(t,e){let r=e==="root"?C:`rsc-slot-${e}`,n=t.getElementById(r);if(n)return n;let s=t.createElement("div");return s.id=r,t.body.appendChild(s),s}function hr(t,e){if(e.type!=="slot")return;let r=x(t,e.id);r.innerHTML=T(String(e.html??""))}function gt(t,e){let r=e.split(`\n`),n=r.pop()??"";for(let s of r){let u=s.trim();if(!u)continue;let c;try{c=JSON.parse(u)}catch(a){i.debug("[client-dom] malformed NDJSON line",{line:u,error:a instanceof Error?a.message:String(a)});continue}if(!c||typeof c!="object")continue;let g=c;if(g.type==="slot"){hr(t,g);try{xr(t,g.id||"root")}catch(a){i.debug("[client-dom] hydration optional failed",a)}}}return n}function Cr(t){return new Promise((e,r)=>{let n=()=>r(new DOMException("aborted","AbortError"));if(t.aborted){n();return}t.addEventListener("abort",n,{once:!0})})}async function pt(t,e=document,r){let n="body"in t?t:null,s=n?.body??t;if(!s)return;let u=s.getReader(),c=new TextDecoder,g="",a=!1;try{for(;;){if(r?.aborted)throw new DOMException("aborted","AbortError");let d=u.read(),{done:f,value:y}=r?await Promise.race([d,Cr(r)]):await d;if(f){a=!0;break}g+=c.decode(y,{stream:!0}),g=gt(e,g)}g&>(e,`${g}\n`)}catch(d){throw d instanceof Error&&d.name==="AbortError"||i.debug("[client-dom] consumeNdjsonStream error",d),d}finally{try{await u.cancel()}catch(d){a||i.debug("[client-dom] reader.cancel failed",d)}try{u.releaseLock()}catch(d){i.debug("[client-dom] reader.releaseLock failed",d)}if(typeof s.cancel=="function")try{await s.cancel()}catch(d){i.debug("[client-dom] stream.cancel failed",d)}if(typeof n?.body?.cancel=="function")try{await n.body.cancel()}catch(d){i.debug("[client-dom] response.body.cancel failed",d)}}}function Tr(t,e){let r=x(t,e),n=[],s=u=>{let c=u;c.dataset?.clientRef&&n.push(c);for(let g of u.children)s(g)};return s(r),n}function xr(t,e){let r=Tr(t,e);for(let n of r){let s=n.dataset?.clientRef;s&&(n.dataset.hydrated="true",i.debug("[client-dom] marked for hydration",s))}}var Ir=new Set(["server","client","html","fragment"]);function ft(t){if(!t)return[];try{let e=JSON.parse(t);return Or(e)?e.nodes:[]}catch{return[]}}async function B(t,e,r){return await Promise.all(t.map(n=>Sr(n,e,r)))}async function Sr(t,e,r){if(t.type==="html")return t.text??t.html??"";let n=await B(t.children??[],e,r);if(t.type==="fragment"||t.type==="server"&&!t.component)return e.createElement(e.Fragment,{},...n);if(t.type==="server")return e.createElement(t.component,t.props??{},...n);let s=await r(t.component);return s?e.createElement(s,t.props??{},...n):null}function Or(t){return!$(t)||t.version!==1||!Array.isArray(t.nodes)?!1:t.nodes.every(e=>mt(e,0))}function mt(t,e){return e>100||!$(t)||!Ir.has(t.type)||t.type==="html"&&typeof t.html!="string"&&typeof t.text!="string"||t.type==="client"&&typeof t.component!="string"||t.type==="server"&&t.component!==void 0&&typeof t.component!="string"||t.props!==void 0&&!$(t.props)?!1:t.children===void 0?!0:Array.isArray(t.children)&&t.children.every(r=>mt(r,e+1))}function $(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function Nr(t){if(!t)return{};let e={};for(let[r,n]of Object.entries(t))e[r]=Array.isArray(n)?n.join("/"):n;return e}async function b(t,e,r=document){try{let n=tt(r);if(!n)return t;let u=(await import(n)).wrapForHydration;return typeof u!="function"?t:u(t,{params:Nr(e?.params),frontmatter:e?.frontmatter??{}})}catch(n){return i.debug("router provider wrap failed",n),t}}var Ar=100;function Dr(t,e){if(globalThis.__VF_CLIENT_MOD_CACHE??(globalThis.__VF_CLIENT_MOD_CACHE=new Map),globalThis.__VF_CLIENT_MOD_CACHE.size>=Ar){let r=globalThis.__VF_CLIENT_MOD_CACHE.keys().next().value;r&&globalThis.__VF_CLIENT_MOD_CACHE.delete(r)}globalThis.__VF_CLIENT_MOD_CACHE.set(t,e)}function Rt(t){let e=t.match(/^\\/app\\/(.+)#([\\w$.-]+)$/);if(e)return{rel:`/${e[1]||""}`,exportName:e[2]||"default"};let r=t.match(/^(\\/_veryfront\\/[^#]+)#([\\w$.-]+)$/);return r?{moduleUrl:r[1],exportName:r[2]||"default"}:(i.debug("hydrate: unrecognised client ref format, skipping",{ref:t}),null)}function vr(t){let e=t.dataset?.rscProps;if(!e)return{};try{let r=JSON.parse(e);return r&&typeof r=="object"&&!Array.isArray(r)?r:{}}catch(r){return i.debug("hydrate: invalid client boundary props, using empty props",r),{}}}function br(t){return ft(t.dataset?.rscChildren)}async function Mr(){try{let t=await fetch("/_veryfront/rsc/manifest");return t.ok?await t.json():null}catch{return null}}async function Et(t,e,r){let n=Lr(t,e,r),s=e.moduleUrl??e.rel;if(!s)return null;let u=`${s}#${t.hash??""}`;try{let c=globalThis.__VF_CLIENT_MOD_CACHE?.get(u);if(c)return c}catch(c){i.debug("hydrate: cache get failed",c)}if(!n)return null;try{let c=await import(n);try{Dr(u,c)}catch(g){i.debug("hydrate: cache set failed",g)}return c}catch(c){return i.debug("hydrate: failed to import module",{moduleUrl:n,error:c}),null}}function Lr(t,e,r){if(e.moduleUrl)return e.moduleUrl;if(!e.rel)return null;let n=t.graphIds?.client.find(s=>s.rel===e.rel)?.path;return D({strategy:r,rel:e.rel,absPath:n,version:t.hash})}function Ur(t){let e=Array.from(t.querySelectorAll("[data-client-ref]")),r=new Set(e);return e.filter(n=>{let s=n.parentElement;for(;s;){if(r.has(s))return!1;s=s.parentElement}return!0})}async function yt(t=document){let e=null;try{e=await Mr()}catch(a){i.debug("hydrate: fetch manifest failed",a)}if(!e){i.debug("hydrate: no manifest");return}let r=Ur(t);try{let a=globalThis.__VF_MANIFEST_HASH;if(!r.some(f=>f.dataset?.hydrated!=="true")&&a&&e.hash&&a===e.hash)return}catch(a){i.debug("hydrate: hmr hash read failed",a)}if(r.length===0){try{globalThis.__VF_MANIFEST_HASH=e.hash??""}catch(a){i.debug("hydrate: set hash failed",a)}return}let n=E(t),s=A(n);try{if(globalThis.__VF_TEST_MODE__){globalThis.__VF_HYDRATE_CALLED=!0,globalThis.__VF_MANIFEST_HASH=e.hash??"";return}}catch(a){i.debug("hydrate: test mode flags failed",a)}let u=v(t,n?.reactVersion),[{default:c},{createRoot:g}]=await Promise.all([import(u.react),import(u.reactDomClient)]);for(let a of r){let d=a.dataset?.clientRef??"";if(!d||a.dataset?.hydrated==="true")continue;let f=Rt(d);if(!f)continue;let y=await Et(e,f,s);if(!y)continue;let j=y[f.exportName]??y.default;if(typeof j=="function")try{let L=g(a),_t=vr(a),ht=br(a),Ct=await B(ht,{Fragment:c.Fragment,createElement(I,U,...S){return c.createElement(I,U,...S)}},async I=>{let U=e.modules.find(xt=>xt.id===I),S=e.components?.[I],W=U?.clientRef??(S?`${S}#default`:void 0);if(!W)return null;let w=Rt(W);if(!w)return null;let P=await Et(e,w,s);if(!P)return null;let q=P[w.exportName]??P.default;return typeof q=="function"?q:null}),Tt=await b(c.createElement(j,_t,...Ct),n,t);L.render(Tt),a.dataset.hydrated="true"}catch(L){i.warn("hydrate: render failed",L)}}try{globalThis.__VF_MANIFEST_HASH=e.hash??""}catch(a){i.debug("hydrate: set hash failed (post)",a)}}async function wr(){let t=E(document),e=v(document,t?.reactVersion),[r,n]=await Promise.all([import(e.react),import(e.reactDomClient)]);return{React:r,ReactDOM:n}}var Pr=new Set(["SCRIPT","STYLE","NOSCRIPT","TEMPLATE"]);function Y(t){let e=t.getAttribute("style")??"";return t.hasAttribute("data-veryfront-head")||t.hasAttribute("hidden")||/(?:^|;)\\s*display\\s*:\\s*none(?:\\s*;|$)/i.test(e)||Pr.has(t.tagName.toUpperCase())}function Hr(t,e){return t.find(r=>r.tagName.toUpperCase()==="DIV"&&!!r.getAttribute("class")?.trim()&&!Y(r))??e}function Vr(t,e){return t===e}function Fr(t,e){let r=document.createElement("div");r.setAttribute("data-veryfront-hydration-root","page");let n=t.find(s=>!Y(s));n?.parentNode===e?e.insertBefore(r,n):e.appendChild(r);for(let s of t)!Y(s)&&s.parentNode===e&&r.appendChild(s);return r}function kr(t,e,r=document){return!!e?.pagePath&&typeof t?.__veryfrontRenderPage=="function"&&!!r.getElementById("root")}function Gr(t,e){return e?.pagePath?!1:!!t.getElementById(C)}function $r(t=import.meta.url){try{return new URL(t,"http://veryfront.local").searchParams.get("hydrate")==="1"}catch{return!1}}function Br(t){return t==="rsc-module"}async function Yr(t){try{let e=await fetch(h+"stream"+t);if(!e.ok||!e.body)return!1;let r=new AbortController;return addEventListener("pagehide",()=>r.abort(),{once:!0}),await pt(e,document,r.signal),!0}catch(e){return i.debug("tryStream failed",e),!1}}async function M(){try{await yt(document)}catch(t){i.debug("hydration failed",t)}}async function jr(t,e){try{let{React:r,ReactDOM:n}=await wr(),s=D({strategy:e,rel:t});if(!s)return!1;i.debug("Loading component from:",s);let c=(await import(s)).default;if(typeof c!="function")return i.debug("Page component is not a function"),!1;let g=Array.from(document.body.children),a=Hr(g,document.body),d=Vr(a,document.body)?Fr(g,document.body):a,f=await b(r.createElement(c,{}),E(document));return Br(e)?n.createRoot(d).render(f):n.hydrateRoot(d,f,{identifierPrefix:"vf",onRecoverableError:()=>{}}),i.debug("Page component hydrated successfully"),!0}catch(r){return i.error("Page hydration failed",r),!1}}async function Wr(t){try{let e=await fetch(h+"payload"+t);if(!e.ok)return!1;let r=await e.json();if(r?.slots){for(let[n,s]of Object.entries(r.slots))x(document,n).innerHTML=T(String(s||""));return!0}return x(document,C).innerHTML=T(String(r?.html||"")),!0}catch(e){return i.debug("payload fetch failed",e),!1}}async function qr(){try{let t=globalThis.window?.location.search??"",e=E(document);if($r()){await M();return}let r=e?.pagePath,n=A(e);if(r){if(kr(globalThis.window,e,document)){i.debug("Page renderer owns hydration");return}i.debug("Found page component in hydration data:",r),await jr(r,n)&&i.debug("Client component hydrated successfully");return}if(!Gr(document,e))return;if(await Yr(t)){await M();return}if(await Wr(t)){await M();return}await M()}catch(t){i.error("boot failed",t)}}if(typeof document<"u"){let t=()=>{qr()};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}export{qr as boot,Hr as selectHydrationRoot,Gr as shouldAttemptRSCTransport,$r as shouldHydrateOnly,Br as shouldRenderPageComponent,kr as shouldUsePageRendererHydration,Vr as shouldWrapPageHydrationRoot};\n'; + 'var It=Object.defineProperty;var St=(t,e,r)=>e in t?It(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var p=(t,e,r)=>St(t,typeof e!="symbol"?e+"":e,r);var Ot="3.2.3";function Nt(t,e,r,n){let s=[];if(n?.external?.length&&s.push(`external=${n.external.join(",")}`),s.push(`target=${n?.target??"es2022"}`),n?.deps){let a=Object.entries(n.deps).map(([d,f])=>`${d}@${f}`).join(",");s.push(`deps=${a}`)}let u=e?`@${e}`:"",c=r??"",g=s.length?`?${s.join("&")}`:"";return`https://esm.sh/${t}${u}${c}${g}`}function m(t,e,r,n=!1){return Nt(t,e,r,{external:n?["react"]:void 0,deps:{csstype:Ot}})}var Dt="19.2.4",_=Dt;function z(t=_){return{react:m("react",t),"react-dom":m("react-dom",t,void 0,!0),"react-dom/client":m("react-dom",t,"/client",!0),"react-dom/server":m("react-dom",t,"/server",!0),"react/jsx-runtime":m("react",t,"/jsx-runtime",!0),"react/jsx-dev-runtime":m("react",t,"/jsx-dev-runtime",!0)}}function J(t=_){return z(t).react}function X(t=_){return z(t)["react-dom/client"]}function vt(t){return t.replaceAll("+","-").replaceAll("/","_").replaceAll("=","")}function bt(t){if(typeof globalThis.btoa=="function")try{return globalThis.btoa(t)}catch{let n=new TextEncoder().encode(t),s="";for(let u of n)s+=String.fromCharCode(u);return globalThis.btoa(s)}let e=globalThis.Buffer;if(e)return e.from(t,"utf8").toString("base64");throw new Error("Base64 encoding is not supported in this runtime")}function H(t){return vt(bt(t))}var so=Object.freeze({react:"","react-dom":"","react-dom/client":"","react-dom/server":"","react/jsx-runtime":"","react/jsx-dev-runtime":""});function O(t,e){if(!e)return!1;if(Object.prototype.hasOwnProperty.call(e,t))return!0;for(let r of Object.keys(e))if(r.endsWith("/")&&t.startsWith(r))return!0;return!1}function Mt(t){try{return JSON.parse(t)?.imports??{}}catch(e){return console.warn("Failed to parse import map JSON; treating as empty",{errorName:e instanceof Error?e.name:typeof e,inputLength:t.length}),{}}}function V(t=document){let e=t.querySelector(\'script[type="importmap"]\');return e?.textContent?Mt(e.textContent):{}}var l="/_veryfront",F={RSC:`${l}/rsc/`,FS:`${l}/fs/`,MODULES:`${l}/modules/`,PAGES:`${l}/pages/`,DATA:`${l}/data/`,LIB:`${l}/lib/`,CHUNKS:`${l}/chunks/`,CLIENT:`${l}/client/`},K={HMR_RUNTIME:`${l}/hmr-runtime.js`,HMR:`${l}/hmr.js`,ERROR_OVERLAY:`${l}/error-overlay.js`,DEV_LOADER:`${l}/dev-loader.js`,CLIENT_LOG:`${l}/log`,CLIENT_JS:`${l}/client.js`,ROUTER_JS:`${l}/router.js`,PREFETCH_JS:`${l}/prefetch.js`,MANIFEST_JSON:`${l}/manifest.json`,APP_JS:`${l}/app.js`,RSC_CLIENT:`${l}/rsc/client.js`,RSC_MANIFEST:`${l}/rsc/manifest`,RSC_STREAM:`${l}/rsc/stream`,RSC_PAYLOAD:`${l}/rsc/payload`,RSC_RENDER:`${l}/rsc/render`,RSC_PAGE:`${l}/rsc/page`,RSC_MODULE:`${l}/rsc/module`,RSC_DOM:`${l}/rsc/dom.js`,LIB_CHAT_REACT:`${l}/lib/chat/react.js`,LIB_CHAT_COMPONENTS:`${l}/lib/chat/components.js`,LIB_CHAT_PRIMITIVES:`${l}/lib/chat/primitives.js`};var wt={ROOT:".veryfront",CACHE:".veryfront/cache",KV:".veryfront/kv",LOGS:".veryfront/logs",TMP:".veryfront/tmp"},uo=wt.CACHE;var go={HMR_RUNTIME:K.HMR_RUNTIME,ERROR_OVERLAY:K.ERROR_OVERLAY};var h=F.RSC,Z=F.FS,Q="veryfront-hydration-data",C="rsc-root";var R=class{constructor(e,r){p(this,"prefix",e);p(this,"level",r)}log(e,r,n,...s){this.level>e||r?.(n,...s)}debug(e,...r){this.log(0,console.debug,`[${this.prefix}] DEBUG: ${e}`,...r)}info(e,...r){this.log(1,console.log,`[${this.prefix}] ${e}`,...r)}warn(e,...r){this.log(2,console.warn,`[${this.prefix}] WARN: ${e}`,...r)}error(e,...r){this.log(3,console.error,`[${this.prefix}] ERROR: ${e}`,...r)}};function Ut(){if(typeof window>"u")return 2;let t=globalThis;return t.__VERYFRONT_DEV__||t.__RSC_DEV__?t.__VERYFRONT_DEBUG__||t.__RSC_DEBUG__?0:1:2}var N=Ut(),i=new R("RSC",N),Eo=new R("PREFETCH",N),yo=new R("HYDRATE",N),_o=new R("VERYFRONT",N);function E(t=document){try{let e=t.getElementById(Q);return e?JSON.parse(e.textContent||"{}"):null}catch(e){return i.debug("hydration data parse failed",e),null}}function A(t){return t?.clientModuleStrategy?t.clientModuleStrategy:t?.dev?"fs":"rsc-module"}function Pt(t,e){if(!e)return t;let r=t.includes("?")?"&":"?";return`${t}${r}v=${encodeURIComponent(e)}`}function Ht(t,e){return Pt(`${Z}${H(t)}.js`,e)}function Vt(t,e){let r=e?`&v=${encodeURIComponent(e)}`:"";return`${h}module?rel=${encodeURIComponent(t)}${r}`}function D(t){if(t.strategy==="fs"){let e=t.absPath??t.rel;return e?Ht(e,t.version):null}return Vt(t.rel,t.version)}function v(t=document,e=_){let r=V(t);return{react:O("react",r)?"react":J(e),reactDomClient:O("react-dom/client",r)?"react-dom/client":X(e)}}function tt(t=document){let e=V(t);return O("veryfront/router",e)?"veryfront/router":null}function o(t){return{...t,create(e){return new k(e?.message||e?.detail||t.title,{slug:t.slug,category:t.category,status:e?.status??t.status,title:t.title,suggestion:t.suggestion,detail:e?.detail,cause:e?.cause,instance:e?.instance,context:e?.context})}}}var k=class extends Error{constructor(r,n){super(r);p(this,"slug");p(this,"category");p(this,"status");p(this,"title");p(this,"suggestion");p(this,"detail");p(this,"cause");p(this,"instance");p(this,"context");this.name="VeryfrontError",this.slug=n.slug,this.category=n.category,this.status=n.status,this.title=n.title,this.suggestion=n.suggestion,this.detail=n.detail,this.cause=n.cause,this.instance=n.instance,this.context=n.context}toRFC9457(){return{type:`https://veryfront.com/docs/errors/${this.slug}`,title:this.title,status:this.status,detail:this.detail,instance:this.instance,category:this.category,suggestion:this.suggestion,cause:typeof this.cause=="string"?this.cause:void 0}}getDocsUrl(){return`https://veryfront.com/docs/errors/${this.slug}`}};var Ft=o({slug:"config-not-found",category:"CONFIG",status:404,title:"Configuration file not found",suggestion:"Run \'vf init\' to create a configuration file"}),kt=o({slug:"config-invalid",category:"CONFIG",status:400,title:"Invalid configuration format",suggestion:"Check your veryfront.config.ts for syntax errors"}),Gt=o({slug:"config-parse-error",category:"CONFIG",status:400,title:"Failed to parse configuration",suggestion:"Ensure your configuration file is valid TypeScript/JSON"}),$t=o({slug:"config-validation-error",category:"CONFIG",status:422,title:"Configuration validation failed",suggestion:"Check the configuration against the schema requirements"}),Bt=o({slug:"config-type-error",category:"CONFIG",status:400,title:"Configuration type mismatch",suggestion:"Ensure configuration values match expected types"}),Yt=o({slug:"import-map-invalid",category:"CONFIG",status:400,title:"Invalid import map configuration",suggestion:"Check your import map syntax and paths"}),jt=o({slug:"cors-config-invalid",category:"CONFIG",status:400,title:"Invalid CORS configuration",suggestion:"Review CORS settings in your configuration"}),Wt=o({slug:"config-validation-failed",category:"CONFIG",status:400,title:"Configuration validation failed",suggestion:"Check configuration values against requirements"}),qt=o({slug:"webhook-config-invalid",category:"CONFIG",status:400,title:"Invalid webhook configuration",suggestion:"Check webhook definition fields, target settings, and eventFilter conditions"}),zt=o({slug:"schedule-config-invalid",category:"CONFIG",status:400,title:"Invalid schedule configuration",suggestion:"Check schedule definition fields, cron expression, target settings, and positive-integer limits"}),Jt=o({slug:"trigger-config-invalid",category:"CONFIG",status:400,title:"Invalid trigger configuration",suggestion:"Check trigger ID format (lowercase, alphanumeric, dots/slashes/hyphens) and ensure all input values are JSON-serializable"}),et={"config-not-found":Ft,"config-invalid":kt,"config-parse-error":Gt,"config-validation-error":$t,"config-type-error":Bt,"import-map-invalid":Yt,"cors-config-invalid":jt,"config-validation-failed":Wt,"webhook-config-invalid":qt,"schedule-config-invalid":zt,"trigger-config-invalid":Jt};var Xt=o({slug:"build-failed",category:"BUILD",status:500,title:"Build process failed",suggestion:"Check the build output for specific errors"}),Kt=o({slug:"bundle-error",category:"BUILD",status:500,title:"Bundle generation failed",suggestion:"Review bundler output for details"}),Zt=o({slug:"typescript-error",category:"BUILD",status:500,title:"TypeScript compilation error",suggestion:"Fix TypeScript errors shown in the output"}),Qt=o({slug:"mdx-compile-error",category:"BUILD",status:500,title:"MDX compilation failed",suggestion:"Check your MDX file syntax"}),te=o({slug:"asset-optimization-error",category:"BUILD",status:500,title:"Asset optimization failed",suggestion:"Check asset file formats and paths"}),ee=o({slug:"ssg-generation-error",category:"BUILD",status:500,title:"Static site generation failed",suggestion:"Review SSG configuration and data fetching"}),re=o({slug:"sourcemap-error",category:"BUILD",status:500,title:"Source map generation failed",suggestion:"Check source map configuration"}),oe=o({slug:"compilation-error",category:"BUILD",status:500,title:"Compilation failed",suggestion:"Review compiler output for specific errors"}),rt={"build-failed":Xt,"bundle-error":Kt,"typescript-error":Zt,"mdx-compile-error":Qt,"asset-optimization-error":te,"ssg-generation-error":ee,"sourcemap-error":re,"compilation-error":oe};var ne=o({slug:"hydration-mismatch",category:"RUNTIME",status:500,title:"Client/server hydration mismatch",suggestion:"Ensure server and client render the same content"}),se=o({slug:"render-error",category:"RUNTIME",status:500,title:"Component render failed",suggestion:"Check component for runtime errors"}),ie=o({slug:"component-error",category:"RUNTIME",status:500,title:"Component execution error",suggestion:"Review component logic and props"}),ae=o({slug:"layout-not-found",category:"RUNTIME",status:404,title:"Layout component not found",suggestion:"Ensure layout file exists at the expected path"}),ce=o({slug:"page-not-found",category:"RUNTIME",status:404,title:"Page component not found",suggestion:"Check that the page file exists in the routes directory"}),le=o({slug:"api-error",category:"RUNTIME",status:500,title:"API route handler error",suggestion:"Review API route handler for errors"}),ue=o({slug:"middleware-error",category:"RUNTIME",status:500,title:"Middleware execution error",suggestion:"Check middleware function for errors"}),de=o({slug:"trigger-target-not-found",category:"RUNTIME",status:404,title:"Trigger target not found",suggestion:"Ensure the referenced task or workflow ID is registered in the project"}),ge=o({slug:"trigger-execution-failed",category:"RUNTIME",status:500,title:"Trigger target execution failed",suggestion:"Check the task or workflow for errors and review the trigger input"}),pe=o({slug:"trigger-not-supported",category:"RUNTIME",status:501,title:"Trigger target type not supported in local runtime",suggestion:"Use a workflow or task target for local trigger runs; agent targets require the Cloud runtime"}),ot={"hydration-mismatch":ne,"render-error":se,"component-error":ie,"layout-not-found":ae,"page-not-found":ce,"api-error":le,"middleware-error":ue,"trigger-target-not-found":de,"trigger-execution-failed":ge,"trigger-not-supported":pe};var fe=o({slug:"route-conflict",category:"ROUTE",status:409,title:"Conflicting route definitions",suggestion:"Rename or reorganize conflicting route files"}),me=o({slug:"invalid-route-file",category:"ROUTE",status:400,title:"Invalid route file structure",suggestion:"Ensure route file exports required functions"}),Re=o({slug:"route-handler-invalid",category:"ROUTE",status:400,title:"Invalid route handler export",suggestion:"Export a valid handler function from the route file"}),Ee=o({slug:"dynamic-route-error",category:"ROUTE",status:500,title:"Dynamic route parsing failed",suggestion:"Check dynamic route segment syntax"}),ye=o({slug:"route-params-error",category:"ROUTE",status:400,title:"Route parameters invalid",suggestion:"Validate route parameter values"}),_e=o({slug:"api-route-error",category:"ROUTE",status:500,title:"API route definition error",suggestion:"Review API route configuration"}),nt={"route-conflict":fe,"invalid-route-file":me,"route-handler-invalid":Re,"dynamic-route-error":Ee,"route-params-error":ye,"api-route-error":_e};var he=o({slug:"module-not-found",category:"MODULE",status:404,title:"Module could not be resolved",suggestion:"Check the import path and ensure the module is installed"}),Ce=o({slug:"import-resolution-error",category:"MODULE",status:500,title:"Import path resolution failed",suggestion:"Verify import paths and module configuration"}),Te=o({slug:"circular-dependency",category:"MODULE",status:500,title:"Circular dependency detected",suggestion:"Refactor imports to break the circular dependency"}),xe=o({slug:"invalid-import",category:"MODULE",status:400,title:"Invalid import statement",suggestion:"Fix import syntax or path"}),Ie=o({slug:"dependency-missing",category:"MODULE",status:404,title:"Required dependency not installed",suggestion:"Install the missing dependency with your package manager"}),Se=o({slug:"version-mismatch",category:"MODULE",status:409,title:"Dependency version mismatch",suggestion:"Update dependencies to compatible versions"}),st={"module-not-found":he,"import-resolution-error":Ce,"circular-dependency":Te,"invalid-import":xe,"dependency-missing":Ie,"version-mismatch":Se};var Oe=o({slug:"port-in-use",category:"SERVER",status:409,title:"Server port already in use",suggestion:"Use a different port or stop the process using this port"}),Ne=o({slug:"server-start-error",category:"SERVER",status:500,title:"Server failed to start",suggestion:"Check server configuration and port availability"}),Ae=o({slug:"cache-error",category:"SERVER",status:500,title:"Cache operation failed",suggestion:"Clear the cache and try again"}),De=o({slug:"file-watch-error",category:"SERVER",status:500,title:"File watcher error",suggestion:"Restart the development server"}),ve=o({slug:"request-error",category:"SERVER",status:500,title:"HTTP request handling error",suggestion:"Check request handler and middleware"}),be=o({slug:"service-overloaded",category:"SERVER",status:503,title:"Service overloaded",suggestion:"Reduce load or scale up resources"}),Me=o({slug:"semaphore-timeout",category:"SERVER",status:503,title:"Semaphore acquire timeout",suggestion:"Reduce concurrency or increase the semaphore acquire timeout"}),Le=o({slug:"circuit-breaker-open",category:"SERVER",status:503,title:"Circuit breaker is open",suggestion:"Wait for the breaker reset timeout before retrying"}),we=o({slug:"cache-path-mismatch",category:"SERVER",status:500,title:"Cache path mismatch",suggestion:"Clear the cache directory and rebuild"}),Ue=o({slug:"network-error",category:"SERVER",status:502,title:"Network operation failed",suggestion:"Check network connectivity and retry"}),Pe=o({slug:"api-client-error",category:"SERVER",status:500,title:"API client request failed",suggestion:"Check API connectivity and authentication"}),He=o({slug:"token-storage-error",category:"SERVER",status:500,title:"Token storage operation failed",suggestion:"Check token storage backend and credentials"}),Ve=o({slug:"cache-invariant-violation",category:"SERVER",status:500,title:"Cache path invariant violated",suggestion:"Clear the cache and rebuild"}),Fe=o({slug:"release-not-found",category:"SERVER",status:404,title:"No active release found",suggestion:"Deploy the project to create a release for this environment"}),ke=o({slug:"fallback-exhausted",category:"SERVER",status:500,title:"Primary and fallback operations both failed",suggestion:"Check service availability and connectivity"}),it={"port-in-use":Oe,"server-start-error":Ne,"cache-error":Ae,"file-watch-error":De,"request-error":ve,"service-overloaded":be,"semaphore-timeout":Me,"circuit-breaker-open":Le,"cache-path-mismatch":we,"network-error":Ue,"api-client-error":Pe,"token-storage-error":He,"cache-invariant-violation":Ve,"release-not-found":Fe,"fallback-exhausted":ke};var Ge=o({slug:"client-boundary-violation",category:"BOUNDARY",status:400,title:"Client boundary rule violation",suggestion:"Add \'use client\' directive or move code to a client component"}),$e=o({slug:"server-only-in-client",category:"BOUNDARY",status:400,title:"Server-only code in client component",suggestion:"Move server-only code to a server component"}),Be=o({slug:"client-only-in-server",category:"BOUNDARY",status:400,title:"Client-only code in server component",suggestion:"Move client-only code to a client component"}),Ye=o({slug:"invalid-use-client",category:"BOUNDARY",status:400,title:"Invalid \'use client\' directive",suggestion:"Place \'use client\' at the top of the file"}),je=o({slug:"invalid-use-server",category:"BOUNDARY",status:400,title:"Invalid \'use server\' directive",suggestion:"Place \'use server\' at the top of the file or function"}),We=o({slug:"rsc-payload-error",category:"BOUNDARY",status:500,title:"RSC payload serialization error",suggestion:"Ensure props are serializable (no functions, symbols, etc.)"}),at={"client-boundary-violation":Ge,"server-only-in-client":$e,"client-only-in-server":Be,"invalid-use-client":Ye,"invalid-use-server":je,"rsc-payload-error":We};var qe=o({slug:"hmr-error",category:"DEV",status:500,title:"Hot module replacement error",suggestion:"Restart the development server"}),ze=o({slug:"dev-server-error",category:"DEV",status:500,title:"Development server error",suggestion:"Check the dev server logs and restart"}),Je=o({slug:"fast-refresh-error",category:"DEV",status:500,title:"Fast refresh failed",suggestion:"Save the file again or restart the dev server"}),Xe=o({slug:"error-overlay-error",category:"DEV",status:500,title:"Error overlay failed",suggestion:"Check browser console for details"}),Ke=o({slug:"source-map-error",category:"DEV",status:500,title:"Source map loading error",suggestion:"Rebuild or clear cache"}),ct={"hmr-error":qe,"dev-server-error":ze,"fast-refresh-error":Je,"error-overlay-error":Xe,"source-map-error":Ke};var Ze=o({slug:"deployment-error",category:"DEPLOY",status:500,title:"Deployment process failed",suggestion:"Check deployment logs for details"}),Qe=o({slug:"platform-error",category:"DEPLOY",status:500,title:"Platform-specific error",suggestion:"Check platform documentation and requirements"}),tr=o({slug:"env-var-missing",category:"DEPLOY",status:500,title:"Required environment variable missing",suggestion:"Set the required environment variable"}),er=o({slug:"production-build-required",category:"DEPLOY",status:400,title:"Production build required",suggestion:"Run \'vf build\' before deploying"}),lt={"deployment-error":Ze,"platform-error":Qe,"env-var-missing":tr,"production-build-required":er};var rr=o({slug:"agent-error",category:"AGENT",status:500,title:"Agent operation error",suggestion:"Check agent configuration and logs"}),or=o({slug:"agent-not-found",category:"AGENT",status:404,title:"Agent not found",suggestion:"Verify the agent ID exists"}),nr=o({slug:"agent-timeout",category:"AGENT",status:408,title:"Agent operation timed out",suggestion:"Increase timeout or simplify the request"}),sr=o({slug:"agent-intent-error",category:"AGENT",status:400,title:"Agent intent parsing error",suggestion:"Rephrase the request more clearly"}),ir=o({slug:"orchestration-error",category:"AGENT",status:500,title:"Multi-agent orchestration error",suggestion:"Check agent coordination logic"}),ar=o({slug:"cost-limit-exceeded",category:"AGENT",status:429,title:"Cost limit exceeded",suggestion:"Wait for the budget period to reset or increase the limit"}),cr=o({slug:"tool-id-conflict",category:"AGENT",status:409,title:"Tool ID conflict",suggestion:"Use a unique tool ID or rename one of the conflicting tools"}),ut={"agent-error":rr,"agent-not-found":or,"agent-timeout":nr,"agent-intent-error":sr,"orchestration-error":ir,"cost-limit-exceeded":ar,"tool-id-conflict":cr};var lr=o({slug:"unknown-error",category:"GENERAL",status:500,title:"Unknown/unclassified error",suggestion:"Check logs for more details"}),ur=o({slug:"permission-denied",category:"GENERAL",status:403,title:"File/resource permission denied",suggestion:"Check file permissions and access rights"}),dr=o({slug:"file-not-found",category:"GENERAL",status:404,title:"File not found",suggestion:"Verify the file path exists"}),gr=o({slug:"resource-not-found",category:"GENERAL",status:404,title:"Requested resource not found",suggestion:"Verify the referenced resource ID or name exists"}),pr=o({slug:"invalid-argument",category:"GENERAL",status:400,title:"Invalid function argument",suggestion:"Check argument types and values"}),fr=o({slug:"timeout-error",category:"GENERAL",status:408,title:"Operation timed out",suggestion:"Increase timeout or optimize the operation"}),mr=o({slug:"initialization-error",category:"GENERAL",status:500,title:"Initialization failed",suggestion:"Check initialization requirements and dependencies"}),Rr=o({slug:"not-supported",category:"GENERAL",status:501,title:"Feature not supported",suggestion:"Check documentation for supported features"}),G=o({slug:"security-violation",category:"GENERAL",status:403,title:"Security violation detected",suggestion:"Check for path traversal or unauthorized access attempts"}),Er=o({slug:"input-validation-failed",category:"GENERAL",status:400,title:"Input validation failed",suggestion:"Check request input against validation rules"}),dt={"unknown-error":lr,"permission-denied":ur,"file-not-found":dr,"resource-not-found":gr,"invalid-argument":pr,"timeout-error":fr,"initialization-error":mr,"not-supported":Rr,"security-violation":G,"input-validation-failed":Er};var Rn={...et,...rt,...ot,...nt,...st,...it,...at,...ct,...lt,...ut,...dt};var yr=[{source:String.raw`]*>[\\s\\S]*?<\\/script>`,flags:"gi",name:"inline script"},{source:String.raw`javascript:`,flags:"gi",name:"javascript: URL"},{source:String.raw`\\bon\\w+\\s*=`,flags:"gi",name:"event handler attribute"},{source:String.raw`data:\\s*text\\/html`,flags:"gi",name:"data: HTML URL"}];function _r(){return yr.map(({source:t,flags:e,name:r})=>({pattern:new RegExp(t,e),name:r}))}function hr(){let t=globalThis;return t.__VERYFRONT_DEV__===!0||t.Deno?.env?.get?.("VERYFRONT_ENV")==="development"}function T(t,e={}){let{allowInlineScripts:r=!1,strict:n=!1,warn:s=!0}=e;for(let{pattern:u,name:c}of _r())if(!(r&&c==="inline script")&&(u.lastIndex=0,!!u.test(t)&&(s&&console.warn(`[Security] Suspicious ${c} detected in server HTML`),n||!hr())))throw G.create({detail:`Potentially unsafe HTML: ${c} detected`});return t}function x(t,e){let r=e==="root"?C:`rsc-slot-${e}`,n=t.getElementById(r);if(n)return n;let s=t.createElement("div");return s.id=r,t.body.appendChild(s),s}function Cr(t,e){if(e.type!=="slot")return;let r=x(t,e.id);r.innerHTML=T(String(e.html??""))}function gt(t,e){let r=e.split(`\n`),n=r.pop()??"";for(let s of r){let u=s.trim();if(!u)continue;let c;try{c=JSON.parse(u)}catch(a){i.debug("[client-dom] malformed NDJSON line",{line:u,error:a instanceof Error?a.message:String(a)});continue}if(!c||typeof c!="object")continue;let g=c;if(g.type==="slot"){Cr(t,g);try{Ir(t,g.id||"root")}catch(a){i.debug("[client-dom] hydration optional failed",a)}}}return n}function Tr(t){return new Promise((e,r)=>{let n=()=>r(new DOMException("aborted","AbortError"));if(t.aborted){n();return}t.addEventListener("abort",n,{once:!0})})}async function pt(t,e=document,r){let n="body"in t?t:null,s=n?.body??t;if(!s)return;let u=s.getReader(),c=new TextDecoder,g="",a=!1;try{for(;;){if(r?.aborted)throw new DOMException("aborted","AbortError");let d=u.read(),{done:f,value:y}=r?await Promise.race([d,Tr(r)]):await d;if(f){a=!0;break}g+=c.decode(y,{stream:!0}),g=gt(e,g)}g&>(e,`${g}\n`)}catch(d){throw d instanceof Error&&d.name==="AbortError"||i.debug("[client-dom] consumeNdjsonStream error",d),d}finally{try{await u.cancel()}catch(d){a||i.debug("[client-dom] reader.cancel failed",d)}try{u.releaseLock()}catch(d){i.debug("[client-dom] reader.releaseLock failed",d)}if(typeof s.cancel=="function")try{await s.cancel()}catch(d){i.debug("[client-dom] stream.cancel failed",d)}if(typeof n?.body?.cancel=="function")try{await n.body.cancel()}catch(d){i.debug("[client-dom] response.body.cancel failed",d)}}}function xr(t,e){let r=x(t,e),n=[],s=u=>{let c=u;c.dataset?.clientRef&&n.push(c);for(let g of u.children)s(g)};return s(r),n}function Ir(t,e){let r=xr(t,e);for(let n of r){let s=n.dataset?.clientRef;s&&(n.dataset.hydrated="true",i.debug("[client-dom] marked for hydration",s))}}var Sr=new Set(["server","client","html","fragment"]);function ft(t){if(!t)return[];try{let e=JSON.parse(t);return Nr(e)?e.nodes:[]}catch{return[]}}async function B(t,e,r){return await Promise.all(t.map(n=>Or(n,e,r)))}async function Or(t,e,r){if(t.type==="html")return t.text??t.html??"";let n=await B(t.children??[],e,r);if(t.type==="fragment"||t.type==="server"&&!t.component)return e.createElement(e.Fragment,{},...n);if(t.type==="server")return e.createElement(t.component,t.props??{},...n);let s=await r(t.component);return s?e.createElement(s,t.props??{},...n):null}function Nr(t){return!$(t)||t.version!==1||!Array.isArray(t.nodes)?!1:t.nodes.every(e=>mt(e,0))}function mt(t,e){return e>100||!$(t)||!Sr.has(t.type)||t.type==="html"&&typeof t.html!="string"&&typeof t.text!="string"||t.type==="client"&&typeof t.component!="string"||t.type==="server"&&t.component!==void 0&&typeof t.component!="string"||t.props!==void 0&&!$(t.props)?!1:t.children===void 0?!0:Array.isArray(t.children)&&t.children.every(r=>mt(r,e+1))}function $(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function Ar(t){if(!t)return{};let e={};for(let[r,n]of Object.entries(t))e[r]=Array.isArray(n)?n.join("/"):n;return e}async function b(t,e,r=document){try{let n=tt(r);if(!n)return t;let u=(await import(n)).wrapForHydration;return typeof u!="function"?t:u(t,{params:Ar(e?.params),frontmatter:e?.frontmatter??{}})}catch(n){return i.debug("router provider wrap failed",n),t}}var Dr=100;function vr(t,e){if(globalThis.__VF_CLIENT_MOD_CACHE??(globalThis.__VF_CLIENT_MOD_CACHE=new Map),globalThis.__VF_CLIENT_MOD_CACHE.size>=Dr){let r=globalThis.__VF_CLIENT_MOD_CACHE.keys().next().value;r&&globalThis.__VF_CLIENT_MOD_CACHE.delete(r)}globalThis.__VF_CLIENT_MOD_CACHE.set(t,e)}function Rt(t){let e=t.match(/^\\/app\\/(.+)#([\\w$.-]+)$/);if(e)return{rel:`/${e[1]||""}`,exportName:e[2]||"default"};let r=t.match(/^(\\/_veryfront\\/[^#]+)#([\\w$.-]+)$/);return r?{moduleUrl:r[1],exportName:r[2]||"default"}:(i.debug("hydrate: unrecognised client ref format, skipping",{ref:t}),null)}function br(t){let e=t.dataset?.rscProps;if(!e)return{};try{let r=JSON.parse(e);return r&&typeof r=="object"&&!Array.isArray(r)?r:{}}catch(r){return i.debug("hydrate: invalid client boundary props, using empty props",r),{}}}function Mr(t){return ft(t.dataset?.rscChildren)}async function Lr(){try{let t=await fetch("/_veryfront/rsc/manifest");return t.ok?await t.json():null}catch{return null}}async function Et(t,e,r){let n=wr(t,e,r),s=e.moduleUrl??e.rel;if(!s)return null;let u=`${s}#${t.hash??""}`;try{let c=globalThis.__VF_CLIENT_MOD_CACHE?.get(u);if(c)return c}catch(c){i.debug("hydrate: cache get failed",c)}if(!n)return null;try{let c=await import(n);try{vr(u,c)}catch(g){i.debug("hydrate: cache set failed",g)}return c}catch(c){return i.debug("hydrate: failed to import module",{moduleUrl:n,error:c}),null}}function wr(t,e,r){if(e.moduleUrl)return e.moduleUrl;if(!e.rel)return null;let n=t.graphIds?.client.find(s=>s.rel===e.rel)?.path;return D({strategy:r,rel:e.rel,absPath:n,version:t.hash})}function Ur(t){let e=Array.from(t.querySelectorAll("[data-client-ref]")),r=new Set(e);return e.filter(n=>{let s=n.parentElement;for(;s;){if(r.has(s))return!1;s=s.parentElement}return!0})}async function yt(t=document){let e=null;try{e=await Lr()}catch(a){i.debug("hydrate: fetch manifest failed",a)}if(!e){i.debug("hydrate: no manifest");return}let r=Ur(t);try{let a=globalThis.__VF_MANIFEST_HASH;if(!r.some(f=>f.dataset?.hydrated!=="true")&&a&&e.hash&&a===e.hash)return}catch(a){i.debug("hydrate: hmr hash read failed",a)}if(r.length===0){try{globalThis.__VF_MANIFEST_HASH=e.hash??""}catch(a){i.debug("hydrate: set hash failed",a)}return}let n=E(t),s=A(n);try{if(globalThis.__VF_TEST_MODE__){globalThis.__VF_HYDRATE_CALLED=!0,globalThis.__VF_MANIFEST_HASH=e.hash??"";return}}catch(a){i.debug("hydrate: test mode flags failed",a)}let u=v(t,n?.reactVersion),[{default:c},{createRoot:g}]=await Promise.all([import(u.react),import(u.reactDomClient)]);for(let a of r){let d=a.dataset?.clientRef??"";if(!d||a.dataset?.hydrated==="true")continue;let f=Rt(d);if(!f)continue;let y=await Et(e,f,s);if(!y)continue;let j=y[f.exportName]??y.default;if(typeof j=="function")try{let L=g(a),_t=br(a),ht=Mr(a),Ct=await B(ht,{Fragment:c.Fragment,createElement(I,w,...S){return c.createElement(I,w,...S)}},async I=>{let w=e.modules.find(xt=>xt.id===I),S=e.components?.[I],W=w?.clientRef??(S?`${S}#default`:void 0);if(!W)return null;let U=Rt(W);if(!U)return null;let P=await Et(e,U,s);if(!P)return null;let q=P[U.exportName]??P.default;return typeof q=="function"?q:null}),Tt=await b(c.createElement(j,_t,...Ct),n,t);L.render(Tt),a.dataset.hydrated="true"}catch(L){i.warn("hydrate: render failed",L)}}try{globalThis.__VF_MANIFEST_HASH=e.hash??""}catch(a){i.debug("hydrate: set hash failed (post)",a)}}async function Pr(){let t=E(document),e=v(document,t?.reactVersion),[r,n]=await Promise.all([import(e.react),import(e.reactDomClient)]);return{React:r,ReactDOM:n}}var Hr=new Set(["SCRIPT","STYLE","NOSCRIPT","TEMPLATE"]);function Y(t){let e=t.getAttribute("style")??"";return t.hasAttribute("data-veryfront-head")||t.hasAttribute("hidden")||/(?:^|;)\\s*display\\s*:\\s*none(?:\\s*;|$)/i.test(e)||Hr.has(t.tagName.toUpperCase())}function Vr(t,e){return t.find(r=>r.tagName.toUpperCase()==="DIV"&&!!r.getAttribute("class")?.trim()&&!Y(r))??e}function Fr(t,e){return t===e}function kr(t,e){let r=document.createElement("div");r.setAttribute("data-veryfront-hydration-root","page");let n=t.find(s=>!Y(s));n?.parentNode===e?e.insertBefore(r,n):e.appendChild(r);for(let s of t)!Y(s)&&s.parentNode===e&&r.appendChild(s);return r}function Gr(t,e,r=document){return!!e?.pagePath&&typeof t?.__veryfrontRenderPage=="function"&&!!r.getElementById("root")}function $r(t,e){return e?.pagePath?!1:!!t.getElementById(C)}function Br(t=import.meta.url){try{return new URL(t,"http://veryfront.local").searchParams.get("hydrate")==="1"}catch{return!1}}function Yr(t){return t==="rsc-module"}async function jr(t){try{let e=await fetch(h+"stream"+t);if(!e.ok||!e.body)return!1;let r=new AbortController;return addEventListener("pagehide",()=>r.abort(),{once:!0}),await pt(e,document,r.signal),!0}catch(e){return i.debug("tryStream failed",e),!1}}async function M(){try{await yt(document)}catch(t){i.debug("hydration failed",t)}}async function Wr(t,e){try{let{React:r,ReactDOM:n}=await Pr(),s=D({strategy:e,rel:t});if(!s)return!1;i.debug("Loading component from:",s);let c=(await import(s)).default;if(typeof c!="function")return i.debug("Page component is not a function"),!1;let g=Array.from(document.body.children),a=Vr(g,document.body),d=Fr(a,document.body)?kr(g,document.body):a,f=await b(r.createElement(c,{}),E(document));return Yr(e)?n.createRoot(d).render(f):n.hydrateRoot(d,f,{identifierPrefix:"vf",onRecoverableError:()=>{}}),i.debug("Page component hydrated successfully"),!0}catch(r){return i.error("Page hydration failed",r),!1}}async function qr(t){try{let e=await fetch(h+"payload"+t);if(!e.ok)return!1;let r=await e.json();if(r?.slots){for(let[n,s]of Object.entries(r.slots))x(document,n).innerHTML=T(String(s||""));return!0}return x(document,C).innerHTML=T(String(r?.html||"")),!0}catch(e){return i.debug("payload fetch failed",e),!1}}async function zr(){try{let t=globalThis.window?.location.search??"",e=E(document);if(Br()){await M();return}let r=e?.pagePath,n=A(e);if(r){if(Gr(globalThis.window,e,document)){i.debug("Page renderer owns hydration");return}i.debug("Found page component in hydration data:",r),await Wr(r,n)&&i.debug("Client component hydrated successfully");return}if(!$r(document,e))return;if(await jr(t)){await M();return}if(await qr(t)){await M();return}await M()}catch(t){i.error("boot failed",t)}}if(typeof document<"u"){let t=()=>{zr()};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}export{zr as boot,Vr as selectHydrationRoot,$r as shouldAttemptRSCTransport,Br as shouldHydrateOnly,Yr as shouldRenderPageComponent,Gr as shouldUsePageRendererHydration,Fr as shouldWrapPageHydrationRoot};\n'; export const CLIENT_DOM_BUNDLE: string = 'var V=Object.defineProperty;var k=(r,t,o)=>t in r?V(r,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):r[t]=o;var l=(r,t,o)=>k(r,typeof t!="symbol"?t+"":t,o);function e(r){return{...r,create(t){return new f(t?.message||t?.detail||r.title,{slug:r.slug,category:r.category,status:t?.status??r.status,title:r.title,suggestion:r.suggestion,detail:t?.detail,cause:t?.cause,instance:t?.instance,context:t?.context})}}}var f=class extends Error{constructor(o,n){super(o);l(this,"slug");l(this,"category");l(this,"status");l(this,"title");l(this,"suggestion");l(this,"detail");l(this,"cause");l(this,"instance");l(this,"context");this.name="VeryfrontError",this.slug=n.slug,this.category=n.category,this.status=n.status,this.title=n.title,this.suggestion=n.suggestion,this.detail=n.detail,this.cause=n.cause,this.instance=n.instance,this.context=n.context}toRFC9457(){return{type:`https://veryfront.com/docs/errors/${this.slug}`,title:this.title,status:this.status,detail:this.detail,instance:this.instance,category:this.category,suggestion:this.suggestion,cause:typeof this.cause=="string"?this.cause:void 0}}getDocsUrl(){return`https://veryfront.com/docs/errors/${this.slug}`}};var w=e({slug:"config-not-found",category:"CONFIG",status:404,title:"Configuration file not found",suggestion:"Run \'vf init\' to create a configuration file"}),F=e({slug:"config-invalid",category:"CONFIG",status:400,title:"Invalid configuration format",suggestion:"Check your veryfront.config.ts for syntax errors"}),H=e({slug:"config-parse-error",category:"CONFIG",status:400,title:"Failed to parse configuration",suggestion:"Ensure your configuration file is valid TypeScript/JSON"}),Y=e({slug:"config-validation-error",category:"CONFIG",status:422,title:"Configuration validation failed",suggestion:"Check the configuration against the schema requirements"}),B=e({slug:"config-type-error",category:"CONFIG",status:400,title:"Configuration type mismatch",suggestion:"Ensure configuration values match expected types"}),$=e({slug:"import-map-invalid",category:"CONFIG",status:400,title:"Invalid import map configuration",suggestion:"Check your import map syntax and paths"}),j=e({slug:"cors-config-invalid",category:"CONFIG",status:400,title:"Invalid CORS configuration",suggestion:"Review CORS settings in your configuration"}),q=e({slug:"config-validation-failed",category:"CONFIG",status:400,title:"Configuration validation failed",suggestion:"Check configuration values against requirements"}),z=e({slug:"webhook-config-invalid",category:"CONFIG",status:400,title:"Invalid webhook configuration",suggestion:"Check webhook definition fields, target settings, and eventFilter conditions"}),W=e({slug:"schedule-config-invalid",category:"CONFIG",status:400,title:"Invalid schedule configuration",suggestion:"Check schedule definition fields, cron expression, target settings, and positive-integer limits"}),J=e({slug:"trigger-config-invalid",category:"CONFIG",status:400,title:"Invalid trigger configuration",suggestion:"Check trigger ID format (lowercase, alphanumeric, dots/slashes/hyphens) and ensure all input values are JSON-serializable"}),y={"config-not-found":w,"config-invalid":F,"config-parse-error":H,"config-validation-error":Y,"config-type-error":B,"import-map-invalid":$,"cors-config-invalid":j,"config-validation-failed":q,"webhook-config-invalid":z,"schedule-config-invalid":W,"trigger-config-invalid":J};var X=e({slug:"build-failed",category:"BUILD",status:500,title:"Build process failed",suggestion:"Check the build output for specific errors"}),K=e({slug:"bundle-error",category:"BUILD",status:500,title:"Bundle generation failed",suggestion:"Review bundler output for details"}),Z=e({slug:"typescript-error",category:"BUILD",status:500,title:"TypeScript compilation error",suggestion:"Fix TypeScript errors shown in the output"}),Q=e({slug:"mdx-compile-error",category:"BUILD",status:500,title:"MDX compilation failed",suggestion:"Check your MDX file syntax"}),ee=e({slug:"asset-optimization-error",category:"BUILD",status:500,title:"Asset optimization failed",suggestion:"Check asset file formats and paths"}),te=e({slug:"ssg-generation-error",category:"BUILD",status:500,title:"Static site generation failed",suggestion:"Review SSG configuration and data fetching"}),re=e({slug:"sourcemap-error",category:"BUILD",status:500,title:"Source map generation failed",suggestion:"Check source map configuration"}),oe=e({slug:"compilation-error",category:"BUILD",status:500,title:"Compilation failed",suggestion:"Review compiler output for specific errors"}),I={"build-failed":X,"bundle-error":K,"typescript-error":Z,"mdx-compile-error":Q,"asset-optimization-error":ee,"ssg-generation-error":te,"sourcemap-error":re,"compilation-error":oe};var ne=e({slug:"hydration-mismatch",category:"RUNTIME",status:500,title:"Client/server hydration mismatch",suggestion:"Ensure server and client render the same content"}),se=e({slug:"render-error",category:"RUNTIME",status:500,title:"Component render failed",suggestion:"Check component for runtime errors"}),ie=e({slug:"component-error",category:"RUNTIME",status:500,title:"Component execution error",suggestion:"Review component logic and props"}),ae=e({slug:"layout-not-found",category:"RUNTIME",status:404,title:"Layout component not found",suggestion:"Ensure layout file exists at the expected path"}),ce=e({slug:"page-not-found",category:"RUNTIME",status:404,title:"Page component not found",suggestion:"Check that the page file exists in the routes directory"}),ue=e({slug:"api-error",category:"RUNTIME",status:500,title:"API route handler error",suggestion:"Review API route handler for errors"}),le=e({slug:"middleware-error",category:"RUNTIME",status:500,title:"Middleware execution error",suggestion:"Check middleware function for errors"}),ge=e({slug:"trigger-target-not-found",category:"RUNTIME",status:404,title:"Trigger target not found",suggestion:"Ensure the referenced task or workflow ID is registered in the project"}),pe=e({slug:"trigger-execution-failed",category:"RUNTIME",status:500,title:"Trigger target execution failed",suggestion:"Check the task or workflow for errors and review the trigger input"}),de=e({slug:"trigger-not-supported",category:"RUNTIME",status:501,title:"Trigger target type not supported in local runtime",suggestion:"Use a workflow or task target for local trigger runs; agent targets require the Cloud runtime"}),O={"hydration-mismatch":ne,"render-error":se,"component-error":ie,"layout-not-found":ae,"page-not-found":ce,"api-error":ue,"middleware-error":le,"trigger-target-not-found":ge,"trigger-execution-failed":pe,"trigger-not-supported":de};var Ee=e({slug:"route-conflict",category:"ROUTE",status:409,title:"Conflicting route definitions",suggestion:"Rename or reorganize conflicting route files"}),Re=e({slug:"invalid-route-file",category:"ROUTE",status:400,title:"Invalid route file structure",suggestion:"Ensure route file exports required functions"}),fe=e({slug:"route-handler-invalid",category:"ROUTE",status:400,title:"Invalid route handler export",suggestion:"Export a valid handler function from the route file"}),me=e({slug:"dynamic-route-error",category:"ROUTE",status:500,title:"Dynamic route parsing failed",suggestion:"Check dynamic route segment syntax"}),_e=e({slug:"route-params-error",category:"ROUTE",status:400,title:"Route parameters invalid",suggestion:"Validate route parameter values"}),ye=e({slug:"api-route-error",category:"ROUTE",status:500,title:"API route definition error",suggestion:"Review API route configuration"}),T={"route-conflict":Ee,"invalid-route-file":Re,"route-handler-invalid":fe,"dynamic-route-error":me,"route-params-error":_e,"api-route-error":ye};var Ie=e({slug:"module-not-found",category:"MODULE",status:404,title:"Module could not be resolved",suggestion:"Check the import path and ensure the module is installed"}),Oe=e({slug:"import-resolution-error",category:"MODULE",status:500,title:"Import path resolution failed",suggestion:"Verify import paths and module configuration"}),Te=e({slug:"circular-dependency",category:"MODULE",status:500,title:"Circular dependency detected",suggestion:"Refactor imports to break the circular dependency"}),he=e({slug:"invalid-import",category:"MODULE",status:400,title:"Invalid import statement",suggestion:"Fix import syntax or path"}),xe=e({slug:"dependency-missing",category:"MODULE",status:404,title:"Required dependency not installed",suggestion:"Install the missing dependency with your package manager"}),Se=e({slug:"version-mismatch",category:"MODULE",status:409,title:"Dependency version mismatch",suggestion:"Update dependencies to compatible versions"}),h={"module-not-found":Ie,"import-resolution-error":Oe,"circular-dependency":Te,"invalid-import":he,"dependency-missing":xe,"version-mismatch":Se};var Ne=e({slug:"port-in-use",category:"SERVER",status:409,title:"Server port already in use",suggestion:"Use a different port or stop the process using this port"}),ve=e({slug:"server-start-error",category:"SERVER",status:500,title:"Server failed to start",suggestion:"Check server configuration and port availability"}),Ce=e({slug:"cache-error",category:"SERVER",status:500,title:"Cache operation failed",suggestion:"Clear the cache and try again"}),Ae=e({slug:"file-watch-error",category:"SERVER",status:500,title:"File watcher error",suggestion:"Restart the development server"}),De=e({slug:"request-error",category:"SERVER",status:500,title:"HTTP request handling error",suggestion:"Check request handler and middleware"}),Le=e({slug:"service-overloaded",category:"SERVER",status:503,title:"Service overloaded",suggestion:"Reduce load or scale up resources"}),Ue=e({slug:"semaphore-timeout",category:"SERVER",status:503,title:"Semaphore acquire timeout",suggestion:"Reduce concurrency or increase the semaphore acquire timeout"}),be=e({slug:"circuit-breaker-open",category:"SERVER",status:503,title:"Circuit breaker is open",suggestion:"Wait for the breaker reset timeout before retrying"}),Me=e({slug:"cache-path-mismatch",category:"SERVER",status:500,title:"Cache path mismatch",suggestion:"Clear the cache directory and rebuild"}),Ge=e({slug:"network-error",category:"SERVER",status:502,title:"Network operation failed",suggestion:"Check network connectivity and retry"}),Pe=e({slug:"api-client-error",category:"SERVER",status:500,title:"API client request failed",suggestion:"Check API connectivity and authentication"}),Ve=e({slug:"token-storage-error",category:"SERVER",status:500,title:"Token storage operation failed",suggestion:"Check token storage backend and credentials"}),ke=e({slug:"cache-invariant-violation",category:"SERVER",status:500,title:"Cache path invariant violated",suggestion:"Clear the cache and rebuild"}),we=e({slug:"release-not-found",category:"SERVER",status:404,title:"No active release found",suggestion:"Deploy the project to create a release for this environment"}),Fe=e({slug:"fallback-exhausted",category:"SERVER",status:500,title:"Primary and fallback operations both failed",suggestion:"Check service availability and connectivity"}),x={"port-in-use":Ne,"server-start-error":ve,"cache-error":Ce,"file-watch-error":Ae,"request-error":De,"service-overloaded":Le,"semaphore-timeout":Ue,"circuit-breaker-open":be,"cache-path-mismatch":Me,"network-error":Ge,"api-client-error":Pe,"token-storage-error":Ve,"cache-invariant-violation":ke,"release-not-found":we,"fallback-exhausted":Fe};var He=e({slug:"client-boundary-violation",category:"BOUNDARY",status:400,title:"Client boundary rule violation",suggestion:"Add \'use client\' directive or move code to a client component"}),Ye=e({slug:"server-only-in-client",category:"BOUNDARY",status:400,title:"Server-only code in client component",suggestion:"Move server-only code to a server component"}),Be=e({slug:"client-only-in-server",category:"BOUNDARY",status:400,title:"Client-only code in server component",suggestion:"Move client-only code to a client component"}),$e=e({slug:"invalid-use-client",category:"BOUNDARY",status:400,title:"Invalid \'use client\' directive",suggestion:"Place \'use client\' at the top of the file"}),je=e({slug:"invalid-use-server",category:"BOUNDARY",status:400,title:"Invalid \'use server\' directive",suggestion:"Place \'use server\' at the top of the file or function"}),qe=e({slug:"rsc-payload-error",category:"BOUNDARY",status:500,title:"RSC payload serialization error",suggestion:"Ensure props are serializable (no functions, symbols, etc.)"}),S={"client-boundary-violation":He,"server-only-in-client":Ye,"client-only-in-server":Be,"invalid-use-client":$e,"invalid-use-server":je,"rsc-payload-error":qe};var ze=e({slug:"hmr-error",category:"DEV",status:500,title:"Hot module replacement error",suggestion:"Restart the development server"}),We=e({slug:"dev-server-error",category:"DEV",status:500,title:"Development server error",suggestion:"Check the dev server logs and restart"}),Je=e({slug:"fast-refresh-error",category:"DEV",status:500,title:"Fast refresh failed",suggestion:"Save the file again or restart the dev server"}),Xe=e({slug:"error-overlay-error",category:"DEV",status:500,title:"Error overlay failed",suggestion:"Check browser console for details"}),Ke=e({slug:"source-map-error",category:"DEV",status:500,title:"Source map loading error",suggestion:"Rebuild or clear cache"}),N={"hmr-error":ze,"dev-server-error":We,"fast-refresh-error":Je,"error-overlay-error":Xe,"source-map-error":Ke};var Ze=e({slug:"deployment-error",category:"DEPLOY",status:500,title:"Deployment process failed",suggestion:"Check deployment logs for details"}),Qe=e({slug:"platform-error",category:"DEPLOY",status:500,title:"Platform-specific error",suggestion:"Check platform documentation and requirements"}),et=e({slug:"env-var-missing",category:"DEPLOY",status:500,title:"Required environment variable missing",suggestion:"Set the required environment variable"}),tt=e({slug:"production-build-required",category:"DEPLOY",status:400,title:"Production build required",suggestion:"Run \'vf build\' before deploying"}),v={"deployment-error":Ze,"platform-error":Qe,"env-var-missing":et,"production-build-required":tt};var rt=e({slug:"agent-error",category:"AGENT",status:500,title:"Agent operation error",suggestion:"Check agent configuration and logs"}),ot=e({slug:"agent-not-found",category:"AGENT",status:404,title:"Agent not found",suggestion:"Verify the agent ID exists"}),nt=e({slug:"agent-timeout",category:"AGENT",status:408,title:"Agent operation timed out",suggestion:"Increase timeout or simplify the request"}),st=e({slug:"agent-intent-error",category:"AGENT",status:400,title:"Agent intent parsing error",suggestion:"Rephrase the request more clearly"}),it=e({slug:"orchestration-error",category:"AGENT",status:500,title:"Multi-agent orchestration error",suggestion:"Check agent coordination logic"}),at=e({slug:"cost-limit-exceeded",category:"AGENT",status:429,title:"Cost limit exceeded",suggestion:"Wait for the budget period to reset or increase the limit"}),ct=e({slug:"tool-id-conflict",category:"AGENT",status:409,title:"Tool ID conflict",suggestion:"Use a unique tool ID or rename one of the conflicting tools"}),C={"agent-error":rt,"agent-not-found":ot,"agent-timeout":nt,"agent-intent-error":st,"orchestration-error":it,"cost-limit-exceeded":at,"tool-id-conflict":ct};var ut=e({slug:"unknown-error",category:"GENERAL",status:500,title:"Unknown/unclassified error",suggestion:"Check logs for more details"}),lt=e({slug:"permission-denied",category:"GENERAL",status:403,title:"File/resource permission denied",suggestion:"Check file permissions and access rights"}),gt=e({slug:"file-not-found",category:"GENERAL",status:404,title:"File not found",suggestion:"Verify the file path exists"}),pt=e({slug:"resource-not-found",category:"GENERAL",status:404,title:"Requested resource not found",suggestion:"Verify the referenced resource ID or name exists"}),dt=e({slug:"invalid-argument",category:"GENERAL",status:400,title:"Invalid function argument",suggestion:"Check argument types and values"}),Et=e({slug:"timeout-error",category:"GENERAL",status:408,title:"Operation timed out",suggestion:"Increase timeout or optimize the operation"}),Rt=e({slug:"initialization-error",category:"GENERAL",status:500,title:"Initialization failed",suggestion:"Check initialization requirements and dependencies"}),ft=e({slug:"not-supported",category:"GENERAL",status:501,title:"Feature not supported",suggestion:"Check documentation for supported features"}),m=e({slug:"security-violation",category:"GENERAL",status:403,title:"Security violation detected",suggestion:"Check for path traversal or unauthorized access attempts"}),mt=e({slug:"input-validation-failed",category:"GENERAL",status:400,title:"Input validation failed",suggestion:"Check request input against validation rules"}),A={"unknown-error":ut,"permission-denied":lt,"file-not-found":gt,"resource-not-found":pt,"invalid-argument":dt,"timeout-error":Et,"initialization-error":Rt,"not-supported":ft,"security-violation":m,"input-validation-failed":mt};var fr={...y,...I,...O,...T,...h,...x,...S,...N,...v,...C,...A};var _t=[{source:String.raw`]*>[\\s\\S]*?<\\/script>`,flags:"gi",name:"inline script"},{source:String.raw`javascript:`,flags:"gi",name:"javascript: URL"},{source:String.raw`\\bon\\w+\\s*=`,flags:"gi",name:"event handler attribute"},{source:String.raw`data:\\s*text\\/html`,flags:"gi",name:"data: HTML URL"}];function yt(){return _t.map(({source:r,flags:t,name:o})=>({pattern:new RegExp(r,t),name:o}))}function It(){let r=globalThis;return r.__VERYFRONT_DEV__===!0||r.Deno?.env?.get?.("VERYFRONT_ENV")==="development"}function D(r,t={}){let{allowInlineScripts:o=!1,strict:n=!1,warn:i=!0}=t;for(let{pattern:c,name:u}of yt())if(!(o&&u==="inline script")&&(c.lastIndex=0,!!c.test(r)&&(i&&console.warn(`[Security] Suspicious ${u} detected in server HTML`),n||!It())))throw m.create({detail:`Potentially unsafe HTML: ${u} detected`});return r}var E=class{constructor(t,o){l(this,"prefix",t);l(this,"level",o)}log(t,o,n,...i){this.level>t||o?.(n,...i)}debug(t,...o){this.log(0,console.debug,`[${this.prefix}] DEBUG: ${t}`,...o)}info(t,...o){this.log(1,console.log,`[${this.prefix}] ${t}`,...o)}warn(t,...o){this.log(2,console.warn,`[${this.prefix}] WARN: ${t}`,...o)}error(t,...o){this.log(3,console.error,`[${this.prefix}] ERROR: ${t}`,...o)}};function Ot(){if(typeof window>"u")return 2;let r=globalThis;return r.__VERYFRONT_DEV__||r.__RSC_DEV__?r.__VERYFRONT_DEBUG__||r.__RSC_DEBUG__?0:1:2}var R=Ot(),p=new E("RSC",R),br=new E("PREFETCH",R),Mr=new E("HYDRATE",R),Gr=new E("VERYFRONT",R);var s="/_veryfront",_={RSC:`${s}/rsc/`,FS:`${s}/fs/`,MODULES:`${s}/modules/`,PAGES:`${s}/pages/`,DATA:`${s}/data/`,LIB:`${s}/lib/`,CHUNKS:`${s}/chunks/`,CLIENT:`${s}/client/`},L={HMR_RUNTIME:`${s}/hmr-runtime.js`,HMR:`${s}/hmr.js`,ERROR_OVERLAY:`${s}/error-overlay.js`,DEV_LOADER:`${s}/dev-loader.js`,CLIENT_LOG:`${s}/log`,CLIENT_JS:`${s}/client.js`,ROUTER_JS:`${s}/router.js`,PREFETCH_JS:`${s}/prefetch.js`,MANIFEST_JSON:`${s}/manifest.json`,APP_JS:`${s}/app.js`,RSC_CLIENT:`${s}/rsc/client.js`,RSC_MANIFEST:`${s}/rsc/manifest`,RSC_STREAM:`${s}/rsc/stream`,RSC_PAYLOAD:`${s}/rsc/payload`,RSC_RENDER:`${s}/rsc/render`,RSC_PAGE:`${s}/rsc/page`,RSC_MODULE:`${s}/rsc/module`,RSC_DOM:`${s}/rsc/dom.js`,LIB_CHAT_REACT:`${s}/lib/chat/react.js`,LIB_CHAT_COMPONENTS:`${s}/lib/chat/components.js`,LIB_CHAT_PRIMITIVES:`${s}/lib/chat/primitives.js`};var ht={ROOT:".veryfront",CACHE:".veryfront/cache",KV:".veryfront/kv",LOGS:".veryfront/logs",TMP:".veryfront/tmp"},Hr=ht.CACHE;var Yr={HMR_RUNTIME:L.HMR_RUNTIME,ERROR_OVERLAY:L.ERROR_OVERLAY};var qr=_.RSC,zr=_.FS;var U="rsc-root";function M(r,t){let o=t==="root"?U:`rsc-slot-${t}`,n=r.getElementById(o);if(n)return n;let i=r.createElement("div");return i.id=o,r.body.appendChild(i),i}function xt(r,t){if(t.type!=="slot")return;let o=M(r,t.id);o.innerHTML=D(String(t.html??""))}function b(r,t){let o=t.split(`\n`),n=o.pop()??"";for(let i of o){let c=i.trim();if(!c)continue;let u;try{u=JSON.parse(c)}catch(d){p.debug("[client-dom] malformed NDJSON line",{line:c,error:d instanceof Error?d.message:String(d)});continue}if(!u||typeof u!="object")continue;let g=u;if(g.type==="slot"){xt(r,g);try{vt(r,g.id||"root")}catch(d){p.debug("[client-dom] hydration optional failed",d)}}}return n}function St(r){return new Promise((t,o)=>{let n=()=>o(new DOMException("aborted","AbortError"));if(r.aborted){n();return}r.addEventListener("abort",n,{once:!0})})}async function Zr(r,t=document,o){let n="body"in r?r:null,i=n?.body??r;if(!i)return;let c=i.getReader(),u=new TextDecoder,g="",d=!1;try{for(;;){if(o?.aborted)throw new DOMException("aborted","AbortError");let a=c.read(),{done:G,value:P}=o?await Promise.race([a,St(o)]):await a;if(G){d=!0;break}g+=u.decode(P,{stream:!0}),g=b(t,g)}g&&b(t,`${g}\n`)}catch(a){throw a instanceof Error&&a.name==="AbortError"||p.debug("[client-dom] consumeNdjsonStream error",a),a}finally{try{await c.cancel()}catch(a){d||p.debug("[client-dom] reader.cancel failed",a)}try{c.releaseLock()}catch(a){p.debug("[client-dom] reader.releaseLock failed",a)}if(typeof i.cancel=="function")try{await i.cancel()}catch(a){p.debug("[client-dom] stream.cancel failed",a)}if(typeof n?.body?.cancel=="function")try{await n.body.cancel()}catch(a){p.debug("[client-dom] response.body.cancel failed",a)}}}function Nt(r,t){let o=M(r,t),n=[],i=c=>{let u=c;u.dataset?.clientRef&&n.push(u);for(let g of c.children)i(g)};return i(o),n}function vt(r,t){let o=Nt(r,t);for(let n of o){let i=n.dataset?.clientRef;i&&(n.dataset.hydrated="true",p.debug("[client-dom] marked for hydration",i))}}export{Zr as consumeNdjsonStream,M as getContainer};\n'; diff --git a/src/server/services/rsc/orchestrators/stream-handler.ts b/src/server/services/rsc/orchestrators/stream-handler.ts index 8dbd78ec44..dac7a1f15d 100644 --- a/src/server/services/rsc/orchestrators/stream-handler.ts +++ b/src/server/services/rsc/orchestrators/stream-handler.ts @@ -1,4 +1,4 @@ -import { serverLogger } from "#veryfront/utils"; +import { serverLogger, sleep } from "#veryfront/utils"; import type { RSCPayload } from "#veryfront/rendering/rsc/types.ts"; import type { RenderHandler } from "./render-handler.ts"; import type { StreamSlot } from "./types.ts"; @@ -80,10 +80,6 @@ export class StreamHandler { } } -function sleep(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); // no cleanup needed: one-shot -} - function enqueueSlot( controller: ReadableStreamDefaultController, encoder: TextEncoder, diff --git a/src/studio/bridge/bridge-bundle.generated.ts b/src/studio/bridge/bridge-bundle.generated.ts index a3dd0f4854..f48552ff5d 100644 --- a/src/studio/bridge/bridge-bundle.generated.ts +++ b/src/studio/bridge/bridge-bundle.generated.ts @@ -7,4 +7,4 @@ */ // deno-fmt-ignore - keep the bundled script as a generated JSON string. -export const STUDIO_BRIDGE_BUNDLE: string = "// src/studio/bridge/bridge-logger.ts\nfunction normalizeContext(context) {\n if (!context) return void 0;\n if (context instanceof Error) {\n return { error: context.message, ...context.stack ? { stack: context.stack } : {} };\n }\n return context;\n}\nfunction formatArgs(message, context) {\n const normalized = normalizeContext(context);\n if (!normalized || Object.keys(normalized).length === 0) {\n return [message];\n }\n return [message, normalized];\n}\nvar logger = {\n debug(message, context) {\n console.debug(...formatArgs(message, context));\n },\n info(message, context) {\n console.log(...formatArgs(message, context));\n },\n warn(message, context) {\n console.warn(...formatArgs(message, context));\n },\n error(message, context) {\n console.error(...formatArgs(message, context));\n }\n};\n\n// src/studio/bridge/bridge-config.ts\nvar config = null;\nvar DEFAULT_CONFIG = {\n projectId: \"\",\n pageId: \"\",\n pagePath: \"\",\n wsUrl: \"\",\n yjsGuid: \"\",\n studioMode: \"advanced\",\n debugSkipInit: false,\n debugExposeInternals: false\n};\nfunction resolveStudioMode(value, queryString) {\n const params = new URLSearchParams(queryString);\n return value === \"simple\" || params.get(\"vf_studio_mode\") === \"simple\" ? \"simple\" : \"advanced\";\n}\nfunction normalizeConfig(raw) {\n const queryString = window.location.search;\n if (!raw || typeof raw !== \"object\") {\n logger.warn(\"No bridge config found on window.__VF_BRIDGE_CONFIG__\");\n return {\n ...DEFAULT_CONFIG,\n studioMode: resolveStudioMode(void 0, queryString)\n };\n }\n return {\n ...DEFAULT_CONFIG,\n projectId: String(raw.projectId ?? \"\"),\n pageId: String(raw.pageId ?? \"\"),\n pagePath: String(raw.pagePath ?? raw.pageId ?? \"\"),\n wsUrl: String(raw.wsUrl ?? \"\"),\n yjsGuid: String(raw.yjsGuid ?? \"\"),\n studioMode: resolveStudioMode(raw.studioMode, queryString),\n debugSkipInit: !!raw.debugSkipInit,\n debugExposeInternals: !!raw.debugExposeInternals\n };\n}\nfunction initConfig() {\n const raw = globalThis.__VF_BRIDGE_CONFIG__;\n config = normalizeConfig(raw);\n}\nfunction getConfig() {\n if (!config) {\n throw new Error(\"[StudioBridge] Config not initialized. Call initConfig() first.\");\n }\n return config;\n}\n\n// src/studio/bridge/bridge-state.ts\nvar state = {\n // Inspector\n inspectMode: false,\n selectedNodeId: null,\n hoveredNodeId: null,\n lastTreeSignature: \"\",\n // Overlays\n hoverOverlay: null,\n selectionOverlay: null,\n // Console\n originalConsole: {},\n logCounter: 0,\n // Screenshot\n html2canvasLoaded: false,\n html2canvasPromise: null\n};\nvar CONSOLE_METHODS = [\n \"log\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\",\n \"table\",\n \"clear\",\n \"dir\"\n];\nvar DOM_IGNORE_TAGS = [\"SCRIPT\", \"STYLE\", \"LINK\", \"META\", \"NOSCRIPT\"];\n\n// src/security/http/studio-origin-policy.ts\nvar HOSTED_STUDIO_ORIGINS = [\n \"https://veryfront.com\",\n \"https://studio.veryfront.com\",\n \"https://veryfront.org\",\n \"https://studio.veryfront.org\"\n];\nvar hostedStudioOrigins = new Set(HOSTED_STUDIO_ORIGINS);\nfunction resolveTrustedStudioOrigin(origin) {\n try {\n const url = new URL(origin);\n const isWebProtocol = url.protocol === \"http:\" || url.protocol === \"https:\";\n if (isWebProtocol && url.hostname === \"localhost\") {\n return url.origin;\n }\n if (url.protocol === \"https:\" && hostedStudioOrigins.has(url.origin)) {\n return url.origin;\n }\n } catch {\n }\n return null;\n}\n\n// src/studio/bridge/bridge-messaging.ts\nvar MAX_PENDING_MESSAGES = 100;\nvar studioOrigin = null;\nvar pendingMessages = [];\nfunction send(message, origin) {\n try {\n window.parent.postMessage(message, origin);\n } catch (e) {\n logger.debug(\"postMessage failed\", e instanceof Error ? e : { error: String(e) });\n }\n}\nfunction flushPending() {\n if (!studioOrigin) return;\n const origin = studioOrigin;\n while (pendingMessages.length > 0) {\n const msg = pendingMessages.shift();\n send(msg, origin);\n }\n}\nfunction postToStudio(message) {\n if (!window.parent || window.parent === window) return;\n if (!studioOrigin) {\n if (pendingMessages.length >= MAX_PENDING_MESSAGES) {\n pendingMessages.shift();\n }\n pendingMessages.push(message);\n return;\n }\n send(message, studioOrigin);\n}\nfunction isFromStudio(event) {\n if (!window.parent || event.source !== window.parent) return false;\n const trustedOrigin = resolveTrustedStudioOrigin(event.origin || \"\");\n if (!trustedOrigin) return false;\n if (studioOrigin) {\n return trustedOrigin === studioOrigin;\n }\n studioOrigin = trustedOrigin;\n flushPending();\n return true;\n}\n\n// src/studio/bridge/bridge-style-helpers.ts\nvar OVERLAY_STYLE_ELEMENT_ID = \"vf-overlay-styles\";\nfunction hasOverlayStyleElement(documentLike) {\n return documentLike.getElementById(OVERLAY_STYLE_ELEMENT_ID) !== null;\n}\nfunction createOverlayStyleElement(documentLike, css) {\n const style = documentLike.createElement(\"style\");\n style.id = OVERLAY_STYLE_ELEMENT_ID;\n style.textContent = css;\n return style;\n}\nfunction normalizeStyleInjectionWarningContext(error) {\n return error instanceof Error ? error : {\n error: String(error)\n };\n}\n\n// src/studio/bridge/bridge-styles.ts\nvar OVERLAY_CSS = `\n /* ------------------------------------------------------------------ */\n /* Overlays (hover / selection inspector) */\n /* ------------------------------------------------------------------ */\n\n .vf-overlay {\n position: fixed;\n pointer-events: none;\n z-index: 99999;\n box-sizing: border-box;\n transition: all 0.05s ease-out;\n }\n .vf-overlay-hover {\n border: 2px solid oklch(0.6852 0.162 241.8);\n background: oklch(0.6852 0.162 241.8 / 0.06);\n }\n .vf-overlay-selection {\n border: 2px solid oklch(0.6852 0.162 241.8);\n background: oklch(0.6852 0.162 241.8 / 0.1);\n }\n .vf-overlay-label {\n position: absolute;\n top: -22px;\n left: -2px;\n background: oklch(0.6852 0.162 241.8);\n color: white;\n font-size: 11px;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n padding: 2px 6px;\n border-radius: 3px 3px 0 0;\n white-space: nowrap;\n pointer-events: none;\n }\n .vf-overlay-label-bottom {\n top: auto;\n bottom: -22px;\n border-radius: 0 0 3px 3px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Edit button (floating CTA) */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-edit-button {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: 100001;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 9999px;\n background: oklch(0.2768 0 0);\n color: oklch(0.9512 0.008 98.88);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 13px;\n font-weight: 500;\n line-height: 1;\n padding: 10px 16px;\n cursor: pointer;\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n transition: transform 100ms ease, box-shadow 100ms ease;\n }\n .vf-markdown-edit-button:hover {\n box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);\n }\n .vf-markdown-edit-button:active {\n transform: scale(0.98);\n }\n\n /* ------------------------------------------------------------------ */\n /* Editor root */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor {\n position: fixed;\n inset: 0;\n z-index: 100000;\n background: oklch(1 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n display: none;\n }\n\n .vf-markdown-editor__history {\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 13px;\n line-height: 1;\n min-width: 28px;\n height: 28px;\n padding: 0 8px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n .vf-markdown-editor__history:hover {\n background: oklch(0.93 0 0);\n }\n\n /* ------------------------------------------------------------------ */\n /* Surface (editor area) */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__surface-wrap {\n position: relative;\n height: 100vh;\n }\n\n .vf-markdown-editor__surface {\n width: 100%;\n max-width: 980px;\n margin: 0 auto;\n height: 100%;\n overflow: auto;\n outline: none;\n position: relative;\n z-index: 1;\n background: transparent;\n padding: 32px 40px;\n box-sizing: border-box;\n }\n\n /* ------------------------------------------------------------------ */\n /* Selection overlay */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__selection-overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 2;\n display: none;\n }\n\n .vf-markdown-editor__selection-highlight {\n position: absolute;\n border-radius: 3px;\n opacity: 0.26;\n }\n\n .vf-markdown-editor__selection-caret {\n position: absolute;\n width: 2px;\n border-radius: 1px;\n }\n\n .vf-markdown-editor__selection-label {\n position: absolute;\n transform: translateY(-100%);\n margin-top: -4px;\n border-radius: 9999px;\n padding: 1px 7px;\n font-size: 10px;\n line-height: 1.4;\n white-space: nowrap;\n color: oklch(1 0 0);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.16);\n }\n\n /* ------------------------------------------------------------------ */\n /* Slash menu */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__slash-menu {\n position: fixed;\n z-index: 100005;\n min-width: 240px;\n max-width: 300px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n display: none;\n }\n\n .vf-markdown-editor__slash-section {\n padding: 8px 10px 4px;\n font-size: 11px;\n font-weight: 600;\n color: oklch(0.55 0.005 95.11);\n text-transform: none;\n letter-spacing: 0;\n }\n\n .vf-markdown-editor__slash-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n border: 0;\n border-radius: 6px;\n background: transparent;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n text-align: left;\n padding: 6px 10px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__slash-item:hover,\n .vf-markdown-editor__slash-item[data-active='true'] {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__slash-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 4px;\n background: oklch(1 0 0);\n font-size: 13px;\n font-weight: 600;\n color: oklch(0.2768 0 0);\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__slash-item-title {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: oklch(0.2768 0 0);\n line-height: 1.35;\n flex: 1;\n }\n\n .vf-markdown-editor__slash-item-desc {\n display: none;\n }\n\n .vf-markdown-editor__slash-shortcut {\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__slash-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 10px;\n margin-top: 2px;\n border-top: 1px solid oklch(0.9 0 0);\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n }\n\n .vf-markdown-editor__slash-footer-key {\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 3px;\n padding: 1px 4px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n }\n\n /* ------------------------------------------------------------------ */\n /* Inline toolbar */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__inline-toolbar {\n position: fixed;\n z-index: 100006;\n display: none;\n flex-direction: column;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n }\n\n .vf-markdown-editor__inline-row {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 2px 0;\n }\n\n .vf-markdown-editor__inline-separator {\n width: 1px;\n height: 20px;\n background: oklch(0.9 0 0);\n margin: 0 2px;\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__inline-button {\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n min-width: 26px;\n height: 26px;\n padding: 0 7px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__inline-button:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__inline-button.active {\n background: oklch(0.6852 0.162 241.8 / 0.14);\n color: oklch(0.6852 0.162 241.8);\n }\n\n .vf-markdown-editor__inline-button[data-format='bold'] {\n font-weight: 700;\n }\n\n .vf-markdown-editor__inline-button[data-format='italic'] {\n font-style: italic;\n }\n\n .vf-markdown-editor__inline-button[data-format='strikethrough'] {\n text-decoration: line-through;\n }\n\n /* ------------------------------------------------------------------ */\n /* Block type trigger */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-trigger {\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n height: 26px;\n padding: 0 7px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 2px;\n white-space: nowrap;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__block-trigger:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__block-trigger::after {\n content: '\\\\25BE';\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block dropdown */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-dropdown {\n position: absolute;\n top: 100%;\n left: 4px;\n z-index: 100007;\n min-width: 160px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n margin-top: 4px;\n display: none;\n }\n\n .vf-markdown-editor__block-option {\n display: block;\n width: 100%;\n border: 0;\n border-radius: 6px;\n background: transparent;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n text-align: left;\n padding: 6px 10px;\n font-size: 13px;\n font-weight: 500;\n color: oklch(0.2768 0 0);\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__block-option:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__block-option.active {\n background: oklch(0.6852 0.162 241.8 / 0.1);\n color: oklch(0.6852 0.162 241.8);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drag handle */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-handle {\n position: fixed;\n z-index: 100007;\n display: none;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-size: 12px;\n font-weight: 700;\n line-height: 1;\n width: 28px;\n height: 28px;\n padding: 0;\n cursor: grab;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1);\n transition: background 75ms ease, box-shadow 75ms ease;\n }\n\n .vf-markdown-editor__block-handle:hover {\n background: oklch(0.6852 0.162 241.8 / 0.1);\n }\n\n .vf-markdown-editor__block-handle[data-dragging='true'] {\n cursor: grabbing;\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drop indicator */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-drop-indicator {\n position: fixed;\n z-index: 100006;\n display: none;\n height: 2px;\n border-radius: 9999px;\n background: oklch(0.6852 0.162 241.8);\n box-shadow: 0 1px 6px oklch(0.6852 0.162 241.8 / 0.5);\n }\n\n .vf-markdown-editor__block-drop-label {\n position: fixed;\n z-index: 100007;\n display: none;\n border-radius: 9999px;\n border: 1px solid oklch(0.6852 0.162 241.8 / 0.24);\n background: oklch(1 0 0 / 0.96);\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n line-height: 1.2;\n padding: 3px 8px;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drag ghost */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-drag-ghost {\n position: fixed;\n top: -9999px;\n left: -9999px;\n width: 260px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);\n padding: 8px 10px;\n }\n\n .vf-markdown-editor__block-drag-ghost-title {\n display: block;\n font-size: 11px;\n font-weight: 700;\n color: oklch(0.2768 0 0);\n }\n\n .vf-markdown-editor__block-drag-ghost-text {\n display: block;\n margin-top: 4px;\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n line-height: 1.35;\n }\n\n /* ------------------------------------------------------------------ */\n /* MDX blocks bar */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__mdx-blocks {\n display: none;\n gap: 8px;\n padding: 8px 16px;\n border-bottom: 1px solid oklch(0.9 0 0);\n background: oklch(0.97 0 0 / 0.95);\n overflow-x: auto;\n }\n\n .vf-markdown-editor__mdx-block {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n padding: 6px 8px;\n }\n\n .vf-markdown-editor__mdx-block-label {\n font-size: 11px;\n color: oklch(0.2768 0 0);\n white-space: nowrap;\n }\n\n .vf-markdown-editor__mdx-note {\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n white-space: nowrap;\n }\n\n .vf-markdown-editor__mdx-open {\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n line-height: 1;\n padding: 5px 7px;\n cursor: pointer;\n white-space: nowrap;\n transition: background 75ms ease;\n }\n .vf-markdown-editor__mdx-open:hover {\n background: oklch(0.93 0 0);\n }\n\n /* ------------------------------------------------------------------ */\n /* Lexical surface overrides */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__surface [data-lexical-editor] {\n outline: none;\n }\n\n .vf-markdown-editor__surface s,\n .vf-markdown-editor__surface del,\n .vf-markdown-editor__surface [style*='line-through'] {\n text-decoration: line-through;\n }\n\n .vf-markdown-editor__surface p:empty::before {\n content: \"Type '/' for commands\";\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n font-style: normal;\n }\n\n .vf-markdown-editor__surface h1:empty::before {\n content: 'Heading 1';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface h2:empty::before {\n content: 'Heading 2';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface h3:empty::before {\n content: 'Heading 3';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface blockquote:empty::before {\n content: 'Quote';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface p {\n min-height: 1.5em;\n }\n\n /* ------------------------------------------------------------------ */\n /* Textarea fallback */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__textarea {\n width: 100%;\n height: 100vh;\n border: 0;\n outline: none;\n resize: none;\n display: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;\n font-size: 14px;\n line-height: 1.6;\n color: oklch(0.2768 0 0);\n background: transparent;\n padding: 16px;\n box-sizing: border-box;\n }\n\n /* ================================================================== */\n /* DARK MODE */\n /* ================================================================== */\n\n [data-theme='dark'] .vf-markdown-editor {\n background: oklch(0.2768 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__history {\n background: oklch(0.3211 0 0);\n border-color: oklch(0.42 0.0017 106.48);\n color: oklch(0.9512 0.008 98.88);\n }\n [data-theme='dark'] .vf-markdown-editor__history:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__textarea {\n color: oklch(0.9512 0.008 98.88);\n }\n\n /* Slash menu \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__slash-menu {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-section {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-item:hover,\n [data-theme='dark'] .vf-markdown-editor__slash-item[data-active='true'] {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-icon {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-item-title {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-shortcut {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-footer {\n border-top-color: oklch(0.3 0.01 220);\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-footer-key {\n border-color: oklch(0.42 0.0017 106.48);\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* Inline toolbar \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__inline-toolbar {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-separator {\n background: oklch(0.3 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button.active {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n color: oklch(0.75 0.14 241.8);\n }\n\n /* Block trigger \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger::after {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* Block dropdown \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-dropdown {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option.active {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n color: oklch(0.75 0.14 241.8);\n }\n\n /* Placeholder text \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__surface p:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h1:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h2:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h3:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface blockquote:empty::before {\n color: oklch(0.5338 0.0046 106.55 / 0.5);\n }\n\n /* Block drag \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-handle {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-handle:hover {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drop-label {\n border-color: oklch(0.6852 0.162 241.8 / 0.35);\n background: oklch(0.18 0.01 220 / 0.96);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost-title {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost-text {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* MDX blocks \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__mdx-blocks {\n border-bottom-color: oklch(0.3 0.01 220);\n background: oklch(0.18 0.01 220 / 0.8);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-block {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-block-label {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-note {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-open {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.2768 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n [data-theme='dark'] .vf-markdown-editor__mdx-open:hover {\n background: oklch(0.25 0.01 220);\n }\n\n /* Edit button \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-edit-button {\n background: oklch(0.9512 0.008 98.88);\n color: oklch(0.2768 0 0);\n border-color: oklch(0.42 0.0017 106.48);\n }\n`;\nfunction injectOverlayStyles() {\n if (hasOverlayStyleElement(document)) return;\n const style = createOverlayStyleElement(document, OVERLAY_CSS);\n try {\n document.head.appendChild(style);\n if (!style.sheet) {\n logger.warn(\"Inline style injection may be blocked by CSP (style-src)\");\n }\n } catch (error) {\n logger.warn(\n \"Failed to inject bridge styles. This may be caused by CSP style-src restrictions.\",\n normalizeStyleInjectionWarningContext(error)\n );\n }\n}\n\n// src/studio/bridge/bridge-constants.ts\nvar DATA_VF_ID = \"data-vf-id\";\nvar DATA_VF_SELECTOR = \"data-vf-selector\";\nvar DATA_VF_TEXT = \"data-vf-text\";\nvar DATA_VF_IGNORE = \"data-vf-ignore\";\nvar DATA_NODE_ID = \"data-node-id\";\nvar DATA_NODE_FILE = \"data-node-file\";\nvar DATA_NODE_NAME = \"data-node-name\";\nvar DATA_NODE_LINE = \"data-node-line\";\nvar DATA_NODE_COLUMN = \"data-node-column\";\nvar DATA_NODE_SOURCE = \"data-node-source\";\n\n// src/studio/bridge/bridge-utils.ts\nfunction debounce(fn, ms) {\n let timer;\n const debounced = function(...args) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, ms);\n };\n debounced.cancel = () => {\n clearTimeout(timer);\n timer = void 0;\n };\n return debounced;\n}\n\n// src/studio/bridge/bridge-inspector.ts\nfunction createOverlay(type) {\n const overlay = document.createElement(\"div\");\n overlay.className = \"vf-overlay vf-overlay-\" + type;\n overlay.setAttribute(DATA_VF_IGNORE, \"true\");\n const label = document.createElement(\"div\");\n label.className = \"vf-overlay-label\";\n overlay.appendChild(label);\n overlay.style.display = \"none\";\n document.body.appendChild(overlay);\n return overlay;\n}\nfunction hideOverlay(overlay) {\n if (overlay) overlay.style.display = \"none\";\n}\nfunction positionOverlay(overlay, element, nodeName) {\n if (!overlay) return;\n if (!element) {\n hideOverlay(overlay);\n return;\n }\n const rect = element.getBoundingClientRect();\n overlay.style.display = \"block\";\n overlay.style.top = rect.top + \"px\";\n overlay.style.left = rect.left + \"px\";\n overlay.style.width = rect.width + \"px\";\n overlay.style.height = rect.height + \"px\";\n const label = overlay.querySelector(\".vf-overlay-label\");\n if (label) {\n label.textContent = nodeName;\n if (rect.top < 24) {\n label.classList.add(\"vf-overlay-label-bottom\");\n } else {\n label.classList.remove(\"vf-overlay-label-bottom\");\n }\n }\n}\nfunction getNodeName(element) {\n const vfId = element.getAttribute(DATA_VF_ID);\n if (vfId) return vfId.split(\"_\")[0] ?? vfId;\n return element.tagName.toLowerCase();\n}\nfunction findElementById(nodeId) {\n if (!nodeId) return null;\n return document.querySelector(\"[\" + DATA_VF_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_VF_SELECTOR + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_NODE_ID + '=\"' + nodeId + '\"]');\n}\nfunction isValidElement(el) {\n return !!el && el.nodeType === Node.ELEMENT_NODE && !DOM_IGNORE_TAGS.includes(el.tagName) && !el.hasAttribute(DATA_VF_IGNORE) && el.style.display !== \"none\";\n}\nfunction getNodeType(el) {\n const vfId = el.getAttribute(DATA_VF_ID) || \"\";\n if (vfId && /^[A-Z]/.test(vfId)) return \"component\";\n if (el.hasAttribute(DATA_VF_TEXT)) return \"text\";\n if (el.getAttribute(DATA_NODE_SOURCE) === \"md\") return \"markdown\";\n return \"element\";\n}\nfunction buildNavigatorTree(root) {\n const config3 = getConfig();\n let nodeIndex = 0;\n function processElement(el, parentId) {\n if (!isValidElement(el)) {\n const children = [];\n Array.from(el.children || []).forEach((child) => {\n children.push(...processElement(child, parentId));\n });\n return children;\n }\n let id = el.getAttribute(DATA_VF_ID) || el.getAttribute(DATA_NODE_ID) || el.getAttribute(DATA_VF_SELECTOR);\n if (!id) {\n id = \"vf-\" + el.tagName.toLowerCase() + \"-\" + ++nodeIndex;\n el.setAttribute(DATA_VF_SELECTOR, id);\n }\n const vfId = el.getAttribute(DATA_VF_ID);\n const name = vfId ? vfId.split(\"_\")[0] ?? vfId : el.tagName.toLowerCase();\n const node = {\n id,\n name,\n type: getNodeType(el),\n path: config3.pagePath,\n parentId,\n start: {\n line: parseInt(el.getAttribute(DATA_NODE_LINE) || \"0\", 10),\n column: parseInt(el.getAttribute(DATA_NODE_COLUMN) || \"0\", 10)\n },\n end: { line: 0, column: 0 },\n children: [],\n text: el.hasAttribute(DATA_VF_TEXT) ? el.textContent?.trim() : void 0,\n isRemote: false\n };\n Array.from(el.children || []).forEach((child) => {\n node.children.push(...processElement(child, id));\n });\n return [node];\n }\n const rootNode = {\n id: \"root\",\n name: \"root\",\n type: \"root\",\n path: \"\",\n parentId: \"\",\n start: { line: 0, column: 0 },\n end: { line: 0, column: 0 },\n children: [],\n isRemote: false\n };\n Array.from(root.children || []).forEach((child) => {\n rootNode.children.push(...processElement(child, \"root\"));\n });\n return rootNode;\n}\nfunction createTreeSignature(root) {\n const validElements = Array.from(root.querySelectorAll(\"*\")).filter((el) => isValidElement(el));\n return validElements.length + \"-\" + validElements.map((el) => el.tagName).join(\"\");\n}\nvar treeUpdateTimer = null;\nvar mutationObserver = null;\nfunction sendTreeUpdate() {\n const config3 = getConfig();\n const root = document.getElementById(\"root\") || document.body;\n if (!root) return;\n const signature = createTreeSignature(root);\n if (signature === state.lastTreeSignature) return;\n state.lastTreeSignature = signature;\n postToStudio({\n action: \"treeUpdated\",\n id: config3.pageId,\n url: window.location.href,\n tree: buildNavigatorTree(root),\n sourceHash: globalThis.__VERYFRONT_SOURCE_HASH__ || null\n });\n}\nfunction debouncedTreeUpdate() {\n if (treeUpdateTimer) clearTimeout(treeUpdateTimer);\n treeUpdateTimer = setTimeout(sendTreeUpdate, 150);\n}\nfunction setupMutationObserver() {\n const root = document.getElementById(\"root\") || document.body;\n if (!root) return;\n mutationObserver = new MutationObserver(function(mutations) {\n const hasRelevantChanges = mutations.some(\n (m) => m.type === \"childList\" || m.type === \"characterData\"\n );\n if (!hasRelevantChanges) return;\n if (state.selectedNodeId && !findElementById(state.selectedNodeId)) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n postToStudio({ action: \"setSelectedNode\", id: null });\n }\n debouncedTreeUpdate();\n });\n mutationObserver.observe(root, { childList: true, characterData: true, subtree: true });\n sendTreeUpdate();\n}\nfunction showOverlay(overlay, nodeId) {\n if (!nodeId) {\n hideOverlay(overlay);\n return;\n }\n const el = findElementById(nodeId);\n if (!el) {\n hideOverlay(overlay);\n return;\n }\n positionOverlay(overlay, el, getNodeName(el));\n}\nfunction showHoverOverlay(nodeId) {\n showOverlay(state.hoverOverlay, nodeId);\n}\nfunction showSelectionOverlay(nodeId) {\n showOverlay(state.selectionOverlay, nodeId);\n}\nfunction scrollToElement(nodeId) {\n const el = document.querySelector(\"[\" + DATA_VF_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_NODE_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_VF_SELECTOR + '*=\"' + nodeId + '\"]');\n if (el) el.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n}\nfunction getDirectText(el) {\n let text = \"\";\n for (let i = 0; i < el.childNodes.length; i++) {\n const node = el.childNodes[i];\n if (node?.nodeType === Node.TEXT_NODE) {\n text += node.textContent || \"\";\n }\n }\n return text.trim();\n}\nfunction setupInspectMode() {\n const INSPECTABLE_SELECTOR = \"[\" + DATA_VF_ID + \"], [\" + DATA_VF_SELECTOR + \"], [\" + DATA_NODE_ID + \"], [\" + DATA_NODE_FILE + \"]\";\n function getElementId(el) {\n return el.getAttribute(DATA_VF_ID) || el.getAttribute(DATA_NODE_ID) || el.getAttribute(DATA_VF_SELECTOR);\n }\n document.addEventListener(\n \"click\",\n function(event) {\n if (!state.inspectMode) return;\n event.preventDefault();\n event.stopPropagation();\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n postToStudio({ action: \"setSelectedNode\", id: null });\n return;\n }\n const id = getElementId(target);\n state.selectedNodeId = id;\n showSelectionOverlay(id);\n postToStudio({\n action: \"setSelectedNode\",\n id,\n node: {\n name: target.getAttribute(DATA_NODE_NAME) || target.tagName.toLowerCase(),\n type: getNodeType(target),\n file: target.getAttribute(DATA_NODE_FILE) || getConfig().pagePath,\n line: parseInt(target.getAttribute(DATA_NODE_LINE) || \"0\", 10),\n column: parseInt(target.getAttribute(DATA_NODE_COLUMN) || \"0\", 10),\n text: getDirectText(target).slice(0, 200)\n }\n });\n },\n true\n );\n document.addEventListener(\"pointerover\", function(event) {\n if (!state.inspectMode || event.pointerType === \"touch\") return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) return;\n const id = getElementId(target);\n if (id === state.hoveredNodeId) return;\n state.hoveredNodeId = id;\n showHoverOverlay(id);\n });\n document.addEventListener(\"pointerout\", function(event) {\n if (!state.inspectMode || event.pointerType === \"touch\") return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) return;\n const relatedTarget = event.relatedTarget;\n if (relatedTarget && target.contains(relatedTarget)) return;\n state.hoveredNodeId = null;\n hideOverlay(state.hoverOverlay);\n });\n const updateOverlays = debounce(function() {\n if (state.inspectMode && state.hoveredNodeId) showHoverOverlay(state.hoveredNodeId);\n if (state.selectedNodeId) showSelectionOverlay(state.selectedNodeId);\n }, 16);\n window.addEventListener(\"scroll\", updateOverlays, true);\n window.addEventListener(\"resize\", updateOverlays);\n}\nfunction setColorMode(mode) {\n document.documentElement.setAttribute(\"data-theme\", mode);\n document.documentElement.classList.remove(\"light\", \"dark\");\n document.documentElement.classList.add(mode);\n}\n\n// src/studio/bridge/bridge-console.ts\nfunction setupConsoleCapture() {\n const consoleObj = console;\n CONSOLE_METHODS.forEach((method) => {\n state.originalConsole[method] = consoleObj[method];\n consoleObj[method] = function(...args) {\n state.originalConsole[method].apply(console, args);\n const logId = \"vf-\" + Date.now() + \"-\" + ++state.logCounter;\n const formattedData = args.map((arg) => {\n try {\n if (arg instanceof Error) {\n return { __isError: true, message: arg.message, stack: arg.stack, name: arg.name };\n }\n if (arg === void 0) return { __isUndefined: true };\n if (arg === null) return null;\n if (typeof arg === \"function\") {\n return { __isFunction: true, name: arg.name || \"anonymous\" };\n }\n if (typeof arg === \"symbol\") return { __isSymbol: true, description: arg.description };\n if (typeof arg === \"object\") return JSON.parse(JSON.stringify(arg));\n return arg;\n } catch (_) {\n return String(arg);\n }\n });\n postToStudio({\n action: \"logEvent\",\n value: {\n id: logId,\n method,\n data: formattedData,\n timestamp: (/* @__PURE__ */ new Date()).toISOString()\n }\n });\n };\n });\n}\nfunction setupErrorHandling() {\n function hideOverlays() {\n hideOverlay(state.hoverOverlay);\n hideOverlay(state.selectionOverlay);\n }\n window.addEventListener(\"error\", function(event) {\n hideOverlays();\n postToStudio({\n action: \"runtimeError\",\n url: window.location.href,\n errors: [\n {\n type: \"error\",\n message: event.message,\n file: event.filename,\n line: event.lineno,\n column: event.colno\n }\n ]\n });\n });\n window.addEventListener(\"unhandledrejection\", function(event) {\n hideOverlays();\n const reason = event.reason;\n postToStudio({\n action: \"runtimeError\",\n url: window.location.href,\n errors: [\n {\n type: \"error\",\n message: reason instanceof Error ? reason.message : String(reason),\n file: reason instanceof Error ? reason.stack : void 0\n }\n ]\n });\n });\n}\n\n// src/studio/bridge/bridge-screenshot.ts\nfunction loadHtml2Canvas() {\n if (state.html2canvasLoaded) return Promise.resolve();\n if (state.html2canvasPromise) return state.html2canvasPromise;\n state.html2canvasPromise = new Promise((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.src = \"https://cdn.jsdelivr.net/npm/html2canvas-pro@2.0.0/dist/html2canvas-pro.min.js\";\n script.onload = () => {\n state.html2canvasLoaded = true;\n resolve();\n };\n script.onerror = (event) => {\n logger.warn(\n \"Failed to load html2canvas script. This may be caused by CSP script-src restrictions.\",\n { event: String(event) }\n );\n reject(new Error(\"Failed to load html2canvas script\"));\n };\n try {\n document.head.appendChild(script);\n } catch (error) {\n logger.warn(\n \"Failed to append html2canvas script element. This may be caused by CSP script-src restrictions.\",\n error instanceof Error ? error : { error: String(error) }\n );\n reject(\n error instanceof Error ? error : new Error(\"Failed to append html2canvas script element\")\n );\n }\n });\n return state.html2canvasPromise;\n}\nasync function captureScreenshot(options) {\n const { scrollTo, fullPage, quality = 0.8 } = options || {};\n const originalScrollY = window.scrollY;\n try {\n await loadHtml2Canvas();\n if (typeof scrollTo === \"number\") {\n window.scrollTo(0, scrollTo);\n await new Promise((r) => setTimeout(r, 150));\n }\n const canvasOptions = {\n useCORS: true,\n logging: false,\n scale: window.devicePixelRatio || 1\n };\n if (fullPage) {\n canvasOptions.height = document.documentElement.scrollHeight;\n canvasOptions.windowHeight = document.documentElement.scrollHeight;\n canvasOptions.y = 0;\n window.scrollTo(0, 0);\n await new Promise((r) => setTimeout(r, 100));\n }\n const html2canvasFn = window.html2canvas.default || window.html2canvas;\n const canvas = await html2canvasFn(document.body, canvasOptions);\n if (!canvas || canvas.width === 0 || canvas.height === 0) {\n logger.error(\"html2canvas produced empty canvas\", {\n width: canvas?.width,\n height: canvas?.height\n });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: \"html2canvas produced empty canvas (0x0 dimensions)\"\n };\n }\n const dataUrl = canvas.toDataURL(\"image/png\", quality);\n if (!dataUrl || !dataUrl.startsWith(\"data:image/\") || dataUrl.length < 100) {\n logger.error(\"html2canvas produced invalid data URL\", {\n dataUrlPreview: dataUrl?.substring(0, 50)\n });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: \"html2canvas produced invalid image data\"\n };\n }\n window.scrollTo(0, originalScrollY);\n return {\n success: true,\n data: dataUrl,\n width: canvas.width,\n height: canvas.height,\n scrollY: window.scrollY,\n totalHeight: document.documentElement.scrollHeight,\n viewportHeight: window.innerHeight,\n url: window.location.href\n };\n } catch (error) {\n logger.error(\"html2canvas error\", error instanceof Error ? error : { error: String(error) });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\nasync function captureMultipleSections(sectionCount) {\n const originalScrollY = window.scrollY;\n const results = [];\n const totalHeight = document.documentElement.scrollHeight;\n const viewportHeight = window.innerHeight;\n const sections = sectionCount || Math.ceil(totalHeight / viewportHeight);\n try {\n for (let i = 0; i < sections; i++) {\n const scrollY = Math.min(i * viewportHeight, totalHeight - viewportHeight);\n const result = await captureScreenshot({ scrollTo: scrollY });\n if (result.success) {\n results.push({ ...result, section: i + 1, totalSections: sections });\n }\n }\n } finally {\n window.scrollTo(0, originalScrollY);\n }\n return results;\n}\n\n// src/studio/bridge/bridge-message-handler.ts\nvar SAFE_PROTOCOLS = /* @__PURE__ */ new Set([\"http:\", \"https:\"]);\nfunction sanitizeNavigationUrl(url) {\n if (typeof url !== \"string\" || url.length === 0) return null;\n try {\n const parsed = new URL(url, window.location.origin);\n if (!SAFE_PROTOCOLS.has(parsed.protocol)) return null;\n if (parsed.origin === window.location.origin) return parsed.href;\n const host = parsed.hostname;\n if (host !== \"veryfront.com\" && !host.endsWith(\".veryfront.com\")) return null;\n return parsed.href;\n } catch {\n return null;\n }\n}\nfunction clearSelection(notifyStudio = false) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n if (notifyStudio) {\n postToStudio({ action: \"setSelectedNode\", id: null });\n }\n}\nfunction disableInspectMode(deselectElements = false) {\n state.inspectMode = false;\n hideOverlay(state.hoverOverlay);\n state.hoveredNodeId = null;\n if (deselectElements) {\n clearSelection();\n }\n}\nfunction postScreenshotResult(requestId, result) {\n postToStudio({\n action: \"screenshotResult\",\n requestId,\n multiple: false,\n ...result\n });\n}\nasync function handleScreenshotRequest(message) {\n if (message.multipleSections) {\n const results = await captureMultipleSections(message.sectionCount);\n postToStudio({\n action: \"screenshotResult\",\n requestId: message.requestId,\n multiple: true,\n results\n });\n return;\n }\n const result = await captureScreenshot(message.options);\n postScreenshotResult(message.requestId, result);\n}\nfunction handleStudioMessage(event) {\n if (!isFromStudio(event)) return;\n const message = event.data;\n if (!message?.action) return;\n const config3 = getConfig();\n switch (message.action) {\n case \"routeChange\": {\n const safeUrl = sanitizeNavigationUrl(message.url);\n if (!safeUrl) {\n logger.warn(\"[StudioBridge] Blocked unsafe URL in routeChange\", { url: message.url });\n return;\n }\n if (state.selectedNodeId) {\n clearSelection(true);\n }\n postToStudio({\n action: \"onPageTransitionStart\",\n url: safeUrl,\n projectId: config3.projectId\n });\n window.location.href = safeUrl;\n return;\n }\n case \"reload\":\n window.location.reload();\n return;\n case \"goBack\":\n window.history.back();\n return;\n case \"goForward\":\n window.history.forward();\n return;\n case \"colorMode\":\n setColorMode(message.value);\n return;\n case \"toggleInspectMode\":\n state.inspectMode = message.value;\n if (state.inspectMode) return;\n disableInspectMode(message.deselectElements);\n return;\n case \"setSelectedNode\":\n state.selectedNodeId = message.id;\n showSelectionOverlay(message.id);\n if (message.scroll) scrollToElement(message.id);\n return;\n case \"setHoveredNode\":\n if (!state.inspectMode) showHoverOverlay(message.id);\n return;\n case \"screenshot\":\n void handleScreenshotRequest(message);\n return;\n default:\n logger.debug(\"Unknown action\", { action: message.action });\n return;\n }\n}\n\n// src/studio/bridge/bridge-init.ts\nfunction notifyAppLoaded() {\n const config3 = getConfig();\n postToStudio({ action: \"appLoaded\", url: window.location.href });\n postToStudio({\n action: \"appUpdated\",\n url: window.location.href,\n id: config3.pageId,\n isInitialLoad: true,\n errors: [],\n warnings: []\n });\n postToStudio({\n action: \"onPageTransitionEnd\",\n url: window.location.href,\n projectId: config3.projectId,\n id: config3.pageId,\n params: {}\n });\n}\nfunction notifyAppUnloaded() {\n postToStudio({ action: \"appUnloaded\", url: window.location.href });\n}\nfunction init() {\n const params = new URLSearchParams(window.location.search);\n const studioEmbed = params.get(\"studio_embed\") === \"true\";\n const isStandalone = window.parent === window && !studioEmbed;\n if (isStandalone) {\n logger.debug(\n \"[StudioBridge] Not in iframe and not studio_embed mode, skipping initialization\"\n );\n return;\n }\n logger.debug(\"Initializing...\");\n if (!isStandalone) {\n injectOverlayStyles();\n state.hoverOverlay = createOverlay(\"hover\");\n state.selectionOverlay = createOverlay(\"selection\");\n setupConsoleCapture();\n setupErrorHandling();\n setupInspectMode();\n }\n window.addEventListener(\"message\", handleStudioMessage);\n if (!isStandalone) {\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", function() {\n notifyAppLoaded();\n setupMutationObserver();\n }, { once: true });\n } else {\n notifyAppLoaded();\n setupMutationObserver();\n }\n window.addEventListener(\"beforeunload\", notifyAppUnloaded, { once: true });\n }\n const colorMode = params.get(\"color_mode\");\n if (colorMode) setColorMode(colorMode);\n if (!isStandalone) {\n const inspectModeParam = params.get(\"inspect_mode\");\n if (inspectModeParam === \"true\") {\n state.inspectMode = true;\n logger.debug(\"Inspect mode enabled from query param\");\n }\n }\n logger.debug(\"Initialized successfully\");\n}\n\n// src/studio/bridge/bridge-coordinator.ts\ninitConfig();\nvar config2 = getConfig();\nif (!config2.debugSkipInit) {\n init();\n}\n"; +export const STUDIO_BRIDGE_BUNDLE: string = "// src/studio/bridge/bridge-logger.ts\nfunction normalizeContext(context) {\n if (!context) return void 0;\n if (context instanceof Error) {\n return { error: context.message, ...context.stack ? { stack: context.stack } : {} };\n }\n return context;\n}\nfunction formatArgs(message, context) {\n const normalized = normalizeContext(context);\n if (!normalized || Object.keys(normalized).length === 0) {\n return [message];\n }\n return [message, normalized];\n}\nvar logger = {\n debug(message, context) {\n console.debug(...formatArgs(message, context));\n },\n info(message, context) {\n console.log(...formatArgs(message, context));\n },\n warn(message, context) {\n console.warn(...formatArgs(message, context));\n },\n error(message, context) {\n console.error(...formatArgs(message, context));\n }\n};\n\n// src/studio/bridge/bridge-config.ts\nvar config = null;\nvar DEFAULT_CONFIG = {\n projectId: \"\",\n pageId: \"\",\n pagePath: \"\",\n wsUrl: \"\",\n yjsGuid: \"\",\n studioMode: \"advanced\",\n debugSkipInit: false,\n debugExposeInternals: false\n};\nfunction resolveStudioMode(value, queryString) {\n const params = new URLSearchParams(queryString);\n return value === \"simple\" || params.get(\"vf_studio_mode\") === \"simple\" ? \"simple\" : \"advanced\";\n}\nfunction normalizeConfig(raw) {\n const queryString = window.location.search;\n if (!raw || typeof raw !== \"object\") {\n logger.warn(\"No bridge config found on window.__VF_BRIDGE_CONFIG__\");\n return {\n ...DEFAULT_CONFIG,\n studioMode: resolveStudioMode(void 0, queryString)\n };\n }\n return {\n ...DEFAULT_CONFIG,\n projectId: String(raw.projectId ?? \"\"),\n pageId: String(raw.pageId ?? \"\"),\n pagePath: String(raw.pagePath ?? raw.pageId ?? \"\"),\n wsUrl: String(raw.wsUrl ?? \"\"),\n yjsGuid: String(raw.yjsGuid ?? \"\"),\n studioMode: resolveStudioMode(raw.studioMode, queryString),\n debugSkipInit: !!raw.debugSkipInit,\n debugExposeInternals: !!raw.debugExposeInternals\n };\n}\nfunction initConfig() {\n const raw = globalThis.__VF_BRIDGE_CONFIG__;\n config = normalizeConfig(raw);\n}\nfunction getConfig() {\n if (!config) {\n throw new Error(\"[StudioBridge] Config not initialized. Call initConfig() first.\");\n }\n return config;\n}\n\n// src/studio/bridge/bridge-state.ts\nvar state = {\n // Inspector\n inspectMode: false,\n selectedNodeId: null,\n hoveredNodeId: null,\n lastTreeSignature: \"\",\n // Overlays\n hoverOverlay: null,\n selectionOverlay: null,\n // Console\n originalConsole: {},\n logCounter: 0,\n // Screenshot\n html2canvasLoaded: false,\n html2canvasPromise: null\n};\nvar CONSOLE_METHODS = [\n \"log\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\",\n \"table\",\n \"clear\",\n \"dir\"\n];\nvar DOM_IGNORE_TAGS = [\"SCRIPT\", \"STYLE\", \"LINK\", \"META\", \"NOSCRIPT\"];\n\n// src/security/http/studio-origin-policy.ts\nvar HOSTED_STUDIO_ORIGINS = [\n \"https://veryfront.com\",\n \"https://veryfront.org\"\n];\nvar hostedStudioOrigins = new Set(HOSTED_STUDIO_ORIGINS);\nfunction resolveTrustedStudioOrigin(origin) {\n try {\n const url = new URL(origin);\n const isWebProtocol = url.protocol === \"http:\" || url.protocol === \"https:\";\n if (isWebProtocol && url.hostname === \"localhost\") {\n return url.origin;\n }\n if (url.protocol === \"https:\" && hostedStudioOrigins.has(url.origin)) {\n return url.origin;\n }\n } catch {\n }\n return null;\n}\n\n// src/studio/bridge/bridge-messaging.ts\nvar MAX_PENDING_MESSAGES = 100;\nvar studioOrigin = null;\nvar pendingMessages = [];\nfunction send(message, origin) {\n try {\n window.parent.postMessage(message, origin);\n } catch (e) {\n logger.debug(\"postMessage failed\", e instanceof Error ? e : { error: String(e) });\n }\n}\nfunction flushPending() {\n if (!studioOrigin) return;\n const origin = studioOrigin;\n while (pendingMessages.length > 0) {\n const msg = pendingMessages.shift();\n send(msg, origin);\n }\n}\nfunction postToStudio(message) {\n if (!window.parent || window.parent === window) return;\n if (!studioOrigin) {\n if (pendingMessages.length >= MAX_PENDING_MESSAGES) {\n pendingMessages.shift();\n }\n pendingMessages.push(message);\n return;\n }\n send(message, studioOrigin);\n}\nfunction isFromStudio(event) {\n if (!window.parent || event.source !== window.parent) return false;\n const trustedOrigin = resolveTrustedStudioOrigin(event.origin || \"\");\n if (!trustedOrigin) return false;\n if (studioOrigin) {\n return trustedOrigin === studioOrigin;\n }\n studioOrigin = trustedOrigin;\n flushPending();\n return true;\n}\n\n// src/studio/bridge/bridge-style-helpers.ts\nvar OVERLAY_STYLE_ELEMENT_ID = \"vf-overlay-styles\";\nfunction hasOverlayStyleElement(documentLike) {\n return documentLike.getElementById(OVERLAY_STYLE_ELEMENT_ID) !== null;\n}\nfunction createOverlayStyleElement(documentLike, css) {\n const style = documentLike.createElement(\"style\");\n style.id = OVERLAY_STYLE_ELEMENT_ID;\n style.textContent = css;\n return style;\n}\nfunction normalizeStyleInjectionWarningContext(error) {\n return error instanceof Error ? error : {\n error: String(error)\n };\n}\n\n// src/studio/bridge/bridge-styles.ts\nvar OVERLAY_CSS = `\n /* ------------------------------------------------------------------ */\n /* Overlays (hover / selection inspector) */\n /* ------------------------------------------------------------------ */\n\n .vf-overlay {\n position: fixed;\n pointer-events: none;\n z-index: 99999;\n box-sizing: border-box;\n transition: all 0.05s ease-out;\n }\n .vf-overlay-hover {\n border: 2px solid oklch(0.6852 0.162 241.8);\n background: oklch(0.6852 0.162 241.8 / 0.06);\n }\n .vf-overlay-selection {\n border: 2px solid oklch(0.6852 0.162 241.8);\n background: oklch(0.6852 0.162 241.8 / 0.1);\n }\n .vf-overlay-label {\n position: absolute;\n top: -22px;\n left: -2px;\n background: oklch(0.6852 0.162 241.8);\n color: white;\n font-size: 11px;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n padding: 2px 6px;\n border-radius: 3px 3px 0 0;\n white-space: nowrap;\n pointer-events: none;\n }\n .vf-overlay-label-bottom {\n top: auto;\n bottom: -22px;\n border-radius: 0 0 3px 3px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Edit button (floating CTA) */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-edit-button {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: 100001;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 9999px;\n background: oklch(0.2768 0 0);\n color: oklch(0.9512 0.008 98.88);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 13px;\n font-weight: 500;\n line-height: 1;\n padding: 10px 16px;\n cursor: pointer;\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n transition: transform 100ms ease, box-shadow 100ms ease;\n }\n .vf-markdown-edit-button:hover {\n box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);\n }\n .vf-markdown-edit-button:active {\n transform: scale(0.98);\n }\n\n /* ------------------------------------------------------------------ */\n /* Editor root */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor {\n position: fixed;\n inset: 0;\n z-index: 100000;\n background: oklch(1 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n display: none;\n }\n\n .vf-markdown-editor__history {\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 13px;\n line-height: 1;\n min-width: 28px;\n height: 28px;\n padding: 0 8px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n .vf-markdown-editor__history:hover {\n background: oklch(0.93 0 0);\n }\n\n /* ------------------------------------------------------------------ */\n /* Surface (editor area) */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__surface-wrap {\n position: relative;\n height: 100vh;\n }\n\n .vf-markdown-editor__surface {\n width: 100%;\n max-width: 980px;\n margin: 0 auto;\n height: 100%;\n overflow: auto;\n outline: none;\n position: relative;\n z-index: 1;\n background: transparent;\n padding: 32px 40px;\n box-sizing: border-box;\n }\n\n /* ------------------------------------------------------------------ */\n /* Selection overlay */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__selection-overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 2;\n display: none;\n }\n\n .vf-markdown-editor__selection-highlight {\n position: absolute;\n border-radius: 3px;\n opacity: 0.26;\n }\n\n .vf-markdown-editor__selection-caret {\n position: absolute;\n width: 2px;\n border-radius: 1px;\n }\n\n .vf-markdown-editor__selection-label {\n position: absolute;\n transform: translateY(-100%);\n margin-top: -4px;\n border-radius: 9999px;\n padding: 1px 7px;\n font-size: 10px;\n line-height: 1.4;\n white-space: nowrap;\n color: oklch(1 0 0);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.16);\n }\n\n /* ------------------------------------------------------------------ */\n /* Slash menu */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__slash-menu {\n position: fixed;\n z-index: 100005;\n min-width: 240px;\n max-width: 300px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n display: none;\n }\n\n .vf-markdown-editor__slash-section {\n padding: 8px 10px 4px;\n font-size: 11px;\n font-weight: 600;\n color: oklch(0.55 0.005 95.11);\n text-transform: none;\n letter-spacing: 0;\n }\n\n .vf-markdown-editor__slash-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n border: 0;\n border-radius: 6px;\n background: transparent;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n text-align: left;\n padding: 6px 10px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__slash-item:hover,\n .vf-markdown-editor__slash-item[data-active='true'] {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__slash-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 4px;\n background: oklch(1 0 0);\n font-size: 13px;\n font-weight: 600;\n color: oklch(0.2768 0 0);\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__slash-item-title {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: oklch(0.2768 0 0);\n line-height: 1.35;\n flex: 1;\n }\n\n .vf-markdown-editor__slash-item-desc {\n display: none;\n }\n\n .vf-markdown-editor__slash-shortcut {\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__slash-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 10px;\n margin-top: 2px;\n border-top: 1px solid oklch(0.9 0 0);\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n }\n\n .vf-markdown-editor__slash-footer-key {\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 3px;\n padding: 1px 4px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n }\n\n /* ------------------------------------------------------------------ */\n /* Inline toolbar */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__inline-toolbar {\n position: fixed;\n z-index: 100006;\n display: none;\n flex-direction: column;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n }\n\n .vf-markdown-editor__inline-row {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 2px 0;\n }\n\n .vf-markdown-editor__inline-separator {\n width: 1px;\n height: 20px;\n background: oklch(0.9 0 0);\n margin: 0 2px;\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__inline-button {\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n min-width: 26px;\n height: 26px;\n padding: 0 7px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__inline-button:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__inline-button.active {\n background: oklch(0.6852 0.162 241.8 / 0.14);\n color: oklch(0.6852 0.162 241.8);\n }\n\n .vf-markdown-editor__inline-button[data-format='bold'] {\n font-weight: 700;\n }\n\n .vf-markdown-editor__inline-button[data-format='italic'] {\n font-style: italic;\n }\n\n .vf-markdown-editor__inline-button[data-format='strikethrough'] {\n text-decoration: line-through;\n }\n\n /* ------------------------------------------------------------------ */\n /* Block type trigger */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-trigger {\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n height: 26px;\n padding: 0 7px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 2px;\n white-space: nowrap;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__block-trigger:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__block-trigger::after {\n content: '\\\\25BE';\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block dropdown */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-dropdown {\n position: absolute;\n top: 100%;\n left: 4px;\n z-index: 100007;\n min-width: 160px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n margin-top: 4px;\n display: none;\n }\n\n .vf-markdown-editor__block-option {\n display: block;\n width: 100%;\n border: 0;\n border-radius: 6px;\n background: transparent;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n text-align: left;\n padding: 6px 10px;\n font-size: 13px;\n font-weight: 500;\n color: oklch(0.2768 0 0);\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__block-option:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__block-option.active {\n background: oklch(0.6852 0.162 241.8 / 0.1);\n color: oklch(0.6852 0.162 241.8);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drag handle */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-handle {\n position: fixed;\n z-index: 100007;\n display: none;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-size: 12px;\n font-weight: 700;\n line-height: 1;\n width: 28px;\n height: 28px;\n padding: 0;\n cursor: grab;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1);\n transition: background 75ms ease, box-shadow 75ms ease;\n }\n\n .vf-markdown-editor__block-handle:hover {\n background: oklch(0.6852 0.162 241.8 / 0.1);\n }\n\n .vf-markdown-editor__block-handle[data-dragging='true'] {\n cursor: grabbing;\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drop indicator */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-drop-indicator {\n position: fixed;\n z-index: 100006;\n display: none;\n height: 2px;\n border-radius: 9999px;\n background: oklch(0.6852 0.162 241.8);\n box-shadow: 0 1px 6px oklch(0.6852 0.162 241.8 / 0.5);\n }\n\n .vf-markdown-editor__block-drop-label {\n position: fixed;\n z-index: 100007;\n display: none;\n border-radius: 9999px;\n border: 1px solid oklch(0.6852 0.162 241.8 / 0.24);\n background: oklch(1 0 0 / 0.96);\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n line-height: 1.2;\n padding: 3px 8px;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drag ghost */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-drag-ghost {\n position: fixed;\n top: -9999px;\n left: -9999px;\n width: 260px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);\n padding: 8px 10px;\n }\n\n .vf-markdown-editor__block-drag-ghost-title {\n display: block;\n font-size: 11px;\n font-weight: 700;\n color: oklch(0.2768 0 0);\n }\n\n .vf-markdown-editor__block-drag-ghost-text {\n display: block;\n margin-top: 4px;\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n line-height: 1.35;\n }\n\n /* ------------------------------------------------------------------ */\n /* MDX blocks bar */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__mdx-blocks {\n display: none;\n gap: 8px;\n padding: 8px 16px;\n border-bottom: 1px solid oklch(0.9 0 0);\n background: oklch(0.97 0 0 / 0.95);\n overflow-x: auto;\n }\n\n .vf-markdown-editor__mdx-block {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n padding: 6px 8px;\n }\n\n .vf-markdown-editor__mdx-block-label {\n font-size: 11px;\n color: oklch(0.2768 0 0);\n white-space: nowrap;\n }\n\n .vf-markdown-editor__mdx-note {\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n white-space: nowrap;\n }\n\n .vf-markdown-editor__mdx-open {\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n line-height: 1;\n padding: 5px 7px;\n cursor: pointer;\n white-space: nowrap;\n transition: background 75ms ease;\n }\n .vf-markdown-editor__mdx-open:hover {\n background: oklch(0.93 0 0);\n }\n\n /* ------------------------------------------------------------------ */\n /* Lexical surface overrides */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__surface [data-lexical-editor] {\n outline: none;\n }\n\n .vf-markdown-editor__surface s,\n .vf-markdown-editor__surface del,\n .vf-markdown-editor__surface [style*='line-through'] {\n text-decoration: line-through;\n }\n\n .vf-markdown-editor__surface p:empty::before {\n content: \"Type '/' for commands\";\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n font-style: normal;\n }\n\n .vf-markdown-editor__surface h1:empty::before {\n content: 'Heading 1';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface h2:empty::before {\n content: 'Heading 2';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface h3:empty::before {\n content: 'Heading 3';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface blockquote:empty::before {\n content: 'Quote';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface p {\n min-height: 1.5em;\n }\n\n /* ------------------------------------------------------------------ */\n /* Textarea fallback */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__textarea {\n width: 100%;\n height: 100vh;\n border: 0;\n outline: none;\n resize: none;\n display: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;\n font-size: 14px;\n line-height: 1.6;\n color: oklch(0.2768 0 0);\n background: transparent;\n padding: 16px;\n box-sizing: border-box;\n }\n\n /* ================================================================== */\n /* DARK MODE */\n /* ================================================================== */\n\n [data-theme='dark'] .vf-markdown-editor {\n background: oklch(0.2768 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__history {\n background: oklch(0.3211 0 0);\n border-color: oklch(0.42 0.0017 106.48);\n color: oklch(0.9512 0.008 98.88);\n }\n [data-theme='dark'] .vf-markdown-editor__history:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__textarea {\n color: oklch(0.9512 0.008 98.88);\n }\n\n /* Slash menu \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__slash-menu {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-section {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-item:hover,\n [data-theme='dark'] .vf-markdown-editor__slash-item[data-active='true'] {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-icon {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-item-title {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-shortcut {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-footer {\n border-top-color: oklch(0.3 0.01 220);\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-footer-key {\n border-color: oklch(0.42 0.0017 106.48);\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* Inline toolbar \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__inline-toolbar {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-separator {\n background: oklch(0.3 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button.active {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n color: oklch(0.75 0.14 241.8);\n }\n\n /* Block trigger \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger::after {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* Block dropdown \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-dropdown {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option.active {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n color: oklch(0.75 0.14 241.8);\n }\n\n /* Placeholder text \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__surface p:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h1:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h2:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h3:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface blockquote:empty::before {\n color: oklch(0.5338 0.0046 106.55 / 0.5);\n }\n\n /* Block drag \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-handle {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-handle:hover {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drop-label {\n border-color: oklch(0.6852 0.162 241.8 / 0.35);\n background: oklch(0.18 0.01 220 / 0.96);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost-title {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost-text {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* MDX blocks \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__mdx-blocks {\n border-bottom-color: oklch(0.3 0.01 220);\n background: oklch(0.18 0.01 220 / 0.8);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-block {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-block-label {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-note {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-open {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.2768 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n [data-theme='dark'] .vf-markdown-editor__mdx-open:hover {\n background: oklch(0.25 0.01 220);\n }\n\n /* Edit button \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-edit-button {\n background: oklch(0.9512 0.008 98.88);\n color: oklch(0.2768 0 0);\n border-color: oklch(0.42 0.0017 106.48);\n }\n`;\nfunction injectOverlayStyles() {\n if (hasOverlayStyleElement(document)) return;\n const style = createOverlayStyleElement(document, OVERLAY_CSS);\n try {\n document.head.appendChild(style);\n if (!style.sheet) {\n logger.warn(\"Inline style injection may be blocked by CSP (style-src)\");\n }\n } catch (error) {\n logger.warn(\n \"Failed to inject bridge styles. This may be caused by CSP style-src restrictions.\",\n normalizeStyleInjectionWarningContext(error)\n );\n }\n}\n\n// src/studio/bridge/bridge-constants.ts\nvar DATA_VF_ID = \"data-vf-id\";\nvar DATA_VF_SELECTOR = \"data-vf-selector\";\nvar DATA_VF_TEXT = \"data-vf-text\";\nvar DATA_VF_IGNORE = \"data-vf-ignore\";\nvar DATA_NODE_ID = \"data-node-id\";\nvar DATA_NODE_FILE = \"data-node-file\";\nvar DATA_NODE_NAME = \"data-node-name\";\nvar DATA_NODE_LINE = \"data-node-line\";\nvar DATA_NODE_COLUMN = \"data-node-column\";\nvar DATA_NODE_SOURCE = \"data-node-source\";\n\n// src/studio/bridge/bridge-utils.ts\nfunction debounce(fn, ms) {\n let timer;\n const debounced = function(...args) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, ms);\n };\n debounced.cancel = () => {\n clearTimeout(timer);\n timer = void 0;\n };\n return debounced;\n}\n\n// src/studio/bridge/bridge-inspector.ts\nfunction createOverlay(type) {\n const overlay = document.createElement(\"div\");\n overlay.className = \"vf-overlay vf-overlay-\" + type;\n overlay.setAttribute(DATA_VF_IGNORE, \"true\");\n const label = document.createElement(\"div\");\n label.className = \"vf-overlay-label\";\n overlay.appendChild(label);\n overlay.style.display = \"none\";\n document.body.appendChild(overlay);\n return overlay;\n}\nfunction hideOverlay(overlay) {\n if (overlay) overlay.style.display = \"none\";\n}\nfunction positionOverlay(overlay, element, nodeName) {\n if (!overlay) return;\n if (!element) {\n hideOverlay(overlay);\n return;\n }\n const rect = element.getBoundingClientRect();\n overlay.style.display = \"block\";\n overlay.style.top = rect.top + \"px\";\n overlay.style.left = rect.left + \"px\";\n overlay.style.width = rect.width + \"px\";\n overlay.style.height = rect.height + \"px\";\n const label = overlay.querySelector(\".vf-overlay-label\");\n if (label) {\n label.textContent = nodeName;\n if (rect.top < 24) {\n label.classList.add(\"vf-overlay-label-bottom\");\n } else {\n label.classList.remove(\"vf-overlay-label-bottom\");\n }\n }\n}\nfunction getNodeName(element) {\n const vfId = element.getAttribute(DATA_VF_ID);\n if (vfId) return vfId.split(\"_\")[0] ?? vfId;\n return element.tagName.toLowerCase();\n}\nfunction findElementById(nodeId) {\n if (!nodeId) return null;\n return document.querySelector(\"[\" + DATA_VF_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_VF_SELECTOR + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_NODE_ID + '=\"' + nodeId + '\"]');\n}\nfunction isValidElement(el) {\n return !!el && el.nodeType === Node.ELEMENT_NODE && !DOM_IGNORE_TAGS.includes(el.tagName) && !el.hasAttribute(DATA_VF_IGNORE) && el.style.display !== \"none\";\n}\nfunction getNodeType(el) {\n const vfId = el.getAttribute(DATA_VF_ID) || \"\";\n if (vfId && /^[A-Z]/.test(vfId)) return \"component\";\n if (el.hasAttribute(DATA_VF_TEXT)) return \"text\";\n if (el.getAttribute(DATA_NODE_SOURCE) === \"md\") return \"markdown\";\n return \"element\";\n}\nfunction buildNavigatorTree(root) {\n const config3 = getConfig();\n let nodeIndex = 0;\n function processElement(el, parentId) {\n if (!isValidElement(el)) {\n const children = [];\n Array.from(el.children || []).forEach((child) => {\n children.push(...processElement(child, parentId));\n });\n return children;\n }\n let id = el.getAttribute(DATA_VF_ID) || el.getAttribute(DATA_NODE_ID) || el.getAttribute(DATA_VF_SELECTOR);\n if (!id) {\n id = \"vf-\" + el.tagName.toLowerCase() + \"-\" + ++nodeIndex;\n el.setAttribute(DATA_VF_SELECTOR, id);\n }\n const vfId = el.getAttribute(DATA_VF_ID);\n const name = vfId ? vfId.split(\"_\")[0] ?? vfId : el.tagName.toLowerCase();\n const node = {\n id,\n name,\n type: getNodeType(el),\n path: config3.pagePath,\n parentId,\n start: {\n line: parseInt(el.getAttribute(DATA_NODE_LINE) || \"0\", 10),\n column: parseInt(el.getAttribute(DATA_NODE_COLUMN) || \"0\", 10)\n },\n end: { line: 0, column: 0 },\n children: [],\n text: el.hasAttribute(DATA_VF_TEXT) ? el.textContent?.trim() : void 0,\n isRemote: false\n };\n Array.from(el.children || []).forEach((child) => {\n node.children.push(...processElement(child, id));\n });\n return [node];\n }\n const rootNode = {\n id: \"root\",\n name: \"root\",\n type: \"root\",\n path: \"\",\n parentId: \"\",\n start: { line: 0, column: 0 },\n end: { line: 0, column: 0 },\n children: [],\n isRemote: false\n };\n Array.from(root.children || []).forEach((child) => {\n rootNode.children.push(...processElement(child, \"root\"));\n });\n return rootNode;\n}\nfunction createTreeSignature(root) {\n const validElements = Array.from(root.querySelectorAll(\"*\")).filter((el) => isValidElement(el));\n return validElements.length + \"-\" + validElements.map((el) => el.tagName).join(\"\");\n}\nvar treeUpdateTimer = null;\nvar mutationObserver = null;\nfunction sendTreeUpdate() {\n const config3 = getConfig();\n const root = document.getElementById(\"root\") || document.body;\n if (!root) return;\n const signature = createTreeSignature(root);\n if (signature === state.lastTreeSignature) return;\n state.lastTreeSignature = signature;\n postToStudio({\n action: \"treeUpdated\",\n id: config3.pageId,\n url: window.location.href,\n tree: buildNavigatorTree(root),\n sourceHash: globalThis.__VERYFRONT_SOURCE_HASH__ || null\n });\n}\nfunction debouncedTreeUpdate() {\n if (treeUpdateTimer) clearTimeout(treeUpdateTimer);\n treeUpdateTimer = setTimeout(sendTreeUpdate, 150);\n}\nfunction setupMutationObserver() {\n const root = document.getElementById(\"root\") || document.body;\n if (!root) return;\n mutationObserver = new MutationObserver(function(mutations) {\n const hasRelevantChanges = mutations.some(\n (m) => m.type === \"childList\" || m.type === \"characterData\"\n );\n if (!hasRelevantChanges) return;\n if (state.selectedNodeId && !findElementById(state.selectedNodeId)) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n postToStudio({ action: \"setSelectedNode\", id: null });\n }\n debouncedTreeUpdate();\n });\n mutationObserver.observe(root, { childList: true, characterData: true, subtree: true });\n sendTreeUpdate();\n}\nfunction showOverlay(overlay, nodeId) {\n if (!nodeId) {\n hideOverlay(overlay);\n return;\n }\n const el = findElementById(nodeId);\n if (!el) {\n hideOverlay(overlay);\n return;\n }\n positionOverlay(overlay, el, getNodeName(el));\n}\nfunction showHoverOverlay(nodeId) {\n showOverlay(state.hoverOverlay, nodeId);\n}\nfunction showSelectionOverlay(nodeId) {\n showOverlay(state.selectionOverlay, nodeId);\n}\nfunction scrollToElement(nodeId) {\n const el = document.querySelector(\"[\" + DATA_VF_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_NODE_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_VF_SELECTOR + '*=\"' + nodeId + '\"]');\n if (el) el.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n}\nfunction getDirectText(el) {\n let text = \"\";\n for (let i = 0; i < el.childNodes.length; i++) {\n const node = el.childNodes[i];\n if (node?.nodeType === Node.TEXT_NODE) {\n text += node.textContent || \"\";\n }\n }\n return text.trim();\n}\nfunction setupInspectMode() {\n const INSPECTABLE_SELECTOR = \"[\" + DATA_VF_ID + \"], [\" + DATA_VF_SELECTOR + \"], [\" + DATA_NODE_ID + \"], [\" + DATA_NODE_FILE + \"]\";\n function getElementId(el) {\n return el.getAttribute(DATA_VF_ID) || el.getAttribute(DATA_NODE_ID) || el.getAttribute(DATA_VF_SELECTOR);\n }\n document.addEventListener(\n \"click\",\n function(event) {\n if (!state.inspectMode) return;\n event.preventDefault();\n event.stopPropagation();\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n postToStudio({ action: \"setSelectedNode\", id: null });\n return;\n }\n const id = getElementId(target);\n state.selectedNodeId = id;\n showSelectionOverlay(id);\n postToStudio({\n action: \"setSelectedNode\",\n id,\n node: {\n name: target.getAttribute(DATA_NODE_NAME) || target.tagName.toLowerCase(),\n type: getNodeType(target),\n file: target.getAttribute(DATA_NODE_FILE) || getConfig().pagePath,\n line: parseInt(target.getAttribute(DATA_NODE_LINE) || \"0\", 10),\n column: parseInt(target.getAttribute(DATA_NODE_COLUMN) || \"0\", 10),\n text: getDirectText(target).slice(0, 200)\n }\n });\n },\n true\n );\n document.addEventListener(\"pointerover\", function(event) {\n if (!state.inspectMode || event.pointerType === \"touch\") return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) return;\n const id = getElementId(target);\n if (id === state.hoveredNodeId) return;\n state.hoveredNodeId = id;\n showHoverOverlay(id);\n });\n document.addEventListener(\"pointerout\", function(event) {\n if (!state.inspectMode || event.pointerType === \"touch\") return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) return;\n const relatedTarget = event.relatedTarget;\n if (relatedTarget && target.contains(relatedTarget)) return;\n state.hoveredNodeId = null;\n hideOverlay(state.hoverOverlay);\n });\n const updateOverlays = debounce(function() {\n if (state.inspectMode && state.hoveredNodeId) showHoverOverlay(state.hoveredNodeId);\n if (state.selectedNodeId) showSelectionOverlay(state.selectedNodeId);\n }, 16);\n window.addEventListener(\"scroll\", updateOverlays, true);\n window.addEventListener(\"resize\", updateOverlays);\n}\nfunction setColorMode(mode) {\n document.documentElement.setAttribute(\"data-theme\", mode);\n document.documentElement.classList.remove(\"light\", \"dark\");\n document.documentElement.classList.add(mode);\n}\n\n// src/studio/bridge/bridge-console.ts\nfunction setupConsoleCapture() {\n const consoleObj = console;\n CONSOLE_METHODS.forEach((method) => {\n state.originalConsole[method] = consoleObj[method];\n consoleObj[method] = function(...args) {\n state.originalConsole[method].apply(console, args);\n const logId = \"vf-\" + Date.now() + \"-\" + ++state.logCounter;\n const formattedData = args.map((arg) => {\n try {\n if (arg instanceof Error) {\n return { __isError: true, message: arg.message, stack: arg.stack, name: arg.name };\n }\n if (arg === void 0) return { __isUndefined: true };\n if (arg === null) return null;\n if (typeof arg === \"function\") {\n return { __isFunction: true, name: arg.name || \"anonymous\" };\n }\n if (typeof arg === \"symbol\") return { __isSymbol: true, description: arg.description };\n if (typeof arg === \"object\") return JSON.parse(JSON.stringify(arg));\n return arg;\n } catch (_) {\n return String(arg);\n }\n });\n postToStudio({\n action: \"logEvent\",\n value: {\n id: logId,\n method,\n data: formattedData,\n timestamp: (/* @__PURE__ */ new Date()).toISOString()\n }\n });\n };\n });\n}\nfunction setupErrorHandling() {\n function hideOverlays() {\n hideOverlay(state.hoverOverlay);\n hideOverlay(state.selectionOverlay);\n }\n window.addEventListener(\"error\", function(event) {\n hideOverlays();\n postToStudio({\n action: \"runtimeError\",\n url: window.location.href,\n errors: [\n {\n type: \"error\",\n message: event.message,\n file: event.filename,\n line: event.lineno,\n column: event.colno\n }\n ]\n });\n });\n window.addEventListener(\"unhandledrejection\", function(event) {\n hideOverlays();\n const reason = event.reason;\n postToStudio({\n action: \"runtimeError\",\n url: window.location.href,\n errors: [\n {\n type: \"error\",\n message: reason instanceof Error ? reason.message : String(reason),\n file: reason instanceof Error ? reason.stack : void 0\n }\n ]\n });\n });\n}\n\n// src/studio/bridge/bridge-screenshot.ts\nfunction loadHtml2Canvas() {\n if (state.html2canvasLoaded) return Promise.resolve();\n if (state.html2canvasPromise) return state.html2canvasPromise;\n state.html2canvasPromise = new Promise((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.src = \"https://cdn.jsdelivr.net/npm/html2canvas-pro@2.0.0/dist/html2canvas-pro.min.js\";\n script.onload = () => {\n state.html2canvasLoaded = true;\n resolve();\n };\n script.onerror = (event) => {\n logger.warn(\n \"Failed to load html2canvas script. This may be caused by CSP script-src restrictions.\",\n { event: String(event) }\n );\n reject(new Error(\"Failed to load html2canvas script\"));\n };\n try {\n document.head.appendChild(script);\n } catch (error) {\n logger.warn(\n \"Failed to append html2canvas script element. This may be caused by CSP script-src restrictions.\",\n error instanceof Error ? error : { error: String(error) }\n );\n reject(\n error instanceof Error ? error : new Error(\"Failed to append html2canvas script element\")\n );\n }\n });\n return state.html2canvasPromise;\n}\nasync function captureScreenshot(options) {\n const { scrollTo, fullPage, quality = 0.8 } = options || {};\n const originalScrollY = window.scrollY;\n try {\n await loadHtml2Canvas();\n if (typeof scrollTo === \"number\") {\n window.scrollTo(0, scrollTo);\n await new Promise((r) => setTimeout(r, 150));\n }\n const canvasOptions = {\n useCORS: true,\n logging: false,\n scale: window.devicePixelRatio || 1\n };\n if (fullPage) {\n canvasOptions.height = document.documentElement.scrollHeight;\n canvasOptions.windowHeight = document.documentElement.scrollHeight;\n canvasOptions.y = 0;\n window.scrollTo(0, 0);\n await new Promise((r) => setTimeout(r, 100));\n }\n const html2canvasFn = window.html2canvas.default || window.html2canvas;\n const canvas = await html2canvasFn(document.body, canvasOptions);\n if (!canvas || canvas.width === 0 || canvas.height === 0) {\n logger.error(\"html2canvas produced empty canvas\", {\n width: canvas?.width,\n height: canvas?.height\n });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: \"html2canvas produced empty canvas (0x0 dimensions)\"\n };\n }\n const dataUrl = canvas.toDataURL(\"image/png\", quality);\n if (!dataUrl || !dataUrl.startsWith(\"data:image/\") || dataUrl.length < 100) {\n logger.error(\"html2canvas produced invalid data URL\", {\n dataUrlPreview: dataUrl?.substring(0, 50)\n });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: \"html2canvas produced invalid image data\"\n };\n }\n window.scrollTo(0, originalScrollY);\n return {\n success: true,\n data: dataUrl,\n width: canvas.width,\n height: canvas.height,\n scrollY: window.scrollY,\n totalHeight: document.documentElement.scrollHeight,\n viewportHeight: window.innerHeight,\n url: window.location.href\n };\n } catch (error) {\n logger.error(\"html2canvas error\", error instanceof Error ? error : { error: String(error) });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\nasync function captureMultipleSections(sectionCount) {\n const originalScrollY = window.scrollY;\n const results = [];\n const totalHeight = document.documentElement.scrollHeight;\n const viewportHeight = window.innerHeight;\n const sections = sectionCount || Math.ceil(totalHeight / viewportHeight);\n try {\n for (let i = 0; i < sections; i++) {\n const scrollY = Math.min(i * viewportHeight, totalHeight - viewportHeight);\n const result = await captureScreenshot({ scrollTo: scrollY });\n if (result.success) {\n results.push({ ...result, section: i + 1, totalSections: sections });\n }\n }\n } finally {\n window.scrollTo(0, originalScrollY);\n }\n return results;\n}\n\n// src/studio/bridge/bridge-message-handler.ts\nvar SAFE_PROTOCOLS = /* @__PURE__ */ new Set([\"http:\", \"https:\"]);\nfunction sanitizeNavigationUrl(url) {\n if (typeof url !== \"string\" || url.length === 0) return null;\n try {\n const parsed = new URL(url, window.location.origin);\n if (!SAFE_PROTOCOLS.has(parsed.protocol)) return null;\n if (parsed.origin === window.location.origin) return parsed.href;\n const host = parsed.hostname;\n if (host !== \"veryfront.com\" && !host.endsWith(\".veryfront.com\")) return null;\n return parsed.href;\n } catch {\n return null;\n }\n}\nfunction clearSelection(notifyStudio = false) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n if (notifyStudio) {\n postToStudio({ action: \"setSelectedNode\", id: null });\n }\n}\nfunction disableInspectMode(deselectElements = false) {\n state.inspectMode = false;\n hideOverlay(state.hoverOverlay);\n state.hoveredNodeId = null;\n if (deselectElements) {\n clearSelection();\n }\n}\nfunction postScreenshotResult(requestId, result) {\n postToStudio({\n action: \"screenshotResult\",\n requestId,\n multiple: false,\n ...result\n });\n}\nasync function handleScreenshotRequest(message) {\n if (message.multipleSections) {\n const results = await captureMultipleSections(message.sectionCount);\n postToStudio({\n action: \"screenshotResult\",\n requestId: message.requestId,\n multiple: true,\n results\n });\n return;\n }\n const result = await captureScreenshot(message.options);\n postScreenshotResult(message.requestId, result);\n}\nfunction handleStudioMessage(event) {\n if (!isFromStudio(event)) return;\n const message = event.data;\n if (!message?.action) return;\n const config3 = getConfig();\n switch (message.action) {\n case \"routeChange\": {\n const safeUrl = sanitizeNavigationUrl(message.url);\n if (!safeUrl) {\n logger.warn(\"[StudioBridge] Blocked unsafe URL in routeChange\", { url: message.url });\n return;\n }\n if (state.selectedNodeId) {\n clearSelection(true);\n }\n postToStudio({\n action: \"onPageTransitionStart\",\n url: safeUrl,\n projectId: config3.projectId\n });\n window.location.href = safeUrl;\n return;\n }\n case \"reload\":\n window.location.reload();\n return;\n case \"goBack\":\n window.history.back();\n return;\n case \"goForward\":\n window.history.forward();\n return;\n case \"colorMode\":\n setColorMode(message.value);\n return;\n case \"toggleInspectMode\":\n state.inspectMode = message.value;\n if (state.inspectMode) return;\n disableInspectMode(message.deselectElements);\n return;\n case \"setSelectedNode\":\n state.selectedNodeId = message.id;\n showSelectionOverlay(message.id);\n if (message.scroll) scrollToElement(message.id);\n return;\n case \"setHoveredNode\":\n if (!state.inspectMode) showHoverOverlay(message.id);\n return;\n case \"screenshot\":\n void handleScreenshotRequest(message);\n return;\n default:\n logger.debug(\"Unknown action\", { action: message.action });\n return;\n }\n}\n\n// src/studio/bridge/bridge-init.ts\nfunction notifyAppLoaded() {\n const config3 = getConfig();\n postToStudio({ action: \"appLoaded\", url: window.location.href });\n postToStudio({\n action: \"appUpdated\",\n url: window.location.href,\n id: config3.pageId,\n isInitialLoad: true,\n errors: [],\n warnings: []\n });\n postToStudio({\n action: \"onPageTransitionEnd\",\n url: window.location.href,\n projectId: config3.projectId,\n id: config3.pageId,\n params: {}\n });\n}\nfunction notifyAppUnloaded() {\n postToStudio({ action: \"appUnloaded\", url: window.location.href });\n}\nfunction init() {\n const params = new URLSearchParams(window.location.search);\n const studioEmbed = params.get(\"studio_embed\") === \"true\";\n const isStandalone = window.parent === window && !studioEmbed;\n if (isStandalone) {\n logger.debug(\n \"[StudioBridge] Not in iframe and not studio_embed mode, skipping initialization\"\n );\n return;\n }\n logger.debug(\"Initializing...\");\n if (!isStandalone) {\n injectOverlayStyles();\n state.hoverOverlay = createOverlay(\"hover\");\n state.selectionOverlay = createOverlay(\"selection\");\n setupConsoleCapture();\n setupErrorHandling();\n setupInspectMode();\n }\n window.addEventListener(\"message\", handleStudioMessage);\n if (!isStandalone) {\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", function() {\n notifyAppLoaded();\n setupMutationObserver();\n }, { once: true });\n } else {\n notifyAppLoaded();\n setupMutationObserver();\n }\n window.addEventListener(\"beforeunload\", notifyAppUnloaded, { once: true });\n }\n const colorMode = params.get(\"color_mode\");\n if (colorMode) setColorMode(colorMode);\n if (!isStandalone) {\n const inspectModeParam = params.get(\"inspect_mode\");\n if (inspectModeParam === \"true\") {\n state.inspectMode = true;\n logger.debug(\"Inspect mode enabled from query param\");\n }\n }\n logger.debug(\"Initialized successfully\");\n}\n\n// src/studio/bridge/bridge-coordinator.ts\ninitConfig();\nvar config2 = getConfig();\nif (!config2.debugSkipInit) {\n init();\n}\n"; diff --git a/src/transforms/mdx/compiler/import-rewriter.ts b/src/transforms/mdx/compiler/import-rewriter.ts index 5862803e1d..3e5666c851 100644 --- a/src/transforms/mdx/compiler/import-rewriter.ts +++ b/src/transforms/mdx/compiler/import-rewriter.ts @@ -1,5 +1,6 @@ import { dirname, join, resolve as pathResolve } from "#veryfront/compat/path/index.ts"; import type { CompilationTarget } from "#veryfront/extensions/content/index.ts"; +import { base64urlEncode } from "#veryfront/utils"; export interface ImportRewriterConfig { filePath: string; @@ -26,7 +27,7 @@ function toAbsPath(spec: string, basedir: string): string { function toBrowserFs(abs: string, baseUrl?: string): string { if (abs.startsWith("http://") || abs.startsWith("https://")) return abs; - const b64 = btoa(abs).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", ""); + const b64 = base64urlEncode(abs); const path = `/_veryfront/fs/${b64}.js`; return baseUrl ? `${baseUrl}${path}` : path; } diff --git a/src/utils/base64url.ts b/src/utils/base64url.ts index 6e95aecf03..81191407f5 100644 --- a/src/utils/base64url.ts +++ b/src/utils/base64url.ts @@ -1,15 +1,42 @@ /*********************** - * Base64url encoding utilities + * Base64 / base64url encoding utilities ***********************/ +import type { Buffer } from "node:buffer"; + function toBase64Url(b64: string): string { return b64.replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", ""); } +/** Encode a UTF-8 string as standard base64 (handles non-Latin1 input). */ +export function encodeBase64(value: string): string { + if (typeof globalThis.btoa === "function") { + try { + return globalThis.btoa(value); + } catch (_) { + /* expected: non-Latin1 string — fall back to TextEncoder */ + const bytes = new TextEncoder().encode(value); + let binary = ""; + for (const byte of bytes) binary += String.fromCharCode(byte); + return globalThis.btoa(binary); + } + } + + const bufferCtor = (globalThis as { Buffer?: typeof Buffer }).Buffer; + if (bufferCtor) return bufferCtor.from(value, "utf8").toString("base64"); + + // This file ships in client bundles — keep it dependency-free (no error registry). + throw new Error("Base64 encoding is not supported in this runtime"); +} + +/** Encode a string as unpadded base64url. */ export function base64urlEncode(input: string): string { - return toBase64Url(btoa(input)); + return toBase64Url(encodeBase64(input)); } +/** Encode raw bytes as unpadded base64url. */ export function base64urlEncodeBytes(bytes: Uint8Array): string { - return toBase64Url(btoa(String.fromCharCode(...bytes))); + let binary = ""; + for (const byte of bytes) binary += String.fromCharCode(byte); + return toBase64Url(btoa(binary)); } diff --git a/src/utils/hash-utils.ts b/src/utils/hash-utils.ts index 3cd4698be3..40456af148 100644 --- a/src/utils/hash-utils.ts +++ b/src/utils/hash-utils.ts @@ -4,12 +4,19 @@ import { HASH_SEED_FNV1A } from "./constants/hash.ts"; /** Number of hex characters kept by shortHash (8 hex chars = 32 bits) */ const SHORT_HASH_LENGTH = 8; -/** Compute hash. */ +function toHex(buffer: ArrayBuffer): string { + return Array.from(new Uint8Array(buffer), (b) => b.toString(16).padStart(2, "0")).join(""); +} + +/** Compute the lowercase hex SHA-256 digest of a UTF-8 string. */ export async function computeHash(content: string): Promise { const data = new TextEncoder().encode(content); - const hashBuffer = await crypto.subtle.digest("SHA-256", data); + return toHex(await crypto.subtle.digest("SHA-256", data)); +} - return Array.from(new Uint8Array(hashBuffer), (b) => b.toString(16).padStart(2, "0")).join(""); +/** Compute the lowercase hex SHA-256 digest of raw bytes. */ +export async function computeHashBytes(bytes: BufferSource): Promise { + return toHex(await crypto.subtle.digest("SHA-256", bytes)); } /** Source bundle content used for hash computation. */ export interface BundleCode { diff --git a/src/utils/index.ts b/src/utils/index.ts index 872d79471c..fc738de901 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -107,11 +107,16 @@ export { type BundleCode as HashBundleCode, computeCodeHash, computeHash, + computeHashBytes, fnv1aHash, shortHash, simpleHash, } from "./hash-utils.ts"; +export { base64urlEncode, base64urlEncodeBytes, encodeBase64 } from "./base64url.ts"; + +export { sleep } from "./sleep.ts"; + export { MemoCache, memoize, memoizeAsync, simpleHash as memoizeHash } from "./memoize.ts"; export { normalizePath } from "./path-utils.ts"; diff --git a/src/utils/sleep.ts b/src/utils/sleep.ts new file mode 100644 index 0000000000..3b6ed40cbc --- /dev/null +++ b/src/utils/sleep.ts @@ -0,0 +1,17 @@ +/** Resolve after `ms` milliseconds; rejects with `abortSignal.reason` if aborted first. */ +export function sleep(ms: number, abortSignal?: AbortSignal): Promise { + abortSignal?.throwIfAborted(); + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + abortSignal?.removeEventListener("abort", onAbort); + resolve(); + }, ms); + const onAbort = () => { + clearTimeout(timeoutId); + abortSignal?.removeEventListener("abort", onAbort); + reject(abortSignal?.reason); + }; + abortSignal?.addEventListener("abort", onAbort, { once: true }); + if (abortSignal?.aborted) onAbort(); + }); +} diff --git a/src/workflow/blob/gcs-storage.ts b/src/workflow/blob/gcs-storage.ts index 1f14295c21..e5aa4eafe3 100644 --- a/src/workflow/blob/gcs-storage.ts +++ b/src/workflow/blob/gcs-storage.ts @@ -1,19 +1,9 @@ -import { logger as baseLogger } from "#veryfront/utils"; +import { base64urlEncode, base64urlEncodeBytes, logger as baseLogger } from "#veryfront/utils"; import type { BlobRef, BlobStorage, StoreBlobOptions } from "./types.ts"; import { API_ERROR, CONFIG_INVALID, INVALID_ARGUMENT } from "#veryfront/errors"; const logger = baseLogger.component("gcs-blob-storage"); -function base64url(str: string): string { - return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); -} - -function base64urlFromBytes(bytes: Uint8Array): string { - let binary = ""; - for (const byte of bytes) binary += String.fromCharCode(byte); - return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); -} - async function importPKCS8Key(pem: string): Promise { const pemBody = pem .replace(/-----BEGIN PRIVATE KEY-----/, "") @@ -95,8 +85,8 @@ export class GCSBlobStorage implements BlobStorage { const iat = Math.floor(now / 1000); const exp = iat + 3600; - const header = base64url(JSON.stringify({ alg: "RS256", typ: "JWT" })); - const claims = base64url( + const header = base64urlEncode(JSON.stringify({ alg: "RS256", typ: "JWT" })); + const claims = base64urlEncode( JSON.stringify({ iss: sa.client_email, scope, @@ -113,7 +103,7 @@ export class GCSBlobStorage implements BlobStorage { privateKey, new TextEncoder().encode(signingInput), ); - const signature = base64urlFromBytes(new Uint8Array(signatureBytes)); + const signature = base64urlEncodeBytes(new Uint8Array(signatureBytes)); const jwt = `${signingInput}.${signature}`; const response = await fetch(tokenEndpoint, { diff --git a/src/workflow/claude-code/workspace-sync.ts b/src/workflow/claude-code/workspace-sync.ts index 33337b0bb7..ca1ce78cde 100644 --- a/src/workflow/claude-code/workspace-sync.ts +++ b/src/workflow/claude-code/workspace-sync.ts @@ -10,7 +10,7 @@ * 3. After execution: Upload changed files back to Veryfront API */ -import { logger as baseLogger } from "#veryfront/utils"; +import { computeHash, logger as baseLogger } from "#veryfront/utils"; import { api } from "../api.ts"; import type { CapturedTenantContext } from "../types.ts"; import { dirname, join, relative, resolve } from "@std/path"; @@ -105,17 +105,6 @@ export interface UploadResult { duration: number; } -/** - * Simple checksum for change detection - */ -async function checksum(content: string): Promise { - const encoder = new TextEncoder(); - const data = encoder.encode(content); - const hashBuffer = await crypto.subtle.digest("SHA-256", data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - return hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); -} - /** * Check if a path matches any of the given patterns. * @@ -253,7 +242,7 @@ export class WorkspaceSync { } // Calculate checksum for change detection - const hash = await checksum(content); + const hash = await computeHash(content); this.fileChecksums.set(path, hash); // Write to local filesystem (use safe path resolution) @@ -390,7 +379,7 @@ export class WorkspaceSync { // It's a regular file - check for changes const content = await Deno.readTextFile(localPath); - const newHash = await checksum(content); + const newHash = await computeHash(content); const originalHash = this.fileChecksums.get(relativePath); if (!originalHash) { diff --git a/src/workflow/executor/dag/composite-node-execution.ts b/src/workflow/executor/dag/composite-node-execution.ts index 53cfa6b95c..a77bc77908 100644 --- a/src/workflow/executor/dag/composite-node-execution.ts +++ b/src/workflow/executor/dag/composite-node-execution.ts @@ -4,7 +4,7 @@ import { ensureError } from "#veryfront/errors/veryfront-error.ts"; import type { RetryConfig, WorkflowNode } from "../../types.ts"; import { parseDuration, validateRetryConfig } from "../../types.ts"; import type { NodeExecutionResult } from "./types.ts"; -import { sleep } from "./utils.ts"; +import { sleep } from "#veryfront/utils"; import { createSetContextPatch } from "./context-patch.ts"; const DEFAULT_RETRY_INITIAL_DELAY_MS = 1_000; diff --git a/src/workflow/executor/dag/loop-node-strategy.ts b/src/workflow/executor/dag/loop-node-strategy.ts index fca1a17add..dad0fb45d8 100644 --- a/src/workflow/executor/dag/loop-node-strategy.ts +++ b/src/workflow/executor/dag/loop-node-strategy.ts @@ -7,7 +7,7 @@ import type { } from "../../types.ts"; import { parseDuration } from "../../types.ts"; import type { NodeExecutionResult } from "./types.ts"; -import { sleep } from "./utils.ts"; +import { sleep } from "#veryfront/utils"; import type { NodeStrategyRuntime } from "./node-strategy-types.ts"; import { captureWorkflowSourceIntegrationPolicy } from "../../source-integration-policy.ts"; import { diff --git a/src/workflow/executor/dag/utils.ts b/src/workflow/executor/dag/utils.ts index ea01c0ca28..e40e06a646 100644 --- a/src/workflow/executor/dag/utils.ts +++ b/src/workflow/executor/dag/utils.ts @@ -9,20 +9,3 @@ export function deriveNodeStatus(completed: boolean, waiting: boolean): NodeStat export function shouldCheckpoint(node: WorkflowNode): boolean { return node.config.checkpoint ?? false; } - -export function sleep(ms: number, abortSignal?: AbortSignal): Promise { - abortSignal?.throwIfAborted(); - return new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { - abortSignal?.removeEventListener("abort", onAbort); - resolve(); - }, ms); - const onAbort = () => { - clearTimeout(timeoutId); - abortSignal?.removeEventListener("abort", onAbort); - reject(abortSignal?.reason); - }; - abortSignal?.addEventListener("abort", onAbort, { once: true }); - if (abortSignal?.aborted) onAbort(); - }); -} diff --git a/src/workflow/executor/workflow-executor.ts b/src/workflow/executor/workflow-executor.ts index 5e74be3453..621a08b3bf 100644 --- a/src/workflow/executor/workflow-executor.ts +++ b/src/workflow/executor/workflow-executor.ts @@ -4,7 +4,7 @@ * Main orchestrator for executing durable workflows **************************/ -import { logger as baseLogger } from "#veryfront/utils"; +import { logger as baseLogger, sleep } from "#veryfront/utils"; import { ensureError, INVALID_ARGUMENT, @@ -1011,8 +1011,7 @@ export class WorkflowExecutor { }); } - // no cleanup needed: one-shot - await new Promise((resolve) => setTimeout(resolve, Math.min(pollInterval, remaining))); + await sleep(Math.min(pollInterval, remaining)); } } diff --git a/src/workflow/worker/workflow-worker.ts b/src/workflow/worker/workflow-worker.ts index 7586e500ac..bf24c779e4 100644 --- a/src/workflow/worker/workflow-worker.ts +++ b/src/workflow/worker/workflow-worker.ts @@ -5,7 +5,7 @@ * Enables distributed workflow execution across multiple pods. */ -import { logger as baseLogger } from "#veryfront/utils"; +import { logger as baseLogger, sleep } from "#veryfront/utils"; import type { WorkflowBackend } from "../backends/types.ts"; import { hasWorkerSupport } from "../backends/types.ts"; import type { WorkflowRun } from "../types.ts"; @@ -193,7 +193,7 @@ export class WorkflowWorker { `[WorkflowWorker] Waiting for ${this.activeResumes.size} active resumes to complete`, ); } - await this.sleep(1000); + await sleep(1000); } this.status = "stopped"; @@ -332,14 +332,6 @@ export class WorkflowWorker { } })(); } - - /** - * Sleep for specified milliseconds - */ - private sleep(ms: number): Promise { - // no cleanup needed: one-shot - return new Promise((resolve) => setTimeout(resolve, ms)); - } } /**