@samva/markup 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/ops.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"ops.js","names":[],"sources":["../src/ops.ts"],"sourcesContent":["import { Result, Schema } from \"effect\";\n\nimport type { Attribute, AttributeValue, ElementNode, SmlDocument, SmlNode, SmlTag } from \"./ast\";\nimport { AttributeValueSchema, SML_TAGS, SmlNodeSchema, documentChannel } from \"./ast\";\nimport { checkSmlCompatibility, isBreakingCompatibilityCheck } from \"./checks\";\nimport { compileSml } from \"./compile\";\nimport type { Diagnostic, Span } from \"./diagnostics\";\nimport { DiagnosticSchema, SmlParseError } from \"./diagnostics\";\nimport { allowedChildren, CHANNEL_TAG_SPECS } from \"./parser\";\nimport { parseSml } from \"./parser\";\nimport { printSml } from \"./printer\";\n\nconst InsertPositionSchema = Schema.Literals([\"before\", \"after\", \"inside\"]);\n\nconst SetPropOpSchema = Schema.TaggedStruct(\"setProp\", {\n nodeId: Schema.String,\n name: Schema.String,\n value: Schema.NullOr(AttributeValueSchema),\n});\n\n/**\n * Replace a node's complete child list. The normal print → parse → compile\n * preflight remains the authority on whether the replacement satisfies the\n * target's content model.\n */\nconst SetChildrenOpSchema = Schema.TaggedStruct(\"setChildren\", {\n nodeId: Schema.String,\n children: Schema.Array(SmlNodeSchema),\n});\n\nconst InsertBlockOpSchema = Schema.TaggedStruct(\"insertBlock\", {\n tag: Schema.Literals(SML_TAGS),\n referenceId: Schema.NullOr(Schema.String),\n position: InsertPositionSchema,\n});\n\nconst InsertSnippetOpSchema = Schema.TaggedStruct(\"insertSnippet\", {\n snippet: Schema.String,\n referenceId: Schema.NullOr(Schema.String),\n position: InsertPositionSchema,\n});\n\n/**\n * `insertionIndex` is an element-child index into `targetParentId` evaluated\n * AFTER the moved node has left its old position — the lifted-out model, so a\n * same-parent caller must compute the index against the sibling list without\n * the node. Indexes past the end append.\n */\nconst MoveNodeOpSchema = Schema.TaggedStruct(\"moveNode\", {\n nodeId: Schema.String,\n targetParentId: Schema.String,\n insertionIndex: Schema.Finite,\n});\n\nconst DuplicateNodeOpSchema = Schema.TaggedStruct(\"duplicateNode\", {\n nodeId: Schema.String,\n});\n\nconst RemoveNodeOpSchema = Schema.TaggedStruct(\"removeNode\", {\n nodeId: Schema.String,\n});\n\n/**\n * A single edit against SML source. `nodeId` (and `referenceId` /\n * `targetParentId`) are positional ids from the source the caller last read;\n * within a batch they always resolve against that snapshot — see {@link applyOps}.\n */\nexport const SmlOpSchema = Schema.Union([\n SetPropOpSchema,\n SetChildrenOpSchema,\n InsertBlockOpSchema,\n InsertSnippetOpSchema,\n MoveNodeOpSchema,\n DuplicateNodeOpSchema,\n RemoveNodeOpSchema,\n]);\n\nexport type SmlOp = typeof SmlOpSchema.Type;\n\nexport const SmlOpResultSchema = Schema.Struct({\n index: Schema.Finite,\n status: Schema.Literals([\"applied\", \"failed\"]),\n op: SmlOpSchema,\n diagnostics: Schema.Array(DiagnosticSchema),\n});\n\nexport interface SmlOpResult extends Schema.Schema.Type<typeof SmlOpResultSchema> {}\n\nexport const ApplyOpsResultSchema = Schema.Struct({\n source: Schema.String,\n revision: Schema.String,\n diagnostics: Schema.Array(DiagnosticSchema),\n results: Schema.Array(SmlOpResultSchema),\n});\n\nexport interface ApplyOpsResult extends Schema.Schema.Type<typeof ApplyOpsResultSchema> {}\n\nexport interface ApplyOpsOptions {\n readonly expectedRevision?: string | number | undefined;\n readonly currentRevision?: string | number | undefined;\n}\n\nexport class SmlOpError extends Schema.TaggedErrorClass<SmlOpError>()(\"SmlOpError\", {\n diagnostics: Schema.Array(DiagnosticSchema),\n}) {\n override get message() {\n return this.diagnostics.map((diagnostic) => diagnostic.message).join(\"\\n\");\n }\n}\n\nconst zeroSpan: Span = { start: 0, end: 0 };\n\nconst diagnostic = (code: string, message: string, span: Span = zeroSpan): Diagnostic => ({\n code,\n message,\n span,\n});\n\nconst cloneAttribute = (attribute: Attribute): Attribute => ({ ...attribute });\n\n/**\n * Deep-copy a subtree, blanking every id to \"\". A duplicated node must not\n * alias the original's object graph or share its ids: batch ids resolve\n * against the pre-batch snapshot, and mapElement transforms every node whose\n * id matches — so two live copies sharing an id would both be rewritten by a\n * later op. Blank ids make the clone unaddressable (like any freshly created\n * node) until the next reprint reassigns positional ids.\n */\nconst cloneSubtree = (node: SmlNode): SmlNode => {\n if (node.kind === \"element\") {\n return {\n ...node,\n id: \"\",\n attributes: node.attributes.map(cloneAttribute),\n children: node.children.map(cloneSubtree),\n };\n }\n return { ...node, id: \"\" };\n};\n\nconst makeAttribute = (name: string, value: AttributeValue): Attribute => ({\n name,\n value,\n span: zeroSpan,\n valueSpan: zeroSpan,\n});\n\nconst classAttr = (value: string): Attribute => makeAttribute(\"class\", { kind: \"string\", value });\n\nexport const sourceRevision = (source: string): string => {\n let hash = 0x811c9dc5;\n for (let index = 0; index < source.length; index++) {\n hash ^= source.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193);\n }\n return `sml:${(hash >>> 0).toString(16).padStart(8, \"0\")}`;\n};\n\nexport const normalizeSml = (source: string): Result.Result<string, SmlParseError> => {\n const parsed = parseSml(source);\n if (Result.isFailure(parsed)) return Result.fail(parsed.failure);\n return Result.succeed(printSml(parsed.success));\n};\n\nconst findElement = (node: SmlNode, nodeId: string): ElementNode | null => {\n if (node.kind !== \"element\") return null;\n if (node.id === nodeId) return node;\n for (const child of node.children) {\n const found = findElement(child, nodeId);\n if (found !== null) return found;\n }\n return null;\n};\n\nconst mapElement = (\n node: SmlNode,\n targetId: string,\n transform: (element: ElementNode) => ElementNode,\n): SmlNode => {\n if (node.kind !== \"element\") return node;\n if (node.id === targetId) return transform(node);\n return {\n ...node,\n children: node.children.map((child) => mapElement(child, targetId, transform)),\n };\n};\n\nconst transformElement = (\n document: SmlDocument,\n targetId: string,\n transform: (element: ElementNode) => ElementNode,\n): Result.Result<SmlDocument, readonly Diagnostic[]> => {\n const target = findElement(document.root, targetId);\n if (target === null) {\n return Result.fail([\n diagnostic(\"op-target-not-found\", `node \"${targetId}\" was not found in the current source`),\n ]);\n }\n return Result.succeed({\n ...document,\n root: mapElement(document.root, targetId, transform) as ElementNode,\n });\n};\n\nconst setElementAttribute = (\n element: ElementNode,\n name: string,\n value: AttributeValue | null,\n): ElementNode => {\n const rest = element.attributes.filter((attribute) => attribute.name !== name);\n if (value === null || (value.kind === \"string\" && value.value === \"\")) {\n return { ...element, attributes: rest };\n }\n const next = makeAttribute(name, value);\n const existed = element.attributes.some((attribute) => attribute.name === name);\n const attributes = existed\n ? element.attributes.map((attribute) =>\n attribute.name === name ? next : cloneAttribute(attribute),\n )\n : [...element.attributes, next];\n return { ...element, attributes };\n};\n\nconst setImageSchemeSource = (\n document: SmlDocument,\n nodeId: string,\n scheme: \"dark\",\n value: AttributeValue | null,\n): Result.Result<SmlDocument, readonly Diagnostic[]> =>\n transformElement(document, nodeId, (element) => {\n if (element.tag !== \"Image\") return element;\n const src = value?.kind === \"string\" ? value.value : \"\";\n const children = element.children.filter(\n (child) =>\n !(\n child.kind === \"element\" &&\n child.tag === \"Source\" &&\n child.attributes.some(\n (attribute) =>\n attribute.name === \"scheme\" &&\n attribute.value.kind === \"string\" &&\n attribute.value.value === scheme,\n )\n ),\n );\n if (src === \"\") return { ...element, children };\n const sourceNode: ElementNode = {\n kind: \"element\",\n id: \"\",\n tag: \"Source\",\n attributes: [\n makeAttribute(\"scheme\", { kind: \"string\", value: scheme }),\n makeAttribute(\"src\", { kind: \"string\", value: src }),\n ],\n children: [],\n span: zeroSpan,\n };\n return { ...element, children: [...children, sourceNode] };\n });\n\nconst setProp = (\n document: SmlDocument,\n op: Extract<SmlOp, { _tag: \"setProp\" }>,\n): Result.Result<SmlDocument, readonly Diagnostic[]> => {\n if (op.name === \"src:dark\") return setImageSchemeSource(document, op.nodeId, \"dark\", op.value);\n return transformElement(document, op.nodeId, (element) => {\n const value =\n op.name === \"class\" && op.value?.kind === \"string\"\n ? { kind: \"string\" as const, value: op.value.value.trim().replace(/\\s+/g, \" \") }\n : op.value;\n return setElementAttribute(element, op.name, value);\n });\n};\n\nconst setChildren = (\n document: SmlDocument,\n op: Extract<SmlOp, { _tag: \"setChildren\" }>,\n): Result.Result<SmlDocument, readonly Diagnostic[]> =>\n transformElement(document, op.nodeId, (element) => ({ ...element, children: [...op.children] }));\n\nconst makeColumn = (): ElementNode => ({\n kind: \"element\",\n id: \"\",\n tag: \"Column\",\n attributes: [classAttr(\"px-8\")],\n children: [\n {\n kind: \"element\",\n id: \"\",\n tag: \"Text\",\n attributes: [classAttr(\"text-base text-gray-700\")],\n children: [{ kind: \"text\", id: \"\", value: \"Column\", span: zeroSpan }],\n span: zeroSpan,\n },\n ],\n span: zeroSpan,\n});\n\nconst makeBlock = (tag: SmlTag): ElementNode => {\n const base = (attributes: Attribute[], children: SmlNode[]): ElementNode => ({\n kind: \"element\",\n id: \"\",\n tag,\n attributes,\n children,\n span: zeroSpan,\n });\n const text = (value: string): SmlNode => ({\n kind: \"text\",\n id: \"\",\n value,\n span: zeroSpan,\n });\n switch (tag) {\n case \"Heading\":\n return base(\n [makeAttribute(\"level\", { kind: \"number\", value: 2 }), classAttr(\"text-xl text-gray-900\")],\n [text(\"Heading\")],\n );\n case \"Text\":\n return base([classAttr(\"text-base text-gray-700\")], [text(\"Add your text here.\")]);\n case \"Button\":\n return base(\n [\n makeAttribute(\"href\", { kind: \"string\", value: \"https://example.com\" }),\n classAttr(\"bg-blue-600 text-white px-20 py-12 rounded-md\"),\n ],\n [text(\"Button\")],\n );\n case \"Image\":\n return base(\n [\n makeAttribute(\"src\", { kind: \"string\", value: \"https://placehold.co/600x200\" }),\n makeAttribute(\"alt\", { kind: \"string\", value: \"\" }),\n makeAttribute(\"width\", { kind: \"number\", value: 600 }),\n ],\n [],\n );\n case \"Divider\":\n return base([classAttr(\"border-gray-200\")], []);\n case \"Spacer\":\n return base([makeAttribute(\"height\", { kind: \"number\", value: 24 })], []);\n case \"Section\":\n return base([classAttr(\"py-16\")], [makeBlock(\"Text\")]);\n case \"Container\":\n return base([classAttr(\"bg-white p-24\")], [makeBlock(\"Text\")]);\n case \"Row\":\n return base([], [makeColumn(), makeColumn()]);\n default:\n return base([], []);\n }\n};\n\nconst elementChildIndexToNodeIndex = (\n children: ReadonlyArray<SmlNode>,\n insertionIndex: number,\n): number => {\n if (insertionIndex <= 0) {\n const firstElement = children.findIndex((child) => child.kind === \"element\");\n return firstElement === -1 ? children.length : firstElement;\n }\n let elementIndex = 0;\n for (let index = 0; index < children.length; index++) {\n if (children[index]?.kind !== \"element\") continue;\n if (elementIndex === insertionIndex) return index;\n elementIndex++;\n }\n return children.length;\n};\n\nconst insertChildren = (\n document: SmlDocument,\n childrenToInsert: readonly ElementNode[],\n referenceId: string | null,\n position: typeof InsertPositionSchema.Type,\n): Result.Result<SmlDocument, readonly Diagnostic[]> => {\n if (childrenToInsert.length === 0) {\n return Result.fail([\n diagnostic(\"op-empty-insert\", \"insert operation produced no element nodes\"),\n ]);\n }\n if (referenceId === null || referenceId === document.root.id) {\n const children =\n position === \"before\"\n ? [...childrenToInsert, ...document.root.children]\n : [...document.root.children, ...childrenToInsert];\n return Result.succeed({ ...document, root: { ...document.root, children } });\n }\n const reference = findElement(document.root, referenceId);\n if (reference === null) {\n return Result.fail([\n diagnostic(\n \"op-target-not-found\",\n `node \"${referenceId}\" was not found in the current source`,\n ),\n ]);\n }\n if (position === \"inside\") {\n return transformElement(document, referenceId, (element) => ({\n ...element,\n children: [...element.children, ...childrenToInsert],\n }));\n }\n const insertInParent = (node: SmlNode): SmlNode => {\n if (node.kind !== \"element\") return node;\n const index = node.children.findIndex(\n (child) => child.kind === \"element\" && child.id === referenceId,\n );\n if (index >= 0) {\n const at = position === \"after\" ? index + 1 : index;\n return {\n ...node,\n children: [...node.children.slice(0, at), ...childrenToInsert, ...node.children.slice(at)],\n };\n }\n return { ...node, children: node.children.map(insertInParent) };\n };\n return Result.succeed({ ...document, root: insertInParent(document.root) as ElementNode });\n};\n\nconst insertBlock = (\n document: SmlDocument,\n op: Extract<SmlOp, { _tag: \"insertBlock\" }>,\n): Result.Result<SmlDocument, readonly Diagnostic[]> =>\n insertChildren(document, [makeBlock(op.tag)], op.referenceId, op.position);\n\nconst insertSnippet = (\n document: SmlDocument,\n op: Extract<SmlOp, { _tag: \"insertSnippet\" }>,\n): Result.Result<SmlDocument, readonly Diagnostic[]> => {\n const rootTag = document.root.tag;\n const parsed = parseSml(`<${rootTag}>${op.snippet}</${rootTag}>`);\n if (Result.isFailure(parsed)) return Result.fail(parsed.failure.diagnostics);\n const children = parsed.success.root.children.filter(\n (child): child is ElementNode => child.kind === \"element\",\n );\n return insertChildren(document, children, op.referenceId, op.position);\n};\n\nconst removeNodeById = (\n document: SmlDocument,\n nodeId: string,\n): Result.Result<SmlDocument, readonly Diagnostic[]> => {\n if (document.root.id === nodeId) {\n return Result.fail([diagnostic(\"op-root-target\", \"the document root cannot be removed\")]);\n }\n if (findElement(document.root, nodeId) === null) {\n return Result.fail([\n diagnostic(\"op-target-not-found\", `node \"${nodeId}\" was not found in the current source`),\n ]);\n }\n const prune = (node: SmlNode): SmlNode => {\n if (node.kind !== \"element\") return node;\n return {\n ...node,\n children: node.children\n .filter((child) => !(child.kind === \"element\" && child.id === nodeId))\n .map(prune),\n };\n };\n return Result.succeed({ ...document, root: prune(document.root) as ElementNode });\n};\n\nconst isDescendant = (ancestor: ElementNode, nodeId: string): boolean =>\n ancestor.children.some((child) => {\n if (child.kind !== \"element\") return false;\n return child.id === nodeId || isDescendant(child, nodeId);\n });\n\nconst removeElement = (\n node: ElementNode,\n nodeId: string,\n): { readonly node: ElementNode; readonly removed: ElementNode | null } => {\n let removed: ElementNode | null = null;\n const children: SmlNode[] = [];\n for (const child of node.children) {\n if (child.kind === \"element\" && child.id === nodeId) {\n removed = child;\n continue;\n }\n if (child.kind === \"element\") {\n const result = removeElement(child, nodeId);\n if (result.removed !== null) removed = result.removed;\n children.push(result.node);\n } else {\n children.push(child);\n }\n }\n return { node: { ...node, children }, removed };\n};\n\nconst insertIntoParent = (\n node: ElementNode,\n targetParentId: string,\n insertionIndex: number,\n moved: ElementNode,\n): ElementNode => {\n if (node.id === targetParentId) {\n const at = elementChildIndexToNodeIndex(node.children, insertionIndex);\n return {\n ...node,\n children: [...node.children.slice(0, at), moved, ...node.children.slice(at)],\n };\n }\n return {\n ...node,\n children: node.children.map((child) =>\n child.kind === \"element\"\n ? insertIntoParent(child, targetParentId, insertionIndex, moved)\n : child,\n ),\n };\n};\n\nconst moveNode = (\n document: SmlDocument,\n op: Extract<SmlOp, { _tag: \"moveNode\" }>,\n): Result.Result<SmlDocument, readonly Diagnostic[]> => {\n if (document.root.id === op.nodeId) {\n return Result.fail([diagnostic(\"op-root-target\", \"the document root cannot be moved\")]);\n }\n const moved = findElement(document.root, op.nodeId);\n if (moved === null) {\n return Result.fail([\n diagnostic(\"op-target-not-found\", `node \"${op.nodeId}\" was not found in the current source`),\n ]);\n }\n const parent = findElement(document.root, op.targetParentId);\n if (parent === null) {\n return Result.fail([\n diagnostic(\n \"op-target-not-found\",\n `target parent \"${op.targetParentId}\" was not found in the current source`,\n ),\n ]);\n }\n if (moved.id === parent.id || isDescendant(moved, parent.id)) {\n return Result.fail([\n diagnostic(\"op-invalid-reparent\", \"a node cannot be moved into itself or its descendants\"),\n ]);\n }\n const channel = documentChannel(document);\n const spec = CHANNEL_TAG_SPECS[channel][parent.tag];\n const childModel = spec?.content;\n const allowed = childModel === undefined ? [] : allowedChildren(childModel);\n if (!allowed.includes(moved.tag)) {\n return Result.fail([\n diagnostic(\n \"op-invalid-reparent\",\n `<${moved.tag}> is not allowed directly inside <${parent.tag}>`,\n parent.span,\n ),\n ]);\n }\n const { node: rootWithoutMoved, removed } = removeElement(document.root, op.nodeId);\n if (removed === null) {\n return Result.fail([\n diagnostic(\"op-target-not-found\", `node \"${op.nodeId}\" was not found in the current source`),\n ]);\n }\n return Result.succeed({\n ...document,\n root: insertIntoParent(rootWithoutMoved, op.targetParentId, op.insertionIndex, removed),\n });\n};\n\nconst duplicateNode = (\n document: SmlDocument,\n nodeId: string,\n): Result.Result<SmlDocument, readonly Diagnostic[]> => {\n if (document.root.id === nodeId) {\n return Result.fail([diagnostic(\"op-root-target\", \"the document root cannot be duplicated\")]);\n }\n if (findElement(document.root, nodeId) === null) {\n return Result.fail([\n diagnostic(\"op-target-not-found\", `node \"${nodeId}\" was not found in the current source`),\n ]);\n }\n const insertClone = (node: SmlNode): SmlNode => {\n if (node.kind !== \"element\") return node;\n const index = node.children.findIndex(\n (child) => child.kind === \"element\" && child.id === nodeId,\n );\n if (index >= 0) {\n const clone = cloneSubtree(node.children[index]!);\n return {\n ...node,\n children: [...node.children.slice(0, index + 1), clone, ...node.children.slice(index + 1)],\n };\n }\n return { ...node, children: node.children.map(insertClone) };\n };\n return Result.succeed({ ...document, root: insertClone(document.root) as ElementNode });\n};\n\nconst mutate = (\n document: SmlDocument,\n op: SmlOp,\n): Result.Result<SmlDocument, readonly Diagnostic[]> => {\n switch (op._tag) {\n case \"setProp\":\n return setProp(document, op);\n case \"setChildren\":\n return setChildren(document, op);\n case \"insertBlock\":\n return insertBlock(document, op);\n case \"insertSnippet\":\n return insertSnippet(document, op);\n case \"moveNode\":\n return moveNode(document, op);\n case \"duplicateNode\":\n return duplicateNode(document, op.nodeId);\n case \"removeNode\":\n return removeNodeById(document, op.nodeId);\n }\n};\n\nconst preflight = (source: string): Result.Result<SmlDocument, readonly Diagnostic[]> => {\n const reparsed = parseSml(source);\n if (Result.isFailure(reparsed)) return Result.fail(reparsed.failure.diagnostics);\n const compiled = compileSml(reparsed.success);\n if (Result.isFailure(compiled)) return Result.fail(compiled.failure.diagnostics);\n const compatibilityErrors = checkSmlCompatibility({ source })\n .filter(isBreakingCompatibilityCheck)\n .map((message) => diagnostic(\"compatibility-error\", message));\n if (compatibilityErrors.length > 0) return Result.fail(compatibilityErrors);\n return Result.succeed(reparsed.success);\n};\n\nconst failedResult = (\n index: number,\n op: SmlOp,\n diagnostics: readonly Diagnostic[],\n): SmlOpResult => ({\n index,\n status: \"failed\",\n op,\n diagnostics: [...diagnostics],\n});\n\n/**\n * Apply a batch of ops to SML source, returning the rewritten source, its new\n * revision, and a per-op result.\n *\n * Id contract: every id in the batch resolves against the id-space of the\n * source the caller read (the `expectedRevision` snapshot). Ids are positional\n * (`n0..nN` in document order) and are NOT rewritten between ops, so:\n *\n * - An op cannot target a node created earlier in the same batch — freshly\n * inserted/duplicated nodes carry a blank id until the next reprint reassigns\n * positional ids, and are unaddressable within the batch (fails with\n * `op-target-not-found`).\n * - After `duplicateNode`, the duplicated id still addresses the ORIGINAL (the\n * first match in document order); the clone is a blank-id copy.\n * - Structural ops (insert/remove/move) do not shift the ids of the nodes that\n * survive, so a later op targeting a surviving sibling lands on the intended\n * node regardless of what earlier ops did.\n *\n * Each op is still preflighted (reprint, reparse, compile, compatibility check)\n * as an independent validity gate; a failing op is recorded as `failed` and\n * does not advance the working document.\n */\nexport const applyOps = (\n source: string,\n ops: readonly SmlOp[],\n options?: ApplyOpsOptions,\n): Result.Result<ApplyOpsResult, SmlOpError> => {\n const parsed = parseSml(source);\n if (Result.isFailure(parsed)) {\n return Result.fail(new SmlOpError({ diagnostics: parsed.failure.diagnostics }));\n }\n\n const currentRevision = String(options?.currentRevision ?? sourceRevision(source));\n if (\n options?.expectedRevision !== undefined &&\n String(options.expectedRevision) !== currentRevision\n ) {\n const stale = diagnostic(\n \"stale-revision\",\n `source revision changed from ${String(options.expectedRevision)} to ${currentRevision}`,\n );\n return Result.succeed({\n source,\n revision: currentRevision,\n diagnostics: [stale],\n results: ops.map((op, index) => failedResult(index, op, [stale])),\n });\n }\n\n let currentSource = source;\n let currentDocument = parsed.success;\n const results: SmlOpResult[] = [];\n const diagnostics: Diagnostic[] = [];\n\n ops.forEach((op, index) => {\n const mutated = mutate(currentDocument, op);\n if (Result.isFailure(mutated)) {\n results.push(failedResult(index, op, mutated.failure));\n diagnostics.push(...mutated.failure);\n return;\n }\n const candidateSource = printSml(mutated.success);\n const checked = preflight(candidateSource);\n if (Result.isFailure(checked)) {\n results.push(failedResult(index, op, checked.failure));\n diagnostics.push(...checked.failure);\n return;\n }\n // Thread the MUTATED document, not the reparsed one: reparsing reassigns\n // positional ids, which would move every id an op targets out from under\n // the caller's snapshot. The mutation helpers spread-copy and preserve the\n // `id` field, so ids from the initial parse stay stable across the batch.\n // Preflight above stays the per-op validity gate; only its result document\n // is discarded.\n currentSource = candidateSource;\n currentDocument = mutated.success;\n results.push({ index, status: \"applied\", op, diagnostics: [] });\n });\n\n return Result.succeed({\n source: currentSource,\n revision: sourceRevision(currentSource),\n diagnostics,\n results,\n });\n};\n\nexport const applyOp = (\n source: string,\n op: SmlOp,\n options?: ApplyOpsOptions,\n): Result.Result<ApplyOpsResult, SmlOpError> => applyOps(source, [op], options);\n"],"mappings":";;;;;;;;AAYA,MAAM,uBAAuB,OAAO,SAAS;CAAC;CAAU;CAAS;AAAQ,CAAC;AAE1E,MAAM,kBAAkB,OAAO,aAAa,WAAW;CACrD,QAAQ,OAAO;CACf,MAAM,OAAO;CACb,OAAO,OAAO,OAAO,oBAAoB;AAC3C,CAAC;;;;;;AAOD,MAAM,sBAAsB,OAAO,aAAa,eAAe;CAC7D,QAAQ,OAAO;CACf,UAAU,OAAO,MAAM,aAAa;AACtC,CAAC;AAED,MAAM,sBAAsB,OAAO,aAAa,eAAe;CAC7D,KAAK,OAAO,SAAS,QAAQ;CAC7B,aAAa,OAAO,OAAO,OAAO,MAAM;CACxC,UAAU;AACZ,CAAC;AAED,MAAM,wBAAwB,OAAO,aAAa,iBAAiB;CACjE,SAAS,OAAO;CAChB,aAAa,OAAO,OAAO,OAAO,MAAM;CACxC,UAAU;AACZ,CAAC;;;;;;;AAQD,MAAM,mBAAmB,OAAO,aAAa,YAAY;CACvD,QAAQ,OAAO;CACf,gBAAgB,OAAO;CACvB,gBAAgB,OAAO;AACzB,CAAC;AAED,MAAM,wBAAwB,OAAO,aAAa,iBAAiB,EACjE,QAAQ,OAAO,OACjB,CAAC;AAED,MAAM,qBAAqB,OAAO,aAAa,cAAc,EAC3D,QAAQ,OAAO,OACjB,CAAC;;;;;;AAOD,MAAa,cAAc,OAAO,MAAM;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAID,MAAa,oBAAoB,OAAO,OAAO;CAC7C,OAAO,OAAO;CACd,QAAQ,OAAO,SAAS,CAAC,WAAW,QAAQ,CAAC;CAC7C,IAAI;CACJ,aAAa,OAAO,MAAM,gBAAgB;AAC5C,CAAC;AAID,MAAa,uBAAuB,OAAO,OAAO;CAChD,QAAQ,OAAO;CACf,UAAU,OAAO;CACjB,aAAa,OAAO,MAAM,gBAAgB;CAC1C,SAAS,OAAO,MAAM,iBAAiB;AACzC,CAAC;AASD,IAAa,aAAb,cAAgC,OAAO,iBAA6B,CAAC,CAAC,cAAc,EAClF,aAAa,OAAO,MAAM,gBAAgB,EAC5C,CAAC,CAAC,CAAC;CACD,IAAa,UAAU;EACrB,OAAO,KAAK,YAAY,KAAK,eAAe,WAAW,OAAO,CAAC,CAAC,KAAK,IAAI;CAC3E;AACF;AAEA,MAAM,WAAiB;CAAE,OAAO;CAAG,KAAK;AAAE;AAE1C,MAAM,cAAc,MAAc,SAAiB,OAAa,cAA0B;CACxF;CACA;CACA;AACF;AAEA,MAAM,kBAAkB,eAAqC,EAAE,GAAG,UAAU;;;;;;;;;AAU5E,MAAM,gBAAgB,SAA2B;CAC/C,IAAI,KAAK,SAAS,WAChB,OAAO;EACL,GAAG;EACH,IAAI;EACJ,YAAY,KAAK,WAAW,IAAI,cAAc;EAC9C,UAAU,KAAK,SAAS,IAAI,YAAY;CAC1C;CAEF,OAAO;EAAE,GAAG;EAAM,IAAI;CAAG;AAC3B;AAEA,MAAM,iBAAiB,MAAc,WAAsC;CACzE;CACA;CACA,MAAM;CACN,WAAW;AACb;AAEA,MAAM,aAAa,UAA6B,cAAc,SAAS;CAAE,MAAM;CAAU;AAAM,CAAC;AAEhG,MAAa,kBAAkB,WAA2B;CACxD,IAAI,OAAO;CACX,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS;EAClD,QAAQ,OAAO,WAAW,KAAK;EAC/B,OAAO,KAAK,KAAK,MAAM,QAAU;CACnC;CACA,OAAO,QAAQ,SAAS,EAAA,CAAG,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;AACzD;AAEA,MAAa,gBAAgB,WAAyD;CACpF,MAAM,SAAS,SAAS,MAAM;CAC9B,IAAI,OAAO,UAAU,MAAM,GAAG,OAAO,OAAO,KAAK,OAAO,OAAO;CAC/D,OAAO,OAAO,QAAQ,SAAS,OAAO,OAAO,CAAC;AAChD;AAEA,MAAM,eAAe,MAAe,WAAuC;CACzE,IAAI,KAAK,SAAS,WAAW,OAAO;CACpC,IAAI,KAAK,OAAO,QAAQ,OAAO;CAC/B,KAAK,MAAM,SAAS,KAAK,UAAU;EACjC,MAAM,QAAQ,YAAY,OAAO,MAAM;EACvC,IAAI,UAAU,MAAM,OAAO;CAC7B;CACA,OAAO;AACT;AAEA,MAAM,cACJ,MACA,UACA,cACY;CACZ,IAAI,KAAK,SAAS,WAAW,OAAO;CACpC,IAAI,KAAK,OAAO,UAAU,OAAO,UAAU,IAAI;CAC/C,OAAO;EACL,GAAG;EACH,UAAU,KAAK,SAAS,KAAK,UAAU,WAAW,OAAO,UAAU,SAAS,CAAC;CAC/E;AACF;AAEA,MAAM,oBACJ,UACA,UACA,cACsD;CAEtD,IADe,YAAY,SAAS,MAAM,QACjC,MAAM,MACb,OAAO,OAAO,KAAK,CACjB,WAAW,uBAAuB,SAAS,SAAS,sCAAsC,CAC5F,CAAC;CAEH,OAAO,OAAO,QAAQ;EACpB,GAAG;EACH,MAAM,WAAW,SAAS,MAAM,UAAU,SAAS;CACrD,CAAC;AACH;AAEA,MAAM,uBACJ,SACA,MACA,UACgB;CAChB,MAAM,OAAO,QAAQ,WAAW,QAAQ,cAAc,UAAU,SAAS,IAAI;CAC7E,IAAI,UAAU,QAAS,MAAM,SAAS,YAAY,MAAM,UAAU,IAChE,OAAO;EAAE,GAAG;EAAS,YAAY;CAAK;CAExC,MAAM,OAAO,cAAc,MAAM,KAAK;CAEtC,MAAM,aADU,QAAQ,WAAW,MAAM,cAAc,UAAU,SAAS,IACjD,IACrB,QAAQ,WAAW,KAAK,cACtB,UAAU,SAAS,OAAO,OAAO,eAAe,SAAS,CAC3D,IACA,CAAC,GAAG,QAAQ,YAAY,IAAI;CAChC,OAAO;EAAE,GAAG;EAAS;CAAW;AAClC;AAEA,MAAM,wBACJ,UACA,QACA,QACA,UAEA,iBAAiB,UAAU,SAAS,YAAY;CAC9C,IAAI,QAAQ,QAAQ,SAAS,OAAO;CACpC,MAAM,MAAM,OAAO,SAAS,WAAW,MAAM,QAAQ;CACrD,MAAM,WAAW,QAAQ,SAAS,QAC/B,UACC,EACE,MAAM,SAAS,aACf,MAAM,QAAQ,YACd,MAAM,WAAW,MACd,cACC,UAAU,SAAS,YACnB,UAAU,MAAM,SAAS,YACzB,UAAU,MAAM,UAAU,MAC9B,EAEN;CACA,IAAI,QAAQ,IAAI,OAAO;EAAE,GAAG;EAAS;CAAS;CAC9C,MAAM,aAA0B;EAC9B,MAAM;EACN,IAAI;EACJ,KAAK;EACL,YAAY,CACV,cAAc,UAAU;GAAE,MAAM;GAAU,OAAO;EAAO,CAAC,GACzD,cAAc,OAAO;GAAE,MAAM;GAAU,OAAO;EAAI,CAAC,CACrD;EACA,UAAU,CAAC;EACX,MAAM;CACR;CACA,OAAO;EAAE,GAAG;EAAS,UAAU,CAAC,GAAG,UAAU,UAAU;CAAE;AAC3D,CAAC;AAEH,MAAM,WACJ,UACA,OACsD;CACtD,IAAI,GAAG,SAAS,YAAY,OAAO,qBAAqB,UAAU,GAAG,QAAQ,QAAQ,GAAG,KAAK;CAC7F,OAAO,iBAAiB,UAAU,GAAG,SAAS,YAAY;EACxD,MAAM,QACJ,GAAG,SAAS,WAAW,GAAG,OAAO,SAAS,WACtC;GAAE,MAAM;GAAmB,OAAO,GAAG,MAAM,MAAM,KAAK,CAAC,CAAC,QAAQ,QAAQ,GAAG;EAAE,IAC7E,GAAG;EACT,OAAO,oBAAoB,SAAS,GAAG,MAAM,KAAK;CACpD,CAAC;AACH;AAEA,MAAM,eACJ,UACA,OAEA,iBAAiB,UAAU,GAAG,SAAS,aAAa;CAAE,GAAG;CAAS,UAAU,CAAC,GAAG,GAAG,QAAQ;AAAE,EAAE;AAEjG,MAAM,oBAAiC;CACrC,MAAM;CACN,IAAI;CACJ,KAAK;CACL,YAAY,CAAC,UAAU,MAAM,CAAC;CAC9B,UAAU,CACR;EACE,MAAM;EACN,IAAI;EACJ,KAAK;EACL,YAAY,CAAC,UAAU,yBAAyB,CAAC;EACjD,UAAU,CAAC;GAAE,MAAM;GAAQ,IAAI;GAAI,OAAO;GAAU,MAAM;EAAS,CAAC;EACpE,MAAM;CACR,CACF;CACA,MAAM;AACR;AAEA,MAAM,aAAa,QAA6B;CAC9C,MAAM,QAAQ,YAAyB,cAAsC;EAC3E,MAAM;EACN,IAAI;EACJ;EACA;EACA;EACA,MAAM;CACR;CACA,MAAM,QAAQ,WAA4B;EACxC,MAAM;EACN,IAAI;EACJ;EACA,MAAM;CACR;CACA,QAAQ,KAAR;EACE,KAAK,WACH,OAAO,KACL,CAAC,cAAc,SAAS;GAAE,MAAM;GAAU,OAAO;EAAE,CAAC,GAAG,UAAU,uBAAuB,CAAC,GACzF,CAAC,KAAK,SAAS,CAAC,CAClB;EACF,KAAK,QACH,OAAO,KAAK,CAAC,UAAU,yBAAyB,CAAC,GAAG,CAAC,KAAK,qBAAqB,CAAC,CAAC;EACnF,KAAK,UACH,OAAO,KACL,CACE,cAAc,QAAQ;GAAE,MAAM;GAAU,OAAO;EAAsB,CAAC,GACtE,UAAU,+CAA+C,CAC3D,GACA,CAAC,KAAK,QAAQ,CAAC,CACjB;EACF,KAAK,SACH,OAAO,KACL;GACE,cAAc,OAAO;IAAE,MAAM;IAAU,OAAO;GAA+B,CAAC;GAC9E,cAAc,OAAO;IAAE,MAAM;IAAU,OAAO;GAAG,CAAC;GAClD,cAAc,SAAS;IAAE,MAAM;IAAU,OAAO;GAAI,CAAC;EACvD,GACA,CAAC,CACH;EACF,KAAK,WACH,OAAO,KAAK,CAAC,UAAU,iBAAiB,CAAC,GAAG,CAAC,CAAC;EAChD,KAAK,UACH,OAAO,KAAK,CAAC,cAAc,UAAU;GAAE,MAAM;GAAU,OAAO;EAAG,CAAC,CAAC,GAAG,CAAC,CAAC;EAC1E,KAAK,WACH,OAAO,KAAK,CAAC,UAAU,OAAO,CAAC,GAAG,CAAC,UAAU,MAAM,CAAC,CAAC;EACvD,KAAK,aACH,OAAO,KAAK,CAAC,UAAU,eAAe,CAAC,GAAG,CAAC,UAAU,MAAM,CAAC,CAAC;EAC/D,KAAK,OACH,OAAO,KAAK,CAAC,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC,CAAC;EAC9C,SACE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC;CACtB;AACF;AAEA,MAAM,gCACJ,UACA,mBACW;CACX,IAAI,kBAAkB,GAAG;EACvB,MAAM,eAAe,SAAS,WAAW,UAAU,MAAM,SAAS,SAAS;EAC3E,OAAO,iBAAiB,KAAK,SAAS,SAAS;CACjD;CACA,IAAI,eAAe;CACnB,KAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS;EACpD,IAAI,SAAS,MAAM,EAAE,SAAS,WAAW;EACzC,IAAI,iBAAiB,gBAAgB,OAAO;EAC5C;CACF;CACA,OAAO,SAAS;AAClB;AAEA,MAAM,kBACJ,UACA,kBACA,aACA,aACsD;CACtD,IAAI,iBAAiB,WAAW,GAC9B,OAAO,OAAO,KAAK,CACjB,WAAW,mBAAmB,4CAA4C,CAC5E,CAAC;CAEH,IAAI,gBAAgB,QAAQ,gBAAgB,SAAS,KAAK,IAAI;EAC5D,MAAM,WACJ,aAAa,WACT,CAAC,GAAG,kBAAkB,GAAG,SAAS,KAAK,QAAQ,IAC/C,CAAC,GAAG,SAAS,KAAK,UAAU,GAAG,gBAAgB;EACrD,OAAO,OAAO,QAAQ;GAAE,GAAG;GAAU,MAAM;IAAE,GAAG,SAAS;IAAM;GAAS;EAAE,CAAC;CAC7E;CAEA,IADkB,YAAY,SAAS,MAAM,WACjC,MAAM,MAChB,OAAO,OAAO,KAAK,CACjB,WACE,uBACA,SAAS,YAAY,sCACvB,CACF,CAAC;CAEH,IAAI,aAAa,UACf,OAAO,iBAAiB,UAAU,cAAc,aAAa;EAC3D,GAAG;EACH,UAAU,CAAC,GAAG,QAAQ,UAAU,GAAG,gBAAgB;CACrD,EAAE;CAEJ,MAAM,kBAAkB,SAA2B;EACjD,IAAI,KAAK,SAAS,WAAW,OAAO;EACpC,MAAM,QAAQ,KAAK,SAAS,WACzB,UAAU,MAAM,SAAS,aAAa,MAAM,OAAO,WACtD;EACA,IAAI,SAAS,GAAG;GACd,MAAM,KAAK,aAAa,UAAU,QAAQ,IAAI;GAC9C,OAAO;IACL,GAAG;IACH,UAAU;KAAC,GAAG,KAAK,SAAS,MAAM,GAAG,EAAE;KAAG,GAAG;KAAkB,GAAG,KAAK,SAAS,MAAM,EAAE;IAAC;GAC3F;EACF;EACA,OAAO;GAAE,GAAG;GAAM,UAAU,KAAK,SAAS,IAAI,cAAc;EAAE;CAChE;CACA,OAAO,OAAO,QAAQ;EAAE,GAAG;EAAU,MAAM,eAAe,SAAS,IAAI;CAAiB,CAAC;AAC3F;AAEA,MAAM,eACJ,UACA,OAEA,eAAe,UAAU,CAAC,UAAU,GAAG,GAAG,CAAC,GAAG,GAAG,aAAa,GAAG,QAAQ;AAE3E,MAAM,iBACJ,UACA,OACsD;CACtD,MAAM,UAAU,SAAS,KAAK;CAC9B,MAAM,SAAS,SAAS,IAAI,QAAQ,GAAG,GAAG,QAAQ,IAAI,QAAQ,EAAE;CAChE,IAAI,OAAO,UAAU,MAAM,GAAG,OAAO,OAAO,KAAK,OAAO,QAAQ,WAAW;CAC3E,MAAM,WAAW,OAAO,QAAQ,KAAK,SAAS,QAC3C,UAAgC,MAAM,SAAS,SAClD;CACA,OAAO,eAAe,UAAU,UAAU,GAAG,aAAa,GAAG,QAAQ;AACvE;AAEA,MAAM,kBACJ,UACA,WACsD;CACtD,IAAI,SAAS,KAAK,OAAO,QACvB,OAAO,OAAO,KAAK,CAAC,WAAW,kBAAkB,qCAAqC,CAAC,CAAC;CAE1F,IAAI,YAAY,SAAS,MAAM,MAAM,MAAM,MACzC,OAAO,OAAO,KAAK,CACjB,WAAW,uBAAuB,SAAS,OAAO,sCAAsC,CAC1F,CAAC;CAEH,MAAM,SAAS,SAA2B;EACxC,IAAI,KAAK,SAAS,WAAW,OAAO;EACpC,OAAO;GACL,GAAG;GACH,UAAU,KAAK,SACZ,QAAQ,UAAU,EAAE,MAAM,SAAS,aAAa,MAAM,OAAO,OAAO,CAAC,CACrE,IAAI,KAAK;EACd;CACF;CACA,OAAO,OAAO,QAAQ;EAAE,GAAG;EAAU,MAAM,MAAM,SAAS,IAAI;CAAiB,CAAC;AAClF;AAEA,MAAM,gBAAgB,UAAuB,WAC3C,SAAS,SAAS,MAAM,UAAU;CAChC,IAAI,MAAM,SAAS,WAAW,OAAO;CACrC,OAAO,MAAM,OAAO,UAAU,aAAa,OAAO,MAAM;AAC1D,CAAC;AAEH,MAAM,iBACJ,MACA,WACyE;CACzE,IAAI,UAA8B;CAClC,MAAM,WAAsB,CAAC;CAC7B,KAAK,MAAM,SAAS,KAAK,UAAU;EACjC,IAAI,MAAM,SAAS,aAAa,MAAM,OAAO,QAAQ;GACnD,UAAU;GACV;EACF;EACA,IAAI,MAAM,SAAS,WAAW;GAC5B,MAAM,SAAS,cAAc,OAAO,MAAM;GAC1C,IAAI,OAAO,YAAY,MAAM,UAAU,OAAO;GAC9C,SAAS,KAAK,OAAO,IAAI;EAC3B,OACE,SAAS,KAAK,KAAK;CAEvB;CACA,OAAO;EAAE,MAAM;GAAE,GAAG;GAAM;EAAS;EAAG;CAAQ;AAChD;AAEA,MAAM,oBACJ,MACA,gBACA,gBACA,UACgB;CAChB,IAAI,KAAK,OAAO,gBAAgB;EAC9B,MAAM,KAAK,6BAA6B,KAAK,UAAU,cAAc;EACrE,OAAO;GACL,GAAG;GACH,UAAU;IAAC,GAAG,KAAK,SAAS,MAAM,GAAG,EAAE;IAAG;IAAO,GAAG,KAAK,SAAS,MAAM,EAAE;GAAC;EAC7E;CACF;CACA,OAAO;EACL,GAAG;EACH,UAAU,KAAK,SAAS,KAAK,UAC3B,MAAM,SAAS,YACX,iBAAiB,OAAO,gBAAgB,gBAAgB,KAAK,IAC7D,KACN;CACF;AACF;AAEA,MAAM,YACJ,UACA,OACsD;CACtD,IAAI,SAAS,KAAK,OAAO,GAAG,QAC1B,OAAO,OAAO,KAAK,CAAC,WAAW,kBAAkB,mCAAmC,CAAC,CAAC;CAExF,MAAM,QAAQ,YAAY,SAAS,MAAM,GAAG,MAAM;CAClD,IAAI,UAAU,MACZ,OAAO,OAAO,KAAK,CACjB,WAAW,uBAAuB,SAAS,GAAG,OAAO,sCAAsC,CAC7F,CAAC;CAEH,MAAM,SAAS,YAAY,SAAS,MAAM,GAAG,cAAc;CAC3D,IAAI,WAAW,MACb,OAAO,OAAO,KAAK,CACjB,WACE,uBACA,kBAAkB,GAAG,eAAe,sCACtC,CACF,CAAC;CAEH,IAAI,MAAM,OAAO,OAAO,MAAM,aAAa,OAAO,OAAO,EAAE,GACzD,OAAO,OAAO,KAAK,CACjB,WAAW,uBAAuB,uDAAuD,CAC3F,CAAC;CAEH,MAAM,UAAU,gBAAgB,QAAQ;CAExC,MAAM,aADO,kBAAkB,QAAQ,CAAC,OAAO,IACxB,EAAE;CAEzB,IAAI,EADY,eAAe,KAAA,IAAY,CAAC,IAAI,gBAAgB,UAAU,EAAA,CAC7D,SAAS,MAAM,GAAG,GAC7B,OAAO,OAAO,KAAK,CACjB,WACE,uBACA,IAAI,MAAM,IAAI,oCAAoC,OAAO,IAAI,IAC7D,OAAO,IACT,CACF,CAAC;CAEH,MAAM,EAAE,MAAM,kBAAkB,YAAY,cAAc,SAAS,MAAM,GAAG,MAAM;CAClF,IAAI,YAAY,MACd,OAAO,OAAO,KAAK,CACjB,WAAW,uBAAuB,SAAS,GAAG,OAAO,sCAAsC,CAC7F,CAAC;CAEH,OAAO,OAAO,QAAQ;EACpB,GAAG;EACH,MAAM,iBAAiB,kBAAkB,GAAG,gBAAgB,GAAG,gBAAgB,OAAO;CACxF,CAAC;AACH;AAEA,MAAM,iBACJ,UACA,WACsD;CACtD,IAAI,SAAS,KAAK,OAAO,QACvB,OAAO,OAAO,KAAK,CAAC,WAAW,kBAAkB,wCAAwC,CAAC,CAAC;CAE7F,IAAI,YAAY,SAAS,MAAM,MAAM,MAAM,MACzC,OAAO,OAAO,KAAK,CACjB,WAAW,uBAAuB,SAAS,OAAO,sCAAsC,CAC1F,CAAC;CAEH,MAAM,eAAe,SAA2B;EAC9C,IAAI,KAAK,SAAS,WAAW,OAAO;EACpC,MAAM,QAAQ,KAAK,SAAS,WACzB,UAAU,MAAM,SAAS,aAAa,MAAM,OAAO,MACtD;EACA,IAAI,SAAS,GAAG;GACd,MAAM,QAAQ,aAAa,KAAK,SAAS,MAAO;GAChD,OAAO;IACL,GAAG;IACH,UAAU;KAAC,GAAG,KAAK,SAAS,MAAM,GAAG,QAAQ,CAAC;KAAG;KAAO,GAAG,KAAK,SAAS,MAAM,QAAQ,CAAC;IAAC;GAC3F;EACF;EACA,OAAO;GAAE,GAAG;GAAM,UAAU,KAAK,SAAS,IAAI,WAAW;EAAE;CAC7D;CACA,OAAO,OAAO,QAAQ;EAAE,GAAG;EAAU,MAAM,YAAY,SAAS,IAAI;CAAiB,CAAC;AACxF;AAEA,MAAM,UACJ,UACA,OACsD;CACtD,QAAQ,GAAG,MAAX;EACE,KAAK,WACH,OAAO,QAAQ,UAAU,EAAE;EAC7B,KAAK,eACH,OAAO,YAAY,UAAU,EAAE;EACjC,KAAK,eACH,OAAO,YAAY,UAAU,EAAE;EACjC,KAAK,iBACH,OAAO,cAAc,UAAU,EAAE;EACnC,KAAK,YACH,OAAO,SAAS,UAAU,EAAE;EAC9B,KAAK,iBACH,OAAO,cAAc,UAAU,GAAG,MAAM;EAC1C,KAAK,cACH,OAAO,eAAe,UAAU,GAAG,MAAM;CAC7C;AACF;AAEA,MAAM,aAAa,WAAsE;CACvF,MAAM,WAAW,SAAS,MAAM;CAChC,IAAI,OAAO,UAAU,QAAQ,GAAG,OAAO,OAAO,KAAK,SAAS,QAAQ,WAAW;CAC/E,MAAM,WAAW,WAAW,SAAS,OAAO;CAC5C,IAAI,OAAO,UAAU,QAAQ,GAAG,OAAO,OAAO,KAAK,SAAS,QAAQ,WAAW;CAC/E,MAAM,sBAAsB,sBAAsB,EAAE,OAAO,CAAC,CAAC,CAC1D,OAAO,4BAA4B,CAAC,CACpC,KAAK,YAAY,WAAW,uBAAuB,OAAO,CAAC;CAC9D,IAAI,oBAAoB,SAAS,GAAG,OAAO,OAAO,KAAK,mBAAmB;CAC1E,OAAO,OAAO,QAAQ,SAAS,OAAO;AACxC;AAEA,MAAM,gBACJ,OACA,IACA,iBACiB;CACjB;CACA,QAAQ;CACR;CACA,aAAa,CAAC,GAAG,WAAW;AAC9B;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,YACX,QACA,KACA,YAC8C;CAC9C,MAAM,SAAS,SAAS,MAAM;CAC9B,IAAI,OAAO,UAAU,MAAM,GACzB,OAAO,OAAO,KAAK,IAAI,WAAW,EAAE,aAAa,OAAO,QAAQ,YAAY,CAAC,CAAC;CAGhF,MAAM,kBAAkB,OAAO,SAAS,mBAAmB,eAAe,MAAM,CAAC;CACjF,IACE,SAAS,qBAAqB,KAAA,KAC9B,OAAO,QAAQ,gBAAgB,MAAM,iBACrC;EACA,MAAM,QAAQ,WACZ,kBACA,gCAAgC,OAAO,QAAQ,gBAAgB,EAAE,MAAM,iBACzE;EACA,OAAO,OAAO,QAAQ;GACpB;GACA,UAAU;GACV,aAAa,CAAC,KAAK;GACnB,SAAS,IAAI,KAAK,IAAI,UAAU,aAAa,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;EAClE,CAAC;CACH;CAEA,IAAI,gBAAgB;CACpB,IAAI,kBAAkB,OAAO;CAC7B,MAAM,UAAyB,CAAC;CAChC,MAAM,cAA4B,CAAC;CAEnC,IAAI,SAAS,IAAI,UAAU;EACzB,MAAM,UAAU,OAAO,iBAAiB,EAAE;EAC1C,IAAI,OAAO,UAAU,OAAO,GAAG;GAC7B,QAAQ,KAAK,aAAa,OAAO,IAAI,QAAQ,OAAO,CAAC;GACrD,YAAY,KAAK,GAAG,QAAQ,OAAO;GACnC;EACF;EACA,MAAM,kBAAkB,SAAS,QAAQ,OAAO;EAChD,MAAM,UAAU,UAAU,eAAe;EACzC,IAAI,OAAO,UAAU,OAAO,GAAG;GAC7B,QAAQ,KAAK,aAAa,OAAO,IAAI,QAAQ,OAAO,CAAC;GACrD,YAAY,KAAK,GAAG,QAAQ,OAAO;GACnC;EACF;EAOA,gBAAgB;EAChB,kBAAkB,QAAQ;EAC1B,QAAQ,KAAK;GAAE;GAAO,QAAQ;GAAW;GAAI,aAAa,CAAC;EAAE,CAAC;CAChE,CAAC;CAED,OAAO,OAAO,QAAQ;EACpB,QAAQ;EACR,UAAU,eAAe,aAAa;EACtC;EACA;CACF,CAAC;AACH;AAEA,MAAa,WACX,QACA,IACA,YAC8C,SAAS,QAAQ,CAAC,EAAE,GAAG,OAAO"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"parser.js","names":[],"sources":["../src/parser.ts"],"sourcesContent":["import { Result } from \"effect\";\n\nimport type {\n Attribute,\n AttributeValue,\n Channel,\n ElementNode,\n SmlDocument,\n SmlNode,\n SmlTag,\n} from \"./ast\";\nimport { CHANNEL_BY_ROOT_TAG, isRootTag, ROOT_TAGS, SML_TAGS } from \"./ast\";\nimport type { Diagnostic, Span } from \"./diagnostics\";\nimport { SmlParseError } from \"./diagnostics\";\nimport type { Token } from \"./lexer\";\nimport { createLexer, TokenKind } from \"./lexer\";\nimport { parsePredicate, printPredicate } from \"./predicate\";\n\n// Recursive-descent SML parser. JSX-strict: every tag closed, attributes\n// quoted (or `{number}`), known vocabulary only — no HTML5 repair semantics.\n// Fail-fast: the first diagnostic aborts the parse; every diagnostic carries a\n// span and names the fix so the agent write → validate → retry loop converges.\n//\n// The root tag declares the channel, and the channel selects the tag table:\n// email documents use EMAIL_TAG_SPECS, sms SMS_TAG_SPECS, whatsapp\n// WHATSAPP_TAG_SPECS. Tag names may repeat across channels with different\n// specs (<Button> is an email link-button and a WhatsApp template button).\n//\n// Whitespace has two regimes. Email inline content collapses runs to single\n// spaces (HTML-style, canonicalized at parse). Sms/WhatsApp text slots are\n// VERBATIM (pre-like): newlines and space runs are content and survive\n// parse ⇄ print byte-exactly.\n\nexport type ContentModel =\n | \"email\" // envelope metadata? then blocks\n | \"block\" // block children only\n | \"columns\" // Column children only\n | \"list\" // ListItem children only\n | \"image\" // Source children only\n | \"if\" // blocks, then at most one <Else> as the last child\n | \"inline\" // text, {{vars}}, strong/em/a\n | \"inline-nolink\" // text, {{vars}}, strong/em\n | \"preview\" // text and {{vars}} only\n | \"whatsapp\" // Header? Body Footer? Buttons? — in that order, each at most once\n | \"buttons\" // Button children only\n | \"verbatim\" // verbatim text, {{vars}}, <If> (sms body / whatsapp text slots)\n | \"verbatim-if\" // verbatim content, then at most one <Else> as the last child\n | \"void\"; // self-closing, no children\n\nexport interface AttributeSpec {\n readonly type: \"string\" | \"number\";\n readonly required?: boolean | undefined;\n /** Inclusive numeric bounds for number attributes. */\n readonly min?: number | undefined;\n readonly max?: number | undefined;\n /** Closed value set for string attributes; anything else is a parse error. */\n readonly values?: ReadonlyArray<string> | undefined;\n}\n\nexport interface TagSpec {\n readonly attributes: Readonly<Record<string, AttributeSpec>>;\n readonly content: ContentModel;\n}\n\n// Attribute value sets belong to the public SML contract.\nconst SMS_CATEGORIES = [\"transactional\", \"promotional\", \"service_implicit\"] as const;\nconst WHATSAPP_CATEGORIES = [\"marketing\", \"utility\", \"authentication\", \"service\"] as const;\nconst WHATSAPP_HEADER_TYPES = [\"text\", \"image\", \"video\", \"document\"] as const;\nconst WHATSAPP_BUTTON_TYPES = [\"quick-reply\", \"url\", \"phone\", \"copy-code\"] as const;\n\nconst BACKGROUND_POSITIONS = [\"center\", \"top\", \"bottom\"] as const;\nconst BACKGROUND_SIZES = [\"cover\", \"contain\", \"auto\"] as const;\n\n/**\n * Background-image attributes on the email block containers (Section/Container).\n * A URL is an ATTRIBUTE, not a class token: a URL inside the closed class\n * grammar is hostile (escaping, cache eviction, no caniemail slug), so it rides\n * the attribute channel like `src`/`href`. The compiler emits email-safe\n * background CSS from these (escaped `url(...)` + an Outlook `background` cell\n * attribute); Outlook for Windows renders no CSS background image, so\n * `checks` warns via `css-background-image`. Position/size are ignored by the\n * compiler when no `backgroundImage` is set.\n */\nconst BACKGROUND_ATTRIBUTES = {\n backgroundImage: { type: \"string\" },\n backgroundPosition: { type: \"string\", values: BACKGROUND_POSITIONS },\n backgroundSize: { type: \"string\", values: BACKGROUND_SIZES },\n} as const satisfies Readonly<Record<string, AttributeSpec>>;\n\n/**\n * The email language surface as data: per-tag attributes\n * (type/required/bounds) and content model. Public API — typed TSX components\n * and the reference-docs generator derive from this table, so it cannot drift\n * from the parser.\n */\nexport const EMAIL_TAG_SPECS = {\n Email: {\n attributes: {\n lang: { type: \"string\" },\n dir: { type: \"string\" },\n width: { type: \"number\", min: 1, max: 10_000 },\n class: { type: \"string\" },\n },\n content: \"email\",\n },\n Subject: { attributes: {}, content: \"preview\" },\n Preview: { attributes: {}, content: \"preview\" },\n Container: {\n attributes: { class: { type: \"string\" }, ...BACKGROUND_ATTRIBUTES },\n content: \"block\",\n },\n Section: {\n attributes: { class: { type: \"string\" }, ...BACKGROUND_ATTRIBUTES },\n content: \"block\",\n },\n Row: { attributes: { class: { type: \"string\" } }, content: \"columns\" },\n Column: {\n attributes: {\n class: { type: \"string\" },\n width: { type: \"string\" },\n valign: { type: \"string\", values: [\"top\", \"middle\", \"bottom\"] },\n },\n content: \"block\",\n },\n Heading: {\n attributes: { level: { type: \"number\", min: 1, max: 6 }, class: { type: \"string\" } },\n content: \"inline\",\n },\n Text: { attributes: { class: { type: \"string\" } }, content: \"inline\" },\n Button: {\n attributes: { href: { type: \"string\", required: true }, class: { type: \"string\" } },\n content: \"inline-nolink\",\n },\n Image: {\n attributes: {\n src: { type: \"string\", required: true },\n alt: { type: \"string\" },\n width: { type: \"number\", min: 0, max: 10_000 },\n height: { type: \"number\", min: 0, max: 10_000 },\n class: { type: \"string\" },\n },\n content: \"image\",\n },\n Source: {\n attributes: {\n scheme: { type: \"string\", required: true, values: [\"dark\"] },\n src: { type: \"string\", required: true },\n },\n content: \"void\",\n },\n Divider: { attributes: { class: { type: \"string\" } }, content: \"void\" },\n Spacer: {\n attributes: { height: { type: \"number\", required: true, min: 1, max: 2_000 } },\n content: \"void\",\n },\n List: {\n attributes: {\n type: { type: \"string\", values: [\"unordered\", \"ordered\"] },\n class: { type: \"string\" },\n },\n content: \"list\",\n },\n ListItem: { attributes: { class: { type: \"string\" } }, content: \"inline\" },\n If: {\n attributes: { test: { type: \"string\", required: true } },\n content: \"if\",\n },\n Else: { attributes: {}, content: \"block\" },\n a: {\n attributes: { href: { type: \"string\", required: true }, class: { type: \"string\" } },\n content: \"inline-nolink\",\n },\n strong: { attributes: {}, content: \"inline-nolink\" },\n em: { attributes: {}, content: \"inline-nolink\" },\n} as const satisfies Readonly<Partial<Record<SmlTag, TagSpec>>>;\n\n/**\n * The sms language surface: a raw verbatim text body with `{{vars}}` and\n * send-time conditionals — no layout tags, no classes, no theming.\n */\nexport const SMS_TAG_SPECS = {\n Sms: {\n attributes: { category: { type: \"string\", values: SMS_CATEGORIES } },\n content: \"verbatim\",\n },\n If: { attributes: { test: { type: \"string\", required: true } }, content: \"verbatim-if\" },\n Else: { attributes: {}, content: \"verbatim\" },\n} as const satisfies Readonly<Partial<Record<SmlTag, TagSpec>>>;\n\n/**\n * The whatsapp language surface: Meta's template anatomy IS the closed\n * grammar — Header? Body Footer? Buttons?, verbatim text slots, no classes.\n * Root attributes carry authoring intent (name defaults from the template\n * slug, language from the variant locale — defaulting happens at the API\n * layer; the language just carries the attributes).\n */\nexport const WHATSAPP_TAG_SPECS = {\n WhatsApp: {\n attributes: {\n name: { type: \"string\" },\n language: { type: \"string\" },\n category: { type: \"string\", required: true, values: WHATSAPP_CATEGORIES },\n },\n content: \"whatsapp\",\n },\n Header: {\n attributes: { type: { type: \"string\", values: WHATSAPP_HEADER_TYPES } },\n content: \"verbatim\",\n },\n Body: { attributes: {}, content: \"verbatim\" },\n Footer: { attributes: {}, content: \"verbatim\" },\n Buttons: { attributes: {}, content: \"buttons\" },\n Button: {\n attributes: {\n type: { type: \"string\", required: true, values: WHATSAPP_BUTTON_TYPES },\n url: { type: \"string\" },\n phone: { type: \"string\" },\n },\n content: \"verbatim\",\n },\n If: { attributes: { test: { type: \"string\", required: true } }, content: \"verbatim-if\" },\n Else: { attributes: {}, content: \"verbatim\" },\n} as const satisfies Readonly<Partial<Record<SmlTag, TagSpec>>>;\n\n/** Per-channel tag tables — the root tag picks one for the whole document. */\nexport const CHANNEL_TAG_SPECS: Readonly<\n Record<Channel, Readonly<Partial<Record<SmlTag, TagSpec>>>>\n> = {\n email: EMAIL_TAG_SPECS,\n sms: SMS_TAG_SPECS,\n whatsapp: WHATSAPP_TAG_SPECS,\n};\n\n/**\n * Editor-only attributes accepted on every email block-level tag on top of the\n * tag's own spec. The compiler ignores both, so neither ever reaches HTML:\n * `label` renames a node in the layout navigator; `hidden=\"true\"` drops it from\n * the compiled artifact while it stays in the source. Email-only — sms/whatsapp\n * compile has no strip pass, so their tags keep their own specs verbatim.\n */\nexport const UNIVERSAL_ATTRIBUTES = {\n label: { type: \"string\" },\n hidden: { type: \"string\", values: [\"true\"] },\n} as const satisfies Readonly<Record<string, AttributeSpec>>;\n\n/** Email tags a user manipulates as layers — the ones the universal attributes attach to. */\nexport const UNIVERSAL_ATTRIBUTE_TAGS: ReadonlySet<SmlTag> = new Set([\n \"Container\",\n \"Section\",\n \"Row\",\n \"Column\",\n \"Heading\",\n \"Text\",\n \"Button\",\n \"Image\",\n \"Divider\",\n \"Spacer\",\n \"List\",\n \"ListItem\",\n \"If\",\n \"Else\",\n]);\n\nconst universalAttributeSpec = (name: string): AttributeSpec | undefined =>\n Object.hasOwn(UNIVERSAL_ATTRIBUTES, name)\n ? UNIVERSAL_ATTRIBUTES[name as keyof typeof UNIVERSAL_ATTRIBUTES]\n : undefined;\n\nconst BLOCK_CHILDREN: ReadonlyArray<SmlTag> = [\n \"Container\",\n \"Section\",\n \"Row\",\n \"Heading\",\n \"Text\",\n \"Button\",\n \"Image\",\n \"Divider\",\n \"Spacer\",\n \"List\",\n \"If\",\n];\n\nconst EMAIL_ENVELOPE_CHILDREN: ReadonlyArray<SmlTag> = [\"Subject\", \"Preview\"];\n\n/** Tags a content model admits as direct children (public alongside the spec tables). */\nexport const allowedChildren = (model: ContentModel): ReadonlyArray<SmlTag> => {\n switch (model) {\n case \"email\":\n return [...EMAIL_ENVELOPE_CHILDREN, ...BLOCK_CHILDREN];\n case \"block\":\n return BLOCK_CHILDREN;\n case \"columns\":\n return [\"Column\"];\n case \"list\":\n return [\"ListItem\"];\n case \"image\":\n return [\"Source\"];\n case \"if\":\n return [...BLOCK_CHILDREN, \"Else\"];\n case \"inline\":\n return [\"strong\", \"em\", \"a\"];\n case \"inline-nolink\":\n return [\"strong\", \"em\"];\n case \"whatsapp\":\n return [\"Header\", \"Body\", \"Footer\", \"Buttons\"];\n case \"buttons\":\n return [\"Button\"];\n case \"verbatim\":\n return [\"If\"];\n case \"verbatim-if\":\n return [\"If\", \"Else\"];\n case \"preview\":\n case \"void\":\n return [];\n }\n};\n\nconst isInlineModel = (model: ContentModel): boolean =>\n model === \"inline\" || model === \"inline-nolink\" || model === \"preview\";\n\nconst isVerbatimModel = (model: ContentModel): boolean =>\n model === \"verbatim\" || model === \"verbatim-if\";\n\n/** Models whose direct children may be text / interpolations. */\nconst admitsText = (model: ContentModel): boolean => isInlineModel(model) || isVerbatimModel(model);\n\nconst listTags = (tags: ReadonlyArray<SmlTag>): string => tags.map((tag) => `<${tag}>`).join(\", \");\n\n/** Wrapper tag suggested when a loose child isn't allowed directly under its model. */\nconst WRAPPER_TAG_BY_MODEL: Partial<Record<ContentModel, string>> = {\n list: \"ListItem\",\n whatsapp: \"Body\",\n buttons: \"Button\",\n};\n\ninterface MutableElement {\n kind: \"element\";\n id: string;\n tag: SmlTag;\n attributes: Array<Attribute>;\n children: Array<SmlNode>;\n span: { start: number; end: number };\n}\n\nexport const parseSml = (source: string): Result.Result<SmlDocument, SmlParseError> => {\n const lexer = createLexer(source);\n let diagnostic: Diagnostic | undefined;\n // Set when the root tag is read; every spec lookup goes through the\n // channel's own table from then on.\n let channel: Channel | null = null;\n let specs: Readonly<Partial<Record<SmlTag, TagSpec>>> = EMAIL_TAG_SPECS;\n\n const fail = (code: string, message: string, span: Span): null => {\n diagnostic ??= { code, message, span };\n return null;\n };\n\n const failToken = (token: Token): null => {\n const error = token.error ?? {\n code: \"unexpected-token\",\n message: `unexpected ${JSON.stringify(lexer.getText(token))}`,\n };\n return fail(error.code, error.message, { start: token.start, end: token.end });\n };\n\n const parseAttributeValue = (\n tag: SmlTag,\n name: string,\n spec: AttributeSpec,\n nameToken: Token,\n ): { value: AttributeValue; valueSpan: Span } | null => {\n const nameSpan: Span = { start: nameToken.start, end: nameToken.end };\n const equalsToken = lexer.scanTagToken();\n if (equalsToken.kind !== TokenKind.Equals) {\n return fail(\n \"missing-attribute-value\",\n `attribute \"${name}\" needs a value — write ${name}=${spec.type === \"number\" ? `{${spec.min ?? 1}}` : '\"...\"'}`,\n nameSpan,\n );\n }\n const valueToken = lexer.scanTagToken();\n if (valueToken.kind === TokenKind.Unknown) return failToken(valueToken);\n\n if (valueToken.kind === TokenKind.Identifier || valueToken.kind === TokenKind.Number) {\n const raw = lexer.getText(valueToken);\n const suggestion = spec.type === \"number\" ? `${name}={${raw}}` : `${name}=\"${raw}\"`;\n return fail(\"unquoted-attribute\", `attribute values must be quoted — write ${suggestion}`, {\n start: valueToken.start,\n end: valueToken.end,\n });\n }\n\n if (valueToken.kind === TokenKind.OpenBrace) {\n const numberToken = lexer.scanTagToken();\n if (numberToken.kind !== TokenKind.Number) {\n return fail(\n \"invalid-attribute-value\",\n `expected a number inside braces — write ${name}={16}`,\n { start: numberToken.start, end: numberToken.end },\n );\n }\n const closeToken = lexer.scanTagToken();\n if (closeToken.kind !== TokenKind.CloseBrace) {\n return fail(\n \"invalid-attribute-value\",\n `missing } after ${name}={${lexer.getText(numberToken)}`,\n {\n start: closeToken.start,\n end: closeToken.end,\n },\n );\n }\n if (spec.type !== \"number\") {\n return fail(\n \"invalid-attribute-value\",\n `\"${name}\" on <${tag}> is a string attribute — write ${name}=\"...\"`,\n { start: valueToken.start, end: closeToken.end },\n );\n }\n const value = Number.parseInt(lexer.getText(numberToken), 10);\n return {\n value: { kind: \"number\", value },\n valueSpan: { start: numberToken.start, end: numberToken.end },\n };\n }\n\n if (valueToken.kind !== TokenKind.String) {\n return fail(\n \"missing-attribute-value\",\n `attribute \"${name}\" needs a value — write ${name}=${spec.type === \"number\" ? \"{16}\" : '\"...\"'}`,\n { start: valueToken.start, end: valueToken.end },\n );\n }\n\n const raw = lexer.getStringValue(valueToken);\n const valueSpan: Span = { start: valueToken.start + 1, end: valueToken.end - 1 };\n if (spec.type === \"number\") {\n if (!/^\\d+$/.test(raw)) {\n return fail(\n \"invalid-attribute-value\",\n `\"${name}\" on <${tag}> must be a number — write ${name}={16}`,\n valueSpan,\n );\n }\n return { value: { kind: \"number\", value: Number.parseInt(raw, 10) }, valueSpan };\n }\n if (spec.values !== undefined && !spec.values.includes(raw)) {\n return fail(\n \"invalid-attribute-value\",\n `\"${name}\" on <${tag}> must be one of ${spec.values.map((value) => `\"${value}\"`).join(\", \")}`,\n valueSpan,\n );\n }\n return { value: { kind: \"string\", value: raw }, valueSpan };\n };\n\n const validateNumberBounds = (\n tag: SmlTag,\n name: string,\n spec: AttributeSpec,\n attribute: Attribute,\n ): boolean => {\n if (attribute.value.kind !== \"number\") return true;\n const { value } = attribute.value;\n if (spec.min !== undefined && value < spec.min) {\n fail(\n \"invalid-attribute-value\",\n `\"${name}\" on <${tag}> must be at least ${spec.min}`,\n attribute.valueSpan,\n );\n return false;\n }\n if (spec.max !== undefined && value > spec.max) {\n fail(\n \"invalid-attribute-value\",\n `\"${name}\" on <${tag}> must be at most ${spec.max}`,\n attribute.valueSpan,\n );\n return false;\n }\n return true;\n };\n\n /** Called with the `<` already consumed; `lessThanToken` is that token. */\n const parseElement = (lessThanToken: Token): MutableElement | null => {\n const tagToken = lexer.scanTagToken();\n if (tagToken.kind !== TokenKind.Identifier) {\n if (tagToken.kind === TokenKind.Unknown) return failToken(tagToken);\n return fail(\"unknown-tag\", \"expected a tag name after <\", {\n start: tagToken.start,\n end: tagToken.end,\n });\n }\n const tagName = lexer.getText(tagToken);\n if (channel === null) {\n // The root tag declares the channel and selects the tag table.\n if (!isRootTag(tagName)) {\n return fail(\n \"invalid-root\",\n `the document root must be ${ROOT_TAGS.map((root) => `<${root}>`).join(\", \")} — the root tag declares the channel`,\n { start: tagToken.start, end: tagToken.end },\n );\n }\n channel = CHANNEL_BY_ROOT_TAG[tagName];\n specs = CHANNEL_TAG_SPECS[channel];\n }\n const tag = SML_TAGS.find((candidate) => candidate === tagName);\n // The channel tables are `as const` (so the typed TSX components can derive\n // precise prop types from them); the parser reads them through the wider\n // TagSpec view.\n const spec: TagSpec | undefined = tag === undefined ? undefined : specs[tag];\n if (tag === undefined || spec === undefined) {\n const channelTags = Object.keys(specs) as ReadonlyArray<SmlTag>;\n const suggestion = channelTags.find(\n (candidate) => candidate.toLowerCase() === tagName.toLowerCase(),\n );\n const hint =\n suggestion !== undefined\n ? `did you mean <${suggestion}>?`\n : tag !== undefined\n ? `<${tag}> is not part of ${channel} documents; ${channel} tags are ${listTags(channelTags)}`\n : `${channel} tags are ${listTags(channelTags)}`;\n return fail(\"unknown-tag\", `unknown tag <${tagName}> — ${hint}`, {\n start: tagToken.start,\n end: tagToken.end,\n });\n }\n\n const element: MutableElement = {\n kind: \"element\",\n id: \"\",\n tag,\n attributes: [],\n children: [],\n span: { start: lessThanToken.start, end: lessThanToken.end },\n };\n\n // ── Attributes + open-tag close ──────────────────────────────────────────\n let selfClosing = false;\n for (;;) {\n const token = lexer.scanTagToken();\n if (token.kind === TokenKind.Identifier) {\n const name = lexer.getText(token);\n // Email block-level tags also accept the universal editor attributes\n // (label/hidden) on top of their own spec.\n const universal = channel === \"email\" && UNIVERSAL_ATTRIBUTE_TAGS.has(tag);\n const attributeSpec =\n spec.attributes[name] ?? (universal ? universalAttributeSpec(name) : undefined);\n if (attributeSpec === undefined) {\n const known = [\n ...Object.keys(spec.attributes),\n ...(universal ? Object.keys(UNIVERSAL_ATTRIBUTES) : []),\n ];\n const hint =\n known.length === 0\n ? `<${tag}> takes no attributes`\n : `<${tag}> accepts ${known.map((k) => `\"${k}\"`).join(\", \")}`;\n return fail(\"unknown-attribute\", `unknown attribute \"${name}\" on <${tag}> — ${hint}`, {\n start: token.start,\n end: token.end,\n });\n }\n if (element.attributes.some((attribute) => attribute.name === name)) {\n return fail(\n \"duplicate-attribute\",\n `duplicate attribute \"${name}\" on <${tag}> — remove one`,\n { start: token.start, end: token.end },\n );\n }\n const parsed = parseAttributeValue(tag, name, attributeSpec, token);\n if (parsed === null) return null;\n const attribute: Attribute = {\n name,\n value: parsed.value,\n span: { start: token.start, end: parsed.valueSpan.end + 1 },\n valueSpan: parsed.valueSpan,\n };\n if (!validateNumberBounds(tag, name, attributeSpec, attribute)) return null;\n element.attributes.push(attribute);\n continue;\n }\n if (token.kind === TokenKind.SlashGreaterThan) {\n selfClosing = true;\n element.span.end = token.end;\n break;\n }\n if (token.kind === TokenKind.GreaterThan) {\n element.span.end = token.end;\n break;\n }\n if (token.kind === TokenKind.EndOfFile) {\n return fail(\"unclosed-tag\", `unclosed <${tag}> — add </${tag}>`, {\n start: lessThanToken.start,\n end: tagToken.end,\n });\n }\n return failToken(token);\n }\n\n for (const [name, attributeSpec] of Object.entries(spec.attributes)) {\n if (attributeSpec.required === true && !element.attributes.some((a) => a.name === name)) {\n const example =\n attributeSpec.type === \"number\"\n ? `${name}={16}`\n : tag === \"If\"\n ? `test=\"plan == 'pro'\"`\n : `${name}=\"...\"`;\n return fail(\n \"missing-attribute\",\n `<${tag}> requires the \"${name}\" attribute — add ${example}`,\n { start: lessThanToken.start, end: element.span.end },\n );\n }\n }\n\n if (tag === \"If\") {\n const testIndex = element.attributes.findIndex((attribute) => attribute.name === \"test\");\n const testAttribute = element.attributes[testIndex]!;\n if (testAttribute.value.kind === \"string\") {\n const parsed = parsePredicate(testAttribute.value.value, testAttribute.valueSpan.start);\n if (Result.isFailure(parsed)) {\n const first = parsed.failure[0]!;\n return fail(first.code, first.message, first.span);\n }\n // Canonicalize the predicate at parse time (like inline whitespace):\n // the stored value is already what the printer emits, so parse ∘ print\n // stays a fixpoint and the compiler embeds it in markers verbatim.\n element.attributes[testIndex] = {\n ...testAttribute,\n value: { kind: \"string\", value: printPredicate(parsed.success) },\n };\n }\n }\n\n if (spec.content === \"void\" && !selfClosing) {\n return fail(\"void-needs-self-close\", `<${tag}> is self-closing — write <${tag} ... />`, {\n start: lessThanToken.start,\n end: element.span.end,\n });\n }\n if (selfClosing) {\n if (spec.content === \"whatsapp\") {\n return fail(\n \"whatsapp-missing-body\",\n \"<WhatsApp> requires a <Body> component — add <Body>...</Body>\",\n element.span,\n );\n }\n return element;\n }\n\n // ── Children per content model ───────────────────────────────────────────\n // Note: <a> appears only in the \"inline\" model (Text/Heading), so links can\n // never nest inside links or buttons — their model is \"inline-nolink\".\n const model = spec.content;\n for (;;) {\n const token = lexer.scanTextToken();\n if (token.kind === TokenKind.EndOfFile) {\n return fail(\"unclosed-tag\", `unclosed <${tag}> — add </${tag}>`, {\n start: lessThanToken.start,\n end: tagToken.end,\n });\n }\n if (token.kind === TokenKind.Unknown) return failToken(token);\n const looseChildMessage = (label: string) => {\n if (model === \"image\") {\n return `${label} is not allowed directly inside <${tag}> — <${tag}> may contain only ${listTags(allowedChildren(model))}`;\n }\n const wrapper = WRAPPER_TAG_BY_MODEL[model] ?? \"Text\";\n return `${label} is not allowed directly inside <${tag}> — wrap it in <${wrapper}>`;\n };\n // In a verbatim <If>, everything after <Else> would be dead content\n // outside both branches — text included (whitespace is content here).\n const afterElse =\n model === \"verbatim-if\" &&\n element.children.some((existing) => existing.kind === \"element\" && existing.tag === \"Else\");\n if (token.kind === TokenKind.Text) {\n const value = lexer.getTextValue(token);\n if (admitsText(model)) {\n if (afterElse) {\n return fail(\n \"else-not-last\",\n \"content after <Else> — move it inside <Else> or above it\",\n { start: token.start, end: token.end },\n );\n }\n element.children.push({\n kind: \"text\",\n id: \"\",\n value,\n span: { start: token.start, end: token.end },\n });\n } else if (value.trim().length > 0) {\n return fail(\"invalid-child\", looseChildMessage(\"text\"), {\n start: token.start,\n end: token.end,\n });\n }\n continue;\n }\n if (token.kind === TokenKind.Interpolation) {\n if (!admitsText(model)) {\n return fail(\"invalid-child\", looseChildMessage(`{{${token.name}}}`), {\n start: token.start,\n end: token.end,\n });\n }\n if (afterElse) {\n return fail(\"else-not-last\", \"content after <Else> — move it inside <Else> or above it\", {\n start: token.start,\n end: token.end,\n });\n }\n element.children.push({\n kind: \"interpolation\",\n id: \"\",\n name: token.name!,\n syntax: token.syntax!,\n span: { start: token.start, end: token.end },\n });\n continue;\n }\n if (token.kind === TokenKind.LessThanSlash) {\n const closeName = lexer.scanTagToken();\n if (closeName.kind !== TokenKind.Identifier || lexer.getText(closeName) !== tag) {\n const found =\n closeName.kind === TokenKind.Identifier ? `</${lexer.getText(closeName)}>` : \"</>\";\n return fail(\n \"mismatched-close-tag\",\n `expected </${tag}> but found ${found} — close <${tag}> first`,\n { start: token.start, end: closeName.end },\n );\n }\n const closeEnd = lexer.scanTagToken();\n if (closeEnd.kind !== TokenKind.GreaterThan) {\n return fail(\"mismatched-close-tag\", `expected > to finish </${tag}>`, {\n start: closeEnd.start,\n end: closeEnd.end,\n });\n }\n element.span.end = closeEnd.end;\n break;\n }\n // token.kind === TokenKind.LessThan — a child element.\n const allowed = allowedChildren(model);\n const child = parseElement(token);\n if (child === null) return null;\n if (!allowed.includes(child.tag)) {\n const message =\n child.tag === \"Else\"\n ? `<Else> is only valid as the last child of <If> — wrap this branch in <If>`\n : isInlineModel(model)\n ? child.tag === \"a\"\n ? `<a> is not allowed inside <${tag}> — links cannot nest inside links or buttons`\n : `<${child.tag}> is not allowed inside <${tag}> — only text, {{variables}}${allowed.length > 0 ? `, and ${listTags(allowed)}` : \"\"} may appear here`\n : `<${child.tag}> is not allowed inside <${tag}> — <${tag}> may contain only ${listTags(allowed)}`;\n return fail(\"invalid-child\", message, child.span);\n }\n if (model === \"email\" && EMAIL_ENVELOPE_CHILDREN.includes(child.tag)) {\n const existingSame = element.children.some(\n (existing) => existing.kind === \"element\" && existing.tag === child.tag,\n );\n if (existingSame) {\n return fail(\n `${child.tag.toLowerCase()}-duplicate`,\n `<Email> allows at most one <${child.tag}> — merge them`,\n child.span,\n );\n }\n const hasBodyBlock = element.children.some(\n (existing) =>\n existing.kind === \"element\" && !EMAIL_ENVELOPE_CHILDREN.includes(existing.tag),\n );\n if (hasBodyBlock) {\n return fail(\n `${child.tag.toLowerCase()}-position`,\n `<${child.tag}> must appear before the body blocks in <Email> — move it above the other blocks`,\n child.span,\n );\n }\n }\n if (\n (model === \"if\" || model === \"verbatim-if\") &&\n element.children.some((existing) => existing.kind === \"element\" && existing.tag === \"Else\")\n ) {\n return fail(\n child.tag === \"Else\" ? \"duplicate-else\" : \"else-not-last\",\n child.tag === \"Else\"\n ? \"only one <Else> is allowed inside <If> — merge the branches\"\n : `<Else> must be the last child of <If> — move <${child.tag}> above it`,\n child.span,\n );\n }\n if (model === \"whatsapp\") {\n // Meta's template anatomy is ordered and single-occurrence:\n // Header? Body Footer? Buttons?.\n const rank: Readonly<Partial<Record<SmlTag, number>>> = {\n Header: 0,\n Body: 1,\n Footer: 2,\n Buttons: 3,\n };\n const childRank = rank[child.tag]!;\n for (const existing of element.children) {\n if (existing.kind !== \"element\") continue;\n if (existing.tag === child.tag) {\n return fail(\n \"whatsapp-duplicate-component\",\n `<WhatsApp> allows at most one <${child.tag}> — merge them`,\n child.span,\n );\n }\n if (rank[existing.tag]! > childRank) {\n return fail(\n \"whatsapp-component-order\",\n `<${child.tag}> must come before <${existing.tag}> — WhatsApp components are Header?, Body, Footer?, Buttons? in that order`,\n child.span,\n );\n }\n }\n }\n if (model === \"image\") {\n const scheme = child.attributes.find((attribute) => attribute.name === \"scheme\");\n if (\n scheme?.value.kind === \"string\" &&\n element.children.some(\n (existing) =>\n existing.kind === \"element\" &&\n existing.tag === \"Source\" &&\n existing.attributes.some(\n (attribute) =>\n attribute.name === \"scheme\" &&\n attribute.value.kind === \"string\" &&\n attribute.value.value === scheme.value.value,\n ),\n )\n ) {\n return fail(\n \"duplicate-source\",\n `<Image> allows one <Source scheme=\"${scheme.value.value}\">; remove the duplicate`,\n child.span,\n );\n }\n }\n element.children.push(child);\n }\n\n if (model === \"whatsapp\") {\n const hasBody = element.children.some(\n (child) => child.kind === \"element\" && child.tag === \"Body\",\n );\n if (!hasBody) {\n return fail(\n \"whatsapp-missing-body\",\n \"<WhatsApp> requires a <Body> component — add <Body>...</Body>\",\n element.span,\n );\n }\n }\n\n if (isInlineModel(model)) normalizeInlineChildren(element);\n return element;\n };\n\n // ── Document ───────────────────────────────────────────────────────────────\n const skipBlankText = (): Token => {\n for (;;) {\n const token = lexer.scanTextToken();\n if (token.kind === TokenKind.Text && lexer.getTextValue(token).trim().length === 0) continue;\n return token;\n }\n };\n\n const first = skipBlankText();\n if (first.kind === TokenKind.Unknown) {\n failToken(first);\n } else if (first.kind !== TokenKind.LessThan) {\n fail(\n \"invalid-root\",\n `the document must start with a channel root element: ${ROOT_TAGS.map((root) => `<${root}>`).join(\", \")}`,\n { start: first.start, end: first.end },\n );\n }\n\n let root: MutableElement | null = null;\n if (diagnostic === undefined) {\n root = parseElement(first);\n }\n if (root !== null) {\n const trailing = skipBlankText();\n if (trailing.kind !== TokenKind.EndOfFile) {\n fail(\n \"multiple-roots\",\n `content after </${root.tag}> — a document has exactly one root element`,\n { start: trailing.start, end: trailing.end },\n );\n root = null;\n }\n }\n\n if (root === null || diagnostic !== undefined) {\n return Result.fail(\n new SmlParseError({\n diagnostics: [\n diagnostic ?? {\n code: \"parse-failed\",\n message: \"parse failed\",\n span: { start: 0, end: 0 },\n },\n ],\n }),\n );\n }\n\n assignNodeIds(root);\n return Result.succeed({ kind: \"document\", root: root as ElementNode });\n};\n\n/**\n * Canonical inline whitespace: collapse runs to a single space, trim at the\n * element boundary, drop text nodes that end up empty. Makes parse ∘ print a\n * fixpoint — the printer emits exactly these values.\n */\nconst normalizeInlineChildren = (element: MutableElement): void => {\n for (const child of element.children) {\n if (child.kind === \"text\") {\n (child as { value: string }).value = child.value.replace(/\\s+/g, \" \");\n }\n }\n const firstChild = element.children[0];\n if (firstChild?.kind === \"text\") {\n (firstChild as { value: string }).value = firstChild.value.replace(/^ /, \"\");\n }\n const lastChild = element.children[element.children.length - 1];\n if (lastChild?.kind === \"text\") {\n (lastChild as { value: string }).value = lastChild.value.replace(/ $/, \"\");\n }\n element.children = element.children.filter(\n (child) => child.kind !== \"text\" || child.value.length > 0,\n );\n};\n\n/** Stable ids in document order — identical source ⇒ identical ids. */\nconst assignNodeIds = (root: MutableElement): void => {\n let next = 0;\n const visit = (node: SmlNode): void => {\n (node as { id: string }).id = `n${next}`;\n next++;\n if (node.kind === \"element\") for (const child of node.children) visit(child);\n };\n visit(root as ElementNode);\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"predicate.js","names":[],"sources":["../src/predicate.ts"],"sourcesContent":["import { Result } from \"effect\";\n\nimport type { Diagnostic, Span } from \"./diagnostics\";\n\n// Closed predicate grammar for <If test=\"...\"> (design brief §7c):\n//\n// expr := or\n// or := and (\"or\" and)*\n// and := unary (\"and\" unary)*\n// unary := \"not\"? primary\n// primary := \"(\" expr \")\" | comparison | name\n// comparison := name op literal op ∈ { == != > >= < <= }\n// literal := 'single-quoted string' | number\n//\n// Bare name = present-and-non-empty. NO arithmetic, calls, methods, or paths.\n// This one module (parse / evaluate / print / collect) is shared by the\n// compiler (artifact markers), checks, send-time branch selection, and the\n// canvas — the semantics cannot fork.\n//\n// String literals have no escape syntax and may not contain `--`: predicates\n// are embedded verbatim in HTML comment markers, and `--` would terminate the\n// comment early.\n\nexport type ComparisonOp = \"==\" | \"!=\" | \">\" | \">=\" | \"<\" | \"<=\";\n\nexport type PredicateLiteral =\n | { readonly kind: \"string\"; readonly value: string }\n | { readonly kind: \"number\"; readonly value: number };\n\nexport type PredicateAst =\n | {\n readonly kind: \"or\";\n readonly left: PredicateAst;\n readonly right: PredicateAst;\n readonly span: Span;\n }\n | {\n readonly kind: \"and\";\n readonly left: PredicateAst;\n readonly right: PredicateAst;\n readonly span: Span;\n }\n | { readonly kind: \"not\"; readonly operand: PredicateAst; readonly span: Span }\n | {\n readonly kind: \"comparison\";\n readonly name: string;\n readonly op: ComparisonOp;\n readonly literal: PredicateLiteral;\n readonly span: Span;\n }\n | { readonly kind: \"name\"; readonly name: string; readonly span: Span };\n\n// ── Lexer ─────────────────────────────────────────────────────────────────────\n\nconst CHAR_TAB = 9;\nconst CHAR_LINE_FEED = 10;\nconst CHAR_CARRIAGE_RETURN = 13;\nconst CHAR_SPACE = 32;\nconst CHAR_BANG = 33;\nconst CHAR_SINGLE_QUOTE = 39;\nconst CHAR_OPEN_PAREN = 40;\nconst CHAR_CLOSE_PAREN = 41;\nconst CHAR_MINUS = 45;\nconst CHAR_DOT = 46;\nconst CHAR_LESS_THAN = 60;\nconst CHAR_EQUALS = 61;\nconst CHAR_GREATER_THAN = 62;\nconst CHAR_UNDERSCORE = 95;\nconst CHAR_0 = 48;\nconst CHAR_9 = 57;\nconst CHAR_UPPER_A = 65;\nconst CHAR_UPPER_Z = 90;\nconst CHAR_LOWER_A = 97;\nconst CHAR_LOWER_Z = 122;\n\nconst isWhitespace = (code: number): boolean =>\n code === CHAR_SPACE ||\n code === CHAR_TAB ||\n code === CHAR_LINE_FEED ||\n code === CHAR_CARRIAGE_RETURN;\n\nconst isDigit = (code: number): boolean => code >= CHAR_0 && code <= CHAR_9;\n\nconst isLetter = (code: number): boolean =>\n (code >= CHAR_UPPER_A && code <= CHAR_UPPER_Z) || (code >= CHAR_LOWER_A && code <= CHAR_LOWER_Z);\n\nconst isNameStart = (code: number): boolean => isLetter(code) || code === CHAR_UNDERSCORE;\n\nconst isNameChar = (code: number): boolean => isNameStart(code) || isDigit(code);\n\ntype PredicateTokenKind = \"name\" | \"number\" | \"string\" | \"op\" | \"lparen\" | \"rparen\" | \"eof\";\n\ninterface PredicateToken {\n readonly kind: PredicateTokenKind;\n /** Absolute span (baseOffset already applied). */\n readonly span: Span;\n /** Name text or operator text. */\n readonly text: string;\n /** String literal contents (without quotes). */\n readonly string?: string | undefined;\n /** Number literal value. */\n readonly number?: number | undefined;\n}\n\nconst KEYWORDS = [\"and\", \"or\", \"not\"] as const;\n\nconst isKeyword = (text: string): boolean => (KEYWORDS as ReadonlyArray<string>).includes(text);\n\ninterface LexFailure {\n readonly diagnostic: Diagnostic;\n}\n\nconst tokenize = (\n source: string,\n baseOffset: number,\n): Result.Result<ReadonlyArray<PredicateToken>, LexFailure> => {\n const tokens: Array<PredicateToken> = [];\n let index = 0;\n const length = source.length;\n\n const failAt = (code: string, message: string, start: number, end: number) =>\n Result.fail<LexFailure>({\n diagnostic: { code, message, span: { start: baseOffset + start, end: baseOffset + end } },\n });\n\n while (index < length) {\n const code = source.charCodeAt(index);\n if (isWhitespace(code)) {\n index++;\n continue;\n }\n const start = index;\n\n if (code === CHAR_OPEN_PAREN) {\n index++;\n tokens.push({\n kind: \"lparen\",\n text: \"(\",\n span: { start: baseOffset + start, end: baseOffset + index },\n });\n continue;\n }\n if (code === CHAR_CLOSE_PAREN) {\n index++;\n tokens.push({\n kind: \"rparen\",\n text: \")\",\n span: { start: baseOffset + start, end: baseOffset + index },\n });\n continue;\n }\n\n if (code === CHAR_EQUALS || code === CHAR_BANG) {\n if (source.charCodeAt(index + 1) === CHAR_EQUALS) {\n index += 2;\n tokens.push({\n kind: \"op\",\n text: code === CHAR_EQUALS ? \"==\" : \"!=\",\n span: { start: baseOffset + start, end: baseOffset + index },\n });\n continue;\n }\n const found = String.fromCharCode(code);\n const fix = code === CHAR_EQUALS ? \"write == to compare\" : \"write != to compare\";\n return failAt(\"predicate-invalid-operator\", `unexpected ${found} — ${fix}`, start, start + 1);\n }\n if (code === CHAR_GREATER_THAN || code === CHAR_LESS_THAN) {\n index++;\n if (source.charCodeAt(index) === CHAR_EQUALS) index++;\n tokens.push({\n kind: \"op\",\n text: source.slice(start, index),\n span: { start: baseOffset + start, end: baseOffset + index },\n });\n continue;\n }\n\n if (code === CHAR_SINGLE_QUOTE) {\n index++;\n const contentStart = index;\n while (index < length && source.charCodeAt(index) !== CHAR_SINGLE_QUOTE) index++;\n if (index >= length) {\n return failAt(\n \"predicate-unterminated-string\",\n \"unterminated string — add a closing '\",\n start,\n length,\n );\n }\n const value = source.slice(contentStart, index);\n index++;\n if (value.includes(\"--\")) {\n return failAt(\n \"predicate-string-double-dash\",\n \"string literals cannot contain -- (predicates are embedded in HTML comment markers)\",\n start,\n index,\n );\n }\n tokens.push({\n kind: \"string\",\n text: source.slice(start, index),\n string: value,\n span: { start: baseOffset + start, end: baseOffset + index },\n });\n continue;\n }\n\n if (isDigit(code) || (code === CHAR_MINUS && isDigit(source.charCodeAt(index + 1)))) {\n index++;\n while (index < length && isDigit(source.charCodeAt(index))) index++;\n if (source.charCodeAt(index) === CHAR_DOT && isDigit(source.charCodeAt(index + 1))) {\n index++;\n while (index < length && isDigit(source.charCodeAt(index))) index++;\n }\n tokens.push({\n kind: \"number\",\n text: source.slice(start, index),\n number: Number.parseFloat(source.slice(start, index)),\n span: { start: baseOffset + start, end: baseOffset + index },\n });\n continue;\n }\n\n if (isNameStart(code)) {\n index++;\n while (index < length && isNameChar(source.charCodeAt(index))) index++;\n if (source.charCodeAt(index) === CHAR_DOT) {\n return failAt(\n \"predicate-path-not-supported\",\n `unexpected . after \"${source.slice(start, index)}\" — predicates use flat variable names, not paths`,\n start,\n index + 1,\n );\n }\n tokens.push({\n kind: \"name\",\n text: source.slice(start, index),\n span: { start: baseOffset + start, end: baseOffset + index },\n });\n continue;\n }\n\n if (code === CHAR_DOT) {\n return failAt(\n \"predicate-path-not-supported\",\n \"unexpected . — predicates use flat variable names, not paths\",\n start,\n start + 1,\n );\n }\n return failAt(\n \"predicate-unexpected-character\",\n `unexpected character ${JSON.stringify(String.fromCharCode(code))} in predicate`,\n start,\n start + 1,\n );\n }\n\n tokens.push({\n kind: \"eof\",\n text: \"\",\n span: { start: baseOffset + length, end: baseOffset + length },\n });\n return Result.succeed(tokens);\n};\n\n// ── Parser ────────────────────────────────────────────────────────────────────\n\nconst COMPARISON_OPS: ReadonlyArray<ComparisonOp> = [\"==\", \"!=\", \">\", \">=\", \"<\", \"<=\"];\n\n/**\n * Parses a predicate. `baseOffset` is the absolute source offset of the\n * predicate's first character (an <If> test attribute passes valueSpan.start),\n * so diagnostic and node spans point into the containing document.\n */\nexport const parsePredicate = (\n source: string,\n baseOffset = 0,\n): Result.Result<PredicateAst, ReadonlyArray<Diagnostic>> => {\n const lexed = tokenize(source, baseOffset);\n if (Result.isFailure(lexed)) return Result.fail([lexed.failure.diagnostic]);\n const tokens = lexed.success;\n let index = 0;\n let diagnostic: Diagnostic | undefined;\n\n const fail = (code: string, message: string, span: Span): null => {\n diagnostic ??= { code, message, span };\n return null;\n };\n\n const peek = (): PredicateToken => tokens[index]!;\n const advance = (): PredicateToken => tokens[index++]!;\n\n const isKeywordToken = (token: PredicateToken, keyword: string): boolean =>\n token.kind === \"name\" && token.text === keyword;\n\n const parsePrimary = (): PredicateAst | null => {\n const token = advance();\n if (token.kind === \"lparen\") {\n const inner = parseOr();\n if (inner === null) return null;\n const close = advance();\n if (close.kind !== \"rparen\") {\n return fail(\"predicate-missing-paren\", \"missing ) — close the group\", close.span);\n }\n return inner;\n }\n if (token.kind === \"name\") {\n if (isKeyword(token.text)) {\n return fail(\n \"predicate-unexpected-token\",\n `unexpected \"${token.text}\" — expected a variable name, comparison, or ( group )`,\n token.span,\n );\n }\n const opToken = peek();\n if (opToken.kind !== \"op\") {\n return { kind: \"name\", name: token.text, span: token.span };\n }\n advance();\n const op = COMPARISON_OPS.find((candidate) => candidate === opToken.text);\n if (op === undefined) {\n return fail(\n \"predicate-invalid-operator\",\n `unknown operator ${opToken.text} — use ==, !=, >, >=, <, or <=`,\n opToken.span,\n );\n }\n const literalToken = advance();\n if (literalToken.kind === \"string\") {\n return {\n kind: \"comparison\",\n name: token.text,\n op,\n literal: { kind: \"string\", value: literalToken.string! },\n span: { start: token.span.start, end: literalToken.span.end },\n };\n }\n if (literalToken.kind === \"number\") {\n return {\n kind: \"comparison\",\n name: token.text,\n op,\n literal: { kind: \"number\", value: literalToken.number! },\n span: { start: token.span.start, end: literalToken.span.end },\n };\n }\n return fail(\n \"predicate-expected-literal\",\n `expected a literal after ${op} — write ${token.text} ${op} 'value' or ${token.text} ${op} 5`,\n literalToken.span,\n );\n }\n if (token.kind === \"eof\") {\n return fail(\n \"predicate-unexpected-end\",\n \"unexpected end of predicate — expected a variable name, comparison, or ( group )\",\n token.span,\n );\n }\n return fail(\n \"predicate-unexpected-token\",\n `unexpected ${token.text} — comparisons put the variable first: name == 'value'`,\n token.span,\n );\n };\n\n const parseUnary = (): PredicateAst | null => {\n const token = peek();\n if (isKeywordToken(token, \"not\")) {\n advance();\n const operand = parsePrimary();\n if (operand === null) return null;\n return {\n kind: \"not\",\n operand,\n span: { start: token.span.start, end: operand.span.end },\n };\n }\n return parsePrimary();\n };\n\n const parseAnd = (): PredicateAst | null => {\n let left = parseUnary();\n while (left !== null && isKeywordToken(peek(), \"and\")) {\n advance();\n const right = parseUnary();\n if (right === null) return null;\n left = {\n kind: \"and\",\n left,\n right,\n span: { start: left.span.start, end: right.span.end },\n };\n }\n return left;\n };\n\n const parseOr = (): PredicateAst | null => {\n let left = parseAnd();\n while (left !== null && isKeywordToken(peek(), \"or\")) {\n advance();\n const right = parseAnd();\n if (right === null) return null;\n left = {\n kind: \"or\",\n left,\n right,\n span: { start: left.span.start, end: right.span.end },\n };\n }\n return left;\n };\n\n if (source.trim().length === 0) {\n return Result.fail([\n {\n code: \"predicate-empty\",\n message: \"empty predicate — write e.g. plan == 'pro'\",\n span: { start: baseOffset, end: baseOffset + source.length },\n },\n ]);\n }\n\n const ast = parseOr();\n if (ast !== null && diagnostic === undefined) {\n const trailing = peek();\n if (trailing.kind !== \"eof\") {\n fail(\n \"predicate-trailing-input\",\n `unexpected ${JSON.stringify(trailing.text)} after the predicate — join clauses with \"and\" or \"or\"`,\n trailing.span,\n );\n }\n }\n if (ast === null || diagnostic !== undefined) {\n return Result.fail([\n diagnostic ?? {\n code: \"predicate-parse-failed\",\n message: \"predicate parse failed\",\n span: { start: baseOffset, end: baseOffset + source.length },\n },\n ]);\n }\n return Result.succeed(ast);\n};\n\n// ── Evaluation ────────────────────────────────────────────────────────────────\n\nconst toNumber = (value: unknown): number => {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\" && value.trim() !== \"\") return Number(value);\n return Number.NaN;\n};\n\nconst compareNumbers = (op: ComparisonOp, left: number, right: number): boolean => {\n switch (op) {\n case \"==\":\n return left === right;\n case \"!=\":\n return left !== right;\n case \">\":\n return left > right;\n case \">=\":\n return left >= right;\n case \"<\":\n return left < right;\n case \"<=\":\n return left <= right;\n }\n};\n\nconst evaluateComparison = (\n node: Extract<PredicateAst, { kind: \"comparison\" }>,\n variables: Record<string, unknown>,\n): boolean => {\n const value = variables[node.name];\n // Missing variable ⇒ comparison is false — for != too: absence is not a\n // value that differs, it is no value at all (brief §7c).\n if (value === undefined || value === null) return false;\n if (node.literal.kind === \"number\") {\n const left = toNumber(value);\n if (Number.isNaN(left)) return false;\n return compareNumbers(node.op, left, node.literal.value);\n }\n if (node.op === \"==\" || node.op === \"!=\") {\n if (typeof value !== \"string\") return false;\n return node.op === \"==\" ? value === node.literal.value : value !== node.literal.value;\n }\n // Ordering against a string literal: numeric ops coerce numbers; a\n // non-numeric side is a type mismatch ⇒ false.\n const left = toNumber(value);\n const right = toNumber(node.literal.value);\n if (Number.isNaN(left) || Number.isNaN(right)) return false;\n return compareNumbers(node.op, left, right);\n};\n\nexport const evaluatePredicate = (\n ast: PredicateAst,\n variables: Record<string, unknown>,\n): boolean => {\n switch (ast.kind) {\n case \"or\":\n return evaluatePredicate(ast.left, variables) || evaluatePredicate(ast.right, variables);\n case \"and\":\n return evaluatePredicate(ast.left, variables) && evaluatePredicate(ast.right, variables);\n case \"not\":\n return !evaluatePredicate(ast.operand, variables);\n case \"comparison\":\n return evaluateComparison(ast, variables);\n case \"name\": {\n const value = variables[ast.name];\n return value !== undefined && value !== null && value !== \"\";\n }\n }\n};\n\n// ── Printing ──────────────────────────────────────────────────────────────────\n\nconst precedence = (node: PredicateAst): number => {\n switch (node.kind) {\n case \"or\":\n return 1;\n case \"and\":\n return 2;\n case \"not\":\n return 3;\n case \"comparison\":\n case \"name\":\n return 4;\n }\n};\n\nconst printLiteral = (literal: PredicateLiteral): string =>\n literal.kind === \"string\" ? `'${literal.value}'` : String(literal.value);\n\nconst printNode = (node: PredicateAst, minPrecedence: number): string => {\n const wrap = (text: string): string => (precedence(node) < minPrecedence ? `(${text})` : text);\n switch (node.kind) {\n case \"or\":\n return wrap(`${printNode(node.left, 1)} or ${printNode(node.right, 1)}`);\n case \"and\":\n return wrap(`${printNode(node.left, 2)} and ${printNode(node.right, 2)}`);\n case \"not\":\n // The grammar allows \"not\" only before a primary — parenthesize anything\n // looser so print ∘ parse is a fixpoint.\n return wrap(`not ${printNode(node.operand, 4)}`);\n case \"comparison\":\n return wrap(`${node.name} ${node.op} ${printLiteral(node.literal)}`);\n case \"name\":\n return wrap(node.name);\n }\n};\n\n/** Canonical form: single space around operators/keywords, minimal parens. */\nexport const printPredicate = (ast: PredicateAst): string => printNode(ast, 0);\n\n// ── Variable collection ───────────────────────────────────────────────────────\n\nexport const collectPredicateVariables = (ast: PredicateAst): Set<string> => {\n const names = new Set<string>();\n const visit = (node: PredicateAst): void => {\n switch (node.kind) {\n case \"or\":\n case \"and\":\n visit(node.left);\n visit(node.right);\n return;\n case \"not\":\n visit(node.operand);\n return;\n case \"comparison\":\n case \"name\":\n names.add(node.name);\n return;\n }\n };\n visit(ast);\n return names;\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"category-mapping.js","names":[],"sources":["../../presets/category-mapping.ts"],"sourcesContent":["import type { PresetCategory } from \"./catalog\";\nimport type { TemplateCollectionId } from \"./public-gallery\";\n\n// One reconciliation point for the category vocabularies that meet when SML Email\n// Lab corpus is promoted into the preset registry and surfaced in the public\n// gallery. The preset catalog `category` (catalog.ts) is the canonical axis: the\n// lab's classification folds into it, and it binds 1:1 to the public gallery's\n// `category`-family collection.\n//\n// The platform's generic message-template categories are deliberately NOT\n// merged here. No preset flow writes them. Preset category stays the corpus\n// axis; the two vocabularies are distinct by design, not by oversight.\n\n// ── SML Email Lab taxonomy (external repo, mirrored here) ────────────────────\n// The 8 corpus dimensions the lab classifies candidates by.\nexport const LAB_DIMENSIONS = [\n \"newsletter\",\n \"receipt\",\n \"security\",\n \"product-update\",\n \"onboarding\",\n \"promotion\",\n \"shipping\",\n \"social-notification\",\n] as const;\nexport type LabDimension = (typeof LAB_DIMENSIONS)[number];\n\n// The full 15-kind classification: the 8 dimensions plus finer/utility kinds the\n// lab emits. `unknown` is the lab's fallback bucket for unclassified candidates.\nexport const LAB_KINDS = [\n ...LAB_DIMENSIONS,\n \"digest\",\n \"invoice\",\n \"account\",\n \"event\",\n \"statement\",\n \"developer-notification\",\n \"unknown\",\n] as const;\nexport type LabKind = (typeof LAB_KINDS)[number];\n\n// ── lab kind → preset category ──────────────────────────────────────────────\n// Total over LAB_KINDS. `unknown` falls to `transactional`: the most\n// conservative bucket (no marketing/list semantics), safe to re-classify later.\nexport const labKindToPresetCategory: Record<LabKind, PresetCategory> = {\n newsletter: \"digest\",\n receipt: \"transactional\",\n security: \"transactional\",\n \"product-update\": \"marketing\",\n onboarding: \"lifecycle\",\n promotion: \"marketing\",\n shipping: \"transactional\",\n \"social-notification\": \"transactional\",\n digest: \"digest\",\n invoice: \"transactional\",\n account: \"transactional\",\n event: \"marketing\",\n statement: \"transactional\",\n \"developer-notification\": \"transactional\",\n unknown: \"transactional\",\n};\n\n// ── preset category → public-gallery `category`-family collection ────────────\n// A bijection onto the four `category:*` collections in public-gallery.ts. The\n// other collection families (industry/stage/style) are per-template secondary\n// axes and are assigned on each PublicTemplate, not derivable from category.\nexport const presetCategoryToCollection: Record<PresetCategory, TemplateCollectionId> = {\n transactional: \"category:transactional\",\n digest: \"category:digest-editorial\",\n marketing: \"category:marketing\",\n lifecycle: \"category:lifecycle\",\n};\n\n// Convenience: fold a lab kind straight to its public `category`-family collection.\nexport const labKindToCollection = (kind: LabKind): TemplateCollectionId =>\n presetCategoryToCollection[labKindToPresetCategory[kind]];\n"],"mappings":";AAeA,MAAa,iBAAiB;CAC5B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAKA,MAAa,YAAY;CACvB,GAAG;CACH;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAMA,MAAa,0BAA2D;CACtE,YAAY;CACZ,SAAS;CACT,UAAU;CACV,kBAAkB;CAClB,YAAY;CACZ,WAAW;CACX,UAAU;CACV,uBAAuB;CACvB,QAAQ;CACR,SAAS;CACT,SAAS;CACT,OAAO;CACP,WAAW;CACX,0BAA0B;CAC1B,SAAS;AACX;AAMA,MAAa,6BAA2E;CACtF,eAAe;CACf,QAAQ;CACR,WAAW;CACX,WAAW;AACb;AAGA,MAAa,uBAAuB,SAClC,2BAA2B,wBAAwB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"public-gallery.js","names":[],"sources":["../../presets/public-gallery.ts"],"sourcesContent":["import type { EmailPresetId } from \"./catalog\";\n\nexport const templateUseCases = [\n \"account-access\",\n \"commerce\",\n \"product-communication\",\n \"lifecycle\",\n \"publishing-campaigns\",\n] as const;\n\nexport const templateCollectionFamilies = [\"category\", \"industry\", \"stage\", \"style\"] as const;\n\nexport type TemplateUseCase = (typeof templateUseCases)[number];\nexport type TemplateCollectionFamily = (typeof templateCollectionFamilies)[number];\n\nexport interface PublicTemplate {\n readonly presetId: EmailPresetId;\n readonly slug: string;\n readonly path: `/templates/${string}`;\n readonly useCase: TemplateUseCase;\n readonly collections: ReadonlyArray<TemplateCollectionId>;\n readonly relatedTemplates: ReadonlyArray<EmailPresetId>;\n}\n\nexport interface TemplateCollection {\n readonly id: TemplateCollectionId;\n readonly family: TemplateCollectionFamily;\n readonly slug: string;\n readonly path: `/templates/${TemplateCollectionFamily}/${string}`;\n readonly templates: ReadonlyArray<EmailPresetId>;\n readonly relatedCollections: ReadonlyArray<TemplateCollectionId>;\n}\n\nexport type TemplateCollectionId =\n | \"category:transactional\"\n | \"category:lifecycle\"\n | \"category:marketing\"\n | \"category:digest-editorial\"\n | \"industry:saas\"\n | \"industry:ecommerce\"\n | \"industry:developer-tools\"\n | \"industry:newsletters-media\"\n | \"stage:acquisition\"\n | \"stage:activation\"\n | \"stage:engagement\"\n | \"stage:transaction\"\n | \"stage:retention\"\n | \"stage:recovery\"\n | \"style:minimal\"\n | \"style:branded\"\n | \"style:editorial\"\n | \"style:data-heavy\"\n | \"style:single-cta\";\n\nconst template = <const T extends Omit<PublicTemplate, \"path\">>(value: T) =>\n ({ ...value, path: `/templates/${value.slug}` }) as T & Pick<PublicTemplate, \"path\">;\n\nexport const publicTemplates = [\n template({\n presetId: \"welcome\",\n slug: \"welcome-email\",\n useCase: \"lifecycle\",\n collections: [\n \"category:transactional\",\n \"industry:saas\",\n \"stage:activation\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"email-verification\", \"onboarding-step\"],\n }),\n template({\n presetId: \"email-verification\",\n slug: \"email-verification\",\n useCase: \"account-access\",\n collections: [\n \"category:transactional\",\n \"industry:saas\",\n \"industry:developer-tools\",\n \"stage:activation\",\n \"style:minimal\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"otp\", \"password-reset\"],\n }),\n template({\n presetId: \"otp\",\n slug: \"one-time-passcode\",\n useCase: \"account-access\",\n collections: [\n \"category:transactional\",\n \"industry:saas\",\n \"industry:developer-tools\",\n \"stage:activation\",\n \"style:minimal\",\n ],\n relatedTemplates: [\"email-verification\", \"password-reset\"],\n }),\n template({\n presetId: \"password-reset\",\n slug: \"password-reset\",\n useCase: \"account-access\",\n collections: [\n \"category:transactional\",\n \"industry:saas\",\n \"industry:developer-tools\",\n \"stage:recovery\",\n \"style:minimal\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"otp\", \"email-verification\"],\n }),\n template({\n presetId: \"receipt\",\n slug: \"receipt\",\n useCase: \"commerce\",\n collections: [\n \"category:transactional\",\n \"industry:ecommerce\",\n \"stage:transaction\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"payment-failed\", \"shipping-update\"],\n }),\n template({\n presetId: \"payment-failed\",\n slug: \"payment-failed\",\n useCase: \"commerce\",\n collections: [\n \"category:transactional\",\n \"industry:ecommerce\",\n \"industry:saas\",\n \"stage:recovery\",\n \"style:data-heavy\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"receipt\", \"trial-ending\"],\n }),\n template({\n presetId: \"shipping-update\",\n slug: \"shipping-update\",\n useCase: \"commerce\",\n collections: [\n \"category:transactional\",\n \"industry:ecommerce\",\n \"stage:transaction\",\n \"style:data-heavy\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"receipt\", \"notification\"],\n }),\n template({\n presetId: \"notification\",\n slug: \"activity-notification\",\n useCase: \"product-communication\",\n collections: [\n \"category:transactional\",\n \"industry:saas\",\n \"industry:developer-tools\",\n \"stage:engagement\",\n \"style:minimal\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"team-invite\", \"product-digest\"],\n }),\n template({\n presetId: \"team-invite\",\n slug: \"team-invite\",\n useCase: \"product-communication\",\n collections: [\n \"category:transactional\",\n \"industry:saas\",\n \"industry:developer-tools\",\n \"stage:acquisition\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"welcome\", \"notification\"],\n }),\n template({\n presetId: \"product-digest\",\n slug: \"product-digest\",\n useCase: \"product-communication\",\n collections: [\n \"category:digest-editorial\",\n \"industry:saas\",\n \"industry:developer-tools\",\n \"stage:engagement\",\n \"style:editorial\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"notification\", \"newsletter\"],\n }),\n template({\n presetId: \"newsletter\",\n slug: \"newsletter\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:digest-editorial\",\n \"industry:newsletters-media\",\n \"stage:engagement\",\n \"style:editorial\",\n \"style:branded\",\n ],\n relatedTemplates: [\"product-digest\", \"announcement\"],\n }),\n template({\n presetId: \"announcement\",\n slug: \"feature-announcement\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:saas\",\n \"industry:developer-tools\",\n \"stage:engagement\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"newsletter\", \"event-invite\"],\n }),\n template({\n presetId: \"promo\",\n slug: \"promotion\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:acquisition\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"announcement\", \"event-invite\"],\n }),\n template({\n presetId: \"event-invite\",\n slug: \"event-invite\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:saas\",\n \"stage:acquisition\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"announcement\", \"promo\"],\n }),\n template({\n presetId: \"membership-promo\",\n slug: \"membership-promotion\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:retention\",\n \"style:branded\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"promo\", \"announcement\"],\n }),\n template({\n presetId: \"onboarding-step\",\n slug: \"onboarding-step\",\n useCase: \"lifecycle\",\n collections: [\n \"category:lifecycle\",\n \"industry:saas\",\n \"industry:developer-tools\",\n \"stage:activation\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"welcome\", \"trial-ending\"],\n }),\n template({\n presetId: \"trial-ending\",\n slug: \"trial-ending\",\n useCase: \"lifecycle\",\n collections: [\n \"category:lifecycle\",\n \"industry:saas\",\n \"stage:retention\",\n \"style:data-heavy\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"onboarding-step\", \"win-back\"],\n }),\n template({\n presetId: \"win-back\",\n slug: \"win-back\",\n useCase: \"lifecycle\",\n collections: [\n \"category:lifecycle\",\n \"industry:saas\",\n \"stage:recovery\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"trial-ending\", \"feedback-request\"],\n }),\n template({\n presetId: \"feedback-request\",\n slug: \"feedback-request\",\n useCase: \"lifecycle\",\n collections: [\n \"category:lifecycle\",\n \"industry:saas\",\n \"stage:retention\",\n \"style:minimal\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"win-back\", \"onboarding-step\"],\n }),\n template({\n presetId: \"editorial-newsletter\",\n slug: \"editorial-newsletter\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:digest-editorial\",\n \"industry:newsletters-media\",\n \"stage:engagement\",\n \"style:editorial\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"newsletter\", \"product-digest\"],\n }),\n template({\n presetId: \"member-referral-offer\",\n slug: \"member-referral-offer\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:acquisition\",\n \"style:branded\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"promo\", \"membership-promo\"],\n }),\n template({\n presetId: \"catalog-sale-newsletter\",\n slug: \"catalog-sale-newsletter\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:newsletters-media\",\n \"stage:engagement\",\n \"style:editorial\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"announcement\", \"promo\"],\n }),\n template({\n presetId: \"limited-time-savings\",\n slug: \"limited-time-savings\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:acquisition\",\n \"style:minimal\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"promo\", \"membership-promo\"],\n }),\n template({\n presetId: \"rewards-milestone\",\n slug: \"rewards-milestone\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:engagement\",\n \"style:branded\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"membership-promo\", \"promo\"],\n }),\n template({\n presetId: \"order-delivered\",\n slug: \"order-delivered\",\n useCase: \"commerce\",\n collections: [\n \"category:transactional\",\n \"industry:ecommerce\",\n \"stage:transaction\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"shipping-update\", \"receipt\"],\n }),\n template({\n presetId: \"travel-rewards-promo\",\n slug: \"travel-rewards-promo\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:retention\",\n \"style:branded\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"membership-promo\", \"promo\"],\n }),\n template({\n presetId: \"events-roundup\",\n slug: \"events-roundup\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:digest-editorial\",\n \"industry:newsletters-media\",\n \"stage:engagement\",\n \"style:editorial\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"newsletter\", \"event-invite\"],\n }),\n template({\n presetId: \"product-onboarding-tour\",\n slug: \"product-onboarding-tour\",\n useCase: \"lifecycle\",\n collections: [\n \"category:lifecycle\",\n \"industry:saas\",\n \"stage:activation\",\n \"style:branded\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"onboarding-step\", \"welcome\"],\n }),\n template({\n presetId: \"product-updates-roundup\",\n slug: \"product-updates-roundup\",\n useCase: \"product-communication\",\n collections: [\n \"category:marketing\",\n \"industry:developer-tools\",\n \"stage:engagement\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"announcement\", \"product-digest\"],\n }),\n template({\n presetId: \"flash-getaway-offer\",\n slug: \"flash-getaway-offer\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:acquisition\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"promo\", \"membership-promo\"],\n }),\n template({\n presetId: \"membership-benefits-promo\",\n slug: \"membership-benefits-promo\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:retention\",\n \"style:branded\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"membership-promo\", \"promo\"],\n }),\n template({\n presetId: \"industry-news-digest\",\n slug: \"industry-news-digest\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:digest-editorial\",\n \"industry:newsletters-media\",\n \"stage:engagement\",\n \"style:editorial\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"newsletter\", \"product-digest\"],\n }),\n template({\n presetId: \"weekly-deals-circular\",\n slug: \"weekly-deals-circular\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:engagement\",\n \"style:branded\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"promo\", \"membership-promo\"],\n }),\n template({\n presetId: \"data-sharing-review\",\n slug: \"data-sharing-review\",\n useCase: \"product-communication\",\n collections: [\n \"category:transactional\",\n \"industry:saas\",\n \"stage:engagement\",\n \"style:minimal\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"notification\", \"team-invite\"],\n }),\n template({\n presetId: \"seasonal-sale-flyer\",\n slug: \"seasonal-sale-flyer\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:acquisition\",\n \"style:branded\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"promo\", \"announcement\"],\n }),\n template({\n presetId: \"setup-checklist\",\n slug: \"setup-checklist\",\n useCase: \"lifecycle\",\n collections: [\n \"category:lifecycle\",\n \"industry:saas\",\n \"stage:activation\",\n \"style:minimal\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"welcome\", \"onboarding-step\"],\n }),\n template({\n presetId: \"feature-release-roundup\",\n slug: \"feature-release-roundup\",\n useCase: \"product-communication\",\n collections: [\n \"category:marketing\",\n \"industry:developer-tools\",\n \"stage:engagement\",\n \"style:branded\",\n \"style:data-heavy\",\n ],\n relatedTemplates: [\"announcement\", \"product-digest\"],\n }),\n template({\n presetId: \"season-clearance-promo\",\n slug: \"season-clearance-promo\",\n useCase: \"publishing-campaigns\",\n collections: [\n \"category:marketing\",\n \"industry:ecommerce\",\n \"stage:acquisition\",\n \"style:branded\",\n \"style:single-cta\",\n ],\n relatedTemplates: [\"promo\", \"announcement\"],\n }),\n] as const satisfies ReadonlyArray<PublicTemplate>;\n\nconst members = (id: TemplateCollectionId) =>\n publicTemplates\n .filter((item) => item.collections.some((collectionId) => collectionId === id))\n .map((item) => item.presetId);\nconst collection = <const I extends TemplateCollectionId>(\n id: I,\n relatedCollections: ReadonlyArray<TemplateCollectionId> = [],\n): TemplateCollection => {\n const [family, slug] = id.split(\":\") as [TemplateCollectionFamily, string];\n return {\n id,\n family,\n slug,\n path: `/templates/${family}/${slug}`,\n templates: members(id),\n relatedCollections,\n };\n};\n\nexport const templateCollections = [\n collection(\"category:transactional\", [\"stage:transaction\", \"style:minimal\"]),\n collection(\"category:lifecycle\", [\"stage:activation\", \"stage:retention\"]),\n collection(\"category:marketing\", [\"stage:acquisition\", \"style:branded\"]),\n collection(\"category:digest-editorial\", [\"style:editorial\", \"industry:newsletters-media\"]),\n collection(\"industry:saas\", [\"industry:developer-tools\", \"category:lifecycle\"]),\n collection(\"industry:ecommerce\", [\"category:transactional\", \"stage:transaction\"]),\n collection(\"industry:developer-tools\", [\"industry:saas\", \"style:minimal\"]),\n collection(\"industry:newsletters-media\", [\"category:digest-editorial\", \"style:editorial\"]),\n collection(\"stage:acquisition\", [\"category:marketing\", \"stage:activation\"]),\n collection(\"stage:activation\", [\"category:lifecycle\", \"style:single-cta\"]),\n collection(\"stage:engagement\", [\"category:digest-editorial\", \"style:editorial\"]),\n collection(\"stage:transaction\", [\"category:transactional\", \"industry:ecommerce\"]),\n collection(\"stage:retention\", [\"category:lifecycle\", \"stage:recovery\"]),\n collection(\"stage:recovery\", [\"category:lifecycle\", \"style:minimal\"]),\n collection(\"style:minimal\", [\"category:transactional\", \"industry:developer-tools\"]),\n collection(\"style:branded\", [\"category:marketing\", \"style:single-cta\"]),\n collection(\"style:editorial\", [\"category:digest-editorial\", \"industry:newsletters-media\"]),\n collection(\"style:data-heavy\", [\"stage:transaction\", \"category:digest-editorial\"]),\n collection(\"style:single-cta\", [\"stage:activation\", \"category:marketing\"]),\n] as const satisfies ReadonlyArray<TemplateCollection>;\n\nexport const publicTemplateGallery = {\n hubPath: \"/templates\",\n useCases: templateUseCases,\n templates: publicTemplates,\n collections: templateCollections,\n} as const;\n"],"mappings":";AAEA,MAAa,mBAAmB;CAC9B;CACA;CACA;CACA;CACA;AACF;AAEA,MAAa,6BAA6B;CAAC;CAAY;CAAY;CAAS;AAAO;AA4CnF,MAAM,YAA0D,WAC7D;CAAE,GAAG;CAAO,MAAM,cAAc,MAAM;AAAO;AAEhD,MAAa,kBAAkB;CAC7B,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,sBAAsB,iBAAiB;CAC5D,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,OAAO,gBAAgB;CAC5C,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,sBAAsB,gBAAgB;CAC3D,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,OAAO,oBAAoB;CAChD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,kBAAkB,iBAAiB;CACxD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,WAAW,cAAc;CAC9C,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,WAAW,cAAc;CAC9C,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,eAAe,gBAAgB;CACpD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,WAAW,cAAc;CAC9C,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,gBAAgB,YAAY;CACjD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,kBAAkB,cAAc;CACrD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,cAAc,cAAc;CACjD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,gBAAgB,cAAc;CACnD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,gBAAgB,OAAO;CAC5C,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,SAAS,cAAc;CAC5C,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,WAAW,cAAc;CAC9C,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,mBAAmB,UAAU;CAClD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,gBAAgB,kBAAkB;CACvD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,YAAY,iBAAiB;CAClD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,cAAc,gBAAgB;CACnD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,SAAS,kBAAkB;CAChD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,gBAAgB,OAAO;CAC5C,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,SAAS,kBAAkB;CAChD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,oBAAoB,OAAO;CAChD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,mBAAmB,SAAS;CACjD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,oBAAoB,OAAO;CAChD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,cAAc,cAAc;CACjD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,mBAAmB,SAAS;CACjD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,gBAAgB,gBAAgB;CACrD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,SAAS,kBAAkB;CAChD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,oBAAoB,OAAO;CAChD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,cAAc,gBAAgB;CACnD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,SAAS,kBAAkB;CAChD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,gBAAgB,aAAa;CAClD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,SAAS,cAAc;CAC5C,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,WAAW,iBAAiB;CACjD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,gBAAgB,gBAAgB;CACrD,CAAC;CACD,SAAS;EACP,UAAU;EACV,MAAM;EACN,SAAS;EACT,aAAa;GACX;GACA;GACA;GACA;GACA;EACF;EACA,kBAAkB,CAAC,SAAS,cAAc;CAC5C,CAAC;AACH;AAEA,MAAM,WAAW,OACf,gBACG,QAAQ,SAAS,KAAK,YAAY,MAAM,iBAAiB,iBAAiB,EAAE,CAAC,CAAC,CAC9E,KAAK,SAAS,KAAK,QAAQ;AAChC,MAAM,cACJ,IACA,qBAA0D,CAAC,MACpC;CACvB,MAAM,CAAC,QAAQ,QAAQ,GAAG,MAAM,GAAG;CACnC,OAAO;EACL;EACA;EACA;EACA,MAAM,cAAc,OAAO,GAAG;EAC9B,WAAW,QAAQ,EAAE;EACrB;CACF;AACF;AAEA,MAAa,sBAAsB;CACjC,WAAW,0BAA0B,CAAC,qBAAqB,eAAe,CAAC;CAC3E,WAAW,sBAAsB,CAAC,oBAAoB,iBAAiB,CAAC;CACxE,WAAW,sBAAsB,CAAC,qBAAqB,eAAe,CAAC;CACvE,WAAW,6BAA6B,CAAC,mBAAmB,4BAA4B,CAAC;CACzF,WAAW,iBAAiB,CAAC,4BAA4B,oBAAoB,CAAC;CAC9E,WAAW,sBAAsB,CAAC,0BAA0B,mBAAmB,CAAC;CAChF,WAAW,4BAA4B,CAAC,iBAAiB,eAAe,CAAC;CACzE,WAAW,8BAA8B,CAAC,6BAA6B,iBAAiB,CAAC;CACzF,WAAW,qBAAqB,CAAC,sBAAsB,kBAAkB,CAAC;CAC1E,WAAW,oBAAoB,CAAC,sBAAsB,kBAAkB,CAAC;CACzE,WAAW,oBAAoB,CAAC,6BAA6B,iBAAiB,CAAC;CAC/E,WAAW,qBAAqB,CAAC,0BAA0B,oBAAoB,CAAC;CAChF,WAAW,mBAAmB,CAAC,sBAAsB,gBAAgB,CAAC;CACtE,WAAW,kBAAkB,CAAC,sBAAsB,eAAe,CAAC;CACpE,WAAW,iBAAiB,CAAC,0BAA0B,0BAA0B,CAAC;CAClF,WAAW,iBAAiB,CAAC,sBAAsB,kBAAkB,CAAC;CACtE,WAAW,mBAAmB,CAAC,6BAA6B,4BAA4B,CAAC;CACzF,WAAW,oBAAoB,CAAC,qBAAqB,2BAA2B,CAAC;CACjF,WAAW,oBAAoB,CAAC,oBAAoB,oBAAoB,CAAC;AAC3E;AAEA,MAAa,wBAAwB;CACnC,SAAS;CACT,UAAU;CACV,WAAW;CACX,aAAa;AACf"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"public-handoff.js","names":[],"sources":["../../presets/public-handoff.ts"],"sourcesContent":["import { Result } from \"effect\";\n\nimport { collectVariableReferences, type Channel } from \"../src/ast\";\nimport { compileSource } from \"../src/compile\";\nimport { parseSml } from \"../src/parser\";\nimport { presetCatalog, RECOMMENDED_THEME, type PresetCategory } from \"./catalog\";\nimport {\n publicTemplates,\n templateCollections,\n type TemplateCollectionId,\n type TemplateUseCase,\n} from \"./public-gallery\";\nimport type { PublicTemplatePreview } from \"./public-preview\";\n\nexport interface ApprovedPublicTemplateArtifact {\n readonly approval: {\n readonly state: \"draft\" | \"approved\" | \"rejected\";\n readonly approvedBy: string;\n /** Stable ISO-8601 timestamp supplied by the owner. */\n readonly approvedAt: string;\n };\n readonly preset: {\n readonly id: string;\n readonly channel: Channel;\n readonly category: PresetCategory;\n readonly name: string;\n readonly description: string;\n readonly variables: ReadonlyArray<string>;\n readonly source: string;\n };\n readonly gallery: {\n readonly slug: string;\n readonly useCase: TemplateUseCase;\n readonly collections: ReadonlyArray<TemplateCollectionId>;\n readonly relatedTemplates: ReadonlyArray<string>;\n };\n readonly preview: PublicTemplatePreview;\n readonly publicMetadata: {\n readonly cardTitle: string;\n readonly cardDescription: string;\n readonly guidance: ReadonlyArray<string>;\n readonly variableDescriptions: Readonly<Record<string, string>>;\n readonly seo: { readonly title: string; readonly description: string };\n };\n}\n\nexport interface ValidatedPublicTemplateArtifact {\n readonly preset: Omit<ApprovedPublicTemplateArtifact[\"preset\"], \"source\">;\n readonly source: string;\n readonly gallery: ApprovedPublicTemplateArtifact[\"gallery\"] & {\n readonly path: `/templates/${string}`;\n };\n readonly preview: ApprovedPublicTemplateArtifact[\"preview\"];\n readonly publicMetadata: ApprovedPublicTemplateArtifact[\"publicMetadata\"];\n readonly approval: ApprovedPublicTemplateArtifact[\"approval\"] & { readonly state: \"approved\" };\n}\n\nexport type PublicHandoffIssueCode =\n | \"approval\"\n | \"identity\"\n | \"duplicate-id\"\n | \"duplicate-slug\"\n | \"source\"\n | \"channel\"\n | \"variables\"\n | \"sample-data\"\n | \"assets\"\n | \"public-metadata\"\n | \"reference\";\n\nexport interface PublicHandoffIssue {\n readonly artifactId: string;\n readonly code: PublicHandoffIssueCode;\n readonly message: string;\n}\n\nconst SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\nconst ISO_TIMESTAMP = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z$/;\n\nconst sortedUnique = (values: ReadonlyArray<string>): ReadonlyArray<string> =>\n [...new Set(values)].sort();\n\nconst sameStrings = (left: ReadonlyArray<string>, right: ReadonlyArray<string>): boolean =>\n JSON.stringify(sortedUnique(left)) === JSON.stringify(sortedUnique(right));\n\nconst nonEmpty = (value: string): boolean => value.trim().length > 0;\n\nconst issue = (\n artifactId: string,\n code: PublicHandoffIssueCode,\n message: string,\n): PublicHandoffIssue => ({ artifactId, code, message });\n\n/**\n * Validates explicitly supplied, owner-approved artifacts without filesystem,\n * network, persistence, or discovery. Successful output is a projection for\n * the existing preset, public-gallery, preview, and editorial contracts; this\n * function never registers or publishes it.\n */\nexport const validatePublicTemplateHandoff = (\n artifacts: ReadonlyArray<ApprovedPublicTemplateArtifact>,\n): Result.Result<\n ReadonlyArray<ValidatedPublicTemplateArtifact>,\n ReadonlyArray<PublicHandoffIssue>\n> => {\n const issues: Array<PublicHandoffIssue> = [];\n const existingIds = new Set<string>(presetCatalog.map((preset) => preset.id));\n const existingSlugs = new Set<string>(publicTemplates.map((template) => template.slug));\n const suppliedIds = new Set<string>();\n const suppliedSlugs = new Set<string>();\n const allKnownIds = new Set<string>([\n ...publicTemplates.map((template) => template.presetId),\n ...artifacts.map((artifact) => artifact.preset.id),\n ]);\n const knownCollections = new Set<string>(templateCollections.map((collection) => collection.id));\n\n for (const artifact of artifacts) {\n const id = artifact.preset.id;\n\n if (\n artifact.approval.state !== \"approved\" ||\n !nonEmpty(artifact.approval.approvedBy) ||\n !ISO_TIMESTAMP.test(artifact.approval.approvedAt)\n ) {\n issues.push(\n issue(id, \"approval\", \"owner approval and an ISO-8601 approval timestamp are required\"),\n );\n }\n\n if (!SLUG.test(id) || !SLUG.test(artifact.gallery.slug)) {\n issues.push(\n issue(id, \"identity\", \"preset id and canonical slug must be lowercase kebab-case\"),\n );\n }\n if (existingIds.has(id) || suppliedIds.has(id)) {\n issues.push(issue(id, \"duplicate-id\", `preset id \"${id}\" is already present`));\n }\n if (existingSlugs.has(artifact.gallery.slug) || suppliedSlugs.has(artifact.gallery.slug)) {\n issues.push(\n issue(id, \"duplicate-slug\", `canonical slug \"${artifact.gallery.slug}\" is already present`),\n );\n }\n suppliedIds.add(id);\n suppliedSlugs.add(artifact.gallery.slug);\n\n const compiled = compileSource(artifact.preset.source, {\n theme: { overrides: [RECOMMENDED_THEME] },\n });\n if (Result.isFailure(compiled)) {\n issues.push(issue(id, \"source\", compiled.failure.message));\n } else if (artifact.preset.channel !== \"email\" || compiled.success.channel !== \"email\") {\n issues.push(issue(id, \"channel\", \"public template source must compile as email\"));\n }\n\n const parsed = parseSml(artifact.preset.source);\n if (Result.isSuccess(parsed)) {\n const sourceVariables = [...collectVariableReferences(parsed.success)];\n if (!sameStrings(artifact.preset.variables, sourceVariables)) {\n issues.push(\n issue(id, \"variables\", \"declared variables must exactly match source references\"),\n );\n }\n }\n\n const sampleVariables = Object.keys(artifact.preview.sampleData);\n if (!sameStrings(sampleVariables, artifact.preset.variables)) {\n issues.push(\n issue(id, \"sample-data\", \"sample data must cover every declared variable exactly\"),\n );\n }\n if (Object.values(artifact.preview.sampleData).some((value) => !nonEmpty(value))) {\n issues.push(issue(id, \"sample-data\", \"sample data values must be non-empty\"));\n }\n\n const assetVariables = artifact.preview.assets.map((asset) => asset.variable);\n if (new Set(assetVariables).size !== assetVariables.length) {\n issues.push(issue(id, \"assets\", \"asset variable references must be unique\"));\n }\n for (const asset of artifact.preview.assets) {\n const value = artifact.preview.sampleData[asset.variable];\n const url = value === undefined ? null : URL.parse(value);\n if (!artifact.preset.variables.includes(asset.variable)) {\n issues.push(\n issue(id, \"assets\", `asset \"${asset.variable}\" is not a declared preset variable`),\n );\n }\n if (url === null || url.protocol !== \"https:\") {\n issues.push(\n issue(id, \"assets\", `asset \"${asset.variable}\" must reference a valid HTTPS sample URL`),\n );\n }\n }\n\n const metadata = artifact.publicMetadata;\n const metadataStrings = [\n artifact.preset.name,\n artifact.preset.description,\n artifact.preview.subject,\n artifact.preview.preheader,\n metadata.cardTitle,\n metadata.cardDescription,\n metadata.seo.title,\n metadata.seo.description,\n ...metadata.guidance,\n ...Object.values(metadata.variableDescriptions),\n ];\n if (metadata.guidance.length === 0 || metadataStrings.some((value) => !nonEmpty(value))) {\n issues.push(issue(id, \"public-metadata\", \"public metadata fields must be non-empty\"));\n }\n if (!sameStrings(Object.keys(metadata.variableDescriptions), artifact.preset.variables)) {\n issues.push(\n issue(\n id,\n \"public-metadata\",\n \"variable descriptions must cover every declared variable exactly\",\n ),\n );\n }\n\n if (new Set(artifact.gallery.collections).size !== artifact.gallery.collections.length) {\n issues.push(issue(id, \"reference\", \"collection references must be unique\"));\n }\n for (const collectionId of artifact.gallery.collections) {\n if (!knownCollections.has(collectionId)) {\n issues.push(issue(id, \"reference\", `collection \"${collectionId}\" is invalid`));\n }\n }\n if (\n new Set(artifact.gallery.relatedTemplates).size !== artifact.gallery.relatedTemplates.length\n ) {\n issues.push(issue(id, \"reference\", \"related template references must be unique\"));\n }\n for (const relatedId of artifact.gallery.relatedTemplates) {\n if (relatedId === id || !allKnownIds.has(relatedId)) {\n issues.push(issue(id, \"reference\", `related template \"${relatedId}\" is invalid`));\n }\n }\n }\n\n if (issues.length > 0) return Result.fail(issues);\n\n return Result.succeed(\n artifacts.map((artifact) => ({\n approval: { ...artifact.approval, state: \"approved\" },\n preset: {\n id: artifact.preset.id,\n channel: artifact.preset.channel,\n category: artifact.preset.category,\n name: artifact.preset.name,\n description: artifact.preset.description,\n variables: artifact.preset.variables,\n },\n source: artifact.preset.source,\n gallery: {\n ...artifact.gallery,\n path: `/templates/${artifact.gallery.slug}`,\n },\n preview: artifact.preview,\n publicMetadata: artifact.publicMetadata,\n })),\n );\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"public-preview-artifacts.js","names":[],"sources":["../../presets/public-preview-artifacts.ts"],"sourcesContent":["import { Result } from \"effect\";\n\nimport { compileSource } from \"../src/compile\";\nimport { interpolate, type DocumentVariable } from \"../src/interpolate\";\nimport { presetCatalog, RECOMMENDED_THEME, type EmailPresetId } from \"./catalog\";\nimport { publicTemplates, type PublicTemplate } from \"./public-gallery\";\nimport { publicTemplatePreviews, type PreviewQuality } from \"./public-preview\";\nimport { presetSource } from \"./sources.gen\";\n\nexport type PreviewSurface = \"card\" | \"detail\" | \"og\";\nexport type PreviewViewport = \"desktop\" | \"mobile\";\nexport type PreviewColorScheme = \"light\" | \"dark\";\n\nexport interface PublicTemplateRenderInput {\n readonly name: `${string}.${PreviewSurface}.${PreviewViewport}.${PreviewColorScheme}.html`;\n readonly surface: PreviewSurface;\n readonly viewport: PreviewViewport;\n readonly colorScheme: PreviewColorScheme;\n readonly width: number;\n readonly height: number;\n readonly html: string;\n}\n\nexport interface PublicTemplatePreviewArtifact {\n readonly presetId: EmailPresetId;\n readonly slug: string;\n readonly subject: string;\n readonly preheader: string;\n readonly quality: PreviewQuality;\n readonly source: string;\n readonly html: string;\n readonly assets: ReadonlyArray<string>;\n readonly renders: ReadonlyArray<PublicTemplateRenderInput>;\n}\n\nexport interface PreviewArtifactBuildIssue {\n readonly presetId: EmailPresetId;\n readonly message: string;\n}\n\nconst renderFrames = [\n { surface: \"card\", viewport: \"desktop\", width: 600, height: 760 },\n { surface: \"card\", viewport: \"mobile\", width: 390, height: 680 },\n { surface: \"detail\", viewport: \"desktop\", width: 720, height: 900 },\n { surface: \"detail\", viewport: \"mobile\", width: 390, height: 844 },\n { surface: \"og\", viewport: \"desktop\", width: 1200, height: 630 },\n] as const satisfies ReadonlyArray<{\n readonly surface: PreviewSurface;\n readonly viewport: PreviewViewport;\n readonly width: number;\n readonly height: number;\n}>;\n\nconst renderInputs = (slug: string, html: string): ReadonlyArray<PublicTemplateRenderInput> =>\n renderFrames.flatMap((frame) =>\n ([\"light\", \"dark\"] as const).map((colorScheme) => ({\n ...frame,\n colorScheme,\n name: `${slug}.${frame.surface}.${frame.viewport}.${colorScheme}.html`,\n html,\n })),\n );\n\n/** Build one static preview artifact without loading the editor runtime or performing I/O. */\nexport const buildPublicTemplatePreviewArtifact = (\n template: PublicTemplate,\n): Result.Result<PublicTemplatePreviewArtifact, PreviewArtifactBuildIssue> => {\n const source = presetSource(template.presetId);\n const preset = presetCatalog.find((entry) => entry.id === template.presetId);\n const preview = publicTemplatePreviews[template.presetId];\n if (source === undefined || preset === undefined) {\n return Result.fail({\n presetId: template.presetId,\n message: \"canonical preset source is missing\",\n });\n }\n\n const compiled = compileSource(source, { theme: { overrides: [RECOMMENDED_THEME] } });\n if (Result.isFailure(compiled) || compiled.success.channel !== \"email\") {\n return Result.fail({\n presetId: template.presetId,\n message: \"canonical preset did not compile as email\",\n });\n }\n\n const variableSchema: ReadonlyArray<DocumentVariable> = preset.variables.map((name) => ({\n name,\n type: \"string\",\n required: true,\n }));\n const rendered = interpolate(compiled.success.html, preview.sampleData, variableSchema);\n if (rendered.missing.size > 0) {\n return Result.fail({\n presetId: template.presetId,\n message: `sample data is missing: ${[...rendered.missing].sort().join(\", \")}`,\n });\n }\n\n const sampleData: Readonly<Record<string, string>> = preview.sampleData;\n const assets: Array<string> = [];\n for (const asset of preview.assets) {\n const value = sampleData[asset.variable];\n if (value === undefined) {\n return Result.fail({\n presetId: template.presetId,\n message: `sample data is missing asset: ${asset.variable}`,\n });\n }\n assets.push(value);\n }\n return Result.succeed({\n presetId: template.presetId,\n slug: template.slug,\n subject: preview.subject,\n preheader: preview.preheader,\n quality: preview.quality,\n source,\n html: rendered.rendered,\n assets,\n renders: renderInputs(template.slug, rendered.rendered),\n });\n};\n\n/** Build only the requested canonical subset, preserving manifest order and aggregated failures. */\nexport const buildPublicTemplatePreviewArtifactsFor = (\n templates: ReadonlyArray<PublicTemplate>,\n): Result.Result<\n ReadonlyArray<PublicTemplatePreviewArtifact>,\n ReadonlyArray<PreviewArtifactBuildIssue>\n> => {\n const artifacts: Array<PublicTemplatePreviewArtifact> = [];\n const issues: Array<PreviewArtifactBuildIssue> = [];\n\n for (const template of templates) {\n const artifact = buildPublicTemplatePreviewArtifact(template);\n if (Result.isSuccess(artifact)) artifacts.push(artifact.success);\n else issues.push(artifact.failure);\n }\n\n return issues.length > 0 ? Result.fail(issues) : Result.succeed(artifacts);\n};\n\n/** Build the complete canonical public gallery. */\nexport const buildPublicTemplatePreviewArtifacts = () =>\n buildPublicTemplatePreviewArtifactsFor(publicTemplates);\n"],"mappings":";;;;;;;;AAwCA,MAAM,eAAe;CACnB;EAAE,SAAS;EAAQ,UAAU;EAAW,OAAO;EAAK,QAAQ;CAAI;CAChE;EAAE,SAAS;EAAQ,UAAU;EAAU,OAAO;EAAK,QAAQ;CAAI;CAC/D;EAAE,SAAS;EAAU,UAAU;EAAW,OAAO;EAAK,QAAQ;CAAI;CAClE;EAAE,SAAS;EAAU,UAAU;EAAU,OAAO;EAAK,QAAQ;CAAI;CACjE;EAAE,SAAS;EAAM,UAAU;EAAW,OAAO;EAAM,QAAQ;CAAI;AACjE;AAOA,MAAM,gBAAgB,MAAc,SAClC,aAAa,SAAS,UACnB,CAAC,SAAS,MAAM,CAAC,CAAW,KAAK,iBAAiB;CACjD,GAAG;CACH;CACA,MAAM,GAAG,KAAK,GAAG,MAAM,QAAQ,GAAG,MAAM,SAAS,GAAG,YAAY;CAChE;AACF,EAAE,CACJ;;AAGF,MAAa,sCACX,aAC4E;CAC5E,MAAM,SAAS,aAAa,SAAS,QAAQ;CAC7C,MAAM,SAAS,cAAc,MAAM,UAAU,MAAM,OAAO,SAAS,QAAQ;CAC3E,MAAM,UAAU,uBAAuB,SAAS;CAChD,IAAI,WAAW,KAAA,KAAa,WAAW,KAAA,GACrC,OAAO,OAAO,KAAK;EACjB,UAAU,SAAS;EACnB,SAAS;CACX,CAAC;CAGH,MAAM,WAAW,cAAc,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,iBAAiB,EAAE,EAAE,CAAC;CACpF,IAAI,OAAO,UAAU,QAAQ,KAAK,SAAS,QAAQ,YAAY,SAC7D,OAAO,OAAO,KAAK;EACjB,UAAU,SAAS;EACnB,SAAS;CACX,CAAC;CAGH,MAAM,iBAAkD,OAAO,UAAU,KAAK,UAAU;EACtF;EACA,MAAM;EACN,UAAU;CACZ,EAAE;CACF,MAAM,WAAW,YAAY,SAAS,QAAQ,MAAM,QAAQ,YAAY,cAAc;CACtF,IAAI,SAAS,QAAQ,OAAO,GAC1B,OAAO,OAAO,KAAK;EACjB,UAAU,SAAS;EACnB,SAAS,2BAA2B,CAAC,GAAG,SAAS,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI;CAC5E,CAAC;CAGH,MAAM,aAA+C,QAAQ;CAC7D,MAAM,SAAwB,CAAC;CAC/B,KAAK,MAAM,SAAS,QAAQ,QAAQ;EAClC,MAAM,QAAQ,WAAW,MAAM;EAC/B,IAAI,UAAU,KAAA,GACZ,OAAO,OAAO,KAAK;GACjB,UAAU,SAAS;GACnB,SAAS,iCAAiC,MAAM;EAClD,CAAC;EAEH,OAAO,KAAK,KAAK;CACnB;CACA,OAAO,OAAO,QAAQ;EACpB,UAAU,SAAS;EACnB,MAAM,SAAS;EACf,SAAS,QAAQ;EACjB,WAAW,QAAQ;EACnB,SAAS,QAAQ;EACjB;EACA,MAAM,SAAS;EACf;EACA,SAAS,aAAa,SAAS,MAAM,SAAS,QAAQ;CACxD,CAAC;AACH;;AAGA,MAAa,0CACX,cAIG;CACH,MAAM,YAAkD,CAAC;CACzD,MAAM,SAA2C,CAAC;CAElD,KAAK,MAAM,YAAY,WAAW;EAChC,MAAM,WAAW,mCAAmC,QAAQ;EAC5D,IAAI,OAAO,UAAU,QAAQ,GAAG,UAAU,KAAK,SAAS,OAAO;OAC1D,OAAO,KAAK,SAAS,OAAO;CACnC;CAEA,OAAO,OAAO,SAAS,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,QAAQ,SAAS;AAC3E;;AAGA,MAAa,4CACX,uCAAuC,eAAe"}