@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.
Files changed (155) hide show
  1. package/README.md +96 -1
  2. package/THIRD_PARTY_NOTICES.md +51 -0
  3. package/dist/agent/index.cjs +9 -8
  4. package/dist/agent/index.cjs.map +1 -1
  5. package/dist/agent/index.d.cts +4 -3
  6. package/dist/agent/index.d.ts +4 -3
  7. package/dist/agent/index.js +3 -2
  8. package/dist/agent/index.js.map +1 -1
  9. package/dist/canvas/index.cjs +1210 -17
  10. package/dist/canvas/index.cjs.map +1 -1
  11. package/dist/canvas/index.d.cts +318 -1
  12. package/dist/canvas/index.d.ts +318 -1
  13. package/dist/canvas/index.js +1199 -6
  14. package/dist/canvas/index.js.map +1 -1
  15. package/dist/{chunk-QBJ22YZR.js → chunk-3CN2WD3X.js} +1354 -206
  16. package/dist/chunk-3CN2WD3X.js.map +1 -0
  17. package/dist/{chunk-JJDLPEWL.js → chunk-3LV6RRPH.js} +2 -2
  18. package/dist/{chunk-MJZBNWG7.js → chunk-5S3Z4PPF.js} +78 -557
  19. package/dist/chunk-5S3Z4PPF.js.map +1 -0
  20. package/dist/chunk-664LQYFJ.js +115 -0
  21. package/dist/chunk-664LQYFJ.js.map +1 -0
  22. package/dist/{chunk-AA4ZEVCC.cjs → chunk-D27YFC46.cjs} +101 -580
  23. package/dist/chunk-D27YFC46.cjs.map +1 -0
  24. package/dist/chunk-D6FH2YAX.cjs +131 -0
  25. package/dist/chunk-D6FH2YAX.cjs.map +1 -0
  26. package/dist/{chunk-4SUNJZWB.cjs → chunk-FD4LPFAE.cjs} +48 -200
  27. package/dist/chunk-FD4LPFAE.cjs.map +1 -0
  28. package/dist/chunk-FODNLX5N.js +749 -0
  29. package/dist/chunk-FODNLX5N.js.map +1 -0
  30. package/dist/{chunk-OYXSKQFR.js → chunk-FPENXAEJ.js} +35 -187
  31. package/dist/chunk-FPENXAEJ.js.map +1 -0
  32. package/dist/chunk-HLIMTC6N.js +1 -0
  33. package/dist/chunk-HLIMTC6N.js.map +1 -0
  34. package/dist/{chunk-QGHGJ56O.js → chunk-ITBSKI26.js} +1483 -19
  35. package/dist/chunk-ITBSKI26.js.map +1 -0
  36. package/dist/{chunk-XDMXXC4Z.cjs → chunk-MA2F5KHF.cjs} +350 -227
  37. package/dist/chunk-MA2F5KHF.cjs.map +1 -0
  38. package/dist/{chunk-RIKBJLS2.cjs → chunk-MCSJFWAO.cjs} +1356 -208
  39. package/dist/chunk-MCSJFWAO.cjs.map +1 -0
  40. package/dist/chunk-MKA3AHLV.cjs +256 -0
  41. package/dist/chunk-MKA3AHLV.cjs.map +1 -0
  42. package/dist/chunk-OLZJXL4R.cjs +749 -0
  43. package/dist/chunk-OLZJXL4R.cjs.map +1 -0
  44. package/dist/chunk-OXSV64JX.js +689 -0
  45. package/dist/chunk-OXSV64JX.js.map +1 -0
  46. package/dist/chunk-P3RQ5UJE.cjs +689 -0
  47. package/dist/chunk-P3RQ5UJE.cjs.map +1 -0
  48. package/dist/chunk-P6ODPZ4W.js +256 -0
  49. package/dist/chunk-P6ODPZ4W.js.map +1 -0
  50. package/dist/chunk-PGNZKFQS.js +685 -0
  51. package/dist/chunk-PGNZKFQS.js.map +1 -0
  52. package/dist/chunk-PL4JB3BF.cjs +1 -0
  53. package/dist/chunk-PL4JB3BF.cjs.map +1 -0
  54. package/dist/{chunk-GVSMRPHB.cjs → chunk-SBCZ6RN5.cjs} +1568 -104
  55. package/dist/chunk-SBCZ6RN5.cjs.map +1 -0
  56. package/dist/{chunk-CL37JGFW.cjs → chunk-SML64VJG.cjs} +4 -4
  57. package/dist/{chunk-CL37JGFW.cjs.map → chunk-SML64VJG.cjs.map} +1 -1
  58. package/dist/chunk-TNNJXF46.cjs +115 -0
  59. package/dist/chunk-TNNJXF46.cjs.map +1 -0
  60. package/dist/chunk-U5HZSFCS.js +131 -0
  61. package/dist/chunk-U5HZSFCS.js.map +1 -0
  62. package/dist/{chunk-K3R5NG2D.js → chunk-U5KWZU4X.js} +251 -128
  63. package/dist/chunk-U5KWZU4X.js.map +1 -0
  64. package/dist/chunk-XNORY7M6.cjs +685 -0
  65. package/dist/chunk-XNORY7M6.cjs.map +1 -0
  66. package/dist/commands/index.d.cts +5 -353
  67. package/dist/commands/index.d.ts +5 -353
  68. package/dist/comment-store-BEMgVlAA.d.cts +135 -0
  69. package/dist/comment-store-BEMgVlAA.d.ts +135 -0
  70. package/dist/compare/index.cjs +15 -0
  71. package/dist/compare/index.cjs.map +1 -0
  72. package/dist/compare/index.d.cts +58 -0
  73. package/dist/compare/index.d.ts +58 -0
  74. package/dist/compare/index.js +15 -0
  75. package/dist/compare/index.js.map +1 -0
  76. package/dist/{controller-pePCJLT1.d.cts → controller-BhLLAoi8.d.cts} +45 -4
  77. package/dist/{controller-pePCJLT1.d.ts → controller-BhLLAoi8.d.ts} +45 -4
  78. package/dist/documents-BQjzJNe2.d.ts +76 -0
  79. package/dist/documents-KRTPhvst.d.cts +76 -0
  80. package/dist/extensions/index.cjs +53 -5
  81. package/dist/extensions/index.cjs.map +1 -1
  82. package/dist/extensions/index.d.cts +7 -3
  83. package/dist/extensions/index.d.ts +7 -3
  84. package/dist/extensions/index.js +52 -4
  85. package/dist/generation/index.cjs +1373 -0
  86. package/dist/generation/index.cjs.map +1 -0
  87. package/dist/generation/index.d.cts +554 -0
  88. package/dist/generation/index.d.ts +554 -0
  89. package/dist/generation/index.js +1373 -0
  90. package/dist/generation/index.js.map +1 -0
  91. package/dist/grammar/index.cjs +48 -3
  92. package/dist/grammar/index.cjs.map +1 -1
  93. package/dist/grammar/index.d.cts +322 -100
  94. package/dist/grammar/index.d.ts +322 -100
  95. package/dist/grammar/index.js +54 -9
  96. package/dist/image-editor/index.cjs +1831 -30
  97. package/dist/image-editor/index.cjs.map +1 -1
  98. package/dist/image-editor/index.d.cts +590 -3
  99. package/dist/image-editor/index.d.ts +590 -3
  100. package/dist/image-editor/index.js +1807 -6
  101. package/dist/image-editor/index.js.map +1 -1
  102. package/dist/{index-CCr_z9kF.d.cts → index-BhJBSm2d.d.cts} +370 -4
  103. package/dist/{index-Ck6ajwXh.d.ts → index-fJrlkJCd.d.ts} +370 -4
  104. package/dist/index.cjs +162 -25
  105. package/dist/index.cjs.map +1 -1
  106. package/dist/index.d.cts +14 -8
  107. package/dist/index.d.ts +14 -8
  108. package/dist/index.js +174 -37
  109. package/dist/index.js.map +1 -1
  110. package/dist/migrate-iTlm3a8h.d.cts +112 -0
  111. package/dist/migrate-iTlm3a8h.d.ts +112 -0
  112. package/dist/persistence/index.cjs +676 -0
  113. package/dist/persistence/index.cjs.map +1 -0
  114. package/dist/persistence/index.d.cts +347 -0
  115. package/dist/persistence/index.d.ts +347 -0
  116. package/dist/persistence/index.js +676 -0
  117. package/dist/persistence/index.js.map +1 -0
  118. package/dist/{registry-D5-R90p0.d.cts → registry-BDe-vlv3.d.ts} +2 -40
  119. package/dist/registry-D32LpIc4.d.cts +354 -0
  120. package/dist/{registry-D5-R90p0.d.ts → registry-DnzpgY27.d.cts} +2 -40
  121. package/dist/registry-evKTP4ls.d.ts +354 -0
  122. package/dist/schema-DYDGdYL9.d.cts +41 -0
  123. package/dist/schema-DYDGdYL9.d.ts +41 -0
  124. package/dist/serializers/index.cjs +4 -3
  125. package/dist/serializers/index.cjs.map +1 -1
  126. package/dist/serializers/index.d.cts +2 -1
  127. package/dist/serializers/index.d.ts +2 -1
  128. package/dist/serializers/index.js +3 -2
  129. package/dist/sourcemap-C8AH3MBQ.d.cts +91 -0
  130. package/dist/sourcemap-DA1Evhrv.d.ts +91 -0
  131. package/dist/spreadsheet/index.cjs +37 -3
  132. package/dist/spreadsheet/index.cjs.map +1 -1
  133. package/dist/spreadsheet/index.d.cts +188 -17
  134. package/dist/spreadsheet/index.d.ts +188 -17
  135. package/dist/spreadsheet/index.js +43 -9
  136. package/dist/{tokens-CyLZmIei.d.cts → tokens-D7yXvUU6.d.cts} +15 -2
  137. package/dist/{tokens-CyLZmIei.d.ts → tokens-D7yXvUU6.d.ts} +15 -2
  138. package/dist/{track-changes-BSF58RK0.d.ts → track-changes-BK5sVf4_.d.cts} +18 -136
  139. package/dist/{track-changes-BSF58RK0.d.cts → track-changes-CDjCrwKe.d.ts} +18 -136
  140. package/package.json +16 -1
  141. package/dist/chunk-4SUNJZWB.cjs.map +0 -1
  142. package/dist/chunk-AA4ZEVCC.cjs.map +0 -1
  143. package/dist/chunk-AXV2ZW34.cjs +0 -118
  144. package/dist/chunk-AXV2ZW34.cjs.map +0 -1
  145. package/dist/chunk-B2ZRLN4S.js +0 -118
  146. package/dist/chunk-B2ZRLN4S.js.map +0 -1
  147. package/dist/chunk-GVSMRPHB.cjs.map +0 -1
  148. package/dist/chunk-K3R5NG2D.js.map +0 -1
  149. package/dist/chunk-MJZBNWG7.js.map +0 -1
  150. package/dist/chunk-OYXSKQFR.js.map +0 -1
  151. package/dist/chunk-QBJ22YZR.js.map +0 -1
  152. package/dist/chunk-QGHGJ56O.js.map +0 -1
  153. package/dist/chunk-RIKBJLS2.cjs.map +0 -1
  154. package/dist/chunk-XDMXXC4Z.cjs.map +0 -1
  155. /package/dist/{chunk-JJDLPEWL.js.map → chunk-3LV6RRPH.js.map} +0 -0
