@pixerate/editor 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -1
- package/THIRD_PARTY_NOTICES.md +51 -0
- package/dist/agent/index.cjs +9 -8
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +4 -3
- package/dist/agent/index.d.ts +4 -3
- package/dist/agent/index.js +3 -2
- package/dist/agent/index.js.map +1 -1
- package/dist/canvas/index.cjs +1210 -17
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +318 -1
- package/dist/canvas/index.d.ts +318 -1
- package/dist/canvas/index.js +1199 -6
- package/dist/canvas/index.js.map +1 -1
- package/dist/{chunk-QBJ22YZR.js → chunk-3CN2WD3X.js} +1354 -206
- package/dist/chunk-3CN2WD3X.js.map +1 -0
- package/dist/{chunk-JJDLPEWL.js → chunk-3LV6RRPH.js} +2 -2
- package/dist/{chunk-MJZBNWG7.js → chunk-5S3Z4PPF.js} +78 -557
- package/dist/chunk-5S3Z4PPF.js.map +1 -0
- package/dist/chunk-664LQYFJ.js +115 -0
- package/dist/chunk-664LQYFJ.js.map +1 -0
- package/dist/{chunk-AA4ZEVCC.cjs → chunk-D27YFC46.cjs} +101 -580
- package/dist/chunk-D27YFC46.cjs.map +1 -0
- package/dist/chunk-D6FH2YAX.cjs +131 -0
- package/dist/chunk-D6FH2YAX.cjs.map +1 -0
- package/dist/{chunk-4SUNJZWB.cjs → chunk-FD4LPFAE.cjs} +48 -200
- package/dist/chunk-FD4LPFAE.cjs.map +1 -0
- package/dist/chunk-FODNLX5N.js +749 -0
- package/dist/chunk-FODNLX5N.js.map +1 -0
- package/dist/{chunk-OYXSKQFR.js → chunk-FPENXAEJ.js} +35 -187
- package/dist/chunk-FPENXAEJ.js.map +1 -0
- package/dist/chunk-HLIMTC6N.js +1 -0
- package/dist/chunk-HLIMTC6N.js.map +1 -0
- package/dist/{chunk-QGHGJ56O.js → chunk-ITBSKI26.js} +1483 -19
- package/dist/chunk-ITBSKI26.js.map +1 -0
- package/dist/{chunk-XDMXXC4Z.cjs → chunk-MA2F5KHF.cjs} +350 -227
- package/dist/chunk-MA2F5KHF.cjs.map +1 -0
- package/dist/{chunk-RIKBJLS2.cjs → chunk-MCSJFWAO.cjs} +1356 -208
- package/dist/chunk-MCSJFWAO.cjs.map +1 -0
- package/dist/chunk-MKA3AHLV.cjs +256 -0
- package/dist/chunk-MKA3AHLV.cjs.map +1 -0
- package/dist/chunk-OLZJXL4R.cjs +749 -0
- package/dist/chunk-OLZJXL4R.cjs.map +1 -0
- package/dist/chunk-OXSV64JX.js +689 -0
- package/dist/chunk-OXSV64JX.js.map +1 -0
- package/dist/chunk-P3RQ5UJE.cjs +689 -0
- package/dist/chunk-P3RQ5UJE.cjs.map +1 -0
- package/dist/chunk-P6ODPZ4W.js +256 -0
- package/dist/chunk-P6ODPZ4W.js.map +1 -0
- package/dist/chunk-PGNZKFQS.js +685 -0
- package/dist/chunk-PGNZKFQS.js.map +1 -0
- package/dist/chunk-PL4JB3BF.cjs +1 -0
- package/dist/chunk-PL4JB3BF.cjs.map +1 -0
- package/dist/{chunk-GVSMRPHB.cjs → chunk-SBCZ6RN5.cjs} +1568 -104
- package/dist/chunk-SBCZ6RN5.cjs.map +1 -0
- package/dist/{chunk-CL37JGFW.cjs → chunk-SML64VJG.cjs} +4 -4
- package/dist/{chunk-CL37JGFW.cjs.map → chunk-SML64VJG.cjs.map} +1 -1
- package/dist/chunk-TNNJXF46.cjs +115 -0
- package/dist/chunk-TNNJXF46.cjs.map +1 -0
- package/dist/chunk-U5HZSFCS.js +131 -0
- package/dist/chunk-U5HZSFCS.js.map +1 -0
- package/dist/{chunk-K3R5NG2D.js → chunk-U5KWZU4X.js} +251 -128
- package/dist/chunk-U5KWZU4X.js.map +1 -0
- package/dist/chunk-XNORY7M6.cjs +685 -0
- package/dist/chunk-XNORY7M6.cjs.map +1 -0
- package/dist/commands/index.d.cts +5 -353
- package/dist/commands/index.d.ts +5 -353
- package/dist/comment-store-BEMgVlAA.d.cts +135 -0
- package/dist/comment-store-BEMgVlAA.d.ts +135 -0
- package/dist/compare/index.cjs +15 -0
- package/dist/compare/index.cjs.map +1 -0
- package/dist/compare/index.d.cts +58 -0
- package/dist/compare/index.d.ts +58 -0
- package/dist/compare/index.js +15 -0
- package/dist/compare/index.js.map +1 -0
- package/dist/{controller-pePCJLT1.d.cts → controller-BhLLAoi8.d.cts} +45 -4
- package/dist/{controller-pePCJLT1.d.ts → controller-BhLLAoi8.d.ts} +45 -4
- package/dist/documents-BQjzJNe2.d.ts +76 -0
- package/dist/documents-KRTPhvst.d.cts +76 -0
- package/dist/extensions/index.cjs +53 -5
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +7 -3
- package/dist/extensions/index.d.ts +7 -3
- package/dist/extensions/index.js +52 -4
- package/dist/generation/index.cjs +1373 -0
- package/dist/generation/index.cjs.map +1 -0
- package/dist/generation/index.d.cts +554 -0
- package/dist/generation/index.d.ts +554 -0
- package/dist/generation/index.js +1373 -0
- package/dist/generation/index.js.map +1 -0
- package/dist/grammar/index.cjs +48 -3
- package/dist/grammar/index.cjs.map +1 -1
- package/dist/grammar/index.d.cts +322 -100
- package/dist/grammar/index.d.ts +322 -100
- package/dist/grammar/index.js +54 -9
- package/dist/image-editor/index.cjs +1831 -30
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +590 -3
- package/dist/image-editor/index.d.ts +590 -3
- package/dist/image-editor/index.js +1807 -6
- package/dist/image-editor/index.js.map +1 -1
- package/dist/{index-CCr_z9kF.d.cts → index-BhJBSm2d.d.cts} +370 -4
- package/dist/{index-Ck6ajwXh.d.ts → index-fJrlkJCd.d.ts} +370 -4
- package/dist/index.cjs +162 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -8
- package/dist/index.d.ts +14 -8
- package/dist/index.js +174 -37
- package/dist/index.js.map +1 -1
- package/dist/migrate-iTlm3a8h.d.cts +112 -0
- package/dist/migrate-iTlm3a8h.d.ts +112 -0
- package/dist/persistence/index.cjs +676 -0
- package/dist/persistence/index.cjs.map +1 -0
- package/dist/persistence/index.d.cts +347 -0
- package/dist/persistence/index.d.ts +347 -0
- package/dist/persistence/index.js +676 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/{registry-D5-R90p0.d.cts → registry-BDe-vlv3.d.ts} +2 -40
- package/dist/registry-D32LpIc4.d.cts +354 -0
- package/dist/{registry-D5-R90p0.d.ts → registry-DnzpgY27.d.cts} +2 -40
- package/dist/registry-evKTP4ls.d.ts +354 -0
- package/dist/schema-DYDGdYL9.d.cts +41 -0
- package/dist/schema-DYDGdYL9.d.ts +41 -0
- package/dist/serializers/index.cjs +4 -3
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.d.cts +2 -1
- package/dist/serializers/index.d.ts +2 -1
- package/dist/serializers/index.js +3 -2
- package/dist/sourcemap-C8AH3MBQ.d.cts +91 -0
- package/dist/sourcemap-DA1Evhrv.d.ts +91 -0
- package/dist/spreadsheet/index.cjs +37 -3
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +188 -17
- package/dist/spreadsheet/index.d.ts +188 -17
- package/dist/spreadsheet/index.js +43 -9
- package/dist/{tokens-CyLZmIei.d.cts → tokens-D7yXvUU6.d.cts} +15 -2
- package/dist/{tokens-CyLZmIei.d.ts → tokens-D7yXvUU6.d.ts} +15 -2
- package/dist/{track-changes-BSF58RK0.d.ts → track-changes-BK5sVf4_.d.cts} +18 -136
- package/dist/{track-changes-BSF58RK0.d.cts → track-changes-CDjCrwKe.d.ts} +18 -136
- package/package.json +16 -1
- package/dist/chunk-4SUNJZWB.cjs.map +0 -1
- package/dist/chunk-AA4ZEVCC.cjs.map +0 -1
- package/dist/chunk-AXV2ZW34.cjs +0 -118
- package/dist/chunk-AXV2ZW34.cjs.map +0 -1
- package/dist/chunk-B2ZRLN4S.js +0 -118
- package/dist/chunk-B2ZRLN4S.js.map +0 -1
- package/dist/chunk-GVSMRPHB.cjs.map +0 -1
- package/dist/chunk-K3R5NG2D.js.map +0 -1
- package/dist/chunk-MJZBNWG7.js.map +0 -1
- package/dist/chunk-OYXSKQFR.js.map +0 -1
- package/dist/chunk-QBJ22YZR.js.map +0 -1
- package/dist/chunk-QGHGJ56O.js.map +0 -1
- package/dist/chunk-RIKBJLS2.cjs.map +0 -1
- package/dist/chunk-XDMXXC4Z.cjs.map +0 -1
- /package/dist/{chunk-JJDLPEWL.js.map → chunk-3LV6RRPH.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-P3RQ5UJE.cjs","../src/grammar/resolver.ts","../src/grammar/sourcemap.ts","../src/grammar/fields.ts","../src/grammar/data-source.ts","../src/grammar/merge.ts"],"names":[],"mappings":"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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-P3RQ5UJE.cjs","sourcesContent":[null,"import {\n TEMPLATE_REGEX,\n VARIABLE_REGEX,\n INSTRUCTION_REGEX,\n Template,\n PromptValues,\n} from \"./tokens\";\nimport { resolvePrompt } from \"./engine\";\nimport { conditionSubject, parseVariableBody } from \"./variables\";\nimport { tokenizePrompt } from \"./tokenizer\";\n\nexport interface ResolveTemplatesOptions {\n bindings?: Record<string, string>;\n maxDepth?: number;\n /**\n * Values for `{variable}` slots and `{?variable}` sections. When omitted,\n * variables, defaults and section tags are left exactly as written.\n */\n values?: PromptValues;\n}\n\n/**\n * Recursively resolves template tags `{{template_name}}` in a given text.\n * A tag that would recurse into itself is replaced with `TEMPLATE_LOOP_ERROR`,\n * and a tag nested deeper than `maxDepth` with `TEMPLATE_DEPTH_ERROR`; the rest\n * of the text still resolves normally.\n *\n * Pass `options.values` to also fill variables (falling back to their\n * `{name|default}`) and evaluate `{?name}…{/name}` / `{!name}…{/name}`\n * sections, including those inside template bodies. See `resolvePrompt` for\n * the source map and diagnostics.\n */\nexport function resolveTemplates(\n text: string,\n templates: Template[] = [],\n options?: ResolveTemplatesOptions,\n): string {\n return resolvePrompt(text, templates, options).text;\n}\n\n/**\n * Replaces `{variableName}` with values from a dictionary, falling back to a\n * `{variableName|default}` when no value is supplied, and evaluates\n * `{?name}…{/name}` / `{!name}…{/name}` sections. Variables with neither a\n * value nor a default are left as written. Templates are not expanded.\n */\nexport function resolveVariables(\n text: string,\n values: PromptValues,\n): string {\n if (!text) return \"\";\n return resolvePrompt(text, [], { values }).text;\n}\n\n/**\n * Extracts unique template names referenced in text.\n */\nexport function extractTemplateNames(text: string): string[] {\n if (!text) return [];\n const set = new Set<string>();\n const regex = new RegExp(TEMPLATE_REGEX.source, TEMPLATE_REGEX.flags);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n set.add(match[1].trim());\n }\n return Array.from(set);\n}\n\n/**\n * Extracts unique variable names referenced in text, including names used by\n * conditional section tags (`amount` for `{?amount>100}…{/amount}`).\n * Defaults and descriptions are stripped.\n */\nexport function extractVariableNames(text: string): string[] {\n if (!text) return [];\n const set = new Set<string>();\n const regex = new RegExp(VARIABLE_REGEX.source, VARIABLE_REGEX.flags);\n let comparisonStarts: Set<number> | undefined;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n const parsed = parseVariableBody(match[1]);\n let name = parsed.name;\n if (parsed.kind === \"conditional\" && parsed.conditional !== \"end\") {\n const subject = conditionSubject(name);\n if (subject) {\n // Only a tag closed by `{/subject}` is a comparison on `subject`.\n comparisonStarts ??= new Set(\n tokenizePrompt(text)\n .filter((token) => token.comparison)\n .map((token) => token.start),\n );\n if (comparisonStarts.has(match.index)) name = subject;\n }\n }\n set.add(name);\n }\n return Array.from(set);\n}\n\n/**\n * Extracts unique instruction names referenced in text.\n */\nexport function extractInstructionNames(text: string): string[] {\n if (!text) return [];\n const set = new Set<string>();\n const regex = new RegExp(INSTRUCTION_REGEX.source, INSTRUCTION_REGEX.flags);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(text)) !== null) {\n set.add(match[1].trim());\n }\n return Array.from(set);\n}\n","import type { PromptDiagnostic, Template } from \"./tokens\";\nimport type { ResolveTemplatesOptions } from \"./resolver\";\nimport { resolvePrompt } from \"./engine\";\nimport { getPromptDiagnostics } from \"./tokenizer\";\n\n/**\n * One contiguous piece of resolved output and the raw range it came from.\n *\n * - `raw`: copied verbatim; offsets map character by character.\n * - `template`: the expansion of a `{{template}}` tag.\n * - `variable`: a substituted value or `|default` for a `{variable}` tag.\n * - `conditional`: a section tag, or a whole removed section. These segments\n * are empty in the resolved text (`resolvedStart === resolvedEnd`).\n *\n * Offsets inside non-raw segments map to the start of their raw range.\n */\nexport interface SourceMapSegment {\n resolvedStart: number;\n resolvedEnd: number;\n source: \"raw\" | \"template\" | \"variable\" | \"conditional\";\n rawStart: number;\n rawEnd: number;\n templateName?: string;\n /** Variable substituted (`variable`) or tested (`conditional`). */\n variableName?: string;\n /** For `variable` segments: whether a supplied value or the default was used. */\n valueSource?: \"value\" | \"default\";\n}\n\n/**\n * Resolves templates in text while tracking character offset mapping\n * between the raw input and the fully resolved output.\n *\n * Pass `options.values` to also substitute variables and evaluate conditional\n * sections (see `resolvePrompt`); without it the output is unchanged from the\n * template-only behavior.\n */\nexport function resolveTemplatesWithMapping(\n text: string,\n templates: Template[] = [],\n templateBindings?: Record<string, string>,\n options?: Pick<ResolveTemplatesOptions, \"maxDepth\" | \"values\">,\n): { text: string; map: SourceMapSegment[]; diagnostics: PromptDiagnostic[] } {\n if (\n options?.values === undefined &&\n (!templates || templates.length === 0 || !text.includes(\"{{\"))\n ) {\n return {\n diagnostics: getPromptDiagnostics(text),\n text,\n map: [\n {\n resolvedStart: 0,\n resolvedEnd: text.length,\n source: \"raw\",\n rawStart: 0,\n rawEnd: text.length,\n },\n ],\n };\n }\n\n return resolvePrompt(text, templates, {\n bindings: templateBindings,\n maxDepth: options?.maxDepth,\n values: options?.values,\n });\n}\n\n/**\n * Maps an offset in resolved text back to the corresponding offset in raw text.\n */\nexport function mapResolvedOffsetToRaw(\n resolvedOffset: number,\n map: SourceMapSegment[],\n): number {\n for (let i = 0; i < map.length; i++) {\n const seg = map[i];\n const isLast = i === map.length - 1;\n const inRange = isLast\n ? resolvedOffset >= seg.resolvedStart && resolvedOffset <= seg.resolvedEnd\n : resolvedOffset >= seg.resolvedStart && resolvedOffset < seg.resolvedEnd;\n\n if (inRange) {\n if (seg.source === \"raw\") {\n return seg.rawStart + (resolvedOffset - seg.resolvedStart);\n }\n // The end of the text maps to the end of the tag; anywhere inside a\n // resolved template maps to the start of its tag.\n if (resolvedOffset === seg.resolvedEnd) {\n return seg.rawEnd;\n }\n return seg.rawStart;\n }\n }\n return resolvedOffset;\n}\n\n/**\n * Maps an offset in raw text to the corresponding offset in resolved text.\n */\nexport function mapRawOffsetToResolved(\n rawOffset: number,\n map: SourceMapSegment[],\n): number {\n for (let i = 0; i < map.length; i++) {\n const seg = map[i];\n const isLast = i === map.length - 1;\n const inRange = isLast\n ? rawOffset >= seg.rawStart && rawOffset <= seg.rawEnd\n : rawOffset >= seg.rawStart && rawOffset < seg.rawEnd;\n\n if (inRange) {\n if (seg.source === \"raw\") {\n return seg.resolvedStart + (rawOffset - seg.rawStart);\n }\n if (rawOffset === seg.rawEnd) {\n return seg.resolvedEnd;\n }\n return seg.resolvedStart;\n }\n }\n return rawOffset;\n}\n","import type { PromptValues, Template } from \"./tokens\";\nimport { tokenizePrompt } from \"./tokenizer\";\nimport { getTemplateBody } from \"./engine\";\n\n/** Everything a prompt (and, optionally, its templates) says about one variable. */\nexport interface VariableDescriptor {\n name: string;\n /** First `{name|default}` found. */\n defaultValue?: string;\n /** First `{name|default|description}` found. */\n description?: string;\n /** Appears as a `{name}` slot. */\n usedAsValue: boolean;\n /** Appears in a `{?name}` / `{!name}` condition (including comparisons). */\n usedInCondition: boolean;\n /**\n * Present (and `true`) when the variable is compared in a condition such as\n * `{?amount>100}`, so a form should ask for its text, not a checkbox.\n */\n usedInComparison?: true;\n /** Number of `{name…}` slots and condition tags (closing tags excluded). */\n occurrences: number;\n /** Templates whose bodies mention the variable, in discovery order. */\n templates: string[];\n}\n\nexport interface ExtractVariablesOptions {\n /** Templates to search recursively for further variables. */\n templates?: Template[];\n bindings?: Record<string, string>;\n /** Maximum template nesting depth to search (default 10). */\n maxDepth?: number;\n}\n\n/**\n * Lists every variable a prompt uses, in order of first appearance, with its\n * default and description. Templates referenced by the prompt are searched\n * recursively when `options.templates` is given (loops are skipped). All\n * sections are searched, whatever their condition would evaluate to.\n */\nexport function extractVariables(\n text: string,\n options: ExtractVariablesOptions = {},\n): VariableDescriptor[] {\n const descriptors = new Map<string, VariableDescriptor>();\n const templateMap = new Map(\n (options.templates ?? []).map((t) => [t.name, t] as const),\n );\n const maxDepth = options.maxDepth ?? 10;\n\n const describe = (name: string, templateName?: string) => {\n let descriptor = descriptors.get(name);\n if (!descriptor) {\n descriptor = {\n name,\n usedAsValue: false,\n usedInCondition: false,\n occurrences: 0,\n templates: [],\n };\n descriptors.set(name, descriptor);\n }\n descriptor.occurrences++;\n if (templateName && !descriptor.templates.includes(templateName)) {\n descriptor.templates.push(templateName);\n }\n return descriptor;\n };\n\n const visit = (\n source: string,\n visited: Set<string>,\n depth: number,\n templateName?: string,\n ) => {\n for (const token of tokenizePrompt(source)) {\n if (token.type === \"variable\") {\n const descriptor = describe(token.value, templateName);\n descriptor.usedAsValue = true;\n if (descriptor.defaultValue === undefined && token.defaultValue !== undefined) {\n descriptor.defaultValue = token.defaultValue;\n }\n if (descriptor.description === undefined && token.description !== undefined) {\n descriptor.description = token.description;\n }\n } else if (token.type === \"conditional\" && token.conditional !== \"end\") {\n const descriptor = describe(token.value, templateName);\n descriptor.usedInCondition = true;\n if (token.comparison) descriptor.usedInComparison = true;\n } else if (token.type === \"template\") {\n const template = templateMap.get(token.value);\n if (!template || visited.has(token.value) || depth + 1 > maxDepth) {\n continue;\n }\n const body = getTemplateBody(template, options.bindings);\n if (body === undefined) continue;\n visit(body, new Set(visited).add(token.value), depth + 1, token.value);\n }\n }\n };\n\n if (text) visit(text, new Set(), 0);\n return Array.from(descriptors.values());\n}\n\n/** A headless description of one input in a variable fill-in form. */\nexport interface VariableFormField {\n name: string;\n /** The variable's description, or its name. */\n label: string;\n /** The variable's default, or an empty string. */\n placeholder: string;\n defaultValue?: string;\n description?: string;\n /**\n * The variable is used as a slot and has no default, so leaving it blank\n * leaves the `{name}` placeholder in the resolved prompt.\n */\n required: boolean;\n /**\n * Only used by `{?name}` / `{!name}` presence conditions, never substituted\n * or compared (a form can show a checkbox).\n */\n conditionOnly: boolean;\n}\n\n/**\n * Builds field descriptors for a fill-in form (Word's FILLIN analogue): one\n * per variable, labelled with its description and using its default as the\n * placeholder. Feed the entered text through `compactVariableValues` before\n * resolving so blank inputs fall back to defaults.\n */\nexport function getVariableFormFields(\n text: string,\n options: ExtractVariablesOptions = {},\n): VariableFormField[] {\n return extractVariables(text, options).map((variable) => ({\n name: variable.name,\n label: variable.description || variable.name,\n placeholder: variable.defaultValue ?? \"\",\n ...(variable.defaultValue !== undefined\n ? { defaultValue: variable.defaultValue }\n : {}),\n ...(variable.description !== undefined\n ? { description: variable.description }\n : {}),\n required: variable.usedAsValue && variable.defaultValue === undefined,\n conditionOnly: !variable.usedAsValue && !variable.usedInComparison,\n }));\n}\n\n/**\n * Drops blank entries from form input so the resolver falls back to each\n * variable's default (an explicit `\"\"` value would otherwise render as empty).\n */\nexport function compactVariableValues(\n input: Record<string, string | null | undefined>,\n): PromptValues {\n const values: PromptValues = {};\n for (const [name, value] of Object.entries(input)) {\n if (value !== null && value !== undefined && value.trim() !== \"\") {\n values[name] = value;\n }\n }\n return values;\n}\n","// Portions ported from wordcraft/crates/engine/src/cmd/mailings.rs and\n// wordcraft/crates/formats/src/sheet.rs (MIT OR Apache-2.0, © ArtCraft Team\n// and contributors). See THIRD_PARTY_NOTICES.md.\n\n/**\n * Mail-merge data sources: CSV / TSV text, JSON, rows or objects normalised\n * into named fields and string records, plus fuzzy matching of prompt\n * variables to those fields.\n *\n * Imports only the self-contained CSV module, never the spreadsheet entry, so\n * the grammar entry stays light.\n */\nimport { parseCsv } from \"../spreadsheet/csv\";\nimport type { PromptValues } from \"./tokens\";\n\n/** Most records a data source keeps by default. */\nexport const MAX_DATA_SOURCE_RECORDS = 100_000;\n/** Most fields a data source keeps by default. */\nexport const MAX_DATA_SOURCE_FIELDS = 1_000;\n\n/** One record: field name → cell text. */\nexport type MergeRecord = Record<string, string>;\n\n/** Anything `parseDataSource` accepts. */\nexport type DataSourceInput =\n | string\n | ReadonlyArray<ReadonlyArray<unknown>>\n | ReadonlyArray<Readonly<Record<string, unknown>>>;\n\nexport interface ParseDataSourceOptions {\n /**\n * Field separator for delimited text. `\"auto\"` (the default) picks whichever\n * of `,` `;` and tab occurs most often in the first line (outside quotes),\n * preferring `,` on a tie.\n */\n delimiter?: \"auto\" | string;\n /**\n * Whether the first row names the fields (default `true`). Without a header\n * the fields are `Column 1`…`Column N`. Ignored for arrays of objects.\n */\n hasHeader?: boolean;\n /**\n * How to read a string: `\"auto\"` (default) parses text starting with `[` as\n * JSON and falls back to delimited text when that fails.\n */\n format?: \"auto\" | \"csv\" | \"json\";\n /** Record cap (default `MAX_DATA_SOURCE_RECORDS`). */\n maxRecords?: number;\n /** Field cap (default `MAX_DATA_SOURCE_FIELDS`). */\n maxFields?: number;\n}\n\nexport type DataSourceWarningCode =\n | \"empty\"\n | \"invalid-json\"\n | \"unsupported-json\"\n | \"blank-header\"\n | \"duplicate-header\"\n | \"ragged-rows\"\n | \"records-truncated\"\n | \"fields-truncated\";\n\nexport interface DataSourceWarning {\n code: DataSourceWarningCode;\n message: string;\n /** For `ragged-rows`: how many records were padded or cut. */\n count?: number;\n}\n\nexport interface MergeDataSource {\n /** Field names: trimmed, unique (case-insensitively), never blank. */\n fields: string[];\n /** One entry per record, with every field present. */\n records: MergeRecord[];\n warnings: DataSourceWarning[];\n /** How the input was read. */\n format: \"csv\" | \"json\" | \"rows\" | \"objects\";\n /** The delimiter used for delimited text. */\n delimiter?: string;\n}\n\nconst AUTO_DELIMITERS = [\",\", \";\", \"\\t\"];\n\n/** Counts the auto-detect candidates in the first line, ignoring quoted text. */\nexport function detectDelimiter(text: string): string {\n const counts = new Map<string, number>(AUTO_DELIMITERS.map((d) => [d, 0]));\n let quoted = false;\n for (let i = 0; i < text.length; i++) {\n const ch = text[i];\n if (ch === '\"') {\n quoted = !quoted;\n continue;\n }\n if (quoted) continue;\n if (ch === \"\\n\" || ch === \"\\r\") break;\n if (counts.has(ch)) counts.set(ch, counts.get(ch)! + 1);\n }\n let best = \",\";\n for (const d of AUTO_DELIMITERS) {\n if (counts.get(d)! > counts.get(best)!) best = d;\n }\n return best;\n}\n\n/**\n * Field names from a header row: names are trimmed, a blank name becomes\n * `Column N` (its 1-based position), and repeats (compared case-insensitively)\n * are numbered `Name 2`, `Name 3`, ….\n */\nexport function normalizeFieldNames(row: ReadonlyArray<unknown>): string[] {\n const out: string[] = [];\n const taken = new Set<string>();\n row.forEach((cell, i) => {\n const trimmed = cellText(cell).trim();\n const base = trimmed === \"\" ? `Column ${i + 1}` : trimmed;\n let name = base;\n for (let k = 2; taken.has(name.toLowerCase()); k++) name = `${base} ${k}`;\n taken.add(name.toLowerCase());\n out.push(name);\n });\n return out;\n}\n\nfunction cellText(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"object\") {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\n/** Sets an own property, even for names such as `__proto__`. */\nfunction setField(record: MergeRecord, name: string, value: string): void {\n Object.defineProperty(record, name, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n}\n\nfunction isBlankRow(row: ReadonlyArray<unknown>): boolean {\n return row.every((cell) => cellText(cell).trim() === \"\");\n}\n\n/**\n * Reads a mail-merge data source.\n *\n * - **Text**: CSV / TSV (RFC 4180 quoting, doubled quotes, CRLF / LF / CR,\n * a leading BOM ignored) with the delimiter auto-detected, or JSON (an\n * array of objects or of arrays).\n * - **Rows** (`unknown[][]`): the first row is the header unless\n * `hasHeader: false`.\n * - **Objects**: the fields are the union of keys in first-seen order.\n *\n * Blank rows are skipped, header names are normalised (`normalizeFieldNames`),\n * records are padded or cut to the header width, and records and fields are\n * capped. Cells become strings (`null` → `\"\"`, nested values → JSON). Problems\n * are reported in `warnings`; this never throws.\n */\nexport function parseDataSource(\n input: DataSourceInput,\n options: ParseDataSourceOptions = {},\n): MergeDataSource {\n const warnings: DataSourceWarning[] = [];\n if (typeof input === \"string\") {\n const format = options.format ?? \"auto\";\n const trimmed = input.trim();\n if (format === \"json\" || (format === \"auto\" && /^\\[/.test(trimmed))) {\n let parsed: unknown;\n let ok = true;\n try {\n parsed = JSON.parse(trimmed);\n } catch (error) {\n ok = false;\n if (format === \"json\") {\n warnings.push({\n code: \"invalid-json\",\n message: `Invalid JSON: ${(error as Error).message}`,\n });\n return { fields: [], records: [], warnings, format: \"json\" };\n }\n }\n if (ok) {\n if (!Array.isArray(parsed)) {\n warnings.push({\n code: \"unsupported-json\",\n message: \"JSON data must be an array of objects or of arrays\",\n });\n return { fields: [], records: [], warnings, format: \"json\" };\n }\n const result = fromArray(parsed, options, warnings);\n return { ...result, format: \"json\" };\n }\n }\n const delimiter =\n !options.delimiter || options.delimiter === \"auto\"\n ? detectDelimiter(input)\n : options.delimiter;\n let rows: string[][];\n try {\n rows = parseCsv(input, { delimiter });\n } catch (error) {\n warnings.push({ code: \"empty\", message: (error as Error).message });\n return { fields: [], records: [], warnings, format: \"csv\", delimiter };\n }\n return {\n ...fromRows(rows, options, warnings),\n format: \"csv\",\n delimiter,\n };\n }\n const result = fromArray(input as ReadonlyArray<unknown>, options, warnings);\n return {\n ...result,\n format: result.objects ? \"objects\" : \"rows\",\n };\n}\n\nfunction fromArray(\n items: ReadonlyArray<unknown>,\n options: ParseDataSourceOptions,\n warnings: DataSourceWarning[],\n): { fields: string[]; records: MergeRecord[]; warnings: DataSourceWarning[]; objects: boolean } {\n const objects = items.some(\n (item) => item !== null && typeof item === \"object\" && !Array.isArray(item),\n );\n if (!objects) {\n const rows = items.filter((item): item is unknown[] => Array.isArray(item));\n return { ...fromRows(rows, options, warnings), objects: false };\n }\n const maxFields = options.maxFields ?? MAX_DATA_SOURCE_FIELDS;\n const maxRecords = options.maxRecords ?? MAX_DATA_SOURCE_RECORDS;\n const fields: string[] = [];\n const seen = new Set<string>();\n let fieldsTruncated = false;\n const rows = items.filter(\n (item): item is Record<string, unknown> =>\n item !== null && typeof item === \"object\" && !Array.isArray(item),\n );\n for (const row of rows) {\n for (const key of Object.keys(row)) {\n if (seen.has(key)) continue;\n if (fields.length >= maxFields) {\n fieldsTruncated = true;\n continue;\n }\n seen.add(key);\n fields.push(key);\n }\n }\n if (fieldsTruncated) {\n warnings.push({\n code: \"fields-truncated\",\n message: `Only the first ${maxFields} fields were kept`,\n });\n }\n const records: MergeRecord[] = [];\n for (const row of rows) {\n if (fields.every((field) => cellText(row[field]).trim() === \"\")) continue;\n if (records.length >= maxRecords) {\n warnings.push({\n code: \"records-truncated\",\n message: `Only the first ${maxRecords} records were kept`,\n });\n break;\n }\n const record: MergeRecord = {};\n for (const field of fields) {\n const value = Object.prototype.hasOwnProperty.call(row, field)\n ? row[field]\n : undefined;\n setField(record, field, cellText(value));\n }\n records.push(record);\n }\n if (records.length === 0) {\n warnings.push({ code: \"empty\", message: \"The data source has no records\" });\n }\n return { fields, records, warnings, objects: true };\n}\n\nfunction fromRows(\n allRows: ReadonlyArray<ReadonlyArray<unknown>>,\n options: ParseDataSourceOptions,\n warnings: DataSourceWarning[],\n): { fields: string[]; records: MergeRecord[]; warnings: DataSourceWarning[] } {\n const maxFields = options.maxFields ?? MAX_DATA_SOURCE_FIELDS;\n const maxRecords = options.maxRecords ?? MAX_DATA_SOURCE_RECORDS;\n const rows = allRows.filter((row) => !isBlankRow(row));\n if (rows.length === 0) {\n warnings.push({ code: \"empty\", message: \"The data source has no records\" });\n return { fields: [], records: [], warnings };\n }\n const hasHeader = options.hasHeader ?? true;\n let fields: string[];\n let body: ReadonlyArray<ReadonlyArray<unknown>>;\n let widest: number;\n if (hasHeader) {\n const header = rows[0];\n widest = header.length;\n if (header.some((cell) => cellText(cell).trim() === \"\")) {\n warnings.push({\n code: \"blank-header\",\n message: \"Blank column headers were named \\\"Column N\\\"\",\n });\n }\n fields = normalizeFieldNames(header.slice(0, maxFields));\n const lower = header.map((cell) => cellText(cell).trim().toLowerCase());\n if (lower.some((name, i) => name !== \"\" && lower.indexOf(name) !== i)) {\n warnings.push({\n code: \"duplicate-header\",\n message: \"Repeated column headers were numbered\",\n });\n }\n body = rows.slice(1);\n } else {\n widest = Math.max(...rows.map((row) => row.length));\n fields = Array.from(\n { length: Math.min(widest, maxFields) },\n (_, i) => `Column ${i + 1}`,\n );\n body = rows;\n }\n if (widest > maxFields) {\n warnings.push({\n code: \"fields-truncated\",\n message: `Only the first ${maxFields} fields were kept`,\n });\n }\n if (body.length > maxRecords) {\n warnings.push({\n code: \"records-truncated\",\n message: `Only the first ${maxRecords} of ${body.length} records were kept`,\n });\n body = body.slice(0, maxRecords);\n }\n let ragged = 0;\n const records = body.map((row) => {\n if (hasHeader && row.length !== widest) ragged++;\n const record: MergeRecord = {};\n fields.forEach((field, i) => setField(record, field, cellText(row[i])));\n return record;\n });\n if (ragged > 0) {\n warnings.push({\n code: \"ragged-rows\",\n message: `${ragged} record${ragged === 1 ? \" was\" : \"s were\"} padded or cut to ${widest} fields`,\n count: ragged,\n });\n }\n if (records.length === 0) {\n warnings.push({ code: \"empty\", message: \"The data source has no records\" });\n }\n return { fields, records, warnings };\n}\n\n/** Lower-cases and drops spaces, `_`, `-` and `.` so `First Name` matches `first_name`. */\nexport function normalizeFieldKey(name: string): string {\n return name.toLowerCase().replace(/[\\s_.\\-]+/g, \"\");\n}\n\n/**\n * Built-in alias groups for `matchFields`: any name in a group matches any\n * other (after `normalizeFieldKey`). Extended from Word's address-block\n * field detection.\n */\nexport const FIELD_ALIASES: ReadonlyArray<ReadonlyArray<string>> = [\n [\"FirstName\", \"First\", \"GivenName\", \"Forename\", \"FName\"],\n [\"LastName\", \"Last\", \"Surname\", \"FamilyName\", \"LName\"],\n [\"Name\", \"FullName\", \"DisplayName\"],\n [\"Company\", \"CompanyName\", \"Organization\", \"Organisation\", \"Employer\", \"Business\"],\n [\"Address\", \"AddressLine1\", \"Address1\", \"Street\", \"StreetAddress\"],\n [\"City\", \"Town\"],\n [\"State\", \"Province\", \"Region\", \"County\"],\n [\"PostalCode\", \"ZIP\", \"ZipCode\", \"Postcode\"],\n [\"Country\", \"CountryName\", \"Nation\"],\n [\"Title\", \"CourtesyTitle\", \"Salutation\", \"Honorific\"],\n [\"Email\", \"EmailAddress\", \"Mail\", \"EMail\"],\n [\"Phone\", \"PhoneNumber\", \"Telephone\", \"Tel\", \"Mobile\"],\n];\n\nexport type FieldMatchKind = \"exact\" | \"normalized\" | \"alias\";\n\nexport interface FieldMatch {\n variable: string;\n /** The matched field, or `null`. */\n field: string | null;\n kind: FieldMatchKind | null;\n}\n\nexport interface FieldMapping {\n /** Variable → field (or `null` when unmatched), in variable order. */\n mapping: Record<string, string | null>;\n matches: FieldMatch[];\n unmatchedVariables: string[];\n /** Fields no variable maps to. */\n unusedFields: string[];\n}\n\nexport interface MatchFieldsOptions {\n /** Extra alias groups, checked after the built-in ones. */\n aliases?: ReadonlyArray<ReadonlyArray<string>>;\n}\n\n/**\n * Maps prompt variables to data source fields: an exact name first, then a\n * case-, space- and punctuation-insensitive match (`first_name` ↔\n * `First Name`), then an alias (`FirstName` ↔ `First` / `GivenName`, see\n * `FIELD_ALIASES`). Several variables may map to the same field.\n */\nexport function matchFields(\n variables: ReadonlyArray<string | { name: string }>,\n fields: ReadonlyArray<string>,\n options: MatchFieldsOptions = {},\n): FieldMapping {\n const names = Array.from(\n new Set(variables.map((v) => (typeof v === \"string\" ? v : v.name))),\n );\n const byKey = new Map<string, string>();\n for (const field of fields) {\n const key = normalizeFieldKey(field);\n if (!byKey.has(key)) byKey.set(key, field);\n }\n const groups = [...FIELD_ALIASES, ...(options.aliases ?? [])].map((group) =>\n group.map(normalizeFieldKey),\n );\n\n const matches: FieldMatch[] = names.map((variable) => {\n if (fields.includes(variable)) {\n return { variable, field: variable, kind: \"exact\" };\n }\n const key = normalizeFieldKey(variable);\n const normalized = byKey.get(key);\n if (normalized !== undefined) {\n return { variable, field: normalized, kind: \"normalized\" };\n }\n for (const group of groups) {\n if (!group.includes(key)) continue;\n for (const alias of group) {\n const field = byKey.get(alias);\n if (field !== undefined) return { variable, field, kind: \"alias\" };\n }\n }\n return { variable, field: null, kind: null };\n });\n\n const mapping: Record<string, string | null> = {};\n for (const match of matches) {\n Object.defineProperty(mapping, match.variable, {\n value: match.field,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n }\n const used = new Set(matches.map((m) => m.field));\n return {\n mapping,\n matches,\n unmatchedVariables: matches.filter((m) => !m.field).map((m) => m.variable),\n unusedFields: fields.filter((field) => !used.has(field)),\n };\n}\n\nexport interface RecordToValuesOptions {\n /**\n * Treat blank cells as missing so `{name|default}` falls back to its\n * default (default `true`). With `false`, a blank cell renders as empty.\n */\n blankAsMissing?: boolean;\n}\n\n/**\n * The prompt values for one record: every field under its own name, then\n * each mapped variable set from its field (a `null` mapping leaves the\n * variable unset even if a field shares its name).\n */\nexport function recordToValues(\n record: MergeRecord,\n mapping: Readonly<Record<string, string | null | undefined>> = {},\n options: RecordToValuesOptions = {},\n): PromptValues {\n const blankAsMissing = options.blankAsMissing ?? true;\n const values: PromptValues = {};\n const assign = (name: string, value: string | undefined) => {\n if (value === undefined || (blankAsMissing && value.trim() === \"\")) {\n delete values[name];\n return;\n }\n Object.defineProperty(values, name, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n };\n const own = (name: string) =>\n Object.prototype.hasOwnProperty.call(record, name) ? record[name] : undefined;\n for (const field of Object.keys(record)) assign(field, record[field]);\n for (const variable of Object.keys(mapping)) {\n const field = mapping[variable];\n if (field === undefined) continue;\n assign(variable, field === null ? undefined : own(field));\n }\n return values;\n}\n","// Portions ported from wordcraft/crates/engine/src/cmd/mailings.rs\n// (MIT OR Apache-2.0, © ArtCraft Team and contributors). See\n// THIRD_PARTY_NOTICES.md.\n\n/**\n * Multi-record merge: resolves one prompt per data source record with\n * `resolvePrompt`, like Word's Finish & Merge.\n */\nimport type { PromptDiagnostic, PromptValues, Template } from \"./tokens\";\nimport type { SourceMapSegment } from \"./sourcemap\";\nimport { resolvePrompt } from \"./engine\";\nimport { extractVariables } from \"./fields\";\nimport {\n evaluateConditionExpression,\n parseConditionExpression,\n} from \"./variables\";\nimport {\n matchFields,\n parseDataSource,\n recordToValues,\n type DataSourceInput,\n type MergeDataSource,\n type MergeRecord,\n type ParseDataSourceOptions,\n} from \"./data-source\";\n\n/** Default cap on merged outputs. */\nexport const MAX_MERGE_OUTPUTS = 10_000;\n/** Default cap on the total length of merged text (characters). */\nexport const MAX_MERGE_LENGTH = 20_000_000;\n\nexport interface MergeRecordsOptions {\n /** Templates for `{{name}}` tags. */\n templates?: Template[];\n /** Template version bindings. */\n bindings?: Record<string, string>;\n /** Maximum template nesting depth (default 10). */\n maxDepth?: number;\n /**\n * Variable → field. Variables left out are matched automatically with\n * `matchFields`; a `null` entry leaves the variable unset.\n */\n mapping?: Record<string, string | null>;\n /**\n * Text placed between outputs in the joined `text`: default `\"\\n\\n\"`.\n * `\"none\"` joins them with nothing in between.\n */\n separator?: string;\n /**\n * Drops a record's output when this holds. Either a function of the record\n * (and its prompt values), or a condition written like the inside of a\n * section tag: `void`, `!email`, `status=cancelled`, `amount<10`. The\n * condition is evaluated against the record's prompt values (mapped\n * variables and raw field names); a name that is neither is matched to a\n * field like a variable (`status` → `Status`).\n */\n skipIf?:\n | string\n | ((record: MergeRecord, values: PromptValues, index: number) => boolean);\n /** Stop after this many outputs (default `MAX_MERGE_OUTPUTS`). */\n maxOutputs?: number;\n /** Stop once the outputs reach this many characters (default `MAX_MERGE_LENGTH`). */\n maxLength?: number;\n /** First record to merge, 1-based (default 1). */\n from?: number;\n /** Last record to merge, 1-based and inclusive (default: the last). */\n to?: number;\n /** See `recordToValues` (default `true`: blank cells use defaults). */\n blankAsMissing?: boolean;\n /** Options for parsing `source` when it is not already parsed. */\n parse?: ParseDataSourceOptions;\n}\n\nexport interface MergeOutput {\n /** 0-based index of the record in the data source. */\n index: number;\n record: MergeRecord;\n /** The values the prompt was resolved with. */\n values: PromptValues;\n text: string;\n map: SourceMapSegment[];\n}\n\nexport interface MergeSkip {\n index: number;\n record: MergeRecord;\n reason: \"skip-if\";\n}\n\nexport type MergeDiagnosticCode =\n | PromptDiagnostic[\"code\"]\n | \"unmapped-variable\"\n | \"invalid-skip-condition\"\n | \"max-outputs\"\n | \"max-length\"\n | \"data-source\";\n\n/** A grammar diagnostic from the template, or a merge-level problem. */\nexport interface MergeDiagnostic {\n code: MergeDiagnosticCode;\n message: string;\n /** Variable, for grammar and `unmapped-variable` diagnostics. */\n name?: string;\n start?: number;\n end?: number;\n templateName?: string;\n}\n\nexport interface MergeRecordsResult {\n outputs: MergeOutput[];\n skipped: MergeSkip[];\n diagnostics: MergeDiagnostic[];\n /** Every output's text joined with the separator. */\n text: string;\n /** The variable → field mapping used. */\n mapping: Record<string, string | null>;\n /** The merge stopped early at `maxOutputs` or `maxLength`. */\n truncated: boolean;\n}\n\nfunction isParsedSource(source: unknown): source is MergeDataSource {\n return (\n !!source &&\n typeof source === \"object\" &&\n !Array.isArray(source) &&\n Array.isArray((source as MergeDataSource).fields) &&\n Array.isArray((source as MergeDataSource).records)\n );\n}\n\n/**\n * Merges a prompt with every record of a data source: one resolved output per\n * record, in order. Fields are matched to the prompt's variables\n * automatically (override with `mapping`), blank cells fall back to the\n * variable's default, `{?field}` / `{?field>3}` sections are evaluated per\n * record, and `skipIf` drops records. Grammar diagnostics in the prompt are\n * reported once. Never throws.\n */\nexport function mergeRecords(\n template: string,\n source: MergeDataSource | DataSourceInput,\n options: MergeRecordsOptions = {},\n): MergeRecordsResult {\n const data = isParsedSource(source)\n ? source\n : parseDataSource(source, options.parse);\n const diagnostics: MergeDiagnostic[] = [];\n if (!isParsedSource(source)) {\n for (const warning of data.warnings) {\n if (warning.code === \"empty\") continue;\n diagnostics.push({ code: \"data-source\", message: warning.message });\n }\n }\n\n const variables = extractVariables(template, {\n templates: options.templates,\n bindings: options.bindings,\n maxDepth: options.maxDepth,\n });\n const auto = matchFields(variables, data.fields).mapping;\n const mapping: Record<string, string | null> = { ...auto };\n for (const [variable, field] of Object.entries(options.mapping ?? {})) {\n Object.defineProperty(mapping, variable, {\n value: field,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n }\n for (const variable of variables) {\n const field = mapping[variable.name];\n if (\n (field === null || field === undefined) &&\n variable.defaultValue === undefined &&\n variable.usedAsValue\n ) {\n diagnostics.push({\n code: \"unmapped-variable\",\n message: `No field is mapped to \"${variable.name}\", so it stays as a placeholder`,\n name: variable.name,\n });\n }\n }\n\n let skip: ((record: MergeRecord, values: PromptValues, index: number) => boolean) | undefined;\n if (typeof options.skipIf === \"function\") {\n skip = options.skipIf;\n } else if (typeof options.skipIf === \"string\" && options.skipIf.trim()) {\n const parsed = parseConditionExpression(options.skipIf);\n if (\"error\" in parsed) {\n diagnostics.push({\n code: \"invalid-skip-condition\",\n message: `Invalid skipIf condition \"${options.skipIf}\": ${parsed.error}`,\n });\n } else {\n // The condition may name a field the template never uses: match it to\n // a field the same way variables are (`status` → `Status`).\n const field =\n mapping[parsed.name] ??\n matchFields([parsed.name], data.fields).mapping[parsed.name];\n skip = (record, values) => {\n if (\n field &&\n !Object.prototype.hasOwnProperty.call(values, parsed.name) &&\n Object.prototype.hasOwnProperty.call(record, field)\n ) {\n return evaluateConditionExpression(parsed, {\n ...values,\n [parsed.name]: record[field],\n });\n }\n return evaluateConditionExpression(parsed, values);\n };\n }\n }\n\n const separator =\n options.separator === undefined\n ? \"\\n\\n\"\n : options.separator === \"none\"\n ? \"\"\n : options.separator;\n const maxOutputs = Math.max(0, options.maxOutputs ?? MAX_MERGE_OUTPUTS);\n const maxLength = Math.max(0, options.maxLength ?? MAX_MERGE_LENGTH);\n const first = Math.max(1, Math.floor(options.from ?? 1)) - 1;\n const last = Math.min(\n data.records.length,\n Math.floor(options.to ?? data.records.length),\n );\n\n const outputs: MergeOutput[] = [];\n const skipped: MergeSkip[] = [];\n let length = 0;\n let truncated = false;\n let reportedGrammar = false;\n\n for (let index = first; index < last; index++) {\n const record = data.records[index];\n const values = recordToValues(record, mapping, {\n blankAsMissing: options.blankAsMissing,\n });\n if (skip?.(record, values, index)) {\n skipped.push({ index, record, reason: \"skip-if\" });\n continue;\n }\n if (outputs.length >= maxOutputs) {\n truncated = true;\n diagnostics.push({\n code: \"max-outputs\",\n message: `Stopped after ${maxOutputs} outputs`,\n });\n break;\n }\n const resolved = resolvePrompt(template, options.templates, {\n bindings: options.bindings,\n maxDepth: options.maxDepth,\n values,\n });\n if (!reportedGrammar) {\n // The template's grammar diagnostics are the same for every record.\n reportedGrammar = true;\n diagnostics.push(...resolved.diagnostics);\n }\n const added = resolved.text.length + (outputs.length > 0 ? separator.length : 0);\n if (length + added > maxLength) {\n truncated = true;\n diagnostics.push({\n code: \"max-length\",\n message: `Stopped at ${outputs.length} outputs: the merged text would exceed ${maxLength} characters`,\n });\n break;\n }\n length += added;\n outputs.push({ index, record, values, text: resolved.text, map: resolved.map });\n }\n\n return {\n outputs,\n skipped,\n diagnostics,\n text: outputs.map((output) => output.text).join(separator),\n mapping,\n truncated,\n };\n}\n\n/**\n * Serializes merge outputs for copying or download: `\"text\"` joins them with\n * `separator` (default `\"\\n\\n\"`), `\"json\"` writes an array of\n * `{ record, text }` objects (`record` is 1-based).\n */\nexport function serializeMergeOutputs(\n outputs: ReadonlyArray<Pick<MergeOutput, \"index\" | \"text\" | \"values\">>,\n format: \"text\" | \"json\" = \"text\",\n separator = \"\\n\\n\",\n): string {\n if (format === \"json\") {\n return JSON.stringify(\n outputs.map((output) => ({ record: output.index + 1, text: output.text })),\n null,\n 2,\n );\n }\n return outputs.map((output) => output.text).join(separator === \"none\" ? \"\" : separator);\n}\n"]}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// src/grammar/tokens.ts
|
|
2
|
+
var TEMPLATE_REGEX = /{{\s*([^}]+?)\s*}}/g;
|
|
3
|
+
var VARIABLE_REGEX = /(?<!\{)\{(?!\})((?:\\[\s\S]|[^{}\\])*\\?)\}(?!\})/g;
|
|
4
|
+
var INSTRUCTION_REGEX = /__(.+?)__/g;
|
|
5
|
+
var templateRegex = TEMPLATE_REGEX;
|
|
6
|
+
var variableRegex = VARIABLE_REGEX;
|
|
7
|
+
var instructionRegex = INSTRUCTION_REGEX;
|
|
8
|
+
|
|
9
|
+
// src/grammar/variables.ts
|
|
10
|
+
var CONDITIONAL_PREFIXES = {
|
|
11
|
+
"?": "if",
|
|
12
|
+
"!": "unless",
|
|
13
|
+
"/": "end"
|
|
14
|
+
};
|
|
15
|
+
function unescapeVariableText(text) {
|
|
16
|
+
return text.replace(/\\([\\{}|])/g, "$1");
|
|
17
|
+
}
|
|
18
|
+
function escapeVariableText(text) {
|
|
19
|
+
return text.replace(/[\\{}|]/g, (ch) => `\\${ch}`);
|
|
20
|
+
}
|
|
21
|
+
function splitUnescapedPipes(body, limit) {
|
|
22
|
+
const parts = [];
|
|
23
|
+
let current = "";
|
|
24
|
+
for (let i = 0; i < body.length; i++) {
|
|
25
|
+
const ch = body[i];
|
|
26
|
+
if (ch === "\\" && i + 1 < body.length) {
|
|
27
|
+
current += ch + body[i + 1];
|
|
28
|
+
i++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (ch === "|" && parts.length < limit - 1) {
|
|
32
|
+
parts.push(current);
|
|
33
|
+
current = "";
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
current += ch;
|
|
37
|
+
}
|
|
38
|
+
parts.push(current);
|
|
39
|
+
return parts;
|
|
40
|
+
}
|
|
41
|
+
function parseVariableBody(inner) {
|
|
42
|
+
const prefix = CONDITIONAL_PREFIXES[inner[0]];
|
|
43
|
+
if (prefix) {
|
|
44
|
+
const name = inner.slice(1).trim();
|
|
45
|
+
if (name && !/[|\\]/.test(name)) {
|
|
46
|
+
return { kind: "conditional", conditional: prefix, name };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const [rawName, rawDefault, rawDescription] = splitUnescapedPipes(inner, 3);
|
|
50
|
+
const parsed = {
|
|
51
|
+
kind: "variable",
|
|
52
|
+
name: unescapeVariableText(rawName.trim())
|
|
53
|
+
};
|
|
54
|
+
if (rawDefault !== void 0) {
|
|
55
|
+
parsed.defaultValue = unescapeVariableText(rawDefault.trim());
|
|
56
|
+
}
|
|
57
|
+
if (rawDescription !== void 0) {
|
|
58
|
+
parsed.description = unescapeVariableText(rawDescription.trim());
|
|
59
|
+
}
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
function isPromptValueSet(value) {
|
|
63
|
+
if (value === null || value === void 0 || value === false) return false;
|
|
64
|
+
return String(value).trim() !== "";
|
|
65
|
+
}
|
|
66
|
+
function getSuppliedValue(values, name) {
|
|
67
|
+
if (!Object.prototype.hasOwnProperty.call(values, name)) return void 0;
|
|
68
|
+
const value = values[name];
|
|
69
|
+
if (value === null || value === void 0) return void 0;
|
|
70
|
+
return String(value);
|
|
71
|
+
}
|
|
72
|
+
function evaluateCondition(conditional, name, values, comparison) {
|
|
73
|
+
const holds = comparison ? compareConditionValues(
|
|
74
|
+
getSuppliedValue(values, name) ?? "",
|
|
75
|
+
comparison.operator,
|
|
76
|
+
comparison.operand
|
|
77
|
+
) : isPromptValueSet(values[name]);
|
|
78
|
+
return conditional === "if" ? holds : !holds;
|
|
79
|
+
}
|
|
80
|
+
var NUMBER_PATTERN = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
81
|
+
function parseComparableNumber(text) {
|
|
82
|
+
const trimmed = text.trim();
|
|
83
|
+
if (!NUMBER_PATTERN.test(trimmed)) return void 0;
|
|
84
|
+
const value = Number(trimmed);
|
|
85
|
+
return Number.isFinite(value) ? value : void 0;
|
|
86
|
+
}
|
|
87
|
+
function compareConditionValues(left, operator, right) {
|
|
88
|
+
const a = parseComparableNumber(left);
|
|
89
|
+
const b = parseComparableNumber(right);
|
|
90
|
+
let order;
|
|
91
|
+
if (a !== void 0 && b !== void 0) {
|
|
92
|
+
order = a === b ? 0 : a < b ? -1 : 1;
|
|
93
|
+
} else {
|
|
94
|
+
const x = left.toLowerCase();
|
|
95
|
+
const y = right.toLowerCase();
|
|
96
|
+
order = x === y ? 0 : x < y ? -1 : 1;
|
|
97
|
+
}
|
|
98
|
+
switch (operator) {
|
|
99
|
+
case "=":
|
|
100
|
+
return order === 0;
|
|
101
|
+
case "<>":
|
|
102
|
+
return order !== 0;
|
|
103
|
+
case "<":
|
|
104
|
+
return order < 0;
|
|
105
|
+
case ">":
|
|
106
|
+
return order > 0;
|
|
107
|
+
case "<=":
|
|
108
|
+
return order <= 0;
|
|
109
|
+
case ">=":
|
|
110
|
+
return order >= 0;
|
|
111
|
+
default:
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
var COMPARISON_PATTERN = /^([^=<>!]+?)\s*(<>|<=|>=|!=|=|<|>)\s*([\s\S]*)$/;
|
|
116
|
+
function conditionSubject(name) {
|
|
117
|
+
const match = COMPARISON_PATTERN.exec(name.trim());
|
|
118
|
+
if (!match) return void 0;
|
|
119
|
+
return match[1].trim() || void 0;
|
|
120
|
+
}
|
|
121
|
+
function parseComparison(text) {
|
|
122
|
+
const match = COMPARISON_PATTERN.exec(text.trim());
|
|
123
|
+
if (!match) return void 0;
|
|
124
|
+
const subject = match[1].trim();
|
|
125
|
+
if (!subject) return void 0;
|
|
126
|
+
const written = match[2];
|
|
127
|
+
const operator = written === "!=" ? "<>" : written;
|
|
128
|
+
const rest = match[3];
|
|
129
|
+
if (rest === "") {
|
|
130
|
+
return {
|
|
131
|
+
subject,
|
|
132
|
+
error: `Missing value after "${written}" (write "" to compare with an empty value)`
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (rest.startsWith('"')) {
|
|
136
|
+
if (rest.length < 2 || !rest.endsWith('"')) {
|
|
137
|
+
return { subject, error: `Unterminated quote in ${rest}` };
|
|
138
|
+
}
|
|
139
|
+
return { subject, comparison: { operator, operand: rest.slice(1, -1) } };
|
|
140
|
+
}
|
|
141
|
+
if (/^[=<>!]/.test(rest)) {
|
|
142
|
+
return {
|
|
143
|
+
subject,
|
|
144
|
+
error: `Unexpected "${rest[0]}" after "${written}"; operators are = <> < > <= >=`
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return { subject, comparison: { operator, operand: rest } };
|
|
148
|
+
}
|
|
149
|
+
function formatComparisonOperand(operand) {
|
|
150
|
+
return operand === "" || operand !== operand.trim() || /^[=<>!"]/.test(operand) ? `"${operand}"` : operand;
|
|
151
|
+
}
|
|
152
|
+
function formatConditionalTag(tag) {
|
|
153
|
+
const prefix = tag.conditional === "unless" ? "!" : tag.conditional === "end" ? "/" : "?";
|
|
154
|
+
const comparison = tag.comparison ? `${tag.comparison.operator}${formatComparisonOperand(tag.comparison.operand)}` : "";
|
|
155
|
+
return `{${prefix}${tag.value}${comparison}}`;
|
|
156
|
+
}
|
|
157
|
+
function describeConditionalTag(tag) {
|
|
158
|
+
if (tag.conditional === "end") return `End of section: ${tag.value}`;
|
|
159
|
+
const shown = tag.conditional === "unless" ? "Hidden" : "Shown";
|
|
160
|
+
if (tag.comparison) {
|
|
161
|
+
return `${shown} when "${tag.value}" ${tag.comparison.operator} ${formatComparisonOperand(tag.comparison.operand)}`;
|
|
162
|
+
}
|
|
163
|
+
return tag.conditional === "unless" ? `Shown when "${tag.value}" is empty` : `Shown when "${tag.value}" has a value`;
|
|
164
|
+
}
|
|
165
|
+
function parseConditionExpression(text) {
|
|
166
|
+
let body = text.trim();
|
|
167
|
+
let negated = false;
|
|
168
|
+
if (body.startsWith("!")) {
|
|
169
|
+
negated = true;
|
|
170
|
+
body = body.slice(1).trim();
|
|
171
|
+
} else if (body.startsWith("?")) {
|
|
172
|
+
body = body.slice(1).trim();
|
|
173
|
+
}
|
|
174
|
+
if (!body) return { error: "Empty condition" };
|
|
175
|
+
const parsed = parseComparison(body);
|
|
176
|
+
if (parsed?.error !== void 0) return { error: parsed.error };
|
|
177
|
+
if (parsed) {
|
|
178
|
+
return { negated, name: parsed.subject, comparison: parsed.comparison };
|
|
179
|
+
}
|
|
180
|
+
if (/[=<>!]/.test(body)) {
|
|
181
|
+
return { error: `Malformed condition: ${text.trim()}` };
|
|
182
|
+
}
|
|
183
|
+
return { negated, name: body };
|
|
184
|
+
}
|
|
185
|
+
function evaluateConditionExpression(expression, values) {
|
|
186
|
+
return evaluateCondition(
|
|
187
|
+
expression.negated ? "unless" : "if",
|
|
188
|
+
expression.name,
|
|
189
|
+
values,
|
|
190
|
+
expression.comparison
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
function matchConditionalTags(tags) {
|
|
194
|
+
const result = tags.map(() => ({ partner: -1 }));
|
|
195
|
+
const stack = [];
|
|
196
|
+
const subjects = tags.map(
|
|
197
|
+
(tag) => tag.conditional === "end" ? void 0 : conditionSubject(tag.name)
|
|
198
|
+
);
|
|
199
|
+
tags.forEach((tag, index) => {
|
|
200
|
+
if (tag.conditional !== "end") {
|
|
201
|
+
stack.push(index);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
let depth = stack.length - 1;
|
|
205
|
+
while (depth >= 0 && tags[stack[depth]].name !== tag.name && subjects[stack[depth]] !== tag.name) {
|
|
206
|
+
depth--;
|
|
207
|
+
}
|
|
208
|
+
if (depth < 0) {
|
|
209
|
+
result[index].error = "unexpected-close";
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
for (const skipped of stack.splice(depth + 1)) {
|
|
213
|
+
result[skipped].error = "unclosed-section";
|
|
214
|
+
}
|
|
215
|
+
const open = stack.pop();
|
|
216
|
+
if (tags[open].name !== tag.name) {
|
|
217
|
+
const comparison = parseComparison(tags[open].name);
|
|
218
|
+
result[open].comparison = result[index].comparison = comparison;
|
|
219
|
+
if (comparison.error !== void 0) {
|
|
220
|
+
result[open].error = result[index].error = "invalid-condition";
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
result[open].partner = index;
|
|
225
|
+
result[index].partner = open;
|
|
226
|
+
});
|
|
227
|
+
for (const open of stack) {
|
|
228
|
+
result[open].error = "unclosed-section";
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export {
|
|
234
|
+
TEMPLATE_REGEX,
|
|
235
|
+
VARIABLE_REGEX,
|
|
236
|
+
INSTRUCTION_REGEX,
|
|
237
|
+
templateRegex,
|
|
238
|
+
variableRegex,
|
|
239
|
+
instructionRegex,
|
|
240
|
+
unescapeVariableText,
|
|
241
|
+
escapeVariableText,
|
|
242
|
+
parseVariableBody,
|
|
243
|
+
isPromptValueSet,
|
|
244
|
+
getSuppliedValue,
|
|
245
|
+
evaluateCondition,
|
|
246
|
+
compareConditionValues,
|
|
247
|
+
conditionSubject,
|
|
248
|
+
parseComparison,
|
|
249
|
+
formatComparisonOperand,
|
|
250
|
+
formatConditionalTag,
|
|
251
|
+
describeConditionalTag,
|
|
252
|
+
parseConditionExpression,
|
|
253
|
+
evaluateConditionExpression,
|
|
254
|
+
matchConditionalTags
|
|
255
|
+
};
|
|
256
|
+
//# sourceMappingURL=chunk-P6ODPZ4W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/grammar/tokens.ts","../src/grammar/variables.ts"],"sourcesContent":["/**\n * Canonical regex patterns for prompt editing tokens.\n */\nexport const TEMPLATE_REGEX = /{{\\s*([^}]+?)\\s*}}/g;\n/**\n * `{name}`, `{name|default}`, `{name|default|description}` and the conditional\n * section tags `{?name}`, `{!name}` and `{/name}`. A backslash escapes the next\n * character, so `\\}`, `\\{` and `\\|` can appear inside defaults and descriptions.\n * Without backslashes this matches exactly what the original `[^{}]+` pattern did.\n */\nexport const VARIABLE_REGEX =\n /(?<!\\{)\\{(?!\\})((?:\\\\[\\s\\S]|[^{}\\\\])*\\\\?)\\}(?!\\})/g;\nexport const INSTRUCTION_REGEX = /__(.+?)__/g;\n\n// Backward-compatible camelCase aliases\nexport const templateRegex = TEMPLATE_REGEX;\nexport const variableRegex = VARIABLE_REGEX;\nexport const instructionRegex = INSTRUCTION_REGEX;\n\nexport type TokenType =\n | \"template\"\n | \"variable\"\n | \"instruction\"\n | \"conditional\"\n | \"text\";\n\n/**\n * Kind of a conditional section tag: `{?name}` opens a section kept when\n * `name` has a value, `{!name}` opens one kept when it has none, and\n * `{/name}` closes either.\n */\nexport type ConditionalKind = \"if\" | \"unless\" | \"end\";\n\nexport interface Token {\n type: TokenType;\n raw: string;\n /** Trimmed name for templates, variables and conditional tags; content otherwise. */\n value: string;\n start: number;\n end: number;\n /** Variables only: unescaped text after the first `|` (may be empty). */\n defaultValue?: string;\n /** Variables only: unescaped text after the second `|` (fill-in prompt). */\n description?: string;\n /** Conditional tags only. */\n conditional?: ConditionalKind;\n /**\n * Opening conditional tags written as a comparison (`{?amount>100}`,\n * closed by `{/amount}`) only. `value` is then the variable name.\n */\n comparison?: ConditionComparison;\n}\n\n/** Operators accepted in a comparison condition such as `{?amount>=100}`. */\nexport type ComparisonOperator = \"=\" | \"<>\" | \"<\" | \">\" | \"<=\" | \">=\";\n\n/** The comparison part of a conditional tag: `{?status=paid}` → `{ operator: \"=\", operand: \"paid\" }`. */\nexport interface ConditionComparison {\n operator: ComparisonOperator;\n /** Right-hand side, unquoted. */\n operand: string;\n}\n\n/** Values supplied for `{variable}` slots and `{?variable}` conditions. */\nexport type PromptValue = string | number | boolean | null | undefined;\nexport type PromptValues = Record<string, PromptValue>;\n\nexport type PromptDiagnosticCode =\n | \"unclosed-section\"\n | \"unexpected-close\"\n | \"invalid-condition\";\n\n/**\n * A recoverable grammar problem. The offending tag is treated as literal text.\n * Offsets are relative to the text that contained the tag: the input prompt,\n * or the body of `templateName` when set.\n */\nexport interface PromptDiagnostic {\n code: PromptDiagnosticCode;\n message: string;\n /** Variable named by the tag. */\n name: string;\n start: number;\n end: number;\n templateName?: string;\n}\n\nexport interface TemplateVersion {\n id: string;\n version: string | number;\n body: string;\n createdAt?: any;\n [key: string]: any;\n}\n\nexport interface Template {\n id?: string;\n name: string;\n body?: string;\n latestVersion?: string | number;\n versions?: TemplateVersion[];\n color?: string;\n [key: string]: any;\n}\n\nexport interface ColorGradient {\n from: string;\n to: string;\n}\n","// Portions ported from wordcraft/crates/engine/src/cmd/mailings.rs (MIT OR\n// Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\nimport type {\n ComparisonOperator,\n ConditionComparison,\n ConditionalKind,\n PromptValue,\n PromptValues,\n} from \"./tokens\";\n\n/** Result of classifying the text between the braces of a `{…}` token. */\nexport type ParsedVariableBody =\n | {\n kind: \"variable\";\n name: string;\n defaultValue?: string;\n description?: string;\n }\n | {\n kind: \"conditional\";\n conditional: ConditionalKind;\n name: string;\n };\n\nconst CONDITIONAL_PREFIXES: Record<string, ConditionalKind> = {\n \"?\": \"if\",\n \"!\": \"unless\",\n \"/\": \"end\",\n};\n\n/**\n * Removes grammar escapes: `\\|`, `\\{`, `\\}` and `\\\\` become the bare character.\n * Any other backslash is kept, so Windows paths and regex-like text survive.\n */\nexport function unescapeVariableText(text: string): string {\n return text.replace(/\\\\([\\\\{}|])/g, \"$1\");\n}\n\n/**\n * Escapes text so it can be embedded as a variable default or description.\n */\nexport function escapeVariableText(text: string): string {\n return text.replace(/[\\\\{}|]/g, (ch) => `\\\\${ch}`);\n}\n\n/** Splits on `|` characters that are not escaped with a backslash. */\nfunction splitUnescapedPipes(body: string, limit: number): string[] {\n const parts: string[] = [];\n let current = \"\";\n for (let i = 0; i < body.length; i++) {\n const ch = body[i];\n if (ch === \"\\\\\" && i + 1 < body.length) {\n current += ch + body[i + 1];\n i++;\n continue;\n }\n if (ch === \"|\" && parts.length < limit - 1) {\n parts.push(current);\n current = \"\";\n continue;\n }\n current += ch;\n }\n parts.push(current);\n return parts;\n}\n\n/**\n * Classifies the inner text of a `{…}` token.\n *\n * - `{?name}`, `{!name}`, `{/name}` are conditional section tags. The name may\n * not contain `|`; otherwise the token is an ordinary variable.\n * - `{name|default|description}` is a variable. Each part is trimmed and\n * unescaped. A default is present whenever a `|` is (it may be empty), and\n * anything after the second unescaped `|` belongs to the description.\n */\nexport function parseVariableBody(inner: string): ParsedVariableBody {\n const prefix = CONDITIONAL_PREFIXES[inner[0]];\n if (prefix) {\n const name = inner.slice(1).trim();\n if (name && !/[|\\\\]/.test(name)) {\n return { kind: \"conditional\", conditional: prefix, name };\n }\n }\n\n const [rawName, rawDefault, rawDescription] = splitUnescapedPipes(inner, 3);\n const parsed: ParsedVariableBody = {\n kind: \"variable\",\n name: unescapeVariableText(rawName.trim()),\n };\n if (rawDefault !== undefined) {\n parsed.defaultValue = unescapeVariableText(rawDefault.trim());\n }\n if (rawDescription !== undefined) {\n parsed.description = unescapeVariableText(rawDescription.trim());\n }\n return parsed;\n}\n\n/**\n * Whether a supplied value counts as \"set\" for a `{?name}` condition:\n * not `null`/`undefined`/`false`, and not blank once stringified.\n */\nexport function isPromptValueSet(value: PromptValue): boolean {\n if (value === null || value === undefined || value === false) return false;\n return String(value).trim() !== \"\";\n}\n\n/**\n * Returns the text a supplied value substitutes, or `undefined` when the\n * variable should fall back to its default. An explicit empty string is a\n * value (it renders as nothing); `null`/`undefined` are not.\n */\nexport function getSuppliedValue(\n values: PromptValues,\n name: string,\n): string | undefined {\n if (!Object.prototype.hasOwnProperty.call(values, name)) return undefined;\n const value = values[name];\n if (value === null || value === undefined) return undefined;\n return String(value);\n}\n\n/**\n * Whether a section opened by `{?name}` / `{!name}` keeps its body. With a\n * `comparison` (from `{?name>3}`), the supplied value is compared instead of\n * tested for presence; a missing value compares as `\"\"`.\n */\nexport function evaluateCondition(\n conditional: Exclude<ConditionalKind, \"end\">,\n name: string,\n values: PromptValues,\n comparison?: ConditionComparison,\n): boolean {\n const holds = comparison\n ? compareConditionValues(\n getSuppliedValue(values, name) ?? \"\",\n comparison.operator,\n comparison.operand,\n )\n : isPromptValueSet(values[name]);\n return conditional === \"if\" ? holds : !holds;\n}\n\nconst NUMBER_PATTERN = /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?$/i;\n\n/** Parses a plain decimal number (surrounding whitespace allowed), or `undefined`. */\nfunction parseComparableNumber(text: string): number | undefined {\n const trimmed = text.trim();\n if (!NUMBER_PATTERN.test(trimmed)) return undefined;\n const value = Number(trimmed);\n return Number.isFinite(value) ? value : undefined;\n}\n\n/**\n * Compares two texts like a mail-merge `IF` field: as numbers when both parse\n * as plain decimal numbers (so `\"7\"` equals `\"7.0\"`), otherwise as\n * case-insensitive text.\n */\nexport function compareConditionValues(\n left: string,\n operator: ComparisonOperator,\n right: string,\n): boolean {\n const a = parseComparableNumber(left);\n const b = parseComparableNumber(right);\n let order: number;\n if (a !== undefined && b !== undefined) {\n order = a === b ? 0 : a < b ? -1 : 1;\n } else {\n const x = left.toLowerCase();\n const y = right.toLowerCase();\n order = x === y ? 0 : x < y ? -1 : 1;\n }\n switch (operator) {\n case \"=\":\n return order === 0;\n case \"<>\":\n return order !== 0;\n case \"<\":\n return order < 0;\n case \">\":\n return order > 0;\n case \"<=\":\n return order <= 0;\n case \">=\":\n return order >= 0;\n default:\n return false;\n }\n}\n\n/**\n * Subject, operator and rest of a comparison. The subject runs up to the\n * first `=`, `<`, `>` or `!=` and may not contain `!`.\n */\nconst COMPARISON_PATTERN = /^([^=<>!]+?)\\s*(<>|<=|>=|!=|=|<|>)\\s*([\\s\\S]*)$/;\n\n/**\n * The variable a conditional tag name compares when it is written as a\n * comparison (`amount>100` → `amount`), or `undefined`. A tag only acts as a\n * comparison when it is closed by `{/subject}`.\n */\nexport function conditionSubject(name: string): string | undefined {\n const match = COMPARISON_PATTERN.exec(name.trim());\n if (!match) return undefined;\n return match[1].trim() || undefined;\n}\n\nexport type ParsedComparison =\n | { subject: string; comparison: ConditionComparison; error?: undefined }\n | { subject: string; comparison?: undefined; error: string };\n\n/**\n * Parses a comparison condition such as `amount >= 100`, `status=paid`,\n * `status <> \"on hold\"` or `note=\"\"`. Returns `undefined` when `text` has no\n * subject and operator, or an `error` when the comparison is malformed\n * (missing value, doubled operator, unterminated quote). `!=` is accepted as\n * a synonym for `<>`.\n */\nexport function parseComparison(text: string): ParsedComparison | undefined {\n const match = COMPARISON_PATTERN.exec(text.trim());\n if (!match) return undefined;\n const subject = match[1].trim();\n if (!subject) return undefined;\n const written = match[2];\n const operator = (written === \"!=\" ? \"<>\" : written) as ComparisonOperator;\n const rest = match[3];\n if (rest === \"\") {\n return {\n subject,\n error: `Missing value after \"${written}\" (write \"\" to compare with an empty value)`,\n };\n }\n if (rest.startsWith('\"')) {\n if (rest.length < 2 || !rest.endsWith('\"')) {\n return { subject, error: `Unterminated quote in ${rest}` };\n }\n return { subject, comparison: { operator, operand: rest.slice(1, -1) } };\n }\n if (/^[=<>!]/.test(rest)) {\n return {\n subject,\n error: `Unexpected \"${rest[0]}\" after \"${written}\"; operators are = <> < > <= >=`,\n };\n }\n return { subject, comparison: { operator, operand: rest } };\n}\n\n/** Writes a comparison operand back as condition text, quoting it when needed. */\nexport function formatComparisonOperand(operand: string): string {\n return operand === \"\" ||\n operand !== operand.trim() ||\n /^[=<>!\"]/.test(operand)\n ? `\"${operand}\"`\n : operand;\n}\n\n/**\n * The display form of a conditional tag: `{?name}`, `{!name}`, `{/name}`, or\n * `{?amount>100}` for a comparison.\n */\nexport function formatConditionalTag(tag: {\n conditional?: ConditionalKind;\n value: string;\n comparison?: ConditionComparison;\n}): string {\n const prefix =\n tag.conditional === \"unless\" ? \"!\" : tag.conditional === \"end\" ? \"/\" : \"?\";\n const comparison = tag.comparison\n ? `${tag.comparison.operator}${formatComparisonOperand(tag.comparison.operand)}`\n : \"\";\n return `{${prefix}${tag.value}${comparison}}`;\n}\n\n/** A tooltip for a conditional tag, e.g. `Shown when \"amount\" > 100`. */\nexport function describeConditionalTag(tag: {\n conditional?: ConditionalKind;\n value: string;\n comparison?: ConditionComparison;\n}): string {\n if (tag.conditional === \"end\") return `End of section: ${tag.value}`;\n const shown = tag.conditional === \"unless\" ? \"Hidden\" : \"Shown\";\n if (tag.comparison) {\n return `${shown} when \"${tag.value}\" ${tag.comparison.operator} ${formatComparisonOperand(tag.comparison.operand)}`;\n }\n return tag.conditional === \"unless\"\n ? `Shown when \"${tag.value}\" is empty`\n : `Shown when \"${tag.value}\" has a value`;\n}\n\n/** A parsed standalone condition (see `parseConditionExpression`). */\nexport interface ConditionExpression {\n /** `true` for `!name` / `!name=value`: the condition is inverted. */\n negated: boolean;\n name: string;\n comparison?: ConditionComparison;\n}\n\n/**\n * Parses a condition written like the inside of a section tag, without the\n * braces: `vip` (has a value), `!vip` (has none), `amount>100`,\n * `!status=paid`. A leading `?` is allowed. Used by options such as\n * `mergeRecords({ skipIf })`. Returns `{ error }` for malformed text.\n */\nexport function parseConditionExpression(\n text: string,\n): ConditionExpression | { error: string } {\n let body = text.trim();\n let negated = false;\n if (body.startsWith(\"!\")) {\n negated = true;\n body = body.slice(1).trim();\n } else if (body.startsWith(\"?\")) {\n body = body.slice(1).trim();\n }\n if (!body) return { error: \"Empty condition\" };\n const parsed = parseComparison(body);\n if (parsed?.error !== undefined) return { error: parsed.error };\n if (parsed) {\n return { negated, name: parsed.subject, comparison: parsed.comparison };\n }\n if (/[=<>!]/.test(body)) {\n return { error: `Malformed condition: ${text.trim()}` };\n }\n return { negated, name: body };\n}\n\n/** Evaluates a parsed condition expression against values. */\nexport function evaluateConditionExpression(\n expression: ConditionExpression,\n values: PromptValues,\n): boolean {\n return evaluateCondition(\n expression.negated ? \"unless\" : \"if\",\n expression.name,\n values,\n expression.comparison,\n );\n}\n\nexport interface ConditionalTagLike {\n conditional: ConditionalKind;\n name: string;\n}\n\nexport interface ConditionalTagMatch {\n /** Index of the partner tag, or -1 when the tag is unbalanced. */\n partner: number;\n /** Set when unbalanced, or on both tags of a malformed comparison pair. */\n error?: \"unclosed-section\" | \"unexpected-close\" | \"invalid-condition\";\n /**\n * Set on both tags of a pair whose open tag is a comparison closed by its\n * subject (`{?amount>100}…{/amount}`).\n */\n comparison?: ParsedComparison;\n}\n\n/**\n * Pairs conditional tags given in document order. A close tag pairs with the\n * nearest open tag of the same name, or with the nearest open comparison tag\n * whose subject it names (`{?amount>100}` is closed by `{/amount}`; closing\n * it with `{/amount>100}` keeps the old meaning, a presence check on a\n * variable named `amount>100`). Open tags skipped over are reported as\n * unclosed, a close tag with no open partner as unexpected, and a malformed\n * comparison pair as an invalid condition. Unbalanced tags never throw:\n * callers treat them as literal text. The surviving pairs are always properly\n * nested.\n */\nexport function matchConditionalTags(\n tags: ConditionalTagLike[],\n): ConditionalTagMatch[] {\n const result: ConditionalTagMatch[] = tags.map(() => ({ partner: -1 }));\n const stack: number[] = [];\n const subjects = tags.map((tag) =>\n tag.conditional === \"end\" ? undefined : conditionSubject(tag.name),\n );\n\n tags.forEach((tag, index) => {\n if (tag.conditional !== \"end\") {\n stack.push(index);\n return;\n }\n let depth = stack.length - 1;\n while (\n depth >= 0 &&\n tags[stack[depth]].name !== tag.name &&\n subjects[stack[depth]] !== tag.name\n ) {\n depth--;\n }\n if (depth < 0) {\n result[index].error = \"unexpected-close\";\n return;\n }\n // Everything opened after the partner was never closed.\n for (const skipped of stack.splice(depth + 1)) {\n result[skipped].error = \"unclosed-section\";\n }\n const open = stack.pop()!;\n if (tags[open].name !== tag.name) {\n const comparison = parseComparison(tags[open].name)!;\n result[open].comparison = result[index].comparison = comparison;\n if (comparison.error !== undefined) {\n result[open].error = result[index].error = \"invalid-condition\";\n return;\n }\n }\n result[open].partner = index;\n result[index].partner = open;\n });\n\n for (const open of stack) {\n result[open].error = \"unclosed-section\";\n }\n return result;\n}\n"],"mappings":"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","names":[]}
|