@samva/vite 0.1.0 → 0.1.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/dist/emit.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"emit.js","names":[],"sources":["../src/emit.ts"],"sourcesContent":["import type { CompileBase, CompiledTemplate, CompileOptions } from \"@samva/markup/compile\";\nimport { compileSource, COMPILER_VERSION } from \"@samva/markup/compile\";\nimport type { Diagnostic } from \"@samva/markup/diagnostics\";\nimport type { EmittedModule, EmittedTemplate } from \"@samva/markup/emit\";\nimport { emitModule } from \"@samva/markup/emit\";\nimport type { TemplateVariable } from \"@samva/markup/render-template\";\nimport { Result } from \"effect\";\n\n// The programmatic emit path shared by the vite plugin (HMR preview) and the\n// CLI (`samva templates push`). It runs the FULL local chain the design brief\n// §7b locks in: an already-evaluated TSX module → emitModule (discover + emit\n// SML with the determinism guard) → compileSource (SML → channel payload). The\n// SML compiler is a pure library; this is simply its fourth caller alongside\n// the Worker, the browser canvas, and the tests.\n//\n// The preview is channel-polymorphic: email carries per-base artifacts (HTML +\n// text for outlook-safe and modern); sms and whatsapp carry their single\n// compiled payload. The channel is derived from the compiled root tag.\n//\n// Bundler-agnostic: this never imports vite/esbuild or touches the filesystem.\n\nexport type PreviewChannel = \"email\" | \"sms\" | \"whatsapp\";\n\n/** Both email compile bases the preview surfaces and a build materializes. */\nexport const COMPILE_BASES = [\"outlook-safe\", \"modern\"] as const satisfies readonly CompileBase[];\n\n/** One email base's compiled artifact, or its compile diagnostics if it failed. */\nexport interface EmailBaseArtifact {\n readonly base: CompileBase;\n readonly ok: boolean;\n readonly html?: string | undefined;\n readonly text?: string | undefined;\n readonly warnings?: readonly Diagnostic[] | undefined;\n readonly diagnostics?: readonly Diagnostic[] | undefined;\n}\n\nexport type WhatsAppButtonPreview =\n | { readonly type: \"QUICK_REPLY\"; readonly text: string }\n | { readonly type: \"URL\"; readonly text: string; readonly url: string }\n | { readonly type: \"PHONE_NUMBER\"; readonly text: string; readonly phone_number: string }\n | { readonly type: \"COPY_CODE\"; readonly example: string };\n\nexport interface AuthoringValidationSummary {\n readonly ok: boolean;\n readonly diagnostics: number;\n readonly warnings: number;\n readonly repairableDiagnostics: number;\n readonly placeholders: readonly string[];\n readonly darkClassCount: number;\n readonly emailBases?:\n | readonly {\n readonly base: CompileBase;\n readonly ok: boolean;\n readonly htmlBytes?: number | undefined;\n readonly warnings: number;\n readonly diagnostics: number;\n readonly repairableDiagnostics: number;\n }[]\n | undefined;\n}\n\n/** The compiled payload for one template, discriminated by channel. */\nexport type CompiledPreview =\n | { readonly channel: \"email\"; readonly bases: readonly EmailBaseArtifact[] }\n | {\n readonly channel: \"sms\";\n readonly ok: boolean;\n readonly body?: string | undefined;\n readonly encoding?: \"gsm7\" | \"ucs2\" | undefined;\n readonly estimatedSegments?: number | undefined;\n readonly maxSegments?: number | undefined;\n readonly warnings?: readonly Diagnostic[] | undefined;\n readonly diagnostics?: readonly Diagnostic[] | undefined;\n }\n | {\n readonly channel: \"whatsapp\";\n readonly ok: boolean;\n readonly headerType?: \"text\" | \"image\" | \"video\" | \"document\" | undefined;\n readonly headerContent?: string | undefined;\n readonly bodyContent?: string | undefined;\n readonly footerContent?: string | undefined;\n readonly buttons?: readonly WhatsAppButtonPreview[] | undefined;\n readonly warnings?: readonly Diagnostic[] | undefined;\n readonly diagnostics?: readonly Diagnostic[] | undefined;\n };\n\n/**\n * One template's full preview payload: its emitted SML, inferred variable\n * schema, emit diagnostics, the derived channel, and the channel's compiled\n * output. `ok` is true only when the template emitted cleanly AND compiled\n * without error — the shape the preview client and the CLI both gate on.\n */\nexport interface EmittedTemplatePreview {\n readonly name: string;\n readonly ok: boolean;\n readonly channel: PreviewChannel;\n /** Emitted SML, present whenever the render itself succeeded (even if unstable). */\n readonly source: string | undefined;\n readonly variables: readonly TemplateVariable[];\n readonly diagnostics: readonly Diagnostic[];\n readonly compiled: CompiledPreview;\n readonly validation: AuthoringValidationSummary;\n}\n\n/** The aggregate for one module file. */\nexport interface ModulePreview {\n readonly ok: boolean;\n readonly compilerVersion: string;\n readonly templates: readonly EmittedTemplatePreview[];\n readonly diagnostics: readonly Diagnostic[];\n}\n\nconst countRepairable = (diagnostics: readonly Diagnostic[]): number =>\n diagnostics.filter((diagnostic) => diagnostic.repair !== undefined).length;\n\nconst uniquePlaceholders = (source: string | undefined): readonly string[] =>\n source === undefined\n ? []\n : [\n ...new Set(\n [...source.matchAll(/{{\\s*([A-Za-z_][\\w.-]*)\\s*}}/g)].map((match) => match[1] ?? \"\"),\n ),\n ]\n .filter(Boolean)\n .sort();\n\nconst darkClassCount = (source: string | undefined): number =>\n source === undefined ? 0 : (source.match(/\\bdark:/g) ?? []).length;\n\nconst validationSummary = (\n source: string | undefined,\n emitDiagnostics: readonly Diagnostic[],\n compiled: CompiledPreview,\n): AuthoringValidationSummary => {\n if (compiled.channel === \"email\") {\n const baseSummaries = compiled.bases.map((artifact) => {\n const warnings = artifact.warnings ?? [];\n const diagnostics = artifact.diagnostics ?? [];\n return {\n base: artifact.base,\n ok: artifact.ok,\n htmlBytes:\n artifact.html === undefined ? undefined : new TextEncoder().encode(artifact.html).length,\n warnings: warnings.length,\n diagnostics: diagnostics.length,\n repairableDiagnostics: countRepairable(diagnostics),\n };\n });\n const warnings = baseSummaries.reduce((sum, base) => sum + base.warnings, 0);\n const diagnostics =\n emitDiagnostics.length + baseSummaries.reduce((sum, base) => sum + base.diagnostics, 0);\n return {\n ok:\n baseSummaries.length > 0 &&\n baseSummaries.every((base) => base.ok) &&\n emitDiagnostics.length === 0,\n diagnostics,\n warnings,\n repairableDiagnostics:\n countRepairable(emitDiagnostics) +\n baseSummaries.reduce((sum, base) => sum + base.repairableDiagnostics, 0),\n placeholders: uniquePlaceholders(source),\n darkClassCount: darkClassCount(source),\n emailBases: baseSummaries,\n };\n }\n\n const warnings = compiled.warnings ?? [];\n const diagnostics = compiled.diagnostics ?? [];\n return {\n ok: compiled.ok && emitDiagnostics.length === 0,\n diagnostics: emitDiagnostics.length + diagnostics.length,\n warnings: warnings.length,\n repairableDiagnostics: countRepairable(emitDiagnostics) + countRepairable(diagnostics),\n placeholders: uniquePlaceholders(source),\n darkClassCount: darkClassCount(source),\n };\n};\n\nconst emailBaseArtifact = (\n source: string,\n base: CompileBase,\n options: CompileOptions | undefined,\n): EmailBaseArtifact => {\n const result = compileSource(source, { ...options, base });\n if (Result.isFailure(result)) {\n return { base, ok: false, diagnostics: result.failure.diagnostics };\n }\n if (result.success.channel !== \"email\") {\n return {\n base,\n ok: false,\n diagnostics: [\n {\n code: \"channel-changed\",\n message: \"template no longer compiles to email\",\n span: { start: 0, end: 0 },\n },\n ],\n };\n }\n return {\n base,\n ok: true,\n html: result.success.html,\n text: result.success.text,\n warnings: result.success.warnings,\n };\n};\n\n/** Compile one source into its channel-polymorphic preview. */\nconst compiledPreview = (\n source: string,\n options: CompileOptions | undefined,\n): {\n readonly channel: PreviewChannel;\n readonly compileOk: boolean;\n readonly compiled: CompiledPreview;\n} => {\n // Probe once (email base is a no-op for sms/whatsapp) to learn the channel.\n const probe = compileSource(source, { ...options, base: \"outlook-safe\" });\n if (Result.isFailure(probe)) {\n return {\n channel: \"email\",\n compileOk: false,\n compiled: {\n channel: \"email\",\n bases: [{ base: \"outlook-safe\", ok: false, diagnostics: probe.failure.diagnostics }],\n },\n };\n }\n const compiled: CompiledTemplate = probe.success;\n\n if (compiled.channel === \"sms\") {\n return {\n channel: \"sms\",\n compileOk: true,\n compiled: {\n channel: \"sms\",\n ok: true,\n body: compiled.body,\n encoding: compiled.encoding,\n estimatedSegments: compiled.estimatedSegments,\n maxSegments: compiled.maxSegments,\n warnings: compiled.warnings,\n },\n };\n }\n\n if (compiled.channel === \"whatsapp\") {\n return {\n channel: \"whatsapp\",\n compileOk: true,\n compiled: {\n channel: \"whatsapp\",\n ok: true,\n headerType: compiled.headerType,\n headerContent: compiled.headerContent,\n bodyContent: compiled.bodyContent,\n footerContent: compiled.footerContent,\n buttons: compiled.buttons,\n warnings: compiled.warnings,\n },\n };\n }\n\n const bases: EmailBaseArtifact[] = [\n {\n base: \"outlook-safe\",\n ok: true,\n html: compiled.html,\n text: compiled.text,\n warnings: compiled.warnings,\n },\n emailBaseArtifact(source, \"modern\", options),\n ];\n return {\n channel: \"email\",\n compileOk: bases.every((artifact) => artifact.ok),\n compiled: { channel: \"email\", bases },\n };\n};\n\nconst previewTemplate = (\n emitted: EmittedTemplate,\n options?: CompileOptions,\n): EmittedTemplatePreview => {\n if (emitted.source === undefined) {\n // Render failed outright (grammar violation) — no source to compile.\n return {\n name: emitted.name,\n ok: false,\n channel: \"email\",\n source: undefined,\n variables: emitted.variables,\n diagnostics: emitted.diagnostics,\n compiled: { channel: \"email\", bases: [] },\n validation: validationSummary(undefined, emitted.diagnostics, {\n channel: \"email\",\n bases: [],\n }),\n };\n }\n const { channel, compileOk, compiled } = compiledPreview(emitted.source, options);\n return {\n name: emitted.name,\n ok: emitted.ok && compileOk,\n channel,\n source: emitted.source,\n variables: emitted.variables,\n diagnostics: emitted.diagnostics,\n compiled,\n validation: validationSummary(emitted.source, emitted.diagnostics, compiled),\n };\n};\n\n/**\n * Emits + compiles the one supported template export from an already-evaluated\n * TSX file. A template file owns exactly one template: its `default` export.\n * Named exports are ordinary helpers and are never inspected or invoked.\n *\n * `name` comes from the file identity rather than the JavaScript function name,\n * keeping the catalog, build output, and CLI push slug stable when a developer\n * changes the implementation shape of the default export. Direct callers of\n * `@samva/vite/emit` must pass that file-backed name as the first argument.\n */\nexport const emitModulePreview = (\n name: string,\n moduleExports: Record<string, unknown>,\n options?: CompileOptions,\n): ModulePreview => {\n if (!Object.hasOwn(moduleExports, \"default\")) {\n return {\n ok: false,\n compilerVersion: COMPILER_VERSION,\n templates: [],\n diagnostics: [\n {\n code: \"missing-default-export\",\n message:\n \"TSX template files must default-export exactly one template; named exports are ignored.\",\n span: { start: 0, end: 0 },\n },\n ],\n };\n }\n\n const emitted: EmittedModule = emitModule({ [name]: moduleExports.default });\n if (emitted.templates.length === 0) {\n return {\n ok: false,\n compilerVersion: COMPILER_VERSION,\n templates: [],\n diagnostics: [\n {\n code: \"invalid-default-export\",\n message:\n \"The default export is not a Samva template returning <Email>, <Sms>, or <WhatsApp>.\",\n span: { start: 0, end: 0 },\n },\n ],\n };\n }\n const templates = emitted.templates.map((template) => previewTemplate(template, options));\n return {\n ok: emitted.ok && templates.every((template) => template.ok),\n compilerVersion: COMPILER_VERSION,\n templates,\n diagnostics: emitted.diagnostics,\n };\n};\n\n/**\n * Compiles a raw `.samva` source string directly (no TSX). Used for `.samva`\n * files the plugin discovers alongside `.tsx` templates and for `samva\n * templates pull` round-trips. Wraps the single source in the same preview\n * shape so the client renders `.samva` and `.tsx` templates identically.\n */\nexport const compileSourcePreview = (\n name: string,\n source: string,\n options?: CompileOptions,\n): EmittedTemplatePreview => {\n const { channel, compileOk, compiled } = compiledPreview(source, options);\n return {\n name,\n ok: compileOk,\n channel,\n source,\n variables: [],\n diagnostics: [],\n compiled,\n validation: validationSummary(source, [], compiled),\n };\n};\n\nexport { COMPILER_VERSION };\n"],"mappings":"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"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/editor-store.ts","../src/editor.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { readFile, realpath, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve, sep } from \"node:path\";\n\nimport type { CompileOptions } from \"@samva/markup/compile\";\nimport type { Diagnostic } from \"@samva/markup/diagnostics\";\nimport { variableReferences } from \"@samva/markup/interpolate\";\nimport { applyOps, type SmlOp } from \"@samva/markup/ops\";\nimport type { TemplateVariable } from \"@samva/markup/render-template\";\nimport { Result } from \"effect\";\n\nimport { discoverTemplateFiles, type TemplateFile, type TemplateFileKind } from \"./discovery\";\nimport { compileSourcePreview, emitModulePreview, type EmittedTemplatePreview } from \"./emit\";\n\n// Node-side repository backing the Vite editor surface. `.samva` is the editable\n// source of truth; `.tsx` is evaluated by Vite and exposed as a read-only authored\n// source plus emitted SML. File paths are document identities: one TSX file has\n// exactly one possible template, its default export.\n\ntype EditorTemplateAccess = \"editable\" | \"readonly\";\n\nexport interface EditorTemplate {\n /** POSIX path relative to the templates directory — the document identity. */\n readonly id: string;\n readonly name: string;\n readonly channel: \"email\" | \"sms\" | \"whatsapp\";\n readonly sourceKind: TemplateFileKind;\n readonly access: EditorTemplateAccess;\n}\n\nexport interface EditorCatalogDiagnostic {\n readonly file: string;\n readonly severity: \"warning\" | \"error\";\n readonly code: string;\n readonly message: string;\n readonly span?: { readonly start: number; readonly end: number } | undefined;\n}\n\nexport interface EditorCatalog {\n readonly templates: ReadonlyArray<EditorTemplate>;\n readonly diagnostics: ReadonlyArray<EditorCatalogDiagnostic>;\n}\n\ninterface SnapshotFields {\n readonly id: null;\n readonly name: string;\n readonly channel: \"email\" | \"sms\" | \"whatsapp\";\n /** Emitted/compiled SML. This is always the renderer's input, including TSX. */\n readonly source: string;\n readonly rev: string;\n readonly variables: ReadonlyArray<{ readonly name: string; readonly required: boolean }>;\n readonly metadata: DocumentMetadataValue;\n}\n\nexport type EditorSnapshot =\n | (SnapshotFields & {\n readonly origin: { readonly kind: \"samva\"; readonly file: string };\n readonly access: { readonly kind: \"editable\" };\n })\n | (SnapshotFields & {\n readonly origin: {\n readonly kind: \"tsx\";\n readonly file: string;\n readonly authoredSource: string;\n };\n readonly access: { readonly kind: \"readonly\"; readonly reason: string };\n });\n\ninterface FromDefaultValue {\n readonly email: string;\n readonly name?: string | undefined;\n}\n\ninterface DocumentMetadataValue {\n readonly fromDefault?: FromDefaultValue | undefined;\n readonly replyToDefault?: ReadonlyArray<string> | undefined;\n}\n\nexport type EditorUpdate =\n | { readonly kind: \"ops\"; readonly baseRev: string; readonly ops: ReadonlyArray<SmlOp> }\n | { readonly kind: \"source\"; readonly baseRev: string; readonly source: string }\n | {\n readonly kind: \"metadata\";\n readonly patch: {\n readonly name?: string | undefined;\n readonly fromDefault?: FromDefaultValue | null | undefined;\n readonly replyToDefault?: ReadonlyArray<string> | null | undefined;\n };\n };\n\nexport type SaveOutcome =\n | { readonly ok: true; readonly rev: string }\n | {\n readonly ok: false;\n readonly conflict: { readonly expectedRev: string; readonly actualRev: string };\n }\n | { readonly ok: false; readonly error: string };\n\nexport interface StoreChange {\n readonly id: string;\n readonly rev: string;\n /** Emitted SML; the renderer never consumes authored TSX. */\n readonly source: string;\n /** Present for a TSX refresh so the read-only source panel advances too. */\n readonly authoredSource?: string | undefined;\n readonly sourceClient?: string | undefined;\n}\n\nexport type TsxEvaluation =\n | {\n readonly ok: true;\n readonly source: string;\n readonly channel: \"email\" | \"sms\" | \"whatsapp\";\n readonly variables: ReadonlyArray<TemplateVariable>;\n }\n | { readonly ok: false; readonly diagnostics: ReadonlyArray<EditorCatalogDiagnostic> };\n\nexport type EvaluateTsx = (\n path: string,\n id: string,\n name: string,\n compile: CompileOptions | undefined,\n) => Promise<TsxEvaluation>;\n\ninterface SessionMeta {\n readonly name?: string | undefined;\n readonly metadata: DocumentMetadataValue;\n}\n\ninterface CachedTemplate {\n readonly template: EditorTemplate;\n readonly source: string;\n readonly authoredSource?: string | undefined;\n readonly rev: string;\n readonly variables: ReadonlyArray<{ readonly name: string; readonly required: boolean }>;\n}\n\ninterface LoadedTemplate {\n readonly entry?: CachedTemplate | undefined;\n readonly diagnostics: ReadonlyArray<EditorCatalogDiagnostic>;\n}\n\nexport interface EditorFileStoreOptions {\n readonly evaluateTsx?: EvaluateTsx | undefined;\n /** Read lazily so theme HMR applies to the next evaluation without rebuilding the store. */\n readonly compile?: (() => CompileOptions | undefined) | undefined;\n}\n\nconst noop = (): void => {};\n\nconst hashSource = (source: string): string =>\n createHash(\"sha256\").update(source, \"utf8\").digest(\"hex\").slice(0, 16);\n\nconst deriveName = (id: string): string => {\n const base = id.slice(id.lastIndexOf(\"/\") + 1);\n return base.replace(/\\.(?:samva|tsx)$/, \"\");\n};\n\nconst kindForId = (id: string): TemplateFileKind | undefined => {\n if (id.endsWith(\".samva\")) return \"samva\";\n if (id.endsWith(\".tsx\")) return \"tsx\";\n return undefined;\n};\n\nconst within = (child: string, parent: string): boolean =>\n child === parent || child.startsWith(parent + sep);\n\nconst flattenCompiledDiagnostics = (\n template: EmittedTemplatePreview,\n): ReadonlyArray<Diagnostic> => {\n if (template.compiled.channel === \"email\") {\n return [\n ...template.diagnostics,\n ...template.compiled.bases.flatMap((base) => base.diagnostics ?? []),\n ];\n }\n return [...template.diagnostics, ...(template.compiled.diagnostics ?? [])];\n};\n\nconst diagnostic = (\n file: string,\n severity: EditorCatalogDiagnostic[\"severity\"],\n code: string,\n message: string,\n span?: EditorCatalogDiagnostic[\"span\"],\n): EditorCatalogDiagnostic => ({ file, severity, code, message, span });\n\n/**\n * Enforces the TSX file contract after Vite evaluates the module. Named exports\n * are deliberately never inspected or invoked; only `default` can be a template.\n */\nexport const evaluateDefaultExport = (\n id: string,\n name: string,\n moduleExports: Record<string, unknown>,\n compile?: CompileOptions | undefined,\n): TsxEvaluation => {\n const preview = emitModulePreview(name, moduleExports, compile);\n const template = preview.templates[0];\n if (template === undefined) {\n return {\n ok: false,\n diagnostics: preview.diagnostics.map((problem) =>\n diagnostic(\n id,\n problem.code === \"missing-default-export\" ? \"warning\" : \"error\",\n problem.code,\n problem.message,\n problem.span,\n ),\n ),\n };\n }\n\n if (!template.ok || template.source === undefined) {\n const problems = flattenCompiledDiagnostics(template);\n return {\n ok: false,\n diagnostics:\n problems.length > 0\n ? problems.map((problem) =>\n diagnostic(id, \"error\", problem.code, problem.message, problem.span),\n )\n : [\n diagnostic(\n id,\n \"error\",\n \"invalid-default-export\",\n \"The default-exported template could not emit valid SML.\",\n ),\n ],\n };\n }\n\n return {\n ok: true,\n source: template.source,\n channel: template.channel,\n variables: template.variables,\n };\n};\n\nexport class EditorFileStore {\n private readonly dir: string;\n private readonly evaluateTsx: EvaluateTsx | undefined;\n private readonly compile: () => CompileOptions | undefined;\n private realDir: string | undefined;\n private loaded = false;\n private initialLoad: Promise<EditorCatalog> | undefined;\n private readonly entries = new Map<string, CachedTemplate>();\n private readonly diagnostics = new Map<string, ReadonlyArray<EditorCatalogDiagnostic>>();\n private readonly currentRev = new Map<string, string>();\n private readonly writing = new Set<string>();\n private readonly saveChain = new Map<string, Promise<unknown>>();\n private readonly session = new Map<string, SessionMeta>();\n private readonly listeners = new Set<(change: StoreChange) => void>();\n private readonly catalogListeners = new Set<() => void>();\n\n constructor(templatesDir: string, options: EditorFileStoreOptions = {}) {\n this.dir = templatesDir;\n this.evaluateTsx = options.evaluateTsx;\n this.compile = options.compile ?? (() => undefined);\n }\n\n private async resolvePath(id: string): Promise<string | undefined> {\n if (kindForId(id) === undefined || id.includes(\"\\0\")) return undefined;\n const full = resolve(this.dir, id);\n if (!within(full, this.dir)) return undefined;\n const realDir = await this.resolveRealDir();\n if (realDir === undefined) return undefined;\n const real = await realpath(full).catch(() => undefined);\n if (real === undefined || !within(real, realDir)) return undefined;\n return real;\n }\n\n private async resolveRealDir(): Promise<string | undefined> {\n if (this.realDir === undefined) this.realDir = await realpath(this.dir).catch(() => undefined);\n return this.realDir;\n }\n\n idFor(path: string): string | undefined {\n const full = isAbsolute(path) ? path : join(this.dir, path);\n if (!within(full, this.dir)) return undefined;\n const rel = full\n .slice(this.dir.length + 1)\n .split(\"\\\\\")\n .join(\"/\");\n return kindForId(rel) === undefined ? undefined : rel;\n }\n\n private async load(file: TemplateFile): Promise<LoadedTemplate> {\n const path = await this.resolvePath(file.id);\n if (path === undefined) {\n return {\n diagnostics: [\n diagnostic(\n file.id,\n \"error\",\n \"template-unavailable\",\n \"Template file is missing or resolves outside the configured templates directory.\",\n ),\n ],\n };\n }\n const authoredSource = await readFile(path, \"utf8\").catch(() => undefined);\n if (authoredSource === undefined) {\n return {\n diagnostics: [\n diagnostic(file.id, \"error\", \"template-read-error\", \"Template file could not be read.\"),\n ],\n };\n }\n\n const name = this.session.get(file.id)?.name ?? deriveName(file.id);\n if (file.kind === \"samva\") {\n const preview = compileSourcePreview(name, authoredSource, this.compile());\n if (!preview.ok) {\n const problems = flattenCompiledDiagnostics(preview);\n const previous = this.entries.get(file.id);\n const diagnostics = problems.map((problem) =>\n diagnostic(file.id, \"error\", problem.code, problem.message, problem.span),\n );\n // Keep an already-open editable document addressable while its user is\n // typing through an invalid intermediate state. A malformed file at cold\n // discovery has no trustworthy channel and is excluded instead.\n if (previous?.template.sourceKind !== \"samva\") return { diagnostics };\n const rev = hashSource(authoredSource);\n return {\n entry: {\n ...previous,\n template: { ...previous.template, name },\n source: authoredSource,\n rev,\n variables: [...variableReferences(authoredSource)]\n .sort()\n .map((variableName) => ({ name: variableName, required: false })),\n },\n diagnostics,\n };\n }\n const rev = hashSource(authoredSource);\n return {\n entry: {\n template: {\n id: file.id,\n name,\n channel: preview.channel,\n sourceKind: \"samva\",\n access: \"editable\",\n },\n source: authoredSource,\n rev,\n variables: [...variableReferences(authoredSource)]\n .sort()\n .map((variableName) => ({ name: variableName, required: false })),\n },\n diagnostics: [],\n };\n }\n\n if (this.evaluateTsx === undefined) {\n return {\n diagnostics: [\n diagnostic(\n file.id,\n \"error\",\n \"tsx-evaluator-unavailable\",\n \"TSX templates require the Vite evaluator.\",\n ),\n ],\n };\n }\n const evaluated = await this.evaluateTsx(path, file.id, name, this.compile()).catch(\n (cause: unknown): TsxEvaluation => ({\n ok: false,\n diagnostics: [\n diagnostic(\n file.id,\n \"error\",\n \"module-load-error\",\n cause instanceof Error ? cause.message : String(cause),\n ),\n ],\n }),\n );\n if (!evaluated.ok) return { diagnostics: evaluated.diagnostics };\n const rev = hashSource(evaluated.source);\n return {\n entry: {\n template: {\n id: file.id,\n name,\n channel: evaluated.channel,\n sourceKind: \"tsx\",\n access: \"readonly\",\n },\n source: evaluated.source,\n authoredSource,\n rev,\n variables: evaluated.variables.map((variable) => ({\n name: variable.name,\n required: variable.required,\n })),\n },\n diagnostics: [],\n };\n }\n\n private catalogSnapshot(): EditorCatalog {\n return {\n templates: [...this.entries.values()]\n .map((entry) => entry.template)\n .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)),\n diagnostics: [...this.diagnostics.values()].flat(),\n };\n }\n\n private ensureLoaded(): Promise<EditorCatalog> {\n if (this.loaded) return Promise.resolve(this.catalogSnapshot());\n if (this.initialLoad !== undefined) return this.initialLoad;\n const loading = this.rebuild(false);\n this.initialLoad = loading;\n void loading.then(\n () => {\n if (this.initialLoad === loading) this.initialLoad = undefined;\n },\n () => {\n if (this.initialLoad === loading) this.initialLoad = undefined;\n },\n );\n return loading;\n }\n\n private async rebuild(emitDocuments: boolean): Promise<EditorCatalog> {\n const previous = new Map(this.entries);\n const files = await discoverTemplateFiles(this.dir);\n const loaded = await Promise.all(\n files.map(async (file) => [file.id, await this.load(file)] as const),\n );\n\n this.entries.clear();\n this.diagnostics.clear();\n for (const [id, result] of loaded) {\n if (result.entry !== undefined) {\n this.entries.set(id, result.entry);\n this.currentRev.set(id, result.entry.rev);\n } else {\n this.currentRev.delete(id);\n }\n if (result.diagnostics.length > 0) this.diagnostics.set(id, result.diagnostics);\n }\n this.loaded = true;\n\n if (emitDocuments) {\n for (const [id, entry] of this.entries) {\n const before = previous.get(id);\n if (\n before === undefined ||\n before.source !== entry.source ||\n before.authoredSource !== entry.authoredSource\n ) {\n this.publish({\n id,\n rev: entry.rev,\n source: entry.source,\n authoredSource: entry.authoredSource,\n });\n }\n }\n }\n for (const listener of this.catalogListeners) listener();\n return this.catalogSnapshot();\n }\n\n async catalog(): Promise<EditorCatalog> {\n return this.ensureLoaded();\n }\n\n /** Re-evaluate discovery and every TSX entry after template/dependency/theme HMR. */\n async refreshAll(\n options: { readonly documents?: boolean | undefined } = {},\n ): Promise<EditorCatalog> {\n const loading = this.initialLoad;\n if (loading !== undefined) await loading;\n else if (!this.loaded) return this.ensureLoaded();\n return this.rebuild(options.documents ?? true);\n }\n\n /** Recompile one changed editable file without re-evaluating unrelated TSX modules. */\n async refreshSamva(id: string): Promise<EditorCatalog> {\n if (kindForId(id) !== \"samva\") return this.catalog();\n if (!this.loaded) return this.ensureLoaded();\n const result = await this.load({ id, path: join(this.dir, id), kind: \"samva\" });\n if (result.entry !== undefined) {\n this.entries.set(id, result.entry);\n this.currentRev.set(id, result.entry.rev);\n } else {\n this.entries.delete(id);\n this.currentRev.delete(id);\n }\n if (result.diagnostics.length > 0) this.diagnostics.set(id, result.diagnostics);\n else this.diagnostics.delete(id);\n for (const listener of this.catalogListeners) listener();\n return this.catalogSnapshot();\n }\n\n async list(): Promise<ReadonlyArray<EditorTemplate>> {\n return (await this.catalog()).templates;\n }\n\n async open(id: string): Promise<EditorSnapshot | undefined> {\n if (!this.loaded) await this.ensureLoaded();\n const entry = this.entries.get(id);\n if (entry === undefined) return undefined;\n const meta = this.session.get(id);\n const fields: SnapshotFields = {\n id: null,\n name: meta?.name ?? entry.template.name,\n channel: entry.template.channel,\n source: entry.source,\n rev: entry.rev,\n variables: entry.variables,\n metadata: meta?.metadata ?? {},\n };\n if (entry.template.sourceKind === \"tsx\") {\n return {\n ...fields,\n origin: {\n kind: \"tsx\",\n file: id,\n authoredSource: entry.authoredSource ?? \"\",\n },\n access: {\n kind: \"readonly\",\n reason: \"TSX templates are code-authored. Edit the default export in your code editor.\",\n },\n };\n }\n return {\n ...fields,\n origin: { kind: \"samva\", file: id },\n access: { kind: \"editable\" },\n };\n }\n\n async save(id: string, update: EditorUpdate, client?: string): Promise<SaveOutcome> {\n if (kindForId(id) === \"tsx\") {\n return {\n ok: false,\n error: \"TSX templates are read-only; edit the default export in source.\",\n };\n }\n const path = await this.resolvePath(id);\n if (path === undefined || kindForId(id) !== \"samva\") {\n return { ok: false, error: \"template not found\" };\n }\n\n if (update.kind === \"metadata\") {\n const prev = this.session.get(id) ?? { metadata: {} };\n const next: SessionMeta = {\n name: update.patch.name ?? prev.name,\n metadata: {\n fromDefault:\n update.patch.fromDefault === undefined\n ? prev.metadata.fromDefault\n : (update.patch.fromDefault ?? undefined),\n replyToDefault:\n update.patch.replyToDefault === undefined\n ? prev.metadata.replyToDefault\n : (update.patch.replyToDefault ?? undefined),\n },\n };\n this.session.set(id, next);\n const entry = this.entries.get(id);\n if (entry !== undefined && next.name !== undefined) {\n this.entries.set(id, { ...entry, template: { ...entry.template, name: next.name } });\n for (const listener of this.catalogListeners) listener();\n }\n const current = await readFile(path, \"utf8\").catch(() => undefined);\n return {\n ok: true,\n rev: current === undefined ? (this.currentRev.get(id) ?? \"\") : hashSource(current),\n };\n }\n\n return this.withLock(id, () => this.commit(id, path, update, client));\n }\n\n private async commit(\n id: string,\n path: string,\n update: Extract<EditorUpdate, { kind: \"ops\" | \"source\" }>,\n client: string | undefined,\n ): Promise<SaveOutcome> {\n const current = await readFile(path, \"utf8\").catch(() => undefined);\n if (current === undefined) return { ok: false, error: \"template not found\" };\n const actualRev = hashSource(current);\n if (update.baseRev !== actualRev) {\n return { ok: false, conflict: { expectedRev: update.baseRev, actualRev } };\n }\n\n let nextSource: string;\n if (update.kind === \"ops\") {\n const applied = applyOps(current, [...update.ops]);\n if (Result.isFailure(applied)) return { ok: false, error: applied.failure.message };\n nextSource = applied.success.source;\n } else {\n nextSource = update.source;\n }\n\n const rev = hashSource(nextSource);\n this.writing.add(id);\n // oxlint-disable-next-line samva/no-try-catch-or-throw -- fs resource guard; plain Node adapter boundary\n try {\n const temp = join(dirname(path), `.${randomUUID()}.samva.tmp`);\n await writeFile(temp, nextSource, \"utf8\");\n await rename(temp, path);\n // Reuse the targeted editable-file loader while the watcher echo is\n // suppressed. It recompiles the new root channel and diagnostics, updates\n // the open snapshot, and publishes the catalog refresh that remounts the\n // browser editor with the new channel/check context.\n await this.refreshSamva(id);\n } finally {\n this.writing.delete(id);\n }\n\n this.publish({ id, rev, source: nextSource, sourceClient: client });\n return { ok: true, rev };\n }\n\n private withLock<A>(id: string, run: () => Promise<A>): Promise<A> {\n const prev = this.saveChain.get(id) ?? Promise.resolve();\n const next = prev.then(run, run);\n this.saveChain.set(id, next.then(noop, noop));\n return next;\n }\n\n async handleFileChange(path: string): Promise<StoreChange | undefined> {\n const id = this.idFor(path);\n if (id === undefined || kindForId(id) !== \"samva\") return undefined;\n if (this.writing.has(id)) return undefined;\n const real = await this.resolvePath(id);\n if (real === undefined) return undefined;\n const source = await readFile(real, \"utf8\").catch(() => undefined);\n if (source === undefined) return undefined;\n const rev = hashSource(source);\n if (rev === this.currentRev.get(id)) return undefined;\n this.currentRev.set(id, rev);\n const previous = this.entries.get(id);\n if (previous !== undefined) {\n this.entries.set(id, {\n ...previous,\n source,\n rev,\n variables: [...variableReferences(source)]\n .sort()\n .map((name) => ({ name, required: false })),\n });\n }\n const change: StoreChange = { id, rev, source, sourceClient: undefined };\n this.publish(change);\n return change;\n }\n\n private publish(change: StoreChange): void {\n for (const listener of this.listeners) listener(change);\n }\n\n subscribe(listener: (change: StoreChange) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n subscribeCatalog(listener: () => void): () => void {\n this.catalogListeners.add(listener);\n return () => this.catalogListeners.delete(listener);\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { extname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { CompileOptions } from \"@samva/markup/compile\";\nimport { Predicate } from \"effect\";\nimport type { Connect, EnvironmentModuleNode, Plugin, ResolvedConfig, ViteDevServer } from \"vite\";\nimport { createServerModuleRunner, loadEnv } from \"vite\";\n\nimport {\n EditorFileStore,\n evaluateDefaultExport,\n type EditorCatalogDiagnostic,\n type StoreChange,\n} from \"./editor-store\";\nimport { resolveThemePath, ThemeState } from \"./theme\";\n\n// samvaEditor() — the sole Vite dev-server surface for Samva templates. It owns\n// TSX evaluation, theme discovery, mixed-source cataloging, compiled previews,\n// editing, and HMR. `.samva` files are editable; `.tsx` files are evaluated by\n// Vite and shown read-only as authored TSX plus emitted SML.\n//\n// Serving model: the editor UI is a PRE-BUILT static bundle (dist/editor),\n// served by this middleware — the same pattern vite-plugin-inspect and\n// @tanstack/devtools use, and the robust one here specifically because a Samva\n// project sets the automatic JSX runtime to `@samva/markup` for its templates;\n// running the editor's own React JSX through that transform would break it. The\n// bundle ships its own React, Tailwind (baked at build time), and editor chrome.\n//\n// Transport (see editor-store.ts for the revision/echo model):\n// GET {route}/api/capabilities → { authenticated } (API key stays server-side)\n// GET {route}/api/templates → mixed-source catalog + diagnostics/theme\n// GET {route}/api/document?id= → open snapshot { source, rev, variables, … }\n// POST {route}/api/document?id=&client= → save → { rev } | 409 conflict | 422 error\n// GET {route}/api/events?id=&client= → SSE stream of changes (origin per client)\n// GET {route}/api/catalog-events → SSE catalog/theme refresh stream\n\nconst DEFAULT_ROUTE = \"/\";\n\nconst MODULE_EXTENSIONS = new Set([\n \".cjs\",\n \".css\",\n \".cts\",\n \".js\",\n \".json\",\n \".jsx\",\n \".mjs\",\n \".mts\",\n \".ts\",\n \".tsx\",\n]);\n\n// The pre-built editor SPA ships beside the plugin bundle in dist/editor. The\n// source-tree fallback keeps real middleware integration tests and local source\n// execution on the same built artifact.\nconst packagedEditorDir = fileURLToPath(new URL(\"./editor/\", import.meta.url));\nconst EDITOR_DIR = existsSync(packagedEditorDir)\n ? packagedEditorDir\n : fileURLToPath(new URL(\"../dist/editor/\", import.meta.url));\n\nconst MIME: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".json\": \"application/json\",\n \".map\": \"application/json\",\n \".ico\": \"image/x-icon\",\n \".png\": \"image/png\",\n \".woff2\": \"font/woff2\",\n};\n\nexport interface SamvaEditorPluginOptions {\n /** Directory containing `.samva` / `.tsx` templates, relative to project root. Default `emails`. */\n readonly templatesDir?: string | undefined;\n /** Editor UI route. Default `/`; its API and assets are mounted as siblings below it. */\n readonly route?: string | undefined;\n /** Compile options applied to catalog validation and browser previews. */\n readonly compile?: CompileOptions | undefined;\n /** Project theme file relative to root. Default `theme.css`; false disables discovery. */\n readonly theme?: string | false | undefined;\n}\n\n/** Normalize + validate the mount route: absolute, root allowed, no trailing slash. */\nconst normalizeRoute = (raw: string): string => {\n const route = raw === \"/\" ? \"/\" : raw.replace(/\\/+$/, \"\");\n if (\n !route.startsWith(\"/\") ||\n route === \"\" ||\n route.includes(\"//\") ||\n route.includes(\"?\") ||\n route.includes(\"#\")\n ) {\n // oxlint-disable-next-line samva/no-try-catch-or-throw, samva/no-error-constructor -- vite plugin config validation boundary, not Effect domain\n throw new Error(\n `samvaEditor: \\`route\\` must be an absolute URL path (received ${JSON.stringify(raw)})`,\n );\n }\n return route;\n};\n\nconst sendJson = (res: ServerResponse, status: number, body: unknown): void => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(body));\n};\n\n/** Serve a file under the built editor dir; returns false (untouched res) if absent/outside. */\nconst serveStatic = async (res: ServerResponse, filePath: string): Promise<boolean> => {\n const resolved = resolve(filePath);\n const root = EDITOR_DIR.replace(/\\/$/, \"\");\n if (resolved !== root && !resolved.startsWith(`${root}/`)) return false;\n const body = await readFile(resolved).catch(() => undefined);\n if (body === undefined) return false;\n res.statusCode = 200;\n res.setHeader(\"content-type\", MIME[extname(resolved)] ?? \"application/octet-stream\");\n res.end(body);\n return true;\n};\n\nconst VITE_INTERNAL_PREFIXES = [\n \"/@vite/\",\n \"/@id/\",\n \"/@fs/\",\n \"/@react-refresh\",\n \"/__vite\",\n \"/node_modules/.vite/\",\n] as const;\n\nconst isViteInternalPath = (pathname: string): boolean =>\n VITE_INTERNAL_PREFIXES.some(\n (prefix) => pathname === prefix.replace(/\\/$/, \"\") || pathname.startsWith(prefix),\n );\n\nconst hasFileExtension = (pathname: string): boolean => extname(pathname) !== \"\";\n\nconst underRoute = (route: string, pathname: string): boolean =>\n route === \"/\" || pathname === route || pathname.startsWith(`${route}/`);\n\nconst childRoute = (route: string, child: string): string =>\n route === \"/\" ? `/${child}` : `${route}/${child}`;\n\nconst readBody = (req: IncomingMessage): Promise<string> =>\n new Promise((done, fail) => {\n let data = \"\";\n req.on(\"data\", (chunk) => {\n data += chunk;\n });\n req.on(\"end\", () => done(data));\n req.on(\"error\", fail);\n });\n\n/** One SSE connection watching one document. Honors write backpressure by coalescing to latest-change-wins (revisions make intermediate frames droppable). */\ninterface SseClient {\n readonly res: ServerResponse;\n readonly docId: string;\n readonly clientId: string | undefined;\n push(payload: Record<string, unknown>): void;\n}\n\nconst createSseClient = (\n res: ServerResponse,\n docId: string,\n clientId: string | undefined,\n): SseClient => {\n let pending: string | undefined;\n let draining = false;\n\n const flush = (): void => {\n if (pending === undefined) {\n draining = false;\n return;\n }\n const frame = pending;\n pending = undefined;\n if (res.write(frame)) flush();\n else draining = true;\n };\n res.on(\"drain\", flush);\n\n return {\n res,\n docId,\n clientId,\n push(payload) {\n const frame = `event: samva:change\\ndata: ${JSON.stringify(payload)}\\n\\n`;\n if (draining) {\n pending = frame; // latest-change-wins: an older queued frame is superseded\n return;\n }\n if (!res.write(frame)) draining = true;\n },\n };\n};\n\nexport const samvaEditor = (options: SamvaEditorPluginOptions = {}): Plugin => {\n const route = normalizeRoute(options.route ?? DEFAULT_ROUTE);\n const apiPrefix = childRoute(route, \"api\");\n let root = process.cwd();\n let templatesDir = resolve(root, options.templatesDir ?? \"emails\");\n let theme = new ThemeState(resolveThemePath(root, options.theme));\n let store = new EditorFileStore(templatesDir, {\n compile: () => theme.applyTo(options.compile),\n });\n let authenticated = false;\n\n const clients = new Set<SseClient>();\n const catalogClients = new Set<ServerResponse>();\n let refreshTail: Promise<unknown> = Promise.resolve();\n\n const queueStoreRefresh = (\n server: ViteDevServer,\n run: () => Promise<unknown>,\n ): Promise<unknown> => {\n refreshTail = refreshTail.then(run, run).catch((cause: unknown) => {\n server.config.logger.error(\n `samvaEditor refresh failed: ${cause instanceof Error ? cause.message : String(cause)}`,\n );\n });\n return refreshTail;\n };\n const queueRefresh = (server: ViteDevServer, documents: boolean): Promise<unknown> =>\n queueStoreRefresh(server, () => store.refreshAll({ documents }));\n\n return {\n name: \"samva-editor\",\n\n config() {\n return {\n oxc: { jsx: { runtime: \"automatic\", importSource: \"@samva/markup\" } },\n optimizeDeps: { exclude: [\"@samva/markup\"] },\n };\n },\n\n configResolved(resolved: ResolvedConfig) {\n root = resolved.root;\n templatesDir = isAbsolute(options.templatesDir ?? \"\")\n ? (options.templatesDir as string)\n : resolve(root, options.templatesDir ?? \"emails\");\n theme = new ThemeState(resolveThemePath(root, options.theme));\n store = new EditorFileStore(templatesDir, {\n compile: () => theme.applyTo(options.compile),\n });\n // API key stays server-side: `loadEnv(mode, root, \"\")` reads it from the\n // project's `.env` and from process.env, but — lacking a `VITE_` prefix — it\n // is NOT exposed to the client bundle. Presence lights up the authenticated\n // capability set.\n const env = loadEnv(resolved.mode, root, \"\");\n authenticated = Boolean(env.SAMVA_API_KEY);\n },\n\n async configureServer(server: ViteDevServer) {\n await theme.refresh();\n const evaluator = createServerModuleRunner(server.environments.ssr, { hmr: false });\n store = new EditorFileStore(templatesDir, {\n compile: () => theme.applyTo(options.compile),\n evaluateTsx: async (path, id, name, compile) => {\n const moduleExports = (await evaluator.import(path)) as Record<string, unknown>;\n return evaluateDefaultExport(id, name, moduleExports, compile);\n },\n });\n\n server.watcher.add(templatesDir);\n if (theme.themePath !== undefined) server.watcher.add(theme.themePath);\n\n const refreshTheme = (): void => {\n void theme.refresh().then(() => queueRefresh(server, false));\n };\n\n const refreshTsxDependents = (path: string, shapeChange: boolean): void => {\n const ssr = server.environments.ssr;\n if (ssr === undefined) return;\n const changedModules = ssr.moduleGraph.getModulesByFile(path) ?? new Set();\n const directTemplate = path.endsWith(\".tsx\") && store.idFor(path) !== undefined;\n const potentialModuleShapeChange =\n shapeChange && changedModules.size === 0 && MODULE_EXTENSIONS.has(extname(path));\n if (!directTemplate && changedModules.size === 0 && !potentialModuleShapeChange) return;\n\n void queueStoreRefresh(server, async () => {\n // Vite's SSR graph is the dependency oracle. Invalidate the changed\n // module and every importer so entry modules cannot serve a cached\n // emission. Add/unlink can change module resolution itself (including\n // restoring a dependency whose failed resolution has no stable graph\n // edge), so those rare shape changes invalidate the graph wholesale.\n const invalidated = new Set<EnvironmentModuleNode>();\n const collectImporters = (module: EnvironmentModuleNode): void => {\n if (invalidated.has(module)) return;\n invalidated.add(module);\n for (const importer of module.importers) collectImporters(importer);\n };\n for (const module of changedModules) collectImporters(module);\n if (shapeChange) ssr.moduleGraph.invalidateAll();\n else for (const module of invalidated) ssr.moduleGraph.invalidateModule(module);\n evaluator.evaluatedModules.clear();\n return store.refreshAll({ documents: true });\n });\n };\n\n const onChange = (path: string): void => {\n if (theme.themePath !== undefined && path === theme.themePath) {\n refreshTheme();\n return;\n }\n if (path.endsWith(\".samva\")) {\n void store.handleFileChange(path).then((change) => {\n if (change !== undefined) {\n void queueStoreRefresh(server, () => store.refreshSamva(change.id));\n }\n });\n return;\n }\n refreshTsxDependents(path, false);\n };\n const onCatalogShape = (path: string): void => {\n if (theme.themePath !== undefined && path === theme.themePath) {\n refreshTheme();\n return;\n }\n const id = store.idFor(path);\n if (id?.endsWith(\".samva\")) {\n void queueRefresh(server, true);\n return;\n }\n refreshTsxDependents(path, true);\n };\n server.watcher.on(\"change\", onChange);\n server.watcher.on(\"add\", onCatalogShape);\n server.watcher.on(\"unlink\", onCatalogShape);\n\n // Fan each store change out to the clients watching that document, attributing\n // origin per client: the client that saved sees `self`, everyone else `external`.\n const unsubscribe = store.subscribe((change: StoreChange) => {\n server.ws.send({ type: \"custom\", event: \"samva:document\", data: change });\n for (const client of clients) {\n if (client.docId !== change.id) continue;\n const origin =\n change.sourceClient !== undefined && change.sourceClient === client.clientId\n ? \"self\"\n : \"external\";\n client.push({\n id: change.id,\n rev: change.rev,\n source: change.source,\n authoredSource: change.authoredSource,\n origin,\n });\n }\n });\n const unsubscribeCatalog = store.subscribeCatalog(() => {\n for (const res of catalogClients) {\n res.write(\"event: samva:catalog\\ndata: refresh\\n\\n\");\n }\n server.ws.send({ type: \"custom\", event: \"samva:catalog\" });\n });\n\n let cleaned = false;\n const cleanup = (): void => {\n if (cleaned) return;\n cleaned = true;\n server.watcher.off(\"change\", onChange);\n server.watcher.off(\"add\", onCatalogShape);\n server.watcher.off(\"unlink\", onCatalogShape);\n server.watcher.off(\"close\", cleanup);\n server.httpServer?.off(\"close\", cleanup);\n unsubscribe();\n unsubscribeCatalog();\n void evaluator.close();\n for (const client of clients) client.res.end();\n clients.clear();\n for (const res of catalogClients) res.end();\n catalogClients.clear();\n };\n // In full mode this fires on server close; in middleware mode (no httpServer)\n // Vite closes the watcher with the server, which also covers middleware mode.\n server.httpServer?.once(\"close\", cleanup);\n server.watcher.once(\"close\", cleanup);\n\n const handler: Connect.NextHandleFunction = (req, res, next) => {\n const url = req.url ?? \"\";\n const pathname = url.split(\"?\")[0] || \"/\";\n const query = new URLSearchParams(url.split(\"?\")[1] ?? \"\");\n\n const apiRequest = pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`);\n if (!apiRequest && !underRoute(route, pathname)) return next();\n\n // A root-mounted editor is the default dedicated-server surface, but it\n // must never intercept Vite's own runtime/module URLs. Static requests\n // that are not editor bundle assets also fall through below.\n if (!apiRequest && route === \"/\" && isViteInternalPath(pathname)) return next();\n\n void (async () => {\n // ── JSON / SSE API ──\n if (pathname === `${apiPrefix}/capabilities`) {\n return sendJson(res, 200, { authenticated });\n }\n\n if (pathname === `${apiPrefix}/templates`) {\n const catalog = await store.catalog();\n const themeFile =\n theme.themePath === undefined ? undefined : relative(root, theme.themePath);\n const themeDiagnostics: ReadonlyArray<EditorCatalogDiagnostic> = theme\n .current()\n .diagnostics.map((item) => ({\n file: themeFile ?? \"theme.css\",\n severity: \"error\",\n code: item.code,\n message: item.message,\n span: item.span,\n }));\n return sendJson(res, 200, {\n templates: catalog.templates,\n diagnostics: [...catalog.diagnostics, ...themeDiagnostics],\n theme: {\n file: themeFile,\n tokens: theme.current().tokens,\n },\n compile: theme.applyTo(options.compile),\n });\n }\n\n if (pathname === `${apiPrefix}/document`) {\n const id = query.get(\"id\");\n if (!id) return sendJson(res, 400, { error: \"missing id\" });\n\n if (req.method === \"POST\") {\n const raw = await readBody(req);\n // Raw HTTP boundary: malformed JSON is a client error, not a server crash.\n let update: Parameters<EditorFileStore[\"save\"]>[1];\n // oxlint-disable-next-line samva/no-try-catch-or-throw -- raw HTTP JSON parsing boundary\n try {\n update = JSON.parse(raw) as Parameters<EditorFileStore[\"save\"]>[1];\n } catch {\n return sendJson(res, 400, { error: \"malformed JSON body\" });\n }\n if (!Predicate.isObject(update)) {\n return sendJson(res, 400, { error: \"malformed JSON body\" });\n }\n const outcome = await store.save(id, update, query.get(\"client\") ?? undefined);\n if (outcome.ok) return sendJson(res, 200, { rev: outcome.rev });\n if (\"conflict\" in outcome) return sendJson(res, 409, outcome.conflict);\n return sendJson(res, 422, { error: outcome.error });\n }\n\n const snapshot = await store.open(id);\n if (snapshot === undefined) return sendJson(res, 404, { error: \"template not found\" });\n return sendJson(res, 200, snapshot);\n }\n\n if (pathname === `${apiPrefix}/events`) {\n const id = query.get(\"id\");\n if (!id) return sendJson(res, 400, { error: \"missing id\" });\n res.statusCode = 200;\n res.setHeader(\"content-type\", \"text/event-stream\");\n res.setHeader(\"cache-control\", \"no-cache\");\n res.setHeader(\"connection\", \"keep-alive\");\n res.write(\": connected\\n\\n\");\n const client = createSseClient(res, id, query.get(\"client\") ?? undefined);\n clients.add(client);\n req.on(\"close\", () => clients.delete(client));\n return;\n }\n\n if (pathname === `${apiPrefix}/catalog-events`) {\n res.statusCode = 200;\n res.setHeader(\"content-type\", \"text/event-stream\");\n res.setHeader(\"cache-control\", \"no-cache\");\n res.setHeader(\"connection\", \"keep-alive\");\n res.write(\": connected\\n\\n\");\n catalogClients.add(res);\n req.on(\"close\", () => catalogClients.delete(res));\n return;\n }\n\n if (apiRequest) return sendJson(res, 404, { error: \"not found\" });\n\n // ── Static editor SPA (mounted at `route`) ──\n // A page served at `route/` makes relative asset URLs resolve under it,\n // so a bare `route` must redirect to `route/` first.\n if (route !== \"/\" && pathname === route) {\n res.statusCode = 302;\n res.setHeader(\"location\", `${route}/`);\n res.end();\n return;\n }\n const rel =\n route === \"/\"\n ? pathname.replace(/^\\//, \"\")\n : pathname.slice(route.length).replace(/^\\//, \"\");\n const file = rel === \"\" ? \"index.html\" : rel;\n if (await serveStatic(res, join(EDITOR_DIR, file))) return;\n // Never answer a missing asset with HTML. Let Vite or later customer\n // middleware own file-like paths; only navigation paths get the SPA.\n const assetPrefix = childRoute(route, \"assets\");\n if (\n hasFileExtension(pathname) ||\n pathname === assetPrefix ||\n pathname.startsWith(`${assetPrefix}/`)\n ) {\n return next();\n }\n // SPA fallback: unknown navigation paths render the app shell.\n if (await serveStatic(res, join(EDITOR_DIR, \"index.html\"))) return;\n return next();\n })().catch((error: unknown) => {\n sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) });\n });\n };\n\n server.middlewares.use(handler);\n },\n };\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"theme-CfJe8s3k.js","names":[],"sources":["../src/discovery.ts","../src/theme.ts"],"sourcesContent":["import { readdir } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\n\n// Template file discovery: walk a templates directory for the two authoring\n// shapes the plugin serves — `.tsx` (evaluated through the emit chain) and\n// `.samva` (compiled directly). Kept dependency-free (node:fs only) so the\n// plugin's preview server and the CLI build share one discovery definition.\n\nexport type TemplateFileKind = \"tsx\" | \"samva\";\n\nexport interface TemplateFile {\n /** Absolute path on disk. */\n readonly path: string;\n /** Path relative to the templates dir, POSIX-normalized (stable id + display). */\n readonly id: string;\n readonly kind: TemplateFileKind;\n}\n\nconst kindForFile = (name: string): TemplateFileKind | undefined => {\n if (name.endsWith(\".tsx\")) return \"tsx\";\n if (name.endsWith(\".samva\")) return \"samva\";\n return undefined;\n};\n\nconst toPosix = (value: string): string => (sep === \"/\" ? value : value.split(sep).join(\"/\"));\n\n/**\n * Recursively lists template files under `dir`. Hidden entries and\n * `node_modules` are skipped. Results are sorted by id for deterministic\n * ordering (the preview list and build output must be stable).\n */\nexport const discoverTemplateFiles = async (dir: string): Promise<ReadonlyArray<TemplateFile>> => {\n const found: Array<TemplateFile> = [];\n\n const walk = async (current: string): Promise<void> => {\n const entries = await readdir(current, { withFileTypes: true }).catch(() => []);\n const visible = entries.filter(\n (entry) => !entry.name.startsWith(\".\") && entry.name !== \"node_modules\",\n );\n await Promise.all(\n visible.map(async (entry) => {\n const full = join(current, entry.name);\n if (entry.isDirectory()) {\n await walk(full);\n return;\n }\n const kind = kindForFile(entry.name);\n if (kind !== undefined) found.push({ path: full, id: toPosix(relative(dir, full)), kind });\n }),\n );\n };\n\n await walk(dir);\n return found.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));\n};\n","import { readFile } from \"node:fs/promises\";\nimport { isAbsolute, resolve } from \"node:path\";\n\nimport type { CompileOptions } from \"@samva/markup/compile\";\nimport type { Diagnostic } from \"@samva/markup/diagnostics\";\nimport { parseThemeCss, type ParsedTheme } from \"@samva/markup/theme-css\";\nimport { Result } from \"effect\";\n\n// Local preview theming: a project's `theme.css` (Tailwind-v4 `@theme` syntax,\n// design brief §7e) becomes the org-token layer of every preview compile. The\n// SML→HTML compiler takes an ordered `overrides` list of `TokenOverrides` layers;\n// `ParsedTheme` IS a `TokenOverrides`, so a parsed theme drops straight in as the\n// least-specific layer. Base tokens ← theme.css ← (nothing else here; template\n// overrides are authored per-template, not project-wide).\n//\n// SCOPE: local preview only. `samva theme push` / platform theme endpoints are a\n// separate surface that lands with the platform theme API — nothing here talks to\n// the platform (the emitted SML carries no theme; the platform recompiles with the\n// org's own theme layers server-side).\n\n/** Default theme file name, looked up at the project root. */\nexport const DEFAULT_THEME_FILE = \"theme.css\";\n\n/**\n * Result of parsing a project's `theme.css`. `tokens` is the parsed org layer to\n * merge into every compile (`undefined` when there is no usable theme); parse\n * diagnostics carry spans into the theme file, surfaced through the same channel\n * as template emit/compile diagnostics.\n */\nexport interface ProjectTheme {\n /** Parsed org-token layer, or `undefined` when the file is missing/unparsed. */\n readonly tokens: ParsedTheme | undefined;\n /** Parse diagnostics (spans into `theme.css`). Empty on success or no file. */\n readonly diagnostics: ReadonlyArray<Diagnostic>;\n}\n\nconst NO_THEME: ProjectTheme = { tokens: undefined, diagnostics: [] };\n\n/**\n * Resolves the `theme` plugin option to an absolute path, or `undefined` when\n * theming is explicitly disabled (`theme: false`). A string is taken relative to\n * `root` (absolute strings pass through); the default is `<root>/theme.css`.\n */\nexport const resolveThemePath = (\n root: string,\n option: string | false | undefined,\n): string | undefined => {\n if (option === false) return undefined;\n const file = option ?? DEFAULT_THEME_FILE;\n return isAbsolute(file) ? file : resolve(root, file);\n};\n\n/**\n * Reads and parses the project theme file. A missing file is not an error —\n * previews render with base tokens only, no warning (per the brief). A present\n * file with parse errors yields the diagnostics and no tokens; the caller decides\n * whether to keep a previously-good theme.\n */\nexport const loadProjectTheme = async (themePath: string | undefined): Promise<ProjectTheme> => {\n if (themePath === undefined) return NO_THEME;\n const css = await readFile(themePath, \"utf8\").catch((cause: NodeJS.ErrnoException) =>\n // Only a MISSING file is the silent opt-out. Any other read failure\n // (permissions, I/O) must surface as a diagnostic, not masquerade as\n // \"no theme.css\".\n cause.code === \"ENOENT\" ? undefined : cause,\n );\n if (css === undefined) return NO_THEME; // no theme.css → base tokens only, no warning.\n if (typeof css !== \"string\") {\n return {\n tokens: undefined,\n diagnostics: [\n {\n code: \"theme-read-error\",\n message: `could not read theme file: ${css.message}`,\n span: { start: 0, end: 0 },\n },\n ],\n };\n }\n const parsed = parseThemeCss(css);\n if (Result.isFailure(parsed)) return { tokens: undefined, diagnostics: parsed.failure };\n return { tokens: parsed.success, diagnostics: [] };\n};\n\n/**\n * Folds a parsed theme into per-preview `CompileOptions` as the org-token layer,\n * preserving any base/template layers the caller already configured. Returns the\n * input unchanged when there is no theme to apply.\n */\nexport const withThemeLayer = (\n compile: CompileOptions | undefined,\n tokens: ParsedTheme | undefined,\n): CompileOptions | undefined => {\n if (tokens === undefined) return compile;\n return {\n ...compile,\n theme: { ...compile?.theme, overrides: [tokens, ...(compile?.theme?.overrides ?? [])] },\n };\n};\n\n/**\n * Mutable holder for the dev server: parses `theme.css` on demand and keeps the\n * LAST GOOD tokens when a re-parse fails, so an in-flight syntax error never\n * blanks the preview — it renders the previous theme plus the parse diagnostics.\n */\nexport class ThemeState {\n private tokens: ParsedTheme | undefined = undefined;\n private diagnostics: ReadonlyArray<Diagnostic> = [];\n // Refreshes chain behind each other so two rapid saves can't interleave —\n // without this, the FIRST read could finish last and clobber the second\n // save's tokens while its diagnostics claim to describe the newer file.\n private pending: Promise<ProjectTheme> = Promise.resolve(NO_THEME);\n\n constructor(readonly themePath: string | undefined) {}\n\n /** Re-reads + re-parses (serialized); on failure keeps the last good tokens. */\n refresh(): Promise<ProjectTheme> {\n const run = async (): Promise<ProjectTheme> => {\n const next = await loadProjectTheme(this.themePath);\n if (next.diagnostics.length === 0) {\n // Success (or no file): adopt the new tokens (possibly undefined).\n this.tokens = next.tokens;\n this.diagnostics = [];\n } else {\n // Parse error: keep the last good tokens, surface the new diagnostics.\n this.diagnostics = next.diagnostics;\n }\n return this.current();\n };\n this.pending = this.pending.then(run, run);\n return this.pending;\n }\n\n /** Current tokens (last good) + diagnostics (from the most recent parse). */\n current(): ProjectTheme {\n return { tokens: this.tokens, diagnostics: this.diagnostics };\n }\n\n /** Applies the current theme layer to a caller's compile options. */\n applyTo(compile: CompileOptions | undefined): CompileOptions | undefined {\n return withThemeLayer(compile, this.tokens);\n }\n}\n"],"mappings":"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"}