@samva/markup 0.8.0 → 0.10.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 (172) hide show
  1. package/AGENTS.md +107 -46
  2. package/CHANGELOG.md +345 -0
  3. package/README.md +188 -52
  4. package/dist/{brand-DcoVkh_S.d.ts → assets-BIsyMQ92.d.ts} +41 -11
  5. package/dist/{email/brand.js → brand-C0MKTwmX.js} +64 -4
  6. package/dist/{theme-css.d.ts → brand.d.ts} +12 -3
  7. package/dist/brand.js +4 -0
  8. package/dist/channel-specs-C2feJwuP.d.ts +13 -0
  9. package/dist/compiler.d.ts +88 -115
  10. package/dist/compiler.js +750 -171
  11. package/dist/components-BZCqegS8.js +1864 -0
  12. package/dist/diagnostic-model-DJtqbETd.js +38 -0
  13. package/dist/{diagnostics-OcQKQYSY.d.ts → diagnostic-model-DxthUZBT.d.ts} +3 -21
  14. package/dist/{diagnostics-BMYtBCQk.d.ts → diagnostics-B-Gym34R.d.ts} +8 -4
  15. package/dist/{email/diagnostics.js → diagnostics-CnKFefKz.js} +4 -3
  16. package/dist/diagnostics.d.ts +410 -2
  17. package/dist/diagnostics.js +402 -52
  18. package/dist/edit.d.ts +234 -0
  19. package/dist/edit.js +947 -0
  20. package/dist/email/jsx-dev-runtime.d.ts +2 -2
  21. package/dist/email/jsx-dev-runtime.js +1 -2
  22. package/dist/email/jsx-runtime.d.ts +1 -1
  23. package/dist/email/jsx-runtime.js +1 -85
  24. package/dist/{email/components.d.ts → email.d.ts} +22 -11
  25. package/dist/email.js +2 -0
  26. package/dist/fmt.d.ts +32 -0
  27. package/dist/fmt.js +174 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.js +2 -0
  30. package/dist/{render-vAv_4r9v.js → ir-CLhlklKV.js} +96 -279
  31. package/dist/jsx-runtime-CBlexoeR.js +98 -0
  32. package/dist/{elements-BzjjnoCS.d.ts → jsx-runtime-ylIOnaSf.d.ts} +76 -76
  33. package/dist/profile-pWGtWZXF.js +3803 -0
  34. package/dist/render-BPonDpCl.d.ts +539 -0
  35. package/dist/render-BQmfh94k.js +257 -0
  36. package/dist/render.d.ts +35 -0
  37. package/dist/render.js +49 -0
  38. package/dist/sms.d.ts +16 -0
  39. package/dist/sms.js +12 -0
  40. package/dist/source-locations-C8wCsiAy.d.ts +23 -0
  41. package/dist/template-DMPwxMSq.d.ts +65 -0
  42. package/dist/template-zLUjcwfF.js +42 -0
  43. package/dist/{theme-css-hFqcPSar.js → theme-css-JtRCm1fb.js} +2 -2
  44. package/dist/whatsapp.d.ts +57 -0
  45. package/dist/whatsapp.js +22 -0
  46. package/generated/contract.json +1788 -0
  47. package/generated/reference.md +360 -0
  48. package/package.json +46 -239
  49. package/samva-brand.d.ts +4 -4
  50. package/skills/sml/SKILL.md +123 -0
  51. package/dist/ast-BrDtApwS.d.ts +0 -107
  52. package/dist/ast.d.ts +0 -2
  53. package/dist/ast.js +0 -124
  54. package/dist/candidate.d.ts +0 -29
  55. package/dist/candidate.js +0 -88
  56. package/dist/catalog-CKuOooB8.d.ts +0 -354
  57. package/dist/checks-D73Xizbw.js +0 -59366
  58. package/dist/checks.d.ts +0 -33
  59. package/dist/checks.js +0 -2
  60. package/dist/classes.d.ts +0 -26
  61. package/dist/classes.js +0 -184
  62. package/dist/compile-sms.d.ts +0 -28
  63. package/dist/compile-sms.js +0 -44
  64. package/dist/compile-whatsapp.d.ts +0 -56
  65. package/dist/compile-whatsapp.js +0 -186
  66. package/dist/compile.d.ts +0 -18
  67. package/dist/compile.js +0 -29
  68. package/dist/components.d.ts +0 -289
  69. package/dist/components.js +0 -106
  70. package/dist/conditionals.d.ts +0 -10
  71. package/dist/conditionals.js +0 -74
  72. package/dist/corpus-CPPfgAHN.d.ts +0 -106
  73. package/dist/css-CS2jLID6.d.ts +0 -109
  74. package/dist/email/assets.d.ts +0 -40
  75. package/dist/email/assets.js +0 -63
  76. package/dist/email/brand-footer.d.ts +0 -34
  77. package/dist/email/brand-footer.js +0 -62
  78. package/dist/email/brand.d.ts +0 -2
  79. package/dist/email/components.js +0 -240
  80. package/dist/email/diagnostics.d.ts +0 -2
  81. package/dist/email/elements.d.ts +0 -2
  82. package/dist/email/elements.js +0 -299
  83. package/dist/email/html.d.ts +0 -2
  84. package/dist/email/html.js +0 -278
  85. package/dist/email/render.d.ts +0 -50
  86. package/dist/email/render.js +0 -3
  87. package/dist/email/selection.d.ts +0 -2
  88. package/dist/email/selection.js +0 -91
  89. package/dist/email/source-edit.d.ts +0 -67
  90. package/dist/email/source-edit.js +0 -185
  91. package/dist/email/stylesheets.d.ts +0 -15
  92. package/dist/email/stylesheets.js +0 -48
  93. package/dist/email/text.d.ts +0 -7
  94. package/dist/email/text.js +0 -100
  95. package/dist/emit.d.ts +0 -51
  96. package/dist/emit.js +0 -151
  97. package/dist/html-Bl9BAdqc.d.ts +0 -42
  98. package/dist/interpolate.d.ts +0 -24
  99. package/dist/interpolate.js +0 -43
  100. package/dist/jsx-dev-runtime.d.ts +0 -11
  101. package/dist/jsx-dev-runtime.js +0 -15
  102. package/dist/jsx-runtime-I-1nCtl2.d.ts +0 -57
  103. package/dist/jsx-runtime.d.ts +0 -2
  104. package/dist/jsx-runtime.js +0 -121
  105. package/dist/lexer.d.ts +0 -60
  106. package/dist/lexer.js +0 -281
  107. package/dist/model.d.ts +0 -24
  108. package/dist/model.js +0 -77
  109. package/dist/ops-BvwqHl7V.js +0 -630
  110. package/dist/ops.d.ts +0 -114
  111. package/dist/ops.js +0 -2
  112. package/dist/parser-t29cHLVL.d.ts +0 -396
  113. package/dist/parser.d.ts +0 -2
  114. package/dist/parser.js +0 -859
  115. package/dist/predicate-BI6uRRM-.js +0 -549
  116. package/dist/predicate-M-05Nd7R.d.ts +0 -56
  117. package/dist/predicate.d.ts +0 -2
  118. package/dist/predicate.js +0 -2
  119. package/dist/printer.d.ts +0 -6
  120. package/dist/printer.js +0 -122
  121. package/dist/registry-Dcp6QEK_.d.ts +0 -73
  122. package/dist/registry.d.ts +0 -2
  123. package/dist/registry.js +0 -646
  124. package/dist/render-template.d.ts +0 -45
  125. package/dist/render-template.js +0 -76
  126. package/dist/selection-CCArx9lU.d.ts +0 -91
  127. package/dist/showcase-egkusQBf.d.ts +0 -415
  128. package/dist/sms-segments-y4FpPilJ.d.ts +0 -13
  129. package/dist/sms-segments.d.ts +0 -2
  130. package/dist/sms-segments.js +0 -46
  131. package/dist/source-locations-DrCM82Ym.d.ts +0 -67
  132. package/dist/source-locations.d.ts +0 -2
  133. package/dist/source-locations.js +0 -102
  134. package/dist/starters/category-mapping.d.ts +0 -13
  135. package/dist/starters/category-mapping.js +0 -49
  136. package/dist/starters/corpus.d.ts +0 -2
  137. package/dist/starters/corpus.js +0 -194
  138. package/dist/starters/library.d.ts +0 -18
  139. package/dist/starters/library.js +0 -692
  140. package/dist/starters/public-gallery.d.ts +0 -266
  141. package/dist/starters/public-gallery.js +0 -916
  142. package/dist/starters/public-handoff.d.ts +0 -164
  143. package/dist/starters/public-handoff.js +0 -361
  144. package/dist/starters/public-preview-artifacts.d.ts +0 -43
  145. package/dist/starters/public-preview-artifacts.js +0 -104
  146. package/dist/starters/public-preview.d.ts +0 -61
  147. package/dist/starters/public-preview.js +0 -83
  148. package/dist/starters/showcase.d.ts +0 -2
  149. package/dist/starters/showcase.js +0 -335
  150. package/dist/starters/sources.d.ts +0 -45
  151. package/dist/starters/sources.js +0 -7762
  152. package/dist/starters/theme.d.ts +0 -45
  153. package/dist/starters/theme.js +0 -77
  154. package/dist/starters.d.ts +0 -2
  155. package/dist/starters.js +0 -874
  156. package/dist/tailwind-BYq7Lo6u.js +0 -727
  157. package/dist/template-DWftqh4x.d.ts +0 -60
  158. package/dist/template.d.ts +0 -2
  159. package/dist/template.js +0 -37
  160. package/dist/theme-css.js +0 -2
  161. package/dist/tsx-source-instrumentation.d.ts +0 -30
  162. package/dist/tsx-source-instrumentation.js +0 -115
  163. package/dist/tsx-tracer.d.ts +0 -112
  164. package/dist/tsx-tracer.js +0 -985
  165. package/dist/variables-J_L3qxRa.d.ts +0 -77
  166. package/dist/variables.d.ts +0 -2
  167. package/dist/variables.js +0 -141
  168. package/dist/version-BITr58kk.js +0 -96
  169. package/generated/sml-agent-contract.json +0 -709
  170. package/generated/sml-email-agent-contract.json +0 -615
  171. package/generated/sml-email-language-reference.md +0 -94
  172. package/generated/sml-language-reference.md +0 -195
