@verbatra/studio 0.1.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/dist/app/assets/index-CxoW3Rct.js +9 -0
- package/dist/app/assets/index-mZ-7WS7w.css +1 -0
- package/dist/app/index.html +13 -0
- package/dist/index.d.ts +115 -0
- package/dist/index.js +971 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/banner.ts","../src/server/cookie.ts","../src/server/default-port.ts","../src/server/body-reader.ts","../src/server/content-type.ts","../src/server/host-origin.ts","../src/server/request-content-type.ts","../src/server/request-log.ts","../src/shared/rpc/check.ts","../src/shared/rpc/diff.ts","../src/shared/rpc/glossary.ts","../src/shared/rpc/history.ts","../src/shared/rpc/lock.ts","../src/shared/rpc/snapshot.ts","../src/shared/rpc/contract.ts","../src/server/redaction.ts","../src/server/methods/check.ts","../src/server/methods/diff.ts","../src/server/projection.ts","../src/server/methods/glossary.ts","../src/server/path-normalize.ts","../src/server/git.ts","../src/server/locale-paths.ts","../src/server/methods/history.ts","../src/server/methods/lock.ts","../src/server/methods/snapshot.ts","../src/server/rpc.ts","../src/server/rpc-gate.ts","../src/server/security-headers.ts","../src/server/static-assets.ts","../src/server/token.ts","../src/server/transport-responses.ts","../src/server/dispatch.ts","../src/server/errors.ts","../src/server/resolve-bound-port.ts","../src/shared/sse-events.ts","../src/server/sse.ts","../src/server/watcher.ts","../src/server/create-studio-server.ts"],"names":["resolve","z","execFileCb","sep","resolvePath","chokidarWatch"],"mappings":"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","file":"index.js","sourcesContent":["/** Builds the one-time startup banner: the loopback URL with the bootstrap token attached as a query parameter. */\nexport function buildBanner(url: string, token: string): string {\n return `verbatra studio listening at ${url}?token=${token}`;\n}\n","const COOKIE_PREFIX = \"verbatra_studio_\";\n\n/**\n * The session cookie name for a given bound port. Cookies are not port-scoped by the browser, so\n * two Studio instances on different ports would otherwise clobber one another's session; suffixing\n * the port keeps each instance's cookie distinct.\n */\nexport function cookieName(port: number): string {\n return `${COOKIE_PREFIX}${port}`;\n}\n\nfunction splitCookiePairs(header: string): string[] {\n return header.split(\";\").map((pair) => pair.trim());\n}\n\n/** Reads the value of exactly the named cookie from a raw Cookie header, ignoring every other cookie present. */\nexport function readCookieValue(header: string | undefined, name: string): string | undefined {\n if (header === undefined) {\n return undefined;\n }\n for (const pair of splitCookiePairs(header)) {\n const separatorIndex = pair.indexOf(\"=\");\n if (separatorIndex === -1) {\n continue;\n }\n if (pair.slice(0, separatorIndex) === name) {\n return pair.slice(separatorIndex + 1);\n }\n }\n return undefined;\n}\n\n/**\n * Builds the Set-Cookie header for a successful bootstrap: HttpOnly, SameSite=Strict, scoped to\n * the whole app under Path=/, with no Secure flag (loopback only, never sent over TLS) and no\n * Max-Age or Expires so it lasts only for the browser session.\n */\nexport function buildSetCookieHeader(name: string, value: string): string {\n return `${name}=${value}; Path=/; HttpOnly; SameSite=Strict`;\n}\n","/**\n * The port Verbatra Studio listens on when the caller does not specify one. Chosen once so the\n * printed URL is stable and bookmarkable; there is no fallback to another port if it is busy.\n */\nexport const DEFAULT_STUDIO_PORT = 5849;\n\n/** Resolves the port to bind: the given port when set (including 0 for an OS-assigned port used in tests), or the default. */\nexport function resolvePort(port: number | undefined): number {\n return port ?? DEFAULT_STUDIO_PORT;\n}\n","import type { IncomingMessage } from \"node:http\";\n\n/** The request body cap: 1 MiB. The bundled SPA is the only client, so this costs it nothing. */\nexport const BODY_CAP_BYTES = 1024 * 1024;\n\n/** Thrown when a request body exceeds {@link BODY_CAP_BYTES}, whether declared or streamed. */\nexport class PayloadTooLargeError extends Error {\n constructor() {\n super(\"request body exceeds the size cap\");\n this.name = \"PayloadTooLargeError\";\n }\n}\n\nfunction declaredLengthExceedsCap(request: IncomingMessage, capBytes: number): boolean {\n const header = request.headers[\"content-length\"];\n if (header === undefined) {\n return false;\n }\n const declared = Number(header);\n return Number.isFinite(declared) && declared > capBytes;\n}\n\n/**\n * Reads a request body up to `capBytes`. A declared Content-Length over the cap rejects\n * immediately without buffering a single byte; the socket is drained (not destroyed) so the\n * client's upload completes normally and it still receives the 413 response. Otherwise bytes are\n * counted as they stream in and the connection is destroyed the instant the cap is crossed, since\n * without a Content-Length header there is no bound on how much more data might follow.\n */\nexport function readBodyWithCap(request: IncomingMessage, capBytes: number): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n if (declaredLengthExceedsCap(request, capBytes)) {\n request.resume();\n reject(new PayloadTooLargeError());\n return;\n }\n\n const chunks: Buffer[] = [];\n let total = 0;\n let settled = false;\n\n const settleReject = (error: Error): void => {\n if (!settled) {\n settled = true;\n reject(error);\n }\n };\n\n request.on(\"data\", (chunk: Buffer) => {\n total += chunk.length;\n if (total > capBytes) {\n settled = true;\n reject(new PayloadTooLargeError());\n request.destroy();\n return;\n }\n chunks.push(chunk);\n });\n request.on(\"end\", () => {\n settled = true;\n resolve(Buffer.concat(chunks));\n });\n request.on(\"error\", settleReject);\n // Fires whether the connection ended normally or the client vanished mid-upload; without this,\n // an aborted upload (no more data, no \"end\", no stream \"error\") would leave the promise pending\n // forever instead of failing the request.\n request.on(\"close\", () =>\n settleReject(new Error(\"request closed before the body was fully received\")),\n );\n });\n}\n","import { extname } from \"node:path\";\n\n// Outbound direction: what Content-Type this server sets on a served static asset, chosen from\n// the asset's file extension. For the inbound direction, checking a request's own Content-Type\n// header on POST /rpc, see request-content-type.ts.\nconst CONTENT_TYPES: Readonly<Record<string, string>> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".mjs\": \"text/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".ico\": \"image/x-icon\",\n};\n\n/** Maps a served asset path to a response Content-Type, falling back to a generic binary type. */\nexport function contentTypeFor(assetPath: string): string {\n return CONTENT_TYPES[extname(assetPath)] ?? \"application/octet-stream\";\n}\n","/**\n * The Host header must be exactly `127.0.0.1:PORT` (the host part compared case-insensitively).\n * `localhost` and `[::1]` are rejected on purpose: cookies are not port-scoped, so an alternate\n * hostname would dead-end in an authentication failure anyway, and an IPv6 Host on an IPv4-only\n * socket is dead surface. The printed 127.0.0.1 URL is the only supported entry point.\n */\nexport function isAllowedHost(hostHeader: string | undefined, port: number): boolean {\n if (hostHeader === undefined) {\n return false;\n }\n return hostHeader.toLowerCase() === `127.0.0.1:${port}`;\n}\n\n/**\n * Origin is only checked on state-changing (non-GET) requests. An absent Origin is allowed (many\n * same-origin navigations and non-browser clients omit it); a present Origin must match exactly,\n * including the literal string \"null\" for an opaque origin, which is rejected.\n */\nexport function isAllowedOrigin(originHeader: string | undefined, port: number): boolean {\n if (originHeader === undefined) {\n return true;\n }\n return originHeader === `http://127.0.0.1:${port}`;\n}\n","// Inbound direction: whether an incoming request's own Content-Type header on POST /rpc is\n// acceptable. For the outbound direction, choosing the Content-Type this server sets on a served\n// static asset, see content-type.ts.\n\n/** Checks a request Content-Type for an exact `application/json` match after trim and lowercase; parameterized variants such as a charset are rejected. */\nexport function isJsonRequestContentType(header: string | undefined): boolean {\n if (header === undefined) {\n return false;\n }\n return header.trim().toLowerCase() === \"application/json\";\n}\n","/** One logged request, deliberately narrow: a method, a path with no query string, and a status. */\nexport interface RequestLogEntry {\n readonly method: string;\n readonly path: string;\n readonly status: number;\n}\n\n/** Formats a request log line. Never includes a query string, a header value, or a token. */\nexport function formatRequestLog(entry: RequestLogEntry): string {\n return `${entry.method} ${entry.path} ${entry.status}`;\n}\n","import type { CheckSummary } from \"@verbatra/sdk\";\nimport { z } from \"zod\";\n\n/** The RPC method name for the aggregate drift status view. */\nexport const STATUS_CHECK_METHOD = \"status.check\";\n\n/**\n * An omitted `locales` means \"all configured target locales\"; an explicitly empty array is\n * rejected as invalid params rather than treated as select-none.\n */\nexport const statusCheckParamsSchema = z.strictObject({\n locales: z.array(z.string().min(1)).min(1).optional(),\n});\n\nexport type StatusCheckParams = z.infer<typeof statusCheckParamsSchema>;\n\n/** The result is the sdk's own {@link CheckSummary}, unchanged: counts only, no key lists. */\nexport type StatusCheckResult = CheckSummary;\n","import type { DiffSummary } from \"@verbatra/sdk\";\nimport { z } from \"zod\";\n\n/** The RPC method name for the detailed pending-change view. */\nexport const STATUS_DIFF_METHOD = \"status.diff\";\n\n/**\n * An omitted `locales` means \"all configured target locales\"; an explicitly empty array is\n * rejected as invalid params rather than treated as select-none.\n */\nexport const statusDiffParamsSchema = z.strictObject({\n locales: z.array(z.string().min(1)).min(1).optional(),\n});\n\nexport type StatusDiffParams = z.infer<typeof statusDiffParamsSchema>;\n\n/** The result is the sdk's own {@link DiffSummary}, unchanged: the three key lists per locale. */\nexport type StatusDiffResult = DiffSummary;\n","import { z } from \"zod\";\n\n/** The RPC method name for the read-only glossary view. */\nexport const GLOSSARY_GET_METHOD = \"glossary.get\";\n\n/** Takes no parameters: the glossary always reflects the single loaded project. */\nexport const glossaryGetParamsSchema = z.strictObject({});\n\nexport type GlossaryGetParams = z.infer<typeof glossaryGetParamsSchema>;\n\n/**\n * Where a project's glossary came from, mirroring the sdk's own provenance domain: absent, an\n * inline record in the config, or a file whose path is shown relativized against the project\n * root. The view never infers this from the config shape; it always comes from the loader's own\n * result.\n */\nexport type GlossaryIndicator =\n | { readonly source: \"none\" }\n | { readonly source: \"inline\" }\n | { readonly source: \"file\"; readonly path: string };\n\n/** The glossary entries, redacted, plus their provenance. Strictly read-only; no write affordance. */\nexport interface GlossaryGetResult {\n readonly indicator: GlossaryIndicator;\n readonly entries: Readonly<Record<string, string>>;\n}\n","import { z } from \"zod\";\n\n/** The RPC method name for the git-log history view. */\nexport const HISTORY_LIST_METHOD = \"history.list\";\n\n/** Bounded by a cap on the server side regardless; the schema only rejects an obviously invalid limit. */\nexport const historyListParamsSchema = z.strictObject({\n limit: z.number().int().positive().optional(),\n});\n\nexport type HistoryListParams = z.infer<typeof historyListParamsSchema>;\n\n/** One commit that touched the watched locale files. */\nexport interface HistoryCommit {\n readonly hash: string;\n readonly authorDate: string;\n readonly subject: string;\n readonly touchedPaths: readonly string[];\n}\n\n/**\n * The commit history for the watched locale files, or unavailable when git itself is missing or\n * the project is not a repository. Never an error: both are ordinary, renderable results.\n */\nexport type HistoryListResult =\n | { readonly available: false }\n | { readonly available: true; readonly commits: readonly HistoryCommit[] };\n","import { z } from \"zod\";\n\n/** The RPC method name for the read-only lock-drift view. */\nexport const LOCK_STATE_METHOD = \"lock.state\";\n\n/** Takes no parameters: the lock state always reflects the single loaded project's lock-file. */\nexport const lockStateParamsSchema = z.strictObject({});\n\nexport type LockStateParams = z.infer<typeof lockStateParamsSchema>;\n\n/** Per-locale key count and drift against the source and target files, from the recorded lock baseline. */\nexport interface LockLocaleState {\n readonly locale: string;\n readonly keyCount: number;\n readonly missing: number;\n readonly stale: number;\n readonly upToDate: number;\n}\n\n/**\n * The lock-file's existence, version, and per-locale drift. `exists` comes from an explicit\n * probe of the lock-file path, never from a read that degrades a missing file to an empty lock,\n * so \"no lock-file yet\" and \"an empty but present lock-file\" stay distinguishable.\n */\nexport type LockStateResult =\n | { readonly exists: false }\n | {\n readonly exists: true;\n readonly version: number;\n readonly locales: readonly LockLocaleState[];\n };\n","import type { ProviderId, SupportedFormat } from \"@verbatra/sdk\";\nimport { z } from \"zod\";\nimport type { GlossaryIndicator } from \"./glossary.js\";\n\n/** The RPC method name for the project configuration snapshot. */\nexport const PROJECT_SNAPSHOT_METHOD = \"project.snapshot\";\n\n/** Takes no parameters: the snapshot always reflects the single loaded project. */\nexport const projectSnapshotParamsSchema = z.strictObject({});\n\nexport type ProjectSnapshotParams = z.infer<typeof projectSnapshotParamsSchema>;\n\n/**\n * The read-only, allowlisted projection of the loaded config (see the config projection\n * allowlist rule): never the raw config object, never provider options or secrets, and only\n * fields the config actually sets. `configSource` is the config file path relativized against the\n * project root, or the literal \"override\" when the config was supplied in memory.\n */\nexport interface ProjectSnapshotResult {\n readonly sourceLocale: string;\n readonly targetLocales: readonly string[];\n readonly format: SupportedFormat;\n readonly files: { readonly pattern: string };\n readonly provider: { readonly id: ProviderId };\n readonly configSource: string;\n readonly glossary: GlossaryIndicator;\n readonly prune?: boolean;\n readonly generatePlurals?: boolean;\n readonly maxBatchSize?: number;\n readonly tone?: \"formal\" | \"informal\" | \"neutral\";\n}\n","import type { z } from \"zod\";\nimport { STATUS_CHECK_METHOD, type StatusCheckResult, statusCheckParamsSchema } from \"./check.js\";\nimport { STATUS_DIFF_METHOD, type StatusDiffResult, statusDiffParamsSchema } from \"./diff.js\";\nimport {\n GLOSSARY_GET_METHOD,\n type GlossaryGetResult,\n glossaryGetParamsSchema,\n} from \"./glossary.js\";\nimport { HISTORY_LIST_METHOD, type HistoryListResult, historyListParamsSchema } from \"./history.js\";\nimport { LOCK_STATE_METHOD, type LockStateResult, lockStateParamsSchema } from \"./lock.js\";\nimport {\n PROJECT_SNAPSHOT_METHOD,\n type ProjectSnapshotResult,\n projectSnapshotParamsSchema,\n} from \"./snapshot.js\";\n\n/**\n * The single source of truth for the RPC surface: one params schema per method, keyed by its\n * method name. Everything else in this module (the method name list, the method name type, and\n * the request/result type maps) is derived from this record so the six methods can never drift\n * out of step with each other.\n */\nexport const rpcParamsSchemas = {\n [PROJECT_SNAPSHOT_METHOD]: projectSnapshotParamsSchema,\n [STATUS_CHECK_METHOD]: statusCheckParamsSchema,\n [STATUS_DIFF_METHOD]: statusDiffParamsSchema,\n [GLOSSARY_GET_METHOD]: glossaryGetParamsSchema,\n [LOCK_STATE_METHOD]: lockStateParamsSchema,\n [HISTORY_LIST_METHOD]: historyListParamsSchema,\n} as const;\n\n/** The exact six agreed RPC methods, derived from {@link rpcParamsSchemas}. */\nexport type RpcMethodName = keyof typeof rpcParamsSchemas;\n\n/** The method name list, derived from the same record every schema lookup uses. */\nexport const RPC_METHOD_NAMES = Object.keys(rpcParamsSchemas) as readonly RpcMethodName[];\n\n/** Maps each method name to its result type; each method module owns its own entry. */\nexport interface RpcResultMap {\n readonly [PROJECT_SNAPSHOT_METHOD]: ProjectSnapshotResult;\n readonly [STATUS_CHECK_METHOD]: StatusCheckResult;\n readonly [STATUS_DIFF_METHOD]: StatusDiffResult;\n readonly [GLOSSARY_GET_METHOD]: GlossaryGetResult;\n readonly [LOCK_STATE_METHOD]: LockStateResult;\n readonly [HISTORY_LIST_METHOD]: HistoryListResult;\n}\n\nexport type RpcParamsFor<M extends RpcMethodName> = z.infer<(typeof rpcParamsSchemas)[M]>;\nexport type RpcResultFor<M extends RpcMethodName> = RpcResultMap[M];\n\n/** The discriminated union of every valid `{ method, params }` request shape. */\nexport type RpcRequest = {\n [M in RpcMethodName]: { readonly method: M; readonly params: RpcParamsFor<M> };\n}[RpcMethodName];\n","/**\n * Redaction backstop for everything the server sends to the client: every projected config string\n * and every mapped domain-error message passes through {@link redact} before leaving the process.\n * studio must never import from @verbatra/ai-providers (dependency direction), so the three key-shaped\n * patterns below are duplicated, not imported, from packages/ai-providers/src/redaction.ts; the\n * four scrubbed environment variable names mirror packages/ai-providers/src/env.ts (PROVIDER_ENV).\n * Keep both redaction files in sync when a provider's key shape or environment variable changes.\n */\n\nconst REDACTED = \"[REDACTED]\";\n\n// Mirrors packages/ai-providers/src/redaction.ts KEY_PATTERNS exactly; each quantifier is over one\n// character class to stay ReDoS-safe.\nconst KEY_PATTERNS: readonly RegExp[] = [\n // The `\\b` anchors `sk-` to a word start so hyphenated words like \"risk-\" or \"task-\" pass through.\n /\\bsk-[A-Za-z0-9_-]{8,}/g,\n /AIza[0-9A-Za-z_-]{35}/g,\n /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(?::fx)?/g,\n];\n\n// Mirrors packages/ai-providers/src/env.ts PROVIDER_ENV: the four environment variables a provider\n// reads its API key from. studio never reads a key itself; this is a defense-in-depth exact-value scrub\n// in case one leaked into a config string or an error message.\nconst PROVIDER_ENV_VAR_NAMES = [\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"GEMINI_API_KEY\",\n \"DEEPL_API_KEY\",\n] as const;\n\nfunction escapeForRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction scrubConfiguredEnvValues(text: string): string {\n let out = text;\n for (const name of PROVIDER_ENV_VAR_NAMES) {\n const value = process.env[name];\n if (value !== undefined && value.length > 0) {\n out = out.replace(new RegExp(escapeForRegExp(value), \"g\"), REDACTED);\n }\n }\n return out;\n}\n\n/**\n * Replaces anything that looks like a provider secret with `[REDACTED]`: the three key-shaped\n * patterns above, plus the exact value of any of the four provider API key environment variables\n * that happen to be set. Returns the input unchanged when nothing matches.\n */\nexport function redact(text: string): string {\n let out = text;\n for (const pattern of KEY_PATTERNS) {\n out = out.replace(pattern, REDACTED);\n }\n return scrubConfiguredEnvValues(out);\n}\n","import { check } from \"@verbatra/sdk\";\nimport type { RpcHandler } from \"../rpc.js\";\n\n/**\n * Wraps the sdk's read-only `check`: reports per-locale drift (missing, stale, up-to-date counts\n * and whether each locale, and the aggregate, is in sync) without calling a provider or touching\n * the lock. Reads the config resolved once at startup, but re-reads the source, target, and lock\n * files from disk on every call, never caching them.\n */\nexport const statusCheckHandler: RpcHandler<\"status.check\"> = async (params, deps) =>\n check({\n config: deps.config.config,\n cwd: deps.projectRoot,\n ...(params.locales !== undefined ? { locales: params.locales } : {}),\n });\n","import { diff } from \"@verbatra/sdk\";\nimport type { RpcHandler } from \"../rpc.js\";\n\n/**\n * Wraps the sdk's read-only `diff`: reports the exact pending change per target locale as three\n * key lists (missing, changed, orphaned), without calling a provider, writing any file, or\n * touching the lock. Reads the config resolved once at startup, but re-reads the source, target,\n * and lock file from disk on every call, never caching them. The result carries every key\n * unfiltered and uncapped (G27); only the browser-facing filter module caps what renders.\n */\nexport const statusDiffHandler: RpcHandler<\"status.diff\"> = async (params, deps) =>\n diff({\n config: deps.config.config,\n cwd: deps.projectRoot,\n ...(params.locales !== undefined ? { locales: params.locales } : {}),\n });\n","import { relative } from \"node:path\";\nimport type { LoadedConfig } from \"@verbatra/sdk\";\nimport type { GlossaryIndicator } from \"../shared/rpc/glossary.js\";\nimport type { ProjectSnapshotResult } from \"../shared/rpc/snapshot.js\";\nimport { redact } from \"./redaction.js\";\n\nfunction projectConfigSource(source: LoadedConfig[\"source\"], projectRoot: string): string {\n if (source.kind === \"override\") {\n return \"override\";\n }\n return redact(relative(projectRoot, source.filepath));\n}\n\n/**\n * Derives the glossary indicator from the loader's own {@link LoadedConfig.glossary} provenance\n * (never inferred from the config's shape), relativizing a file path against the project root the\n * same way {@link projectConfigSource} does. Shared by {@link buildProjectSnapshot} and the\n * `glossary.get` handler so the relativization logic exists in exactly one place.\n */\nexport function projectGlossaryIndicator(\n glossary: LoadedConfig[\"glossary\"],\n projectRoot: string,\n): GlossaryIndicator {\n if (glossary.source === \"file\") {\n return { source: \"file\", path: redact(relative(projectRoot, glossary.path)) };\n }\n return { source: glossary.source };\n}\n\n/**\n * Builds the allowlisted, read-only view of the loaded config a client is allowed to see (the\n * config projection allowlist rule): never the raw config object, never provider options or\n * secrets, and only fields the config actually sets (no synthesized defaults, for example a\n * default batch size). `format`, `provider.id`, and `tone` are closed enums fixed by the config\n * schema and are passed through as-is; they have no free-form capacity to carry a secret. Every\n * other projected string passes through the redaction backstop.\n */\nexport function buildProjectSnapshot(\n loaded: LoadedConfig,\n projectRoot: string,\n): ProjectSnapshotResult {\n const { config } = loaded;\n return {\n sourceLocale: redact(config.sourceLocale),\n targetLocales: config.targetLocales.map((locale) => redact(locale)),\n format: config.format,\n files: { pattern: redact(config.files.pattern) },\n provider: { id: config.provider.id },\n configSource: projectConfigSource(loaded.source, projectRoot),\n glossary: projectGlossaryIndicator(loaded.glossary, projectRoot),\n ...(config.prune !== undefined ? { prune: config.prune } : {}),\n ...(config.generatePlurals !== undefined ? { generatePlurals: config.generatePlurals } : {}),\n ...(config.maxBatchSize !== undefined ? { maxBatchSize: config.maxBatchSize } : {}),\n ...(config.tone !== undefined ? { tone: config.tone } : {}),\n };\n}\n","import type { VerbatraConfig } from \"@verbatra/sdk\";\nimport { projectGlossaryIndicator } from \"../projection.js\";\nimport { redact } from \"../redaction.js\";\nimport type { RpcHandler } from \"../rpc.js\";\n\n/**\n * Redacts every glossary value before it leaves the server (G16); keys are not free-form user\n * input in the same sense (they are config-authored terms) but are left untouched here since only\n * values are documented as passing through the redaction backstop.\n */\nfunction redactGlossaryEntries(\n glossary: VerbatraConfig[\"glossary\"],\n): Readonly<Record<string, string>> {\n if (glossary === undefined) {\n return {};\n }\n const redacted: Record<string, string> = {};\n for (const [key, value] of Object.entries(glossary)) {\n redacted[key] = redact(value);\n }\n return redacted;\n}\n\n/**\n * Wraps the loaded config's resolved glossary: the entries (every value redacted) plus the\n * inline-vs-file indicator read from the loader's own provenance (`deps.config.glossary`),\n * reusing the exact projection helper `project.snapshot` uses for the same field. Strictly\n * read-only; there is no write, edit, or save path anywhere in this handler.\n */\nexport const glossaryGetHandler: RpcHandler<\"glossary.get\"> = async (_params, deps) => ({\n indicator: projectGlossaryIndicator(deps.config.glossary, deps.projectRoot),\n entries: redactGlossaryEntries(deps.config.config.glossary),\n});\n","import { sep } from \"node:path\";\n\n/**\n * Strips a trailing path separator so a root is comparable both as a directory prefix and as an\n * exact match. Callers may pass a root derived from a URL (for example via `fileURLToPath` on a\n * `file://.../` directory URL), which keeps its trailing slash. Shared by the static-asset\n * containment check and the git-log history view's containment check.\n */\nexport function withoutTrailingSep(path: string): string {\n return path.length > sep.length && path.endsWith(sep) ? path.slice(0, -sep.length) : path;\n}\n","import { execFile as execFileCb } from \"node:child_process\";\nimport { resolve as resolvePath, sep } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { HistoryCommit, HistoryListResult } from \"../shared/rpc/history.js\";\nimport { withoutTrailingSep } from \"./path-normalize.js\";\nimport type { ExecFileImpl } from \"./types.js\";\n\nconst execFileAsync = promisify(execFileCb);\n\n/**\n * Default {@link ExecFileImpl}: `node:child_process.execFile` via `util.promisify`, decoded as\n * utf8. Production wiring for the git-log history view; tests inject a stub instead.\n */\nexport const defaultExecFileImpl: ExecFileImpl = async (file, args, options) => {\n const { stdout, stderr } = await execFileAsync(file, args as string[], {\n cwd: options.cwd,\n encoding: \"utf8\",\n });\n return { stdout, stderr };\n};\n\n/** Default `--max-count` when `history.list` receives no `limit` (pinned at consolidation). */\nexport const HISTORY_LIMIT_DEFAULT = 50;\n/** Hard cap on `--max-count`, regardless of what a caller requests (G25). */\nexport const HISTORY_LIMIT_CAP = 200;\n\n/** Clamps a requested history limit to the server's bound: default 50, hard cap 200, never rejected. */\nexport function clampHistoryLimit(limit: number | undefined): number {\n return Math.min(limit ?? HISTORY_LIMIT_DEFAULT, HISTORY_LIMIT_CAP);\n}\n\n/** True when `candidate` is an absolute path equal to, or nested inside, `root`. */\nexport function isPathContained(root: string, candidate: string): boolean {\n const normalizedRoot = withoutTrailingSep(root);\n return candidate === normalizedRoot || candidate.startsWith(normalizedRoot + sep);\n}\n\n/**\n * True when a raw path argument could be misread as a git flag; such a path is never sent to git.\n * Must be checked against the raw candidate before it is resolved to an absolute path: once\n * resolved against an absolute `projectRoot`, a path can never start with a dash, so checking the\n * resolved form would never reject anything.\n */\nexport function hasLeadingDash(path: string): boolean {\n return path.startsWith(\"-\");\n}\n\n/**\n * Resolves each candidate to an absolute path under `projectRoot` and drops anything that could be\n * misread as a flag or that escapes the root (G25). Both checks are defense in depth: a path built\n * from the project's own configuration never legitimately produces either shape, but a malformed\n * or unusual configuration must degrade by omission, not by handing git an unsafe argument. The\n * leading-dash check runs on each raw candidate, before resolution, since resolution against an\n * absolute root would make the check unable to ever fire. Duplicate resolved paths (for example\n * the same file used as both source and target) collapse to one entry.\n */\nexport function resolveWatchedPaths(projectRoot: string, candidates: readonly string[]): string[] {\n const root = resolvePath(projectRoot);\n const safe = candidates\n .filter((candidate) => !hasLeadingDash(candidate))\n .map((candidate) => resolvePath(root, candidate))\n .filter((candidate) => isPathContained(root, candidate));\n return Array.from(new Set(safe));\n}\n\n/** Record separator (ASCII RS) marking the start of each commit's header; unambiguous even if a subject is empty. */\nconst RECORD_SEPARATOR = \"\\x1e\";\n/** Field separator (ASCII US) between the header fields within one record. */\nconst FIELD_SEPARATOR = \"\\x1f\";\nconst GIT_LOG_FORMAT = `${RECORD_SEPARATOR}%H${FIELD_SEPARATOR}%aI${FIELD_SEPARATOR}%s`;\n\n/**\n * Builds the argument-array `git log` invocation (G25): bounded by `--max-count`, `--name-only`\n * with `-z` for NUL-separated parsing, and a `--` sentinel before every path so no path can ever\n * be misread as an option. Never includes `--follow`: history before a file rename is not shown,\n * a deliberate trade-off, not a bug.\n */\nexport function buildGitLogArgs(maxCount: number, paths: readonly string[]): string[] {\n return [\n \"log\",\n `--max-count=${maxCount}`,\n \"--name-only\",\n \"-z\",\n `--format=${GIT_LOG_FORMAT}`,\n \"--\",\n ...paths,\n ];\n}\n\nfunction parseCommitHeader(header: string): Omit<HistoryCommit, \"touchedPaths\"> | undefined {\n const [hash, authorDate, subject] = header.split(FIELD_SEPARATOR);\n if (hash === undefined || authorDate === undefined || subject === undefined) {\n return undefined;\n }\n return { hash, authorDate, subject };\n}\n\nfunction parseTouchedPaths(filesPart: string): string[] {\n return filesPart\n .split(\"\\0\")\n .map((entry) => (entry.startsWith(\"\\n\") ? entry.slice(1) : entry))\n .filter((entry) => entry.length > 0);\n}\n\nfunction parseCommitRecord(record: string): HistoryCommit | undefined {\n const nulIndex = record.indexOf(\"\\0\");\n const header = nulIndex === -1 ? record : record.slice(0, nulIndex);\n const parsedHeader = parseCommitHeader(header);\n if (parsedHeader === undefined) {\n return undefined;\n }\n const touchedPaths = nulIndex === -1 ? [] : parseTouchedPaths(record.slice(nulIndex + 1));\n return { ...parsedHeader, touchedPaths };\n}\n\n/** Parses the `-z`, `--name-only` output of a `git log` invocation built by {@link buildGitLogArgs}. */\nexport function parseGitLogOutput(stdout: string): HistoryCommit[] {\n return stdout\n .split(RECORD_SEPARATOR)\n .filter((record) => record.length > 0)\n .map(parseCommitRecord)\n .filter((commit): commit is HistoryCommit => commit !== undefined);\n}\n\n/** The minimal shape of a promisified `execFile` rejection this module distinguishes on. */\ninterface ExecFileFailure {\n readonly code?: string | number;\n readonly stderr?: string;\n}\n\nfunction isMissingGitBinary(error: ExecFileFailure): boolean {\n return error.code === \"ENOENT\";\n}\n\nfunction isNotARepository(error: ExecFileFailure): boolean {\n return typeof error.stderr === \"string\" && error.stderr.includes(\"not a git repository\");\n}\n\n/**\n * Maps a `git log` failure to its result. `{ available: false }` is reserved for exactly the two\n * cases {@link HistoryListResult} documents: git itself is missing, or `projectRoot` is not inside\n * a git repository at all. Every other failure, notably an unborn branch with no commits yet,\n * still means the repository itself is fine; there is simply no history to report, so it answers\n * `available: true` with an empty commit list rather than propagating the error.\n */\nfunction interpretGitLogFailure(error: unknown): HistoryListResult {\n const failure = error as ExecFileFailure;\n if (isMissingGitBinary(failure) || isNotARepository(failure)) {\n return { available: false };\n }\n return { available: true, commits: [] };\n}\n\n/** Input for {@link runGitLog}. */\nexport interface RunGitLogInput {\n /** The bounded, argument-array process runner (production default or an injected stub). */\n readonly execFileImpl: ExecFileImpl;\n /** Directory `git log` runs from; may be anywhere inside the repository, not necessarily its root. */\n readonly projectRoot: string;\n /** Absolute, already-contained paths to scope the log to (see {@link resolveWatchedPaths}). */\n readonly watchedPaths: readonly string[];\n /** Requested `--max-count`; clamped by {@link clampHistoryLimit}. */\n readonly limit?: number;\n}\n\n/**\n * Runs `git log` scoped to `watchedPaths` from `projectRoot` and returns the parsed result, never\n * throwing: a missing git binary or a non-repository directory degrades to `{ available: false }`\n * (see {@link interpretGitLogFailure}); an empty `watchedPaths` list (nothing to scope the log to)\n * short-circuits to an empty, available history without ever invoking git, since an unscoped `git\n * log` would report the whole repository's history instead of the watched locale files' history.\n */\nexport async function runGitLog(input: RunGitLogInput): Promise<HistoryListResult> {\n if (input.watchedPaths.length === 0) {\n return { available: true, commits: [] };\n }\n const args = buildGitLogArgs(clampHistoryLimit(input.limit), input.watchedPaths);\n try {\n const { stdout } = await input.execFileImpl(\"git\", args, { cwd: input.projectRoot });\n return { available: true, commits: parseGitLogOutput(stdout) };\n } catch (error) {\n return interpretGitLogFailure(error);\n }\n}\n","import { resolve } from \"node:path\";\n\nconst LOCALE_TOKEN = \"{locale}\";\n\n/** Resolves the file path for one locale from the configured `{locale}` pattern, against cwd. */\nexport function localeFilePath(cwd: string, pattern: string, locale: string): string {\n return resolve(cwd, pattern.replaceAll(LOCALE_TOKEN, locale));\n}\n","import type { VerbatraConfig } from \"@verbatra/sdk\";\nimport { defaultExecFileImpl, resolveWatchedPaths, runGitLog } from \"../git.js\";\nimport { localeFilePath } from \"../locale-paths.js\";\nimport type { RpcHandler } from \"../rpc.js\";\n\n/** The source file and every configured target locale file: the files the history view scopes `git log` to. */\nfunction watchedLocalePaths(config: VerbatraConfig, projectRoot: string): string[] {\n const locales = [config.sourceLocale, ...config.targetLocales];\n return locales.map((locale) => localeFilePath(projectRoot, config.files.pattern, locale));\n}\n\n/**\n * Wraps a bounded `git log` (see `../git.js`, G25) scoped to the source and every target locale\n * file: reports the commit history that touched any of them, or `{ available: false }` when git\n * itself is missing or the project root is not inside a git repository at all. Never passes\n * `--follow`, so history before a file rename is not shown; this is a deliberate trade-off. A\n * shallow clone, or a locale file with no commits yet (freshly created, uncommitted, or added but\n * never committed), still answers `available: true`, with whatever commits genuinely exist.\n */\nexport const historyListHandler: RpcHandler<\"history.list\"> = async (params, deps) => {\n const execFileImpl = deps.execFileImpl ?? defaultExecFileImpl;\n const candidates = watchedLocalePaths(deps.config.config, deps.projectRoot);\n const watchedPaths = resolveWatchedPaths(deps.projectRoot, candidates);\n return runGitLog({\n execFileImpl,\n projectRoot: deps.projectRoot,\n watchedPaths,\n ...(params.limit !== undefined ? { limit: params.limit } : {}),\n });\n};\n","import { lockState } from \"@verbatra/sdk\";\nimport type { RpcHandler } from \"../rpc.js\";\n\n/**\n * Wraps the sdk's read-only `lockState`: reports the lock-file's existence, version, and\n * per-locale drift (key count from the recorded baseline, plus missing, stale, and up-to-date\n * counts against the source and target files), without calling a provider, writing any file, or\n * touching the lock. Reads the config resolved once at startup, but re-reads the lock, source,\n * and target files from disk on every call, never caching them.\n */\nexport const lockStateHandler: RpcHandler<\"lock.state\"> = async (_params, deps) =>\n lockState({ config: deps.config.config, cwd: deps.projectRoot });\n","import { buildProjectSnapshot } from \"../projection.js\";\nimport type { RpcHandler } from \"../rpc.js\";\n\nexport const snapshotHandler: RpcHandler<\"project.snapshot\"> = async (_params, deps) =>\n buildProjectSnapshot(deps.config, deps.projectRoot);\n","import type { LoadedConfig } from \"@verbatra/sdk\";\nimport { STATUS_CHECK_METHOD } from \"../shared/rpc/check.js\";\nimport type { RpcMethodName, RpcParamsFor, RpcResultFor } from \"../shared/rpc/contract.js\";\nimport { STATUS_DIFF_METHOD } from \"../shared/rpc/diff.js\";\nimport { GLOSSARY_GET_METHOD } from \"../shared/rpc/glossary.js\";\nimport { HISTORY_LIST_METHOD } from \"../shared/rpc/history.js\";\nimport { LOCK_STATE_METHOD } from \"../shared/rpc/lock.js\";\nimport { PROJECT_SNAPSHOT_METHOD } from \"../shared/rpc/snapshot.js\";\nimport { statusCheckHandler } from \"./methods/check.js\";\nimport { statusDiffHandler } from \"./methods/diff.js\";\nimport { glossaryGetHandler } from \"./methods/glossary.js\";\nimport { historyListHandler } from \"./methods/history.js\";\nimport { lockStateHandler } from \"./methods/lock.js\";\nimport { snapshotHandler } from \"./methods/snapshot.js\";\nimport type { StudioServerDeps } from \"./types.js\";\n\n/**\n * Everything an RPC handler may read: the config resolved once at startup (G11, never re-loaded\n * per request), the project root it resolves relative paths against, and the remaining\n * {@link StudioServerDeps} forwarded unchanged for handlers that need them.\n */\nexport interface RpcHandlerDeps\n extends Omit<StudioServerDeps, \"loader\" | \"token\" | \"output\" | \"assetsRoot\"> {\n readonly config: LoadedConfig;\n readonly projectRoot: string;\n}\n\nexport type RpcHandler<M extends RpcMethodName> = (\n params: RpcParamsFor<M>,\n deps: RpcHandlerDeps,\n) => Promise<RpcResultFor<M>>;\n\n/**\n * The single handlers record. Its keys now equal the contract's full method list\n * ({@link RPC_METHOD_NAMES} in `../shared/rpc/contract.js`) exactly: every one of the six agreed\n * methods, `project.snapshot`, `status.check`, `status.diff`, `glossary.get`, `lock.state`, and\n * `history.list`, has a real handler. A future contract method added without a handler here would\n * still dispatch to `METHOD_UNKNOWN` rather than throw, but that is no longer the steady state.\n *\n * The server caches no project data between requests: `project.snapshot` only reads the config\n * resolved once at startup (see {@link RpcHandlerDeps.config}), which is the one value this whole\n * dashboard intentionally holds in memory. A handler that reads anything else from disk (the\n * status, diff, lock, or history views) must read it fresh on every call, never caching it;\n * `status.check`, `status.diff`, `lock.state`, and `history.list` all do this, and their test\n * files each assert a second call reflects an on-disk (or on-repository) change made between two\n * identical calls. `glossary.get` needs no such test: like `project.snapshot`, it only reads\n * fields already present on the startup-loaded config.\n */\nexport const rpcHandlers: { readonly [M in RpcMethodName]?: RpcHandler<M> } = {\n [PROJECT_SNAPSHOT_METHOD]: snapshotHandler,\n [STATUS_CHECK_METHOD]: statusCheckHandler,\n [STATUS_DIFF_METHOD]: statusDiffHandler,\n [GLOSSARY_GET_METHOD]: glossaryGetHandler,\n [LOCK_STATE_METHOD]: lockStateHandler,\n [HISTORY_LIST_METHOD]: historyListHandler,\n};\n","import type { z } from \"zod\";\nimport { RPC_METHOD_NAMES, type RpcMethodName, rpcParamsSchemas } from \"../shared/rpc/contract.js\";\nimport { redact } from \"./redaction.js\";\nimport { type RpcHandler, type RpcHandlerDeps, rpcHandlers } from \"./rpc.js\";\n\n/** The transport-level result of a POST /rpc call: a status and a body ready to write as-is. */\nexport interface RpcResult {\n readonly statusCode: number;\n readonly body: string;\n}\n\ntype HandlersRegistry = { readonly [M in RpcMethodName]?: RpcHandler<M> };\n\nconst REQUEST_INVALID_MESSAGE = \"The request body must be JSON shaped as { method, params }.\";\nconst METHOD_UNKNOWN_MESSAGE = \"The requested method is not recognized.\";\nconst PARAMS_INVALID_MESSAGE = \"The request parameters failed validation.\";\nconst INTERNAL_ERROR_MESSAGE = \"An unexpected error occurred.\";\n\n/** One failing zod issue, carrying only its path and code: never a message or a received value (G9). */\ninterface ParsedIssue {\n readonly path: readonly string[];\n readonly code: string;\n}\n\ninterface RawRequestShape {\n readonly method: string;\n readonly params: unknown;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Parses the body as JSON and checks it is shaped as `{ method: string, params? }`; anything else is `undefined`. */\nfunction parseRequestShape(body: Buffer): RawRequestShape | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body.toString(\"utf8\"));\n } catch {\n return undefined;\n }\n if (!isPlainObject(parsed)) {\n return undefined;\n }\n const method = parsed.method;\n if (typeof method !== \"string\") {\n return undefined;\n }\n return { method, params: parsed.params };\n}\n\nfunction isKnownMethod(method: string): method is RpcMethodName {\n return (RPC_METHOD_NAMES as readonly string[]).includes(method);\n}\n\nfunction toParsedIssues(error: z.ZodError): ParsedIssue[] {\n return error.issues.map((issue) => ({ path: issue.path.map(String), code: issue.code }));\n}\n\nfunction jsonEnvelope(statusCode: number, body: unknown): RpcResult {\n return { statusCode, body: JSON.stringify(body) };\n}\n\nfunction okEnvelope(result: unknown): RpcResult {\n return jsonEnvelope(200, { ok: true, result });\n}\n\nfunction errorEnvelope(\n statusCode: number,\n code: string,\n message: string,\n issues?: readonly ParsedIssue[],\n): RpcResult {\n const error = issues === undefined ? { code, message } : { code, message, issues };\n return jsonEnvelope(statusCode, { ok: false, error });\n}\n\n/** The minimal shape of an SdkError or an AdapterError: a `name` and a string `code`. */\ninterface DomainError {\n readonly code: string;\n readonly message: string;\n}\n\n/**\n * Structurally detects an SdkError (`@verbatra/sdk`) or an AdapterError (`@verbatra/format-adapters`)\n * without importing either class: studio must never depend on ai-providers, format-adapters, or\n * exchange (dependency direction). Both classes fix `name` to their class name and carry a string\n * `code`; that is exactly what is checked here, so the check stays correct without the import.\n */\nfunction isDomainError(error: unknown): error is DomainError {\n return (\n error instanceof Error &&\n (error.name === \"SdkError\" || error.name === \"AdapterError\") &&\n typeof (error as { code?: unknown }).code === \"string\"\n );\n}\n\n/**\n * Maps a handler throw to its response: a domain error (SdkError or AdapterError) rides HTTP 200\n * with `ok: false` and its code and redacted message; anything else is an unexpected failure,\n * answered with a fixed 500 body that never echoes the original error (it is not logged here\n * either; a caller that wants server-side detail logs the caught error itself).\n */\nfunction mapHandlerError(error: unknown): RpcResult {\n if (isDomainError(error)) {\n return jsonEnvelope(200, {\n ok: false,\n error: { code: error.code, message: redact(error.message) },\n });\n }\n return errorEnvelope(500, \"INTERNAL\", INTERNAL_ERROR_MESSAGE);\n}\n\nasync function invokeHandler(\n method: RpcMethodName,\n params: unknown,\n deps: RpcHandlerDeps,\n handlers: HandlersRegistry,\n): Promise<RpcResult> {\n const schema = rpcParamsSchemas[method];\n const parsedParams = schema.safeParse(params);\n if (!parsedParams.success) {\n return errorEnvelope(\n 400,\n \"PARAMS_INVALID\",\n PARAMS_INVALID_MESSAGE,\n toParsedIssues(parsedParams.error),\n );\n }\n const handler = handlers[method];\n if (handler === undefined) {\n return errorEnvelope(400, \"METHOD_UNKNOWN\", METHOD_UNKNOWN_MESSAGE);\n }\n try {\n // Sound at runtime only: `handler` and `parsedParams.data` are both looked up by the same\n // `method` key, so the params a schema produced always match what its own handler expects.\n // The type system cannot express that correlation across a dynamically chosen union key, so\n // the argument is asserted to `never` (assignable to any parameter type) at this one call site.\n const result = await handler(parsedParams.data as never, deps);\n return okEnvelope(result);\n } catch (error) {\n return mapHandlerError(error);\n }\n}\n\n/**\n * The complete POST /rpc envelope (G8): parses and shape-checks the body, resolves the method\n * against the shared contract, validates its parameters, dispatches to the matching handler, and\n * maps every outcome, a success, a domain error, or an unexpected throw, to the fixed response\n * envelope. `handlers` defaults to the production registry; tests inject a stub registry to\n * exercise every envelope row without needing a real handler for every method.\n */\nexport async function dispatchRpc(\n body: Buffer,\n deps: RpcHandlerDeps,\n handlers: HandlersRegistry = rpcHandlers,\n): Promise<RpcResult> {\n const request = parseRequestShape(body);\n if (request === undefined) {\n return errorEnvelope(400, \"REQUEST_INVALID\", REQUEST_INVALID_MESSAGE);\n }\n if (!isKnownMethod(request.method)) {\n return errorEnvelope(400, \"METHOD_UNKNOWN\", METHOD_UNKNOWN_MESSAGE);\n }\n return invokeHandler(request.method, request.params, deps, handlers);\n}\n\n/**\n * Transport-level extension point for POST /rpc. A request reaches this function only after\n * passing the host, origin, authentication, content-type, and body-size gate.\n */\nexport function handleRpcBody(body: Buffer, deps: RpcHandlerDeps): Promise<RpcResult> {\n return dispatchRpc(body, deps);\n}\n","import type { ServerResponse } from \"node:http\";\n\n/**\n * The exact Content-Security-Policy sent on every response. No unsafe-inline or unsafe-eval:\n * scripts and styles must come from the served origin, nothing may be framed or framed into, and\n * there is no form target or base URI to hijack.\n */\nexport const CONTENT_SECURITY_POLICY =\n \"default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'\";\n\n/** Applies the fixed security headers that every response carries, regardless of route or status. */\nexport function applySecurityHeaders(response: ServerResponse): void {\n response.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n response.setHeader(\"Referrer-Policy\", \"no-referrer\");\n response.setHeader(\"X-Frame-Options\", \"DENY\");\n response.setHeader(\"Content-Security-Policy\", CONTENT_SECURITY_POLICY);\n}\n\n/** Marks a response as never cacheable: used for HTML, the RPC endpoint, and error responses. */\nexport function applyNoStore(response: ServerResponse): void {\n response.setHeader(\"Cache-Control\", \"no-store\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join, normalize, sep } from \"node:path\";\nimport { withoutTrailingSep } from \"./path-normalize.js\";\n\n/** A static asset read from the assets root, keyed by its resolved absolute path. */\nexport interface ResolvedAsset {\n readonly path: string;\n readonly body: Buffer;\n}\n\nfunction decodeRequestPath(requestPath: string): string {\n try {\n return decodeURIComponent(requestPath);\n } catch {\n return requestPath;\n }\n}\n\n/** True when any path segment starts with a dot, for example \".env\" or \".git\". The root request (\"\") never matches. */\nfunction hasDotSegment(pathWithoutLeadingSlash: string): boolean {\n return pathWithoutLeadingSlash.split(\"/\").some((segment) => segment.startsWith(\".\"));\n}\n\n/**\n * Resolves a request path to an absolute path inside the assets root, or `undefined` when the\n * request would escape the root or names a dotfile. The path is percent-decoded exactly once,\n * then normalized and checked for containment; there is never a directory listing since only\n * `readFile` is used, never a directory read.\n */\nfunction stripQuery(requestPath: string): string {\n const queryIndex = requestPath.indexOf(\"?\");\n return queryIndex === -1 ? requestPath : requestPath.slice(0, queryIndex);\n}\n\nexport function resolveAssetPath(assetsRootPath: string, requestPath: string): string | undefined {\n const root = withoutTrailingSep(normalize(assetsRootPath));\n const decoded = decodeRequestPath(stripQuery(requestPath));\n const withoutLeadingSlash = decoded.replace(/^\\/+/, \"\");\n if (hasDotSegment(withoutLeadingSlash)) {\n return undefined;\n }\n const candidate = normalize(join(root, withoutLeadingSlash || \"index.html\"));\n return candidate.startsWith(root + sep) ? candidate : undefined;\n}\n\n/** Reads the asset a request path resolves to, or `undefined` if it does not exist or escapes the root. */\nexport async function readAsset(\n assetsRootPath: string,\n requestPath: string,\n): Promise<ResolvedAsset | undefined> {\n const resolved = resolveAssetPath(assetsRootPath, requestPath);\n if (resolved === undefined) {\n return undefined;\n }\n try {\n const body = await readFile(resolved);\n return { path: resolved, body };\n } catch {\n return undefined;\n }\n}\n","import { createHash, randomBytes, timingSafeEqual } from \"node:crypto\";\n\n/** 32 bytes (256 bits) of randomness, well above the 128-bit floor. */\nconst TOKEN_BYTES = 32;\n\n/** Generates a fresh bootstrap token as a hex string. */\nexport function generateToken(): string {\n return randomBytes(TOKEN_BYTES).toString(\"hex\");\n}\n\nfunction hashToken(value: string): Buffer {\n return createHash(\"sha256\").update(value, \"utf8\").digest();\n}\n\n/**\n * Compares a candidate (a query token or a cookie value) against the server's token. Both sides\n * are hashed to a fixed-size digest first, so a candidate of any length compares in constant time\n * against the stored token and can never throw on a length mismatch; a non-matching or\n * wrong-length candidate simply fails the comparison.\n */\nexport function tokensMatch(candidate: string, stored: string): boolean {\n return timingSafeEqual(hashToken(candidate), hashToken(stored));\n}\n","import type { ServerResponse } from \"node:http\";\nimport { applyNoStore } from \"./security-headers.js\";\n\nexport const UNAUTHORIZED_BODY = \"Unauthorized\";\nexport const FORBIDDEN_BODY = \"Forbidden\";\nexport const NOT_FOUND_BODY = \"Not Found\";\nexport const METHOD_NOT_ALLOWED_BODY = \"Method Not Allowed\";\nexport const PAYLOAD_TOO_LARGE_BODY = \"Payload Too Large\";\nexport const UNSUPPORTED_MEDIA_TYPE_BODY = \"Unsupported Media Type\";\nexport const NOT_IMPLEMENTED_BODY = \"Not Implemented\";\n\n/**\n * Writes one of the fixed, generic transport-error bodies. The body is always a constant string\n * supplied by the caller: it never carries a stack trace, a filesystem path, or an environment\n * value, regardless of what caused the rejection.\n */\nexport function sendConstantResponse(response: ServerResponse, status: number, body: string): void {\n applyNoStore(response);\n response.statusCode = status;\n response.setHeader(\"Content-Type\", \"text/plain; charset=utf-8\");\n response.end(body);\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { BODY_CAP_BYTES, PayloadTooLargeError, readBodyWithCap } from \"./body-reader.js\";\nimport { contentTypeFor } from \"./content-type.js\";\nimport { buildSetCookieHeader, readCookieValue } from \"./cookie.js\";\nimport { isAllowedHost, isAllowedOrigin } from \"./host-origin.js\";\nimport { isJsonRequestContentType } from \"./request-content-type.js\";\nimport { formatRequestLog } from \"./request-log.js\";\nimport type { RpcHandlerDeps } from \"./rpc.js\";\nimport { handleRpcBody } from \"./rpc-gate.js\";\nimport { applyNoStore, applySecurityHeaders } from \"./security-headers.js\";\nimport type { SseHub } from \"./sse.js\";\nimport { readAsset } from \"./static-assets.js\";\nimport { tokensMatch } from \"./token.js\";\nimport {\n FORBIDDEN_BODY,\n METHOD_NOT_ALLOWED_BODY,\n NOT_FOUND_BODY,\n PAYLOAD_TOO_LARGE_BODY,\n sendConstantResponse,\n UNAUTHORIZED_BODY,\n UNSUPPORTED_MEDIA_TYPE_BODY,\n} from \"./transport-responses.js\";\n\n/** Per-server state the dispatcher needs for every request; computed once, after listen. */\nexport interface DispatchContext {\n readonly port: number;\n readonly token: string;\n readonly cookieName: string;\n readonly assetsRootPath: string;\n readonly log: (line: string) => void;\n /** Resolved once at startup (G11); every POST /rpc call reuses this same value, never re-loading it. */\n readonly rpcDeps: RpcHandlerDeps;\n /** The live-refresh SSE hub every `GET /events` connection registers with. */\n readonly sseHub: SseHub;\n}\n\nconst EVENTS_PATH = \"/events\";\n\nfunction pathWithoutQuery(url: string): string {\n const index = url.indexOf(\"?\");\n return index === -1 ? url : url.slice(0, index);\n}\n\n/** Reads the bootstrap \"token\" query parameter on \"/\". Undefined means the key was not present at all. */\nfunction extractBootstrapToken(url: string): string | undefined {\n const index = url.indexOf(\"?\");\n if (index === -1) {\n return undefined;\n }\n const params = new URLSearchParams(url.slice(index + 1));\n return params.has(\"token\") ? (params.get(\"token\") ?? \"\") : undefined;\n}\n\nfunction isAuthenticated(context: DispatchContext, request: IncomingMessage): boolean {\n const cookieValue = readCookieValue(request.headers.cookie, context.cookieName);\n return cookieValue !== undefined && tokensMatch(cookieValue, context.token);\n}\n\nfunction finishConstant(\n context: DispatchContext,\n response: ServerResponse,\n method: string,\n path: string,\n status: number,\n body: string,\n): void {\n sendConstantResponse(response, status, body);\n context.log(formatRequestLog({ method, path, status }));\n}\n\nfunction sendRedirectToRoot(\n context: DispatchContext,\n response: ServerResponse,\n method: string,\n path: string,\n): void {\n applyNoStore(response);\n response.statusCode = 303;\n response.setHeader(\"Location\", \"/\");\n response.end();\n context.log(formatRequestLog({ method, path, status: 303 }));\n}\n\nfunction handleBootstrap(\n context: DispatchContext,\n response: ServerResponse,\n method: string,\n path: string,\n candidate: string,\n): void {\n if (!tokensMatch(candidate, context.token)) {\n finishConstant(context, response, method, path, 401, UNAUTHORIZED_BODY);\n return;\n }\n // Idempotent by construction: a valid token always re-sets the cookie and redirects, whatever\n // cookie (missing, stale, or already valid) the request arrived with.\n response.setHeader(\"Set-Cookie\", buildSetCookieHeader(context.cookieName, context.token));\n sendRedirectToRoot(context, response, method, path);\n}\n\nasync function serveStatic(\n context: DispatchContext,\n response: ServerResponse,\n method: string,\n path: string,\n): Promise<void> {\n const asset =\n (await readAsset(context.assetsRootPath, path)) ??\n (await readAsset(context.assetsRootPath, \"/index.html\"));\n if (asset === undefined) {\n finishConstant(context, response, method, path, 404, NOT_FOUND_BODY);\n return;\n }\n const contentType = contentTypeFor(asset.path);\n if (contentType.startsWith(\"text/html\")) {\n applyNoStore(response);\n }\n response.statusCode = 200;\n response.setHeader(\"Content-Type\", contentType);\n response.end(asset.body);\n context.log(formatRequestLog({ method, path, status: 200 }));\n}\n\n/**\n * Opens the live-refresh SSE stream: registers the response with the hub, which writes every\n * later refresh, heartbeat, and the final shutdown frame. This never goes through the RPC\n * dispatcher (it is not an RPC method); it shares only the same session-cookie authentication as\n * every other GET route.\n */\nfunction handleEvents(\n context: DispatchContext,\n response: ServerResponse,\n method: string,\n path: string,\n): void {\n applyNoStore(response);\n response.writeHead(200, { \"Content-Type\": \"text/event-stream; charset=utf-8\" });\n response.write(\": connected\\n\\n\");\n context.sseHub.register(response);\n context.log(formatRequestLog({ method, path, status: 200 }));\n}\n\nasync function handleGet(\n context: DispatchContext,\n request: IncomingMessage,\n response: ServerResponse,\n method: string,\n path: string,\n): Promise<void> {\n const bootstrapToken = path === \"/\" ? extractBootstrapToken(request.url ?? \"/\") : undefined;\n if (bootstrapToken !== undefined) {\n handleBootstrap(context, response, method, path, bootstrapToken);\n return;\n }\n // Every other GET, including the bare \"/\" with no token query, requires the session cookie:\n // the printed, token-bearing URL is the only entry point into an authenticated session.\n if (!isAuthenticated(context, request)) {\n finishConstant(context, response, method, path, 401, UNAUTHORIZED_BODY);\n return;\n }\n if (path === EVENTS_PATH) {\n handleEvents(context, response, method, path);\n return;\n }\n await serveStatic(context, response, method, path);\n}\n\nasync function readRpcBody(\n context: DispatchContext,\n request: IncomingMessage,\n response: ServerResponse,\n method: string,\n path: string,\n): Promise<Buffer | undefined> {\n try {\n return await readBodyWithCap(request, BODY_CAP_BYTES);\n } catch (error) {\n if (error instanceof PayloadTooLargeError) {\n finishConstant(context, response, method, path, 413, PAYLOAD_TOO_LARGE_BODY);\n return undefined;\n }\n throw error;\n }\n}\n\nasync function handlePost(\n context: DispatchContext,\n request: IncomingMessage,\n response: ServerResponse,\n method: string,\n path: string,\n): Promise<void> {\n if (!isAllowedOrigin(request.headers.origin, context.port)) {\n finishConstant(context, response, method, path, 403, FORBIDDEN_BODY);\n return;\n }\n if (path !== \"/rpc\") {\n finishConstant(context, response, method, path, 404, NOT_FOUND_BODY);\n return;\n }\n if (!isAuthenticated(context, request)) {\n finishConstant(context, response, method, path, 401, UNAUTHORIZED_BODY);\n return;\n }\n if (!isJsonRequestContentType(request.headers[\"content-type\"])) {\n finishConstant(context, response, method, path, 415, UNSUPPORTED_MEDIA_TYPE_BODY);\n return;\n }\n const body = await readRpcBody(context, request, response, method, path);\n if (body === undefined) {\n return;\n }\n // Method dispatch, parameter validation, and the RPC response envelope are a separate concern\n // that plugs in through handleRpcBody; this only gates transport-level access to it.\n const result = await handleRpcBody(body, context.rpcDeps);\n applyNoStore(response);\n response.statusCode = result.statusCode;\n response.setHeader(\"Content-Type\", \"application/json; charset=utf-8\");\n response.end(result.body);\n context.log(formatRequestLog({ method, path, status: result.statusCode }));\n}\n\n/**\n * Handles one request end to end: the Host allowlist and method policy apply before anything\n * else, Origin is checked only for POST, and GET requests need either a valid bootstrap token or\n * an existing session cookie. Every response carries the fixed security headers.\n */\nexport async function handleRequest(\n context: DispatchContext,\n request: IncomingMessage,\n response: ServerResponse,\n): Promise<void> {\n const method = request.method ?? \"GET\";\n const path = pathWithoutQuery(request.url ?? \"/\");\n\n applySecurityHeaders(response);\n\n if (!isAllowedHost(request.headers.host, context.port)) {\n finishConstant(context, response, method, path, 403, FORBIDDEN_BODY);\n return;\n }\n if (method === \"OPTIONS\") {\n finishConstant(context, response, method, path, 403, FORBIDDEN_BODY);\n return;\n }\n if (method === \"GET\") {\n await handleGet(context, request, response, method, path);\n return;\n }\n if (method === \"POST\") {\n await handlePost(context, request, response, method, path);\n return;\n }\n finishConstant(context, response, method, path, 405, METHOD_NOT_ALLOWED_BODY);\n}\n","/** Stable, machine-readable codes for a failure to start the local server. */\nexport type StudioServerErrorCode = \"PORT_IN_USE\" | \"BIND_FAILED\";\n\n/**\n * A structured error for a failure to start the server, so a caller can branch on {@link code}\n * instead of parsing a message. The message is always a fixed, safe string; it never embeds a\n * filesystem path or other environment detail.\n */\nexport class StudioServerStartError extends Error {\n readonly code: StudioServerErrorCode;\n readonly port: number;\n\n constructor(code: StudioServerErrorCode, port: number, message: string) {\n super(message);\n this.name = \"StudioServerStartError\";\n this.code = code;\n this.port = port;\n }\n}\n","import type { AddressInfo } from \"node:net\";\n\n/**\n * Extracts the actual bound TCP address from `server.address()`. A `net.Server` reports `null`\n * before listening and a string for a Unix socket or named pipe; neither applies here since the\n * server always binds a TCP port, so either case is an unexpected failure to bind.\n */\nexport function resolveBoundAddress(address: AddressInfo | string | null): AddressInfo {\n if (address === null || typeof address === \"string\") {\n throw new Error(\"verbatra studio server failed to bind a TCP address\");\n }\n return address;\n}\n\n/** Extracts just the bound port; see {@link resolveBoundAddress} for the failure cases. */\nexport function resolveBoundPort(address: AddressInfo | string | null): number {\n return resolveBoundAddress(address).port;\n}\n","/**\n * The event shapes carried over the live-refresh SSE stream. Kept separate from `shared/rpc/`\n * (the frozen request/response contract for POST /rpc): these are server-to-client push events,\n * not RPC methods, but they are shared, type-only, and used by both the server\n * (`server/sse.ts`) and the client (`client/reconnect.ts`), so they live next to the rpc contract\n * rather than duplicated on each side.\n */\n\n/** Which category of watched file changed: the source locale file, a target locale file, or the lock file. */\nexport type RefreshReason = \"source\" | \"targets\" | \"lock\";\n\n/**\n * A payload-free re-fetch signal (G12): it never carries file content or a diff, only which\n * category of file changed and when. A client reacts by re-fetching whichever RPC view is\n * currently active; it never trusts this event's data directly.\n */\nexport interface RefreshEvent {\n readonly reason: RefreshReason;\n /** ISO-8601 timestamp of when the debounced change settled. */\n readonly at: string;\n}\n\n/** The final event a client ever receives on a stream: the server is shutting down. */\nexport interface ShutdownEvent {\n /** ISO-8601 timestamp of the shutdown. */\n readonly at: string;\n}\n\n/** The named SSE event types this stream ever writes, besides the heartbeat comment. */\nexport const SSE_EVENT_REFRESH = \"refresh\";\nexport const SSE_EVENT_SHUTDOWN = \"shutdown\";\n","/**\n * The SSE hub: tracks every currently connected `GET /events` client and writes payload-free\n * refresh events, periodic heartbeats, and the final shutdown frame to all of them. A client is\n * deregistered the moment its response emits \"close\" or \"error\", or the moment a write to it\n * fails (for example a destroyed socket); no error from any of that ever escapes as an uncaught\n * exception or an unhandled rejection, since every write goes through {@link tryWrite}.\n */\nimport type { RefreshEvent, ShutdownEvent } from \"../shared/sse-events.js\";\nimport { SSE_EVENT_REFRESH, SSE_EVENT_SHUTDOWN } from \"../shared/sse-events.js\";\nimport { redact } from \"./redaction.js\";\n\n/** Default interval between heartbeat frames; overridden by {@link StudioServerDeps.heartbeatIntervalMs} in tests. */\nconst DEFAULT_HEARTBEAT_MS = 15_000;\n\n/**\n * The minimal response surface the hub needs: writing frames, ending the stream, and observing\n * \"close\" or \"error\". Declared independently of `node:http`'s `ServerResponse` (rather than\n * `Pick`-ing from it) so a plain fake object satisfies it in tests without fighting the real\n * class's `this`-typed return values; a real `ServerResponse` still satisfies this structurally.\n */\nexport interface SseClientResponse {\n write(chunk: string): boolean;\n end(): void;\n once(event: \"close\" | \"error\", listener: (...args: unknown[]) => void): void;\n}\n\n/** The live-refresh SSE hub: register clients, broadcast refresh events, and shut down cleanly. */\nexport interface SseHub {\n /** Registers one client's response, writing nothing itself; the caller writes the initial headers. */\n register(response: SseClientResponse): void;\n /** Writes a refresh event to every currently registered client; a failed write deregisters it. */\n broadcastRefresh(event: RefreshEvent): void;\n /** Writes a final shutdown frame to every client, ends each response, and stops the heartbeat. */\n closeAll(): void;\n /** The number of currently registered clients. */\n readonly size: number;\n}\n\n/** Writes one SSE frame, redacted as a defense-in-depth backstop; never throws. */\nfunction tryWrite(response: SseClientResponse, event: string, data: unknown): boolean {\n const frame = redact(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n try {\n return response.write(frame);\n } catch {\n return false;\n }\n}\n\n/** Writes a heartbeat comment line; a comment is never dispatched as a named event to the client. */\nfunction tryWriteHeartbeat(response: SseClientResponse): boolean {\n try {\n return response.write(`: heartbeat ${Date.now()}\\n\\n`);\n } catch {\n return false;\n }\n}\n\n/** Ends a response defensively; an already-destroyed stream must never throw out of shutdown. */\nfunction tryEnd(response: SseClientResponse): void {\n try {\n response.end();\n } catch {\n // Already torn down; there is nothing left to end.\n }\n}\n\n/** Options for {@link createSseHub}. */\nexport interface SseHubOptions {\n /** Interval between heartbeat frames; injectable so tests never wait a real production interval. */\n readonly heartbeatIntervalMs?: number;\n}\n\n/**\n * Builds a fresh SSE hub with its own heartbeat timer. The timer keeps running until\n * {@link SseHub.closeAll} is called; a server that never calls it would leak the interval, so\n * `closeAll` is always the counterpart to starting the hub.\n */\nexport function createSseHub(options: SseHubOptions = {}): SseHub {\n const clients = new Set<SseClientResponse>();\n\n function deregister(response: SseClientResponse): void {\n clients.delete(response);\n }\n\n function register(response: SseClientResponse): void {\n clients.add(response);\n response.once(\"close\", () => deregister(response));\n response.once(\"error\", () => deregister(response));\n }\n\n function broadcastRefresh(event: RefreshEvent): void {\n for (const response of clients) {\n if (!tryWrite(response, SSE_EVENT_REFRESH, event)) {\n deregister(response);\n }\n }\n }\n\n function tickHeartbeat(): void {\n for (const response of clients) {\n if (!tryWriteHeartbeat(response)) {\n deregister(response);\n }\n }\n }\n\n const heartbeatTimer = setInterval(\n tickHeartbeat,\n options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS,\n );\n heartbeatTimer.unref?.();\n\n function closeAll(): void {\n clearInterval(heartbeatTimer);\n const shutdownEvent: ShutdownEvent = { at: new Date().toISOString() };\n for (const response of clients) {\n tryWrite(response, SSE_EVENT_SHUTDOWN, shutdownEvent);\n tryEnd(response);\n }\n clients.clear();\n }\n\n return {\n register,\n broadcastRefresh,\n closeAll,\n get size(): number {\n return clients.size;\n },\n };\n}\n","/**\n * The studio-owned live-refresh watcher (G14/G15): watches the source locale file, every configured\n * target locale file, and the lock file, and raises a debounced, coalesced, payload-free\n * {@link RefreshEvent} per changed category. This never reuses the sdk's own watch mode: that seam\n * means \"translate on source change\" and watches only the source file; this module watches three\n * categories, never translates, and never carries file content or a diff (G12).\n *\n * Caveat (chokidar 5 parent-directory fallback): a target locale file that does not exist yet\n * when Studio starts is still watched, as long as its PARENT DIRECTORY already exists at startup;\n * chokidar detects the file once it is created and this module raises exactly one \"targets\"\n * refresh. A locale file whose parent directory is created after Studio starts is not picked up;\n * Studio must be restarted to pick up a newly created locale directory.\n */\n\nimport { resolve } from \"node:path\";\nimport { LOCK_FILE_NAME, type VerbatraConfig } from \"@verbatra/sdk\";\nimport { watch as chokidarWatch } from \"chokidar\";\nimport type { RefreshEvent, RefreshReason } from \"../shared/sse-events.js\";\nimport { localeFilePath } from \"./locale-paths.js\";\nimport type { CreateStudioWatcher, StudioWatcher } from \"./types.js\";\n\nconst DEFAULT_DEBOUNCE_MS = 300;\n\n/** One category of watched paths and the reason its refresh events carry. */\ninterface WatchedCategory {\n readonly reason: RefreshReason;\n readonly paths: readonly string[];\n}\n\n/** The source file, the target locale files (when any are configured), and the lock file. */\nfunction watchedCategories(\n config: VerbatraConfig,\n projectRoot: string,\n): readonly WatchedCategory[] {\n const source = localeFilePath(projectRoot, config.files.pattern, config.sourceLocale);\n const targets = config.targetLocales.map((locale) =>\n localeFilePath(projectRoot, config.files.pattern, locale),\n );\n const lock = resolve(projectRoot, LOCK_FILE_NAME);\n\n const categories: WatchedCategory[] = [{ reason: \"source\", paths: [source] }];\n if (targets.length > 0) {\n categories.push({ reason: \"targets\", paths: targets });\n }\n categories.push({ reason: \"lock\", paths: [lock] });\n return categories;\n}\n\n/** A debounced trigger for one category: coalesces a burst of raw events into one emitted refresh. */\nfunction createDebouncedTrigger(\n reason: RefreshReason,\n debounceMs: number,\n emit: (event: RefreshEvent) => void,\n): { readonly trigger: () => void; readonly clear: () => void } {\n let timer: ReturnType<typeof setTimeout> | undefined;\n return {\n trigger(): void {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n timer = setTimeout(() => {\n timer = undefined;\n emit({ reason, at: new Date().toISOString() });\n }, debounceMs);\n },\n clear(): void {\n if (timer !== undefined) {\n clearTimeout(timer);\n timer = undefined;\n }\n },\n };\n}\n\n/** Input for {@link createProjectWatcher}: the resolved config, project root, and optional debounce override. */\nexport interface ProjectWatcherInput {\n readonly config: VerbatraConfig;\n readonly projectRoot: string;\n /** Quiet period after the last raw change before a refresh fires; defaults to 300ms. */\n readonly debounceMs?: number;\n}\n\n/** Composition seam: inject the watcher factory (production chokidar, or a fake for tests). */\nexport interface ProjectWatcherDeps {\n readonly createWatcher: CreateStudioWatcher;\n}\n\n/** A running live-refresh watcher over one project's source, target, and lock files. */\nexport interface ProjectWatcher {\n /** Registers a listener invoked once per debounced, coalesced refresh event. */\n onRefresh(listener: (event: RefreshEvent) => void): void;\n /** Stops every underlying watcher and clears any pending debounce timer. */\n close(): Promise<void>;\n}\n\n/**\n * Starts watching the given project's source, target, and lock files. Each category (source,\n * targets, lock) gets its own underlying {@link StudioWatcher} instance and its own debounce timer, so\n * a simultaneous change in two categories raises two distinct, correctly tagged refresh events\n * rather than one ambiguous one. No translation ever runs from this module, and no config watcher\n * exists here: config is process-lifetime only (G11).\n */\nexport function createProjectWatcher(\n input: ProjectWatcherInput,\n deps: ProjectWatcherDeps,\n): ProjectWatcher {\n const debounceMs = input.debounceMs ?? DEFAULT_DEBOUNCE_MS;\n const listeners = new Set<(event: RefreshEvent) => void>();\n\n function emit(event: RefreshEvent): void {\n for (const listener of listeners) {\n listener(event);\n }\n }\n\n const entries = watchedCategories(input.config, input.projectRoot).map((category) => {\n const debounced = createDebouncedTrigger(category.reason, debounceMs, emit);\n const watcher = deps.createWatcher(category.paths);\n watcher.onChange(debounced.trigger);\n return { debounced, watcher };\n });\n\n return {\n onRefresh(listener: (event: RefreshEvent) => void): void {\n listeners.add(listener);\n },\n async close(): Promise<void> {\n for (const entry of entries) {\n entry.debounced.clear();\n }\n await Promise.all(entries.map((entry) => entry.watcher.close()));\n },\n };\n}\n\n/**\n * Production {@link CreateStudioWatcher}: wraps chokidar directly (a studio dependency in its own right,\n * G14/G15), watching exactly the given paths. `ignoreInitial` is set since this module has no\n * initial-run concept, unlike the sdk's own watch mode; both \"change\" and \"add\" map to the same\n * listener so a target file created after startup (chokidar's parent-directory fallback) is\n * treated the same as an edit to an existing one.\n */\nexport const defaultCreateStudioWatcher: CreateStudioWatcher = (paths): StudioWatcher => {\n const fsWatcher = chokidarWatch([...paths], { persistent: true, ignoreInitial: true });\n return {\n onChange(listener: () => void): void {\n fsWatcher.on(\"change\", () => listener());\n fsWatcher.on(\"add\", () => listener());\n },\n close: () => fsWatcher.close(),\n };\n};\n","import { createServer, type Server } from \"node:http\";\nimport type { AddressInfo, Socket } from \"node:net\";\nimport { fileURLToPath } from \"node:url\";\nimport type { LoadedConfig } from \"@verbatra/sdk\";\nimport { buildBanner } from \"./banner.js\";\nimport { cookieName } from \"./cookie.js\";\nimport { resolvePort } from \"./default-port.js\";\nimport { type DispatchContext, handleRequest } from \"./dispatch.js\";\nimport { StudioServerStartError } from \"./errors.js\";\nimport { resolveBoundAddress } from \"./resolve-bound-port.js\";\nimport type { RpcHandlerDeps } from \"./rpc.js\";\nimport { createSseHub, type SseHub } from \"./sse.js\";\nimport { generateToken } from \"./token.js\";\nimport { FORBIDDEN_BODY } from \"./transport-responses.js\";\nimport type { StudioServer, StudioServerOptions } from \"./types.js\";\nimport {\n createProjectWatcher,\n defaultCreateStudioWatcher,\n type ProjectWatcher,\n} from \"./watcher.js\";\n\nconst RAW_FORBIDDEN_RESPONSE = [\n \"HTTP/1.1 403 Forbidden\",\n \"Content-Type: text/plain; charset=utf-8\",\n `Content-Length: ${Buffer.byteLength(FORBIDDEN_BODY)}`,\n \"Connection: close\",\n \"\",\n FORBIDDEN_BODY,\n].join(\"\\r\\n\");\n\n/**\n * Handles a request the HTTP parser itself rejects, for example a garbled request line. Node's\n * default behavior for these is to write its own generic 400 response before the request listener\n * ever runs; this replaces that with the same constant 403 body every other transport rejection\n * gets. This does not cover a genuinely missing Host header: an HTTP/1.1 request with no Host\n * header at all is intercepted by Node's own HTTP server at the protocol level, answered with\n * Node's own 400, and never reaches this handler or the request listener. The request listener's\n * own Host check only ever sees a Host header that is present but wrong, for example\n * \"localhost:1234\" or an explicitly empty value, and answers those with the same constant 403\n * body.\n */\nfunction handleClientError(_error: Error, socket: Socket): void {\n if (socket.writable) {\n socket.end(RAW_FORBIDDEN_RESPONSE);\n } else {\n socket.destroy();\n }\n}\n\nfunction defaultAssetsRoot(): URL {\n return new URL(\"./app/\", import.meta.url);\n}\n\nfunction defaultOutput(line: string): void {\n console.log(line);\n}\n\n/**\n * Builds the deps every RPC handler receives, resolved once here (see the `loader` call in\n * {@link startStudioServer}, before listen) and reused for the life of the process (G11/G12): no\n * handler re-loads the config, and the server otherwise caches no project data between requests.\n */\nfunction buildRpcHandlerDeps(\n config: LoadedConfig,\n projectRoot: string,\n options: StudioServerOptions,\n): RpcHandlerDeps {\n return {\n config,\n projectRoot,\n ...(options.fs !== undefined ? { fs: options.fs } : {}),\n ...(options.adapterRegistry !== undefined ? { adapterRegistry: options.adapterRegistry } : {}),\n ...(options.execFileImpl !== undefined ? { execFileImpl: options.execFileImpl } : {}),\n ...(options.createWatcher !== undefined ? { createWatcher: options.createWatcher } : {}),\n ...(options.heartbeatIntervalMs !== undefined\n ? { heartbeatIntervalMs: options.heartbeatIntervalMs }\n : {}),\n };\n}\n\n/**\n * Confirms the server actually bound the loopback address it asked for. In practice this always\n * holds since the server is only ever told to listen on \"127.0.0.1\", but every downstream check\n * (Host, Origin, the cookie name) depends on it, so it is asserted explicitly rather than assumed.\n */\nexport function assertLoopbackAddress(address: AddressInfo): void {\n if (address.address !== \"127.0.0.1\") {\n throw new StudioServerStartError(\n \"BIND_FAILED\",\n address.port,\n \"verbatra studio server did not bind to 127.0.0.1\",\n );\n }\n}\n\nfunction listen(server: Server, port: number): Promise<AddressInfo> {\n return new Promise((resolve, reject) => {\n server.once(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"EADDRINUSE\") {\n reject(new StudioServerStartError(\"PORT_IN_USE\", port, `port ${port} is already in use`));\n return;\n }\n /* v8 ignore next -- other bind failures (for example EACCES on a privileged port) are OS and environment dependent and not reliably reproducible in a test */\n reject(error);\n });\n server.listen(port, \"127.0.0.1\", () => {\n /* v8 ignore next 5 -- server.address() is only null or a pipe path before listen resolves or after close; neither applies inside this callback */\n try {\n resolve(resolveBoundAddress(server.address()));\n } catch (error) {\n reject(error);\n }\n });\n });\n}\n\n/**\n * Shuts the server down in the pinned order (G23): the SSE hub's close, which writes a final\n * shutdown frame to every open connection and ends each response, runs first, so in-flight\n * streams end cleanly instead of being cut off mid-frame by `closeAllConnections` below. The\n * project watcher is stopped concurrently with the HTTP server's own close; neither depends on\n * the other, but both must settle before this resolves, so no chokidar handle outlives the server.\n */\nfunction closeServer(server: Server, sseHub: SseHub, watcher: ProjectWatcher): Promise<void> {\n sseHub.closeAll();\n const serverClosed = new Promise<void>((resolve, reject) => {\n server.close((error) => (error ? reject(error) : resolve()));\n server.closeAllConnections();\n });\n return Promise.all([serverClosed, watcher.close()]).then(() => undefined);\n}\n\n/**\n * Starts the local Verbatra Studio server: a loopback-only HTTP server that serves the prebuilt\n * SPA and gates every request behind a Host and Origin check, a bootstrap token, and a session\n * cookie. There is no host option and no relaxed mode; the printed loopback URL, shown once at\n * startup, is the only supported entry point.\n *\n * `options.loader` is resolved exactly once here, before the server starts listening (G11): every\n * RPC handler for the life of this process receives that same resolved config, and it is never\n * re-invoked on a later request, whatever the request does.\n *\n * Every RPC handler resolves relative paths against `options.cwd` when given, or `process.cwd()`\n * otherwise; see {@link StudioServerOptions.cwd}.\n */\nexport async function startStudioServer(options: StudioServerOptions): Promise<StudioServer> {\n const assetsRootPath = fileURLToPath(options.assetsRoot ?? defaultAssetsRoot());\n const output = options.output ?? defaultOutput;\n const token = options.token ?? generateToken();\n const config = await options.loader();\n const projectRoot = options.cwd ?? process.cwd();\n\n const watcher = createProjectWatcher(\n { config: config.config, projectRoot },\n { createWatcher: options.createWatcher ?? defaultCreateStudioWatcher },\n );\n const sseHub = createSseHub(\n options.heartbeatIntervalMs !== undefined\n ? { heartbeatIntervalMs: options.heartbeatIntervalMs }\n : {},\n );\n watcher.onRefresh((event) => sseHub.broadcastRefresh(event));\n\n const server = createServer();\n server.on(\"clientError\", handleClientError);\n const address = await listen(server, resolvePort(options.port));\n assertLoopbackAddress(address);\n const port = address.port;\n\n const context: DispatchContext = {\n port,\n token,\n cookieName: cookieName(port),\n assetsRootPath,\n log: output,\n rpcDeps: buildRpcHandlerDeps(config, projectRoot, options),\n sseHub,\n };\n server.on(\"request\", (request, response) => {\n handleRequest(context, request, response).catch(() => {\n // A genuine transport failure, such as the client aborting an upload mid-body, has already\n // left the connection unusable; there is nothing safe left to write, so it is torn down\n // without leaking anything about the failure.\n request.destroy();\n response.destroy();\n });\n });\n\n const url = `http://127.0.0.1:${port}/`;\n output(buildBanner(url, token));\n\n return {\n url,\n port,\n close: () => closeServer(server, sseHub, watcher),\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@verbatra/studio",
|
|
3
|
+
"version": "0.1.0-next.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Local Verbatra Studio dashboard: a read-only web view over a verbatra project, served from a prebuilt single-page app.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"i18n",
|
|
8
|
+
"internationalization",
|
|
9
|
+
"localization",
|
|
10
|
+
"l10n",
|
|
11
|
+
"translation",
|
|
12
|
+
"dashboard",
|
|
13
|
+
"studio",
|
|
14
|
+
"verbatra"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Mario Kreitz",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/mariokreitz/verbatra.git",
|
|
21
|
+
"directory": "packages/studio"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/mariokreitz/verbatra/tree/main/packages/studio",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/mariokreitz/verbatra/issues"
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22.14.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public",
|
|
42
|
+
"provenance": true
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup && vite build",
|
|
46
|
+
"dev:app": "vite build --watch --minify false",
|
|
47
|
+
"dev:server": "tsx watch src/dev/server.ts",
|
|
48
|
+
"lint": "biome check .",
|
|
49
|
+
"test": "vitest run --coverage",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.app.json"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@verbatra/sdk": "workspace:*",
|
|
55
|
+
"chokidar": "catalog:",
|
|
56
|
+
"zod": "catalog:"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/node": "catalog:",
|
|
60
|
+
"@types/react": "catalog:",
|
|
61
|
+
"@types/react-dom": "catalog:",
|
|
62
|
+
"@verbatra/config": "workspace:*",
|
|
63
|
+
"@verbatra/format-adapters": "workspace:*",
|
|
64
|
+
"@vitejs/plugin-react": "6.0.3",
|
|
65
|
+
"@vitest/coverage-v8": "catalog:",
|
|
66
|
+
"react": "catalog:",
|
|
67
|
+
"react-dom": "catalog:",
|
|
68
|
+
"tsup": "catalog:",
|
|
69
|
+
"tsx": "4.23.0",
|
|
70
|
+
"typescript": "catalog:",
|
|
71
|
+
"vite": "8.0.16",
|
|
72
|
+
"vitest": "catalog:"
|
|
73
|
+
}
|
|
74
|
+
}
|