@@ -0,0 +1,256 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// 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
+ _nullishCoalesce(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 (_optionalChain([parsed, 'optionalAccess', _ => _.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
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+ exports.TEMPLATE_REGEX = TEMPLATE_REGEX; exports.VARIABLE_REGEX = VARIABLE_REGEX; exports.INSTRUCTION_REGEX = INSTRUCTION_REGEX; exports.templateRegex = templateRegex; exports.variableRegex = variableRegex; exports.instructionRegex = instructionRegex; exports.unescapeVariableText = unescapeVariableText; exports.escapeVariableText = escapeVariableText; exports.parseVariableBody = parseVariableBody; exports.isPromptValueSet = isPromptValueSet; exports.getSuppliedValue = getSuppliedValue; exports.evaluateCondition = evaluateCondition; exports.compareConditionValues = compareConditionValues; exports.conditionSubject = conditionSubject; exports.parseComparison = parseComparison; exports.formatComparisonOperand = formatComparisonOperand; exports.formatConditionalTag = formatConditionalTag; exports.describeConditionalTag = describeConditionalTag; exports.parseConditionExpression = parseConditionExpression; exports.evaluateConditionExpression = evaluateConditionExpression; exports.matchConditionalTags = matchConditionalTags;
256
+ //# sourceMappingURL=chunk-MKA3AHLV.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-MKA3AHLV.cjs","../src/grammar/tokens.ts","../src/grammar/variables.ts"],"names":[],"mappings":"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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-MKA3AHLV.cjs","sourcesContent":[null,"/**\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"]}