@@ -1,630 +0,0 @@
1
- import { documentChannel } from "./ast.js";
2
- import { CHANNEL_TAG_SPECS, allowedChildren, parseSml } from "./parser.js";
3
- import { compileSml } from "./compile.js";
4
- import { a as isBreakingCompatibilityCheck, r as checkSmlCompatibility } from "./checks-D73Xizbw.js";
5
- import { printSml } from "./printer.js";
6
- import { Result, TaggedError } from "better-result";
7
- //#region src/internal/sha256.ts
8
- const ROUND_CONSTANTS = new Uint32Array([
9
- 1116352408,
10
- 1899447441,
11
- 3049323471,
12
- 3921009573,
13
- 961987163,
14
- 1508970993,
15
- 2453635748,
16
- 2870763221,
17
- 3624381080,
18
- 310598401,
19
- 607225278,
20
- 1426881987,
21
- 1925078388,
22
- 2162078206,
23
- 2614888103,
24
- 3248222580,
25
- 3835390401,
26
- 4022224774,
27
- 264347078,
28
- 604807628,
29
- 770255983,
30
- 1249150122,
31
- 1555081692,
32
- 1996064986,
33
- 2554220882,
34
- 2821834349,
35
- 2952996808,
36
- 3210313671,
37
- 3336571891,
38
- 3584528711,
39
- 113926993,
40
- 338241895,
41
- 666307205,
42
- 773529912,
43
- 1294757372,
44
- 1396182291,
45
- 1695183700,
46
- 1986661051,
47
- 2177026350,
48
- 2456956037,
49
- 2730485921,
50
- 2820302411,
51
- 3259730800,
52
- 3345764771,
53
- 3516065817,
54
- 3600352804,
55
- 4094571909,
56
- 275423344,
57
- 430227734,
58
- 506948616,
59
- 659060556,
60
- 883997877,
61
- 958139571,
62
- 1322822218,
63
- 1537002063,
64
- 1747873779,
65
- 1955562222,
66
- 2024104815,
67
- 2227730452,
68
- 2361852424,
69
- 2428436474,
70
- 2756734187,
71
- 3204031479,
72
- 3329325298
73
- ]);
74
- const rotateRight = (value, count) => value >>> count | value << 32 - count;
75
- /**
76
- * Encode valid Unicode as UTF-8 while preserving lone UTF-16 surrogates as
77
- * their three-byte WTF-8 form. TextEncoder replaces every lone surrogate with
78
- * U+FFFD, which would make distinct JavaScript source strings hash identically.
79
- */
80
- const encodeSource = (source) => {
81
- const output = [];
82
- for (let index = 0; index < source.length; index++) {
83
- const first = source.charCodeAt(index);
84
- let codePoint = first;
85
- if (first >= 55296 && first <= 56319) {
86
- const second = source.charCodeAt(index + 1);
87
- if (second >= 56320 && second <= 57343) {
88
- codePoint = 65536 + (first - 55296 << 10) + (second - 56320);
89
- index++;
90
- }
91
- }
92
- if (codePoint <= 127) output.push(codePoint);
93
- else if (codePoint <= 2047) output.push(192 | codePoint >>> 6, 128 | codePoint & 63);
94
- else if (codePoint <= 65535) output.push(224 | codePoint >>> 12, 128 | codePoint >>> 6 & 63, 128 | codePoint & 63);
95
- else output.push(240 | codePoint >>> 18, 128 | codePoint >>> 12 & 63, 128 | codePoint >>> 6 & 63, 128 | codePoint & 63);
96
- }
97
- return Uint8Array.from(output);
98
- };
99
- /** Browser-safe synchronous SHA-256 for compiler identity tokens. */
100
- const sha256 = (source) => {
101
- const bytes = encodeSource(source);
102
- const paddedLength = Math.ceil((bytes.length + 9) / 64) * 64;
103
- const padded = new Uint8Array(paddedLength);
104
- padded.set(bytes);
105
- padded[bytes.length] = 128;
106
- const view = new DataView(padded.buffer);
107
- const bitLength = bytes.length * 8;
108
- view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296));
109
- view.setUint32(paddedLength - 4, bitLength >>> 0);
110
- const state = new Uint32Array([
111
- 1779033703,
112
- 3144134277,
113
- 1013904242,
114
- 2773480762,
115
- 1359893119,
116
- 2600822924,
117
- 528734635,
118
- 1541459225
119
- ]);
120
- const words = /* @__PURE__ */ new Uint32Array(64);
121
- for (let offset = 0; offset < paddedLength; offset += 64) {
122
- for (let index = 0; index < 16; index++) words[index] = view.getUint32(offset + index * 4);
123
- for (let index = 16; index < 64; index++) {
124
- const previous15 = words[index - 15];
125
- const previous2 = words[index - 2];
126
- const sigma0 = rotateRight(previous15, 7) ^ rotateRight(previous15, 18) ^ previous15 >>> 3;
127
- const sigma1 = rotateRight(previous2, 17) ^ rotateRight(previous2, 19) ^ previous2 >>> 10;
128
- words[index] = words[index - 16] + sigma0 + words[index - 7] + sigma1 >>> 0;
129
- }
130
- let a = state[0];
131
- let b = state[1];
132
- let c = state[2];
133
- let d = state[3];
134
- let e = state[4];
135
- let f = state[5];
136
- let g = state[6];
137
- let h = state[7];
138
- for (let index = 0; index < 64; index++) {
139
- const sum1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
140
- const choice = e & f ^ ~e & g;
141
- const temporary1 = h + sum1 + choice + ROUND_CONSTANTS[index] + words[index] >>> 0;
142
- const temporary2 = (rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
143
- h = g;
144
- g = f;
145
- f = e;
146
- e = d + temporary1 >>> 0;
147
- d = c;
148
- c = b;
149
- b = a;
150
- a = temporary1 + temporary2 >>> 0;
151
- }
152
- state[0] = state[0] + a >>> 0;
153
- state[1] = state[1] + b >>> 0;
154
- state[2] = state[2] + c >>> 0;
155
- state[3] = state[3] + d >>> 0;
156
- state[4] = state[4] + e >>> 0;
157
- state[5] = state[5] + f >>> 0;
158
- state[6] = state[6] + g >>> 0;
159
- state[7] = state[7] + h >>> 0;
160
- }
161
- return [...state].map((word) => word.toString(16).padStart(8, "0")).join("");
162
- };
163
- //#endregion
164
- //#region src/ops.ts
165
- var SmlOpError = class extends TaggedError("SmlOpError") {
166
- constructor(args) {
167
- super({
168
- diagnostics: args.diagnostics,
169
- message: args.diagnostics.map((diagnostic) => diagnostic.message).join("\n")
170
- });
171
- }
172
- };
173
- const zeroSpan = {
174
- start: 0,
175
- end: 0
176
- };
177
- const diagnostic = (code, message, span = zeroSpan) => ({
178
- code,
179
- message,
180
- span
181
- });
182
- const cloneAttribute = (attribute) => ({ ...attribute });
183
- /**
184
- * Deep-copy a subtree, blanking every id to "". A duplicated node must not
185
- * alias the original's object graph or share its ids: batch ids resolve
186
- * against the pre-batch snapshot, and mapElement transforms every node whose
187
- * id matches — so two live copies sharing an id would both be rewritten by a
188
- * later op. Blank ids make the clone unaddressable (like any freshly created
189
- * node) until the next reprint reassigns positional ids.
190
- */
191
- const cloneSubtree = (node) => {
192
- if (node.kind === "element") return {
193
- ...node,
194
- id: "",
195
- attributes: node.attributes.map(cloneAttribute),
196
- children: node.children.map(cloneSubtree)
197
- };
198
- return {
199
- ...node,
200
- id: ""
201
- };
202
- };
203
- const makeAttribute = (name, value) => ({
204
- name,
205
- value,
206
- span: zeroSpan,
207
- valueSpan: zeroSpan
208
- });
209
- const classAttr = (value) => makeAttribute("class", {
210
- kind: "string",
211
- value
212
- });
213
- const sourceRevision = (source) => `sml:${sha256(source)}`;
214
- const normalizeSml = (source) => {
215
- const parsed = parseSml(source);
216
- if (Result.isError(parsed)) return Result.err(parsed.error);
217
- return Result.ok(printSml(parsed.value));
218
- };
219
- const findElement = (node, nodeId) => {
220
- if (node.kind !== "element") return null;
221
- if (node.id === nodeId) return node;
222
- for (const child of node.children) {
223
- const found = findElement(child, nodeId);
224
- if (found !== null) return found;
225
- }
226
- return null;
227
- };
228
- const mapElement = (node, targetId, transform) => {
229
- if (node.kind !== "element") return node;
230
- if (node.id === targetId) return transform(node);
231
- return {
232
- ...node,
233
- children: node.children.map((child) => mapElement(child, targetId, transform))
234
- };
235
- };
236
- const transformElement = (document, targetId, transform) => {
237
- if (findElement(document.root, targetId) === null) return Result.err([diagnostic("op-target-not-found", `node "${targetId}" was not found in the current source`)]);
238
- return Result.ok({
239
- ...document,
240
- root: mapElement(document.root, targetId, transform)
241
- });
242
- };
243
- const setElementAttribute = (element, name, value) => {
244
- const rest = element.attributes.filter((attribute) => attribute.name !== name);
245
- if (value === null || value.kind === "string" && value.value === "") return {
246
- ...element,
247
- attributes: rest
248
- };
249
- const next = makeAttribute(name, value);
250
- const attributes = element.attributes.some((attribute) => attribute.name === name) ? element.attributes.map((attribute) => attribute.name === name ? next : cloneAttribute(attribute)) : [...element.attributes, next];
251
- return {
252
- ...element,
253
- attributes
254
- };
255
- };
256
- const setImageSchemeSource = (document, nodeId, scheme, value) => transformElement(document, nodeId, (element) => {
257
- if (element.tag !== "Image") return element;
258
- const src = value?.kind === "string" ? value.value : "";
259
- const children = element.children.filter((child) => !(child.kind === "element" && child.tag === "Source" && child.attributes.some((attribute) => attribute.name === "scheme" && attribute.value.kind === "string" && attribute.value.value === scheme)));
260
- if (src === "") return {
261
- ...element,
262
- children
263
- };
264
- const sourceNode = {
265
- kind: "element",
266
- id: "",
267
- tag: "Source",
268
- attributes: [makeAttribute("scheme", {
269
- kind: "string",
270
- value: scheme
271
- }), makeAttribute("src", {
272
- kind: "string",
273
- value: src
274
- })],
275
- children: [],
276
- span: zeroSpan
277
- };
278
- return {
279
- ...element,
280
- children: [...children, sourceNode]
281
- };
282
- });
283
- const setProp = (document, op) => {
284
- if (op.name === "src:dark") return setImageSchemeSource(document, op.nodeId, "dark", op.value);
285
- return transformElement(document, op.nodeId, (element) => {
286
- const value = op.name === "class" && op.value?.kind === "string" ? {
287
- kind: "string",
288
- value: op.value.value.trim().replace(/\s+/g, " ")
289
- } : op.value;
290
- return setElementAttribute(element, op.name, value);
291
- });
292
- };
293
- const setChildren = (document, op) => transformElement(document, op.nodeId, (element) => ({
294
- ...element,
295
- children: [...op.children]
296
- }));
297
- const makeColumn = () => ({
298
- kind: "element",
299
- id: "",
300
- tag: "Column",
301
- attributes: [classAttr("px-8")],
302
- children: [{
303
- kind: "element",
304
- id: "",
305
- tag: "Text",
306
- attributes: [classAttr("text-base text-gray-700")],
307
- children: [{
308
- kind: "text",
309
- id: "",
310
- value: "Column",
311
- span: zeroSpan
312
- }],
313
- span: zeroSpan
314
- }],
315
- span: zeroSpan
316
- });
317
- const makeBlock = (tag) => {
318
- const base = (attributes, children) => ({
319
- kind: "element",
320
- id: "",
321
- tag,
322
- attributes,
323
- children,
324
- span: zeroSpan
325
- });
326
- const text = (value) => ({
327
- kind: "text",
328
- id: "",
329
- value,
330
- span: zeroSpan
331
- });
332
- switch (tag) {
333
- case "Heading": return base([makeAttribute("level", {
334
- kind: "number",
335
- value: 2
336
- }), classAttr("text-xl text-gray-900")], [text("Heading")]);
337
- case "Text": return base([classAttr("text-base text-gray-700")], [text("Add your text here.")]);
338
- case "Button": return base([makeAttribute("href", {
339
- kind: "string",
340
- value: "https://example.com"
341
- }), classAttr("bg-blue-600 text-white px-20 py-12 rounded-md")], [text("Button")]);
342
- case "Image": return base([
343
- makeAttribute("src", {
344
- kind: "string",
345
- value: "https://placehold.co/600x200"
346
- }),
347
- makeAttribute("alt", {
348
- kind: "string",
349
- value: ""
350
- }),
351
- makeAttribute("width", {
352
- kind: "number",
353
- value: 600
354
- })
355
- ], []);
356
- case "Divider": return base([classAttr("border-gray-200")], []);
357
- case "Spacer": return base([makeAttribute("height", {
358
- kind: "number",
359
- value: 24
360
- })], []);
361
- case "Section": return base([classAttr("py-16")], [makeBlock("Text")]);
362
- case "Container": return base([classAttr("bg-white p-24")], [makeBlock("Text")]);
363
- case "Row": return base([], [makeColumn(), makeColumn()]);
364
- default: return base([], []);
365
- }
366
- };
367
- const elementChildIndexToNodeIndex = (children, insertionIndex) => {
368
- if (insertionIndex <= 0) {
369
- const firstElement = children.findIndex((child) => child.kind === "element");
370
- return firstElement === -1 ? children.length : firstElement;
371
- }
372
- let elementIndex = 0;
373
- for (let index = 0; index < children.length; index++) {
374
- if (children[index]?.kind !== "element") continue;
375
- if (elementIndex === insertionIndex) return index;
376
- elementIndex++;
377
- }
378
- return children.length;
379
- };
380
- const insertChildren = (document, childrenToInsert, referenceId, position) => {
381
- if (childrenToInsert.length === 0) return Result.err([diagnostic("op-empty-insert", "insert operation produced no element nodes")]);
382
- if (referenceId === null || referenceId === document.root.id) {
383
- const children = position === "before" ? [...childrenToInsert, ...document.root.children] : [...document.root.children, ...childrenToInsert];
384
- return Result.ok({
385
- ...document,
386
- root: {
387
- ...document.root,
388
- children
389
- }
390
- });
391
- }
392
- if (findElement(document.root, referenceId) === null) return Result.err([diagnostic("op-target-not-found", `node "${referenceId}" was not found in the current source`)]);
393
- if (position === "inside") return transformElement(document, referenceId, (element) => ({
394
- ...element,
395
- children: [...element.children, ...childrenToInsert]
396
- }));
397
- const insertInParent = (node) => {
398
- if (node.kind !== "element") return node;
399
- const index = node.children.findIndex((child) => child.kind === "element" && child.id === referenceId);
400
- if (index >= 0) {
401
- const at = position === "after" ? index + 1 : index;
402
- return {
403
- ...node,
404
- children: [
405
- ...node.children.slice(0, at),
406
- ...childrenToInsert,
407
- ...node.children.slice(at)
408
- ]
409
- };
410
- }
411
- return {
412
- ...node,
413
- children: node.children.map(insertInParent)
414
- };
415
- };
416
- return Result.ok({
417
- ...document,
418
- root: insertInParent(document.root)
419
- });
420
- };
421
- const insertBlock = (document, op) => insertChildren(document, [makeBlock(op.tag)], op.referenceId, op.position);
422
- const insertSnippet = (document, op) => {
423
- const rootTag = document.root.tag;
424
- const parsed = parseSml(`<${rootTag}>${op.snippet}</${rootTag}>`);
425
- if (Result.isError(parsed)) return Result.err(parsed.error.diagnostics);
426
- const children = parsed.value.root.children.filter((child) => child.kind === "element");
427
- return insertChildren(document, children, op.referenceId, op.position);
428
- };
429
- const removeNodeById = (document, nodeId) => {
430
- if (document.root.id === nodeId) return Result.err([diagnostic("op-root-target", "the document root cannot be removed")]);
431
- if (findElement(document.root, nodeId) === null) return Result.err([diagnostic("op-target-not-found", `node "${nodeId}" was not found in the current source`)]);
432
- const prune = (node) => {
433
- if (node.kind !== "element") return node;
434
- return {
435
- ...node,
436
- children: node.children.filter((child) => !(child.kind === "element" && child.id === nodeId)).map(prune)
437
- };
438
- };
439
- return Result.ok({
440
- ...document,
441
- root: prune(document.root)
442
- });
443
- };
444
- const isDescendant = (ancestor, nodeId) => ancestor.children.some((child) => {
445
- if (child.kind !== "element") return false;
446
- return child.id === nodeId || isDescendant(child, nodeId);
447
- });
448
- const removeElement = (node, nodeId) => {
449
- let removed = null;
450
- const children = [];
451
- for (const child of node.children) {
452
- if (child.kind === "element" && child.id === nodeId) {
453
- removed = child;
454
- continue;
455
- }
456
- if (child.kind === "element") {
457
- const result = removeElement(child, nodeId);
458
- if (result.removed !== null) removed = result.removed;
459
- children.push(result.node);
460
- } else children.push(child);
461
- }
462
- return {
463
- node: {
464
- ...node,
465
- children
466
- },
467
- removed
468
- };
469
- };
470
- const insertIntoParent = (node, targetParentId, insertionIndex, moved) => {
471
- if (node.id === targetParentId) {
472
- const at = elementChildIndexToNodeIndex(node.children, insertionIndex);
473
- return {
474
- ...node,
475
- children: [
476
- ...node.children.slice(0, at),
477
- moved,
478
- ...node.children.slice(at)
479
- ]
480
- };
481
- }
482
- return {
483
- ...node,
484
- children: node.children.map((child) => child.kind === "element" ? insertIntoParent(child, targetParentId, insertionIndex, moved) : child)
485
- };
486
- };
487
- const moveNode = (document, op) => {
488
- if (document.root.id === op.nodeId) return Result.err([diagnostic("op-root-target", "the document root cannot be moved")]);
489
- const moved = findElement(document.root, op.nodeId);
490
- if (moved === null) return Result.err([diagnostic("op-target-not-found", `node "${op.nodeId}" was not found in the current source`)]);
491
- const parent = findElement(document.root, op.targetParentId);
492
- if (parent === null) return Result.err([diagnostic("op-target-not-found", `target parent "${op.targetParentId}" was not found in the current source`)]);
493
- if (moved.id === parent.id || isDescendant(moved, parent.id)) return Result.err([diagnostic("op-invalid-reparent", "a node cannot be moved into itself or its descendants")]);
494
- const channel = documentChannel(document);
495
- const childModel = CHANNEL_TAG_SPECS[channel][parent.tag]?.content;
496
- if (!(childModel === void 0 ? [] : allowedChildren(childModel)).includes(moved.tag)) return Result.err([diagnostic("op-invalid-reparent", `<${moved.tag}> is not allowed directly inside <${parent.tag}>`, parent.span)]);
497
- const { node: rootWithoutMoved, removed } = removeElement(document.root, op.nodeId);
498
- if (removed === null) return Result.err([diagnostic("op-target-not-found", `node "${op.nodeId}" was not found in the current source`)]);
499
- return Result.ok({
500
- ...document,
501
- root: insertIntoParent(rootWithoutMoved, op.targetParentId, op.insertionIndex, removed)
502
- });
503
- };
504
- const duplicateNode = (document, nodeId) => {
505
- if (document.root.id === nodeId) return Result.err([diagnostic("op-root-target", "the document root cannot be duplicated")]);
506
- if (findElement(document.root, nodeId) === null) return Result.err([diagnostic("op-target-not-found", `node "${nodeId}" was not found in the current source`)]);
507
- const insertClone = (node) => {
508
- if (node.kind !== "element") return node;
509
- const index = node.children.findIndex((child) => child.kind === "element" && child.id === nodeId);
510
- if (index >= 0) {
511
- const clone = cloneSubtree(node.children[index]);
512
- return {
513
- ...node,
514
- children: [
515
- ...node.children.slice(0, index + 1),
516
- clone,
517
- ...node.children.slice(index + 1)
518
- ]
519
- };
520
- }
521
- return {
522
- ...node,
523
- children: node.children.map(insertClone)
524
- };
525
- };
526
- return Result.ok({
527
- ...document,
528
- root: insertClone(document.root)
529
- });
530
- };
531
- const mutate = (document, op) => {
532
- switch (op.type) {
533
- case "setProp": return setProp(document, op);
534
- case "setChildren": return setChildren(document, op);
535
- case "insertBlock": return insertBlock(document, op);
536
- case "insertSnippet": return insertSnippet(document, op);
537
- case "moveNode": return moveNode(document, op);
538
- case "duplicateNode": return duplicateNode(document, op.nodeId);
539
- case "removeNode": return removeNodeById(document, op.nodeId);
540
- }
541
- };
542
- const preflight = (source) => {
543
- const reparsed = parseSml(source);
544
- if (Result.isError(reparsed)) return Result.err(reparsed.error.diagnostics);
545
- const compiled = compileSml(reparsed.value);
546
- if (Result.isError(compiled)) return Result.err(compiled.error.diagnostics);
547
- const compatibilityErrors = checkSmlCompatibility({ source }).filter(isBreakingCompatibilityCheck).map((message) => diagnostic("compatibility-error", message));
548
- if (compatibilityErrors.length > 0) return Result.err(compatibilityErrors);
549
- return Result.ok(reparsed.value);
550
- };
551
- const failedResult = (index, op, diagnostics) => ({
552
- index,
553
- status: "failed",
554
- op,
555
- diagnostics: [...diagnostics]
556
- });
557
- /**
558
- * Apply a batch of ops to SML source, returning the rewritten source, its new
559
- * revision, and a per-op result.
560
- *
561
- * Id contract: every id in the batch resolves against the id-space of the
562
- * source the caller read (the `expectedRevision` snapshot). Ids are positional
563
- * (`n0..nN` in document order) and are NOT rewritten between ops, so:
564
- *
565
- * - An op cannot target a node created earlier in the same batch — freshly
566
- * inserted/duplicated nodes carry a blank id until the next reprint reassigns
567
- * positional ids, and are unaddressable within the batch (fails with
568
- * `op-target-not-found`).
569
- * - After `duplicateNode`, the duplicated id still addresses the ORIGINAL (the
570
- * first match in document order); the clone is a blank-id copy.
571
- * - Structural ops (insert/remove/move) do not shift the ids of the nodes that
572
- * survive, so a later op targeting a surviving sibling lands on the intended
573
- * node regardless of what earlier ops did.
574
- *
575
- * Each op is still preflighted (reprint, reparse, compile, compatibility check)
576
- * as an independent validity gate; a failing op is recorded as `failed` and
577
- * does not advance the working document.
578
- */
579
- const applyOps = (source, ops, options) => {
580
- const parsed = parseSml(source);
581
- if (Result.isError(parsed)) return Result.err(new SmlOpError({ diagnostics: parsed.error.diagnostics }));
582
- const currentRevision = String(options?.currentRevision ?? sourceRevision(source));
583
- if (options?.expectedRevision !== void 0 && String(options.expectedRevision) !== currentRevision) {
584
- const stale = diagnostic("stale-revision", `source revision changed from ${String(options.expectedRevision)} to ${currentRevision}`);
585
- return Result.ok({
586
- source,
587
- revision: currentRevision,
588
- diagnostics: [stale],
589
- results: ops.map((op, index) => failedResult(index, op, [stale]))
590
- });
591
- }
592
- let currentSource = source;
593
- let currentDocument = parsed.value;
594
- const results = [];
595
- const diagnostics = [];
596
- ops.forEach((op, index) => {
597
- const mutated = mutate(currentDocument, op);
598
- if (Result.isError(mutated)) {
599
- results.push(failedResult(index, op, mutated.error));
600
- diagnostics.push(...mutated.error);
601
- return;
602
- }
603
- const candidateSource = printSml(mutated.value);
604
- const checked = preflight(candidateSource);
605
- if (Result.isError(checked)) {
606
- results.push(failedResult(index, op, checked.error));
607
- diagnostics.push(...checked.error);
608
- return;
609
- }
610
- currentSource = candidateSource;
611
- currentDocument = mutated.value;
612
- results.push({
613
- index,
614
- status: "applied",
615
- op,
616
- diagnostics: []
617
- });
618
- });
619
- return Result.ok({
620
- source: currentSource,
621
- revision: sourceRevision(currentSource),
622
- diagnostics,
623
- results
624
- });
625
- };
626
- const applyOp = (source, op, options) => applyOps(source, [op], options);
627
- //#endregion
628
- export { sourceRevision as a, normalizeSml as i, applyOp as n, applyOps as r, SmlOpError as t };
629
-
630
- //# sourceMappingURL=ops-BvwqHl7V.js.map