@p4code/cli 0.7.2 → 0.7.4

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 (30) hide show
  1. package/dist/bin.mjs +32815 -44088
  2. package/dist/client/assets/DiffPanel-BYKR1lez.js +98 -0
  3. package/dist/client/assets/FilePreviewPanel-Cs9BMyNQ.js +2230 -0
  4. package/dist/client/assets/P4DesignScreen-B2KfGz1K.js +19 -0
  5. package/dist/client/assets/P4DesignScreen-D6gkHJN-.css +1 -0
  6. package/dist/client/assets/PreviewPanel-Cb5mnizs.js +2 -0
  7. package/dist/client/assets/PullRequestCodeTab-C_g7YA7o.js +251 -0
  8. package/dist/client/assets/collapsible-C5OZcGTw.js +2 -0
  9. package/dist/client/assets/entities-DT_qhLch.js +2 -0
  10. package/dist/client/assets/env-jW24S9HL.js +2 -0
  11. package/dist/client/assets/fileCommentAnnotations-CwEFBcQo.js +2 -0
  12. package/dist/client/assets/index-CJxJFMPU.js +3274 -0
  13. package/dist/client/assets/{index-DRONsSry.css → index-DQqa12Zh.css} +1 -1
  14. package/dist/client/assets/renderFileChildren-Bwk6pWNb.js +2 -0
  15. package/dist/client/assets/textarea-B7H3jk59.js +48 -0
  16. package/dist/client/assets/toggle-group-NddHXOMC.js +2 -0
  17. package/dist/client/index.html +6 -3
  18. package/dist/renderer-DZwr3Qmo.mjs +426 -0
  19. package/dist/runtime-DC_ewLnZ.mjs +984 -0
  20. package/dist/src-DjTfFS-z.mjs +17031 -0
  21. package/package.json +6 -1
  22. package/dist/client/assets/DiffPanel-DWqZpGUs.js +0 -98
  23. package/dist/client/assets/FilePreviewPanel-Dp3QnVt8.js +0 -2230
  24. package/dist/client/assets/PreviewPanel-ZBtPO1QC.js +0 -2
  25. package/dist/client/assets/PullRequestCodeTab-DhY0UPI4.js +0 -251
  26. package/dist/client/assets/fileCommentAnnotations-B9r0HUm_.js +0 -2
  27. package/dist/client/assets/index-zsC-N8Hk.js +0 -3257
  28. package/dist/client/assets/previewAssetResource-6mhfd1nG.js +0 -48
  29. package/dist/client/assets/renderFileChildren-svEARvMh.js +0 -2
  30. package/dist/client/assets/toggle-group-DZYR-bSR.js +0 -2
@@ -0,0 +1,984 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { T as DesignDocument, j as DesignNode } from "./src-DjTfFS-z.mjs";
4
+ import * as Schema from "effect/Schema";
5
+ //#region ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/utils.js
6
+ /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
7
+ /** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
8
+ function isBytes(a) {
9
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
10
+ }
11
+ /** Asserts something is Uint8Array. */
12
+ function abytes(b, ...lengths) {
13
+ if (!isBytes(b)) throw new Error("Uint8Array expected");
14
+ if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
15
+ }
16
+ /** Asserts a hash instance has not been destroyed / finished */
17
+ function aexists(instance, checkFinished = true) {
18
+ if (instance.destroyed) throw new Error("Hash instance has been destroyed");
19
+ if (checkFinished && instance.finished) throw new Error("Hash#digest() has already been called");
20
+ }
21
+ /** Asserts output is properly-sized byte array */
22
+ function aoutput(out, instance) {
23
+ abytes(out);
24
+ const min = instance.outputLen;
25
+ if (out.length < min) throw new Error("digestInto() expects output buffer of length at least " + min);
26
+ }
27
+ /** Zeroize a byte array. Warning: JS provides no guarantees. */
28
+ function clean(...arrays) {
29
+ for (let i = 0; i < arrays.length; i++) arrays[i].fill(0);
30
+ }
31
+ /** Create DataView of an array for easy byte-level manipulation. */
32
+ function createView(arr) {
33
+ return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
34
+ }
35
+ /** The rotate right (circular right shift) operation for uint32 */
36
+ function rotr(word, shift) {
37
+ return word << 32 - shift | word >>> shift;
38
+ }
39
+ const hasHexBuiltin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function")();
40
+ const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
41
+ /**
42
+ * Convert byte array to hex string. Uses built-in function, when available.
43
+ * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
44
+ */
45
+ function bytesToHex(bytes) {
46
+ abytes(bytes);
47
+ if (hasHexBuiltin) return bytes.toHex();
48
+ let hex = "";
49
+ for (let i = 0; i < bytes.length; i++) hex += hexes[bytes[i]];
50
+ return hex;
51
+ }
52
+ /**
53
+ * Converts string to bytes using UTF8 encoding.
54
+ * @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])
55
+ */
56
+ function utf8ToBytes(str) {
57
+ if (typeof str !== "string") throw new Error("string expected");
58
+ return new Uint8Array(new TextEncoder().encode(str));
59
+ }
60
+ /**
61
+ * Normalizes (non-hex) string or Uint8Array to Uint8Array.
62
+ * Warning: when Uint8Array is passed, it would NOT get copied.
63
+ * Keep in mind for future mutable operations.
64
+ */
65
+ function toBytes(data) {
66
+ if (typeof data === "string") data = utf8ToBytes(data);
67
+ abytes(data);
68
+ return data;
69
+ }
70
+ /** For runtime check if class implements interface */
71
+ var Hash = class {};
72
+ /** Wraps hash function, creating an interface on top of it */
73
+ function createHasher(hashCons) {
74
+ const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
75
+ const tmp = hashCons();
76
+ hashC.outputLen = tmp.outputLen;
77
+ hashC.blockLen = tmp.blockLen;
78
+ hashC.create = () => hashCons();
79
+ return hashC;
80
+ }
81
+ //#endregion
82
+ //#region ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/_md.js
83
+ /**
84
+ * Internal Merkle-Damgard hash utils.
85
+ * @module
86
+ */
87
+ /** Polyfill for Safari 14. https://caniuse.com/mdn-javascript_builtins_dataview_setbiguint64 */
88
+ function setBigUint64(view, byteOffset, value, isLE) {
89
+ if (typeof view.setBigUint64 === "function") return view.setBigUint64(byteOffset, value, isLE);
90
+ const _32n = BigInt(32);
91
+ const _u32_max = BigInt(4294967295);
92
+ const wh = Number(value >> _32n & _u32_max);
93
+ const wl = Number(value & _u32_max);
94
+ const h = isLE ? 4 : 0;
95
+ const l = isLE ? 0 : 4;
96
+ view.setUint32(byteOffset + h, wh, isLE);
97
+ view.setUint32(byteOffset + l, wl, isLE);
98
+ }
99
+ /** Choice: a ? b : c */
100
+ function Chi(a, b, c) {
101
+ return a & b ^ ~a & c;
102
+ }
103
+ /** Majority function, true if any two inputs is true. */
104
+ function Maj(a, b, c) {
105
+ return a & b ^ a & c ^ b & c;
106
+ }
107
+ /**
108
+ * Merkle-Damgard hash construction base class.
109
+ * Could be used to create MD5, RIPEMD, SHA1, SHA2.
110
+ */
111
+ var HashMD = class extends Hash {
112
+ constructor(blockLen, outputLen, padOffset, isLE) {
113
+ super();
114
+ this.finished = false;
115
+ this.length = 0;
116
+ this.pos = 0;
117
+ this.destroyed = false;
118
+ this.blockLen = blockLen;
119
+ this.outputLen = outputLen;
120
+ this.padOffset = padOffset;
121
+ this.isLE = isLE;
122
+ this.buffer = new Uint8Array(blockLen);
123
+ this.view = createView(this.buffer);
124
+ }
125
+ update(data) {
126
+ aexists(this);
127
+ data = toBytes(data);
128
+ abytes(data);
129
+ const { view, buffer, blockLen } = this;
130
+ const len = data.length;
131
+ for (let pos = 0; pos < len;) {
132
+ const take = Math.min(blockLen - this.pos, len - pos);
133
+ if (take === blockLen) {
134
+ const dataView = createView(data);
135
+ for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);
136
+ continue;
137
+ }
138
+ buffer.set(data.subarray(pos, pos + take), this.pos);
139
+ this.pos += take;
140
+ pos += take;
141
+ if (this.pos === blockLen) {
142
+ this.process(view, 0);
143
+ this.pos = 0;
144
+ }
145
+ }
146
+ this.length += data.length;
147
+ this.roundClean();
148
+ return this;
149
+ }
150
+ digestInto(out) {
151
+ aexists(this);
152
+ aoutput(out, this);
153
+ this.finished = true;
154
+ const { buffer, view, blockLen, isLE } = this;
155
+ let { pos } = this;
156
+ buffer[pos++] = 128;
157
+ clean(this.buffer.subarray(pos));
158
+ if (this.padOffset > blockLen - pos) {
159
+ this.process(view, 0);
160
+ pos = 0;
161
+ }
162
+ for (let i = pos; i < blockLen; i++) buffer[i] = 0;
163
+ setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
164
+ this.process(view, 0);
165
+ const oview = createView(out);
166
+ const len = this.outputLen;
167
+ if (len % 4) throw new Error("_sha2: outputLen should be aligned to 32bit");
168
+ const outLen = len / 4;
169
+ const state = this.get();
170
+ if (outLen > state.length) throw new Error("_sha2: outputLen bigger than state");
171
+ for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);
172
+ }
173
+ digest() {
174
+ const { buffer, outputLen } = this;
175
+ this.digestInto(buffer);
176
+ const res = buffer.slice(0, outputLen);
177
+ this.destroy();
178
+ return res;
179
+ }
180
+ _cloneInto(to) {
181
+ to || (to = new this.constructor());
182
+ to.set(...this.get());
183
+ const { blockLen, buffer, length, finished, destroyed, pos } = this;
184
+ to.destroyed = destroyed;
185
+ to.finished = finished;
186
+ to.length = length;
187
+ to.pos = pos;
188
+ if (length % blockLen) to.buffer.set(buffer);
189
+ return to;
190
+ }
191
+ clone() {
192
+ return this._cloneInto();
193
+ }
194
+ };
195
+ /**
196
+ * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.
197
+ * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.
198
+ */
199
+ /** Initial SHA256 state. Bits 0..32 of frac part of sqrt of primes 2..19 */
200
+ const SHA256_IV = /* @__PURE__ */ Uint32Array.from([
201
+ 1779033703,
202
+ 3144134277,
203
+ 1013904242,
204
+ 2773480762,
205
+ 1359893119,
206
+ 2600822924,
207
+ 528734635,
208
+ 1541459225
209
+ ]);
210
+ //#endregion
211
+ //#region ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/sha2.js
212
+ /**
213
+ * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.
214
+ * SHA256 is the fastest hash implementable in JS, even faster than Blake3.
215
+ * Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and
216
+ * [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
217
+ * @module
218
+ */
219
+ /**
220
+ * Round constants:
221
+ * First 32 bits of fractional parts of the cube roots of the first 64 primes 2..311)
222
+ */
223
+ const SHA256_K = /* @__PURE__ */ Uint32Array.from([
224
+ 1116352408,
225
+ 1899447441,
226
+ 3049323471,
227
+ 3921009573,
228
+ 961987163,
229
+ 1508970993,
230
+ 2453635748,
231
+ 2870763221,
232
+ 3624381080,
233
+ 310598401,
234
+ 607225278,
235
+ 1426881987,
236
+ 1925078388,
237
+ 2162078206,
238
+ 2614888103,
239
+ 3248222580,
240
+ 3835390401,
241
+ 4022224774,
242
+ 264347078,
243
+ 604807628,
244
+ 770255983,
245
+ 1249150122,
246
+ 1555081692,
247
+ 1996064986,
248
+ 2554220882,
249
+ 2821834349,
250
+ 2952996808,
251
+ 3210313671,
252
+ 3336571891,
253
+ 3584528711,
254
+ 113926993,
255
+ 338241895,
256
+ 666307205,
257
+ 773529912,
258
+ 1294757372,
259
+ 1396182291,
260
+ 1695183700,
261
+ 1986661051,
262
+ 2177026350,
263
+ 2456956037,
264
+ 2730485921,
265
+ 2820302411,
266
+ 3259730800,
267
+ 3345764771,
268
+ 3516065817,
269
+ 3600352804,
270
+ 4094571909,
271
+ 275423344,
272
+ 430227734,
273
+ 506948616,
274
+ 659060556,
275
+ 883997877,
276
+ 958139571,
277
+ 1322822218,
278
+ 1537002063,
279
+ 1747873779,
280
+ 1955562222,
281
+ 2024104815,
282
+ 2227730452,
283
+ 2361852424,
284
+ 2428436474,
285
+ 2756734187,
286
+ 3204031479,
287
+ 3329325298
288
+ ]);
289
+ /** Reusable temporary buffer. "W" comes straight from spec. */
290
+ const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
291
+ var SHA256 = class extends HashMD {
292
+ constructor(outputLen = 32) {
293
+ super(64, outputLen, 8, false);
294
+ this.A = SHA256_IV[0] | 0;
295
+ this.B = SHA256_IV[1] | 0;
296
+ this.C = SHA256_IV[2] | 0;
297
+ this.D = SHA256_IV[3] | 0;
298
+ this.E = SHA256_IV[4] | 0;
299
+ this.F = SHA256_IV[5] | 0;
300
+ this.G = SHA256_IV[6] | 0;
301
+ this.H = SHA256_IV[7] | 0;
302
+ }
303
+ get() {
304
+ const { A, B, C, D, E, F, G, H } = this;
305
+ return [
306
+ A,
307
+ B,
308
+ C,
309
+ D,
310
+ E,
311
+ F,
312
+ G,
313
+ H
314
+ ];
315
+ }
316
+ set(A, B, C, D, E, F, G, H) {
317
+ this.A = A | 0;
318
+ this.B = B | 0;
319
+ this.C = C | 0;
320
+ this.D = D | 0;
321
+ this.E = E | 0;
322
+ this.F = F | 0;
323
+ this.G = G | 0;
324
+ this.H = H | 0;
325
+ }
326
+ process(view, offset) {
327
+ for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);
328
+ for (let i = 16; i < 64; i++) {
329
+ const W15 = SHA256_W[i - 15];
330
+ const W2 = SHA256_W[i - 2];
331
+ const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
332
+ const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
333
+ SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
334
+ }
335
+ let { A, B, C, D, E, F, G, H } = this;
336
+ for (let i = 0; i < 64; i++) {
337
+ const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
338
+ const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
339
+ const T2 = (rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22)) + Maj(A, B, C) | 0;
340
+ H = G;
341
+ G = F;
342
+ F = E;
343
+ E = D + T1 | 0;
344
+ D = C;
345
+ C = B;
346
+ B = A;
347
+ A = T1 + T2 | 0;
348
+ }
349
+ A = A + this.A | 0;
350
+ B = B + this.B | 0;
351
+ C = C + this.C | 0;
352
+ D = D + this.D | 0;
353
+ E = E + this.E | 0;
354
+ F = F + this.F | 0;
355
+ G = G + this.G | 0;
356
+ H = H + this.H | 0;
357
+ this.set(A, B, C, D, E, F, G, H);
358
+ }
359
+ roundClean() {
360
+ clean(SHA256_W);
361
+ }
362
+ destroy() {
363
+ this.set(0, 0, 0, 0, 0, 0, 0, 0);
364
+ clean(this.buffer);
365
+ }
366
+ };
367
+ //#endregion
368
+ //#region ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/sha256.js
369
+ /**
370
+ * SHA2-256 a.k.a. sha256. In JS, it is the fastest hash, even faster than Blake3.
371
+ *
372
+ * To break sha256 using birthday attack, attackers need to try 2^128 hashes.
373
+ * BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
374
+ *
375
+ * Check out [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
376
+ * @module
377
+ * @deprecated
378
+ */
379
+ /** @deprecated Use import from `noble/hashes/sha2` module */
380
+ const sha256 = /* @__PURE__ */ createHasher(() => new SHA256());
381
+ //#endregion
382
+ //#region ../../packages/design/src/runtime.ts
383
+ const decodeDocument = Schema.decodeUnknownSync(DesignDocument);
384
+ const decodeNode = Schema.decodeUnknownSync(DesignNode);
385
+ const diagnostic = (code, message, nodeId, severity = "error") => ({
386
+ code,
387
+ message,
388
+ severity,
389
+ ...nodeId ? { nodeId } : {}
390
+ });
391
+ function canonicalSerialize(value) {
392
+ const visit = (v) => {
393
+ if (v === null || typeof v === "string" || typeof v === "boolean") return JSON.stringify(v);
394
+ if (typeof v === "number" && Number.isFinite(v)) return JSON.stringify(v);
395
+ if (Array.isArray(v)) return `[${v.map(visit).join(",")}]`;
396
+ if (typeof v === "object" && v !== null && Object.getPrototypeOf(v) === Object.prototype) {
397
+ const obj = v;
398
+ return `{${Object.keys(obj).sort().map((k) => `${JSON.stringify(k)}:${visit(obj[k])}`).join(",")}}`;
399
+ }
400
+ throw new Error("Design JSON must contain only finite JSON values");
401
+ };
402
+ return `${visit(value)}\n`;
403
+ }
404
+ function fileHash(bytes) {
405
+ return bytesToHex(sha256(typeof bytes === "string" ? new TextEncoder().encode(bytes) : bytes));
406
+ }
407
+ var DesignDependencyError = class extends Error {
408
+ diagnostics;
409
+ constructor(diagnostics) {
410
+ super("Design dependency digests are missing or invalid");
411
+ this.name = "DesignDependencyError";
412
+ this.diagnostics = diagnostics;
413
+ }
414
+ };
415
+ /** Digest of a library's own content. The server supplies its transitive dependency edges separately. */
416
+ function documentContentDigest(doc) {
417
+ const { annotations: _annotations, reviews: _reviews, acceptances: _acceptances, ...content } = doc;
418
+ return fileHash(canonicalSerialize(content));
419
+ }
420
+ function contentHash(doc, dependencies = {}) {
421
+ const libraries = {};
422
+ const assets = {};
423
+ const issues = [];
424
+ const pending = Object.values(doc.imports).map((i) => i.libraryId);
425
+ const visited = /* @__PURE__ */ new Set();
426
+ const requiredAssets = new Set(Object.keys(doc.assets));
427
+ for (const node of Object.values(doc.nodes)) for (const fill of node.fills ?? []) if (fill.kind === "image") requiredAssets.add(fill.assetId);
428
+ for (const instance of Object.values(doc.instances)) for (const patch of Object.values(instance.overrides)) for (const fill of patch.fills ?? []) if (fill.kind === "image") requiredAssets.add(fill.assetId);
429
+ const valid = (digest) => digest !== void 0 && /^[0-9a-f]{64}$/.test(digest);
430
+ while (pending.length) {
431
+ const id = pending.pop();
432
+ if (visited.has(id)) continue;
433
+ visited.add(id);
434
+ const library = dependencies.libraries?.[id];
435
+ if (!library || !valid(library.digest)) {
436
+ issues.push(diagnostic("missing-dependency-digest", `Library digest is missing or invalid: ${id}`));
437
+ continue;
438
+ }
439
+ libraries[id] = library.digest;
440
+ pending.push(...library.libraries);
441
+ for (const asset of library.assets) requiredAssets.add(asset);
442
+ }
443
+ for (const id of requiredAssets) {
444
+ const digest = dependencies.assets?.[id];
445
+ if (!valid(digest)) issues.push(diagnostic("missing-dependency-digest", `Asset digest is missing or invalid: ${id}`));
446
+ else assets[id] = digest;
447
+ }
448
+ if (issues.length) throw new DesignDependencyError(issues);
449
+ return fileHash(canonicalSerialize({
450
+ document: documentContentDigest(doc),
451
+ libraries,
452
+ assets
453
+ }));
454
+ }
455
+ function parseDesign(source) {
456
+ try {
457
+ const raw = JSON.parse(source);
458
+ if (typeof raw !== "object" || raw === null || !("version" in raw) || typeof raw.version !== "string" || !/^\d+\.\d+$/.test(raw.version)) throw new Error("Missing or invalid format version");
459
+ if (raw.version.split(".")[0] !== "1") return {
460
+ status: "read-only",
461
+ raw,
462
+ source,
463
+ fileHash: fileHash(source),
464
+ diagnostics: [diagnostic("unknown-major", `Unsupported design version ${raw.version}`)]
465
+ };
466
+ return {
467
+ status: "editable",
468
+ document: decodeDocument(raw, { onExcessProperty: "error" }),
469
+ fileHash: fileHash(source),
470
+ diagnostics: []
471
+ };
472
+ } catch {
473
+ return {
474
+ status: "invalid",
475
+ source,
476
+ diagnostics: [diagnostic("invalid-schema", "Invalid .p4design JSON or schema")]
477
+ };
478
+ }
479
+ }
480
+ function patchNode(node, patch) {
481
+ return decodeNode({
482
+ ...node,
483
+ ...patch
484
+ }, { onExcessProperty: "error" });
485
+ }
486
+ function buildIndexes(doc) {
487
+ const parents = /* @__PURE__ */ new Map();
488
+ const order = /* @__PURE__ */ new Map();
489
+ const diagnostics = [];
490
+ const attach = (id, parent, index) => {
491
+ if (!doc.nodes[id]) diagnostics.push(diagnostic("missing-node", "Referenced node is missing", parent ?? id));
492
+ if (parents.has(id)) diagnostics.push(diagnostic("multiple-parents", "Node occurs in more than one ordered child list", id));
493
+ else {
494
+ parents.set(id, parent);
495
+ order.set(id, index);
496
+ }
497
+ };
498
+ doc.roots.forEach((id, i) => attach(id, null, i));
499
+ for (const [id, node] of Object.entries(doc.nodes)) if ("children" in node) node.children.forEach((child, i) => attach(child, id, i));
500
+ for (const [id, instance] of Object.entries(doc.instances)) for (const ids of Object.values(instance.slots)) ids.forEach((child, i) => attach(child, id, i));
501
+ const visiting = /* @__PURE__ */ new Set();
502
+ const visited = /* @__PURE__ */ new Set();
503
+ const walk = (id, depth) => {
504
+ if (visiting.has(id)) {
505
+ diagnostics.push(diagnostic("tree-cycle", "Child lists contain a cycle", id));
506
+ return;
507
+ }
508
+ if (depth > 256) {
509
+ diagnostics.push(diagnostic("depth-limit", "Scene exceeds 256 levels", id));
510
+ return;
511
+ }
512
+ if (visited.has(id)) return;
513
+ visiting.add(id);
514
+ const node = doc.nodes[id];
515
+ if (node && "children" in node) for (const child of node.children) walk(child, depth + 1);
516
+ visiting.delete(id);
517
+ visited.add(id);
518
+ };
519
+ for (const id of Object.keys(doc.nodes)) walk(id, 0);
520
+ return {
521
+ parents,
522
+ order,
523
+ diagnostics
524
+ };
525
+ }
526
+ function resolveToken(doc, id, theme = {}, chain = /* @__PURE__ */ new Set()) {
527
+ if (chain.has(id)) throw new Error("token-cycle");
528
+ if (chain.size >= 256) throw new Error("token-depth-limit");
529
+ const token = doc.tokens[id];
530
+ if (!token) throw new Error("missing-token");
531
+ const next = new Set(chain).add(id);
532
+ const matching = token.variants.filter((v) => Object.entries(v.theme).every(([axis, value]) => theme[axis] === value));
533
+ matching.sort((a, b) => Object.keys(b.theme).length - Object.keys(a.theme).length);
534
+ if (matching[0] && matching.some((variant) => Object.keys(variant.theme).length === Object.keys(matching[0].theme).length && canonicalSerialize(variant.value) !== canonicalSerialize(matching[0].value))) throw new Error("ambiguous-token");
535
+ const value = matching[0]?.value ?? token.value;
536
+ const resolved = typeof value === "object" ? resolveToken(doc, value.tokenId, theme, next) : value;
537
+ if (token.kind === "number" ? typeof resolved !== "number" : typeof resolved !== "string") throw new Error("token-type");
538
+ if (token.kind === "color" && (typeof resolved !== "string" || !/^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/.test(resolved))) throw new Error("token-type");
539
+ return resolved;
540
+ }
541
+ function resolveValue(doc, value, theme = {}) {
542
+ return typeof value === "object" ? resolveToken(doc, value.tokenId, theme) : value;
543
+ }
544
+ function expandComponents(doc, context = {}) {
545
+ const diagnostics = [];
546
+ const expand = (source, id, key, ownerId, chain, inherited, depth) => {
547
+ const original = source.nodes[id];
548
+ if (!original) {
549
+ diagnostics.push(diagnostic("missing-node", "Expansion target is missing", ownerId));
550
+ return;
551
+ }
552
+ if (depth > 256 || chain.has(`${source.documentId}/node/${id}`)) {
553
+ diagnostics.push(diagnostic("component-cycle", "Expansion contains a cycle or exceeds 256 levels", ownerId));
554
+ return;
555
+ }
556
+ let node = original;
557
+ try {
558
+ if (inherited[id]) node = patchNode(node, inherited[id]);
559
+ } catch {
560
+ diagnostics.push(diagnostic("invalid-override", "Override does not match its source node", ownerId));
561
+ }
562
+ const next = new Set(chain).add(`${source.documentId}/node/${id}`);
563
+ if (node.type === "ref") {
564
+ const imported = node.importId ? source.imports[node.importId] : void 0;
565
+ const library = imported ? context.manifest?.libraries[imported.libraryId] : void 0;
566
+ const target = node.importId ? library?.exports.includes(node.componentId) && imported ? context.documents?.[imported.libraryId] : void 0 : source;
567
+ const component = target?.components[node.componentId];
568
+ if (!target || !component) {
569
+ diagnostics.push(diagnostic("missing-import", "Component or exported library symbol is unavailable", ownerId));
570
+ return {
571
+ key,
572
+ sourceId: id,
573
+ ownerId,
574
+ document: source,
575
+ node,
576
+ children: []
577
+ };
578
+ }
579
+ const instance = source.instances[id];
580
+ const overrides = {
581
+ ...inherited,
582
+ ...instance?.overrides
583
+ };
584
+ const scopedTarget = target === source ? target : {
585
+ ...target,
586
+ tokens: {
587
+ ...target.tokens,
588
+ ...source.tokens
589
+ },
590
+ themeAxes: {
591
+ ...target.themeAxes,
592
+ ...source.themeAxes
593
+ }
594
+ };
595
+ const root = expand(scopedTarget, component.rootId, `${key}/${component.rootId}`, ownerId, next, overrides, depth + 1);
596
+ if (!root) return;
597
+ const substitute = (item) => {
598
+ const slot = Object.entries(component.slots).find(([, s]) => s.nodeId === item.sourceId);
599
+ const replacement = slot ? instance?.slots[slot[0]] : void 0;
600
+ if (replacement) return {
601
+ ...item,
602
+ children: replacement.flatMap((child) => {
603
+ const expanded = expand(source, child, `${item.key}/slot/${child}`, ownerId, next, {}, depth + 1);
604
+ return expanded ? [expanded] : [];
605
+ })
606
+ };
607
+ return {
608
+ ...item,
609
+ children: item.children.map(substitute)
610
+ };
611
+ };
612
+ const { type: _type, componentId: _componentId, importId: _importId, ...presentation } = node;
613
+ return {
614
+ ...substitute(root),
615
+ key,
616
+ ownerId,
617
+ node: {
618
+ ...root.node,
619
+ ...presentation
620
+ }
621
+ };
622
+ }
623
+ const children = "children" in node ? node.children.flatMap((child) => {
624
+ const expanded = expand(source, child, `${key}/${child}`, ownerId === id ? child : ownerId, next, inherited, depth + 1);
625
+ return expanded ? [expanded] : [];
626
+ }) : [];
627
+ return {
628
+ key,
629
+ sourceId: id,
630
+ ownerId,
631
+ document: source,
632
+ node,
633
+ children
634
+ };
635
+ };
636
+ return {
637
+ roots: doc.roots.flatMap((id) => {
638
+ const root = expand(doc, id, id, id, /* @__PURE__ */ new Set(), {}, 0);
639
+ return root ? [root] : [];
640
+ }),
641
+ diagnostics
642
+ };
643
+ }
644
+ /** Select authored nodes and their rendered descendants, excluding other component instances. */
645
+ function selectLayoutItems(items, nodeIds) {
646
+ const ids = new Set(nodeIds);
647
+ const keys = new Set(items.filter((item) => ids.has(item.ownerId)).map((item) => item.key));
648
+ return items.filter((item) => {
649
+ let key = item.key;
650
+ while (key) {
651
+ if (keys.has(key)) return true;
652
+ const separator = key.lastIndexOf("/");
653
+ if (separator < 0) break;
654
+ key = key.slice(0, separator);
655
+ }
656
+ return false;
657
+ });
658
+ }
659
+ function computeLayout(doc, theme = {}, context = {}) {
660
+ const expanded = expandComponents(doc, context);
661
+ const diagnostics = [...expanded.diagnostics];
662
+ const items = [];
663
+ const numeric = (item, value) => {
664
+ try {
665
+ const n = resolveValue(item.document, value, theme);
666
+ if (typeof n !== "number" || !Number.isFinite(n)) throw new Error("token-type");
667
+ return n;
668
+ } catch {
669
+ diagnostics.push(diagnostic("invalid-layout-token", "Layout value requires a finite number", item.ownerId));
670
+ return 0;
671
+ }
672
+ };
673
+ const intrinsic = /* @__PURE__ */ new Map();
674
+ const measure = (item) => {
675
+ const cached = intrinsic.get(item.key);
676
+ if (cached) return cached;
677
+ const node = item.node;
678
+ const children = item.children.map(measure);
679
+ const layout = "layout" in node ? node.layout : void 0;
680
+ const padding = layout ? Object.fromEntries(Object.entries(layout.padding).map(([k, v]) => [k, numeric(item, v)])) : {
681
+ top: 0,
682
+ right: 0,
683
+ bottom: 0,
684
+ left: 0
685
+ };
686
+ const gap = layout ? numeric(item, layout.gap) : 0;
687
+ const direction = layout?.direction ?? "none";
688
+ let naturalWidth = direction === "horizontal" ? children.reduce((s, c) => s + c.width, 0) + Math.max(0, children.length - 1) * gap : Math.max(0, ...children.map((c) => c.width));
689
+ let naturalHeight = direction === "vertical" ? children.reduce((s, c) => s + c.height, 0) + Math.max(0, children.length - 1) * gap : Math.max(0, ...children.map((c) => c.height));
690
+ if (direction === "none") {
691
+ naturalWidth = Math.max(0, ...item.children.map((c, i) => numeric(c, c.node.x) + children[i].width));
692
+ naturalHeight = Math.max(0, ...item.children.map((c, i) => numeric(c, c.node.y) + children[i].height));
693
+ }
694
+ if (node.type === "text") {
695
+ const size = numeric(item, node.fontSize);
696
+ const lh = node.lineHeight ? numeric(item, node.lineHeight) : 1.2;
697
+ const lines = node.text.split("\n");
698
+ naturalWidth = Math.max(0, ...lines.map((l) => l.length)) * size * .6;
699
+ naturalHeight = lines.length * size * lh;
700
+ }
701
+ naturalWidth += padding.left + padding.right;
702
+ naturalHeight += padding.top + padding.bottom;
703
+ const size = {
704
+ width: node.width.mode === "fixed" ? numeric(item, node.width.value) : node.width.mode === "hug" ? naturalWidth : 0,
705
+ height: node.height.mode === "fixed" ? numeric(item, node.height.value) : node.height.mode === "hug" ? naturalHeight : 0
706
+ };
707
+ for (const axis of ["width", "height"]) {
708
+ if (size[axis] < 0) {
709
+ diagnostics.push(diagnostic("negative-size", "Dimensions cannot be negative", item.ownerId));
710
+ size[axis] = 0;
711
+ }
712
+ if (node[axis].mode === "hug" && item.children.some((c) => c.node[axis].mode === "fill")) diagnostics.push(diagnostic("layout-cycle", "A hug dimension cannot contain fill on the same axis", item.ownerId));
713
+ }
714
+ intrinsic.set(item.key, size);
715
+ return size;
716
+ };
717
+ const place = (item, x, y, width, height, clips, parentVisible = true) => {
718
+ const bounds = {
719
+ x,
720
+ y,
721
+ width,
722
+ height
723
+ };
724
+ const visible = parentVisible && item.node.visible !== false;
725
+ items.push({
726
+ ...item,
727
+ node: {
728
+ ...item.node,
729
+ visible
730
+ },
731
+ bounds,
732
+ clips
733
+ });
734
+ const layout = "layout" in item.node ? item.node.layout : void 0;
735
+ const padding = layout ? Object.fromEntries(Object.entries(layout.padding).map(([k, v]) => [k, numeric(item, v)])) : {
736
+ top: 0,
737
+ right: 0,
738
+ bottom: 0,
739
+ left: 0
740
+ };
741
+ const gap = layout ? numeric(item, layout.gap) : 0;
742
+ const direction = layout?.direction ?? "none";
743
+ const available = {
744
+ width: Math.max(0, width - padding.left - padding.right),
745
+ height: Math.max(0, height - padding.top - padding.bottom)
746
+ };
747
+ const main = direction === "horizontal" ? "width" : "height";
748
+ const fillCount = item.children.filter((c) => c.node[main].mode === "fill").length;
749
+ const used = item.children.reduce((sum, c) => sum + (c.node[main].mode === "fill" ? 0 : measure(c)[main]), 0) + Math.max(0, item.children.length - 1) * gap;
750
+ const share = fillCount ? Math.max(0, available[main] - used) / fillCount : 0;
751
+ let offset = 0;
752
+ const nextClips = item.node.type === "frame" && item.node.clip ? [...clips, bounds] : clips;
753
+ for (const child of item.children) {
754
+ const natural = measure(child);
755
+ const size = {
756
+ width: child.node.width.mode === "fill" ? direction === "horizontal" ? share : available.width : natural.width,
757
+ height: child.node.height.mode === "fill" ? direction === "vertical" ? share : available.height : natural.height
758
+ };
759
+ const cx = x + padding.left + (direction === "horizontal" ? offset : direction === "none" ? numeric(child, child.node.x) : 0);
760
+ const cy = y + padding.top + (direction === "vertical" ? offset : direction === "none" ? numeric(child, child.node.y) : 0);
761
+ place(child, cx, cy, size.width, size.height, nextClips, visible);
762
+ offset += size[main] + gap;
763
+ }
764
+ };
765
+ for (const root of expanded.roots) {
766
+ const size = measure(root);
767
+ if (root.node.width.mode === "fill" || root.node.height.mode === "fill") diagnostics.push(diagnostic("root-fill", "Root dimensions need fixed or hug sizing", root.ownerId));
768
+ place(root, numeric(root, root.node.x), numeric(root, root.node.y), size.width, size.height, []);
769
+ }
770
+ return {
771
+ items,
772
+ diagnostics
773
+ };
774
+ }
775
+ function isConfinedRelativePath(path) {
776
+ return path.length > 0 && !path.startsWith("/") && !path.includes("\\") && !/[%?#:]/.test(path) && !Array.from(path).some((c) => c.charCodeAt(0) < 32) && path.split("/").every((p) => p !== ".." && p !== "." && p !== "");
777
+ }
778
+ function reviewState(doc, dependencies = {}) {
779
+ const blockers = Object.entries(doc.annotations).filter(([, a]) => a.blocking && a.status === "open").map(([id]) => id);
780
+ let revision;
781
+ try {
782
+ revision = contentHash(doc, dependencies);
783
+ } catch (error) {
784
+ if (!(error instanceof DesignDependencyError)) throw error;
785
+ return {
786
+ revision: null,
787
+ blockers,
788
+ canAccept: false,
789
+ approved: false,
790
+ diagnostics: error.diagnostics
791
+ };
792
+ }
793
+ const reviews = Object.values(doc.reviews).filter((r) => r.revision === revision).sort((a, b) => (a.sequence ?? 0) - (b.sequence ?? 0) || a.createdAt.localeCompare(b.createdAt));
794
+ return {
795
+ revision,
796
+ blockers,
797
+ canAccept: blockers.length === 0,
798
+ approved: blockers.length === 0 && reviews.at(-1)?.decision === "approve",
799
+ diagnostics: []
800
+ };
801
+ }
802
+ function validateDesign(doc, context = {}, theme = {}) {
803
+ const issues = [...buildIndexes(doc).diagnostics];
804
+ const validateValues = (node, source, id) => {
805
+ const check = (value, kind, label, nonNegative = false) => {
806
+ try {
807
+ const resolved = resolveValue(source, value, theme);
808
+ if (kind === "number" ? typeof resolved !== "number" || !Number.isFinite(resolved) || nonNegative && resolved < 0 : typeof resolved !== "string" || kind === "color" && !/^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/.test(resolved)) throw new Error();
809
+ } catch {
810
+ issues.push(diagnostic("invalid-property", `${label} requires a valid ${kind} value`, id));
811
+ }
812
+ };
813
+ if (node.stroke) check(node.stroke, "color", "Stroke");
814
+ if (node.strokeWidth !== void 0) check(node.strokeWidth, "number", "Stroke width", true);
815
+ if ("radius" in node && node.radius !== void 0) check(node.radius, "number", "Radius", true);
816
+ if (node.type === "text") {
817
+ check(node.fontSize, "number", "Font size", true);
818
+ check(node.fontFamily, "string", "Font family");
819
+ if (node.lineHeight !== void 0) check(node.lineHeight, "number", "Line height", true);
820
+ }
821
+ if ("layout" in node) {
822
+ check(node.layout.gap, "number", "Layout gap", true);
823
+ for (const value of Object.values(node.layout.padding)) check(value, "number", "Padding", true);
824
+ }
825
+ for (const fill of node.fills ?? []) {
826
+ if (fill.kind === "solid") check(fill.color, "color", "Fill");
827
+ if (fill.kind === "gradient") for (const stop of fill.stops) check(stop.color, "color", "Gradient stop");
828
+ }
829
+ for (const effect of node.effects ?? []) if (effect.kind === "shadow") {
830
+ check(effect.x, "number", "Shadow x");
831
+ check(effect.y, "number", "Shadow y");
832
+ check(effect.blur, "number", "Shadow blur", true);
833
+ check(effect.color, "color", "Shadow color");
834
+ } else check(effect.radius, "number", "Blur radius", true);
835
+ };
836
+ for (const [axis, value] of Object.entries(theme)) if (!doc.themeAxes[axis]?.includes(value)) issues.push(diagnostic("unknown-theme", "Theme preview value is not declared"));
837
+ let themes = [{}];
838
+ for (const [axis, values] of Object.entries(doc.themeAxes).sort(([a], [b]) => a.localeCompare(b))) {
839
+ const next = themes.flatMap((preview) => [preview, ...values.map((value) => ({
840
+ ...preview,
841
+ [axis]: value
842
+ }))]);
843
+ if (next.length > 256) {
844
+ issues.push(diagnostic("theme-limit", "Theme validation exceeds 256 combinations"));
845
+ break;
846
+ }
847
+ themes = next;
848
+ }
849
+ for (const token of Object.values(doc.tokens)) for (const variant of token.variants) {
850
+ themes.push(variant.theme);
851
+ for (const [axis, value] of Object.entries(variant.theme)) if (!doc.themeAxes[axis]?.includes(value)) issues.push(diagnostic("unknown-theme", "Token variant uses an undeclared theme"));
852
+ }
853
+ for (const id of Object.keys(doc.tokens)) for (const preview of themes) try {
854
+ resolveToken(doc, id, preview);
855
+ } catch (e) {
856
+ issues.push(diagnostic(e instanceof Error ? e.message : "invalid-token", "Token could not be resolved"));
857
+ }
858
+ const covered = /* @__PURE__ */ new Set();
859
+ const cover = (id) => {
860
+ if (covered.has(id)) return;
861
+ covered.add(id);
862
+ const n = doc.nodes[id];
863
+ if (n && "children" in n) n.children.forEach(cover);
864
+ };
865
+ doc.roots.forEach(cover);
866
+ for (const c of Object.values(doc.components)) {
867
+ if (!doc.nodes[c.rootId]) issues.push(diagnostic("missing-component-root", "Component root is missing", c.rootId));
868
+ cover(c.rootId);
869
+ const subtree = /* @__PURE__ */ new Set();
870
+ const collect = (id) => {
871
+ if (subtree.has(id)) return;
872
+ subtree.add(id);
873
+ const n = doc.nodes[id];
874
+ if (n && "children" in n) n.children.forEach(collect);
875
+ };
876
+ collect(c.rootId);
877
+ for (const slot of Object.values(c.slots)) if (!subtree.has(slot.nodeId) || !doc.nodes[slot.nodeId] || !("children" in doc.nodes[slot.nodeId])) issues.push(diagnostic("invalid-slot", "Slot must target a container inside its component", c.rootId));
878
+ }
879
+ for (const [id, instance] of Object.entries(doc.instances)) {
880
+ if (doc.nodes[id]?.type !== "ref") issues.push(diagnostic("invalid-instance", "Instance metadata must target a ref node", id));
881
+ for (const ids of Object.values(instance.slots)) ids.forEach(cover);
882
+ }
883
+ for (const [id, node] of Object.entries(doc.nodes)) {
884
+ validateValues(node, doc, id);
885
+ if (!covered.has(id)) issues.push(diagnostic("orphan-node", "Node is outside the scene and component definitions", id));
886
+ if (node.type === "ref" && !doc.instances[id]) issues.push(diagnostic("missing-instance", "Ref node requires instance metadata", id));
887
+ if (node.type === "ref") {
888
+ const imported = node.importId ? doc.imports[node.importId] : void 0;
889
+ const lib = imported ? context.manifest?.libraries[imported.libraryId] : void 0;
890
+ const source = node.importId ? context.documents?.[imported?.libraryId ?? ""] : doc;
891
+ const comp = source?.components[node.componentId];
892
+ if (!comp || node.importId && (!lib || !lib.exports.includes(node.componentId))) issues.push(diagnostic("missing-import", "Component symbol is not available through the manifest", id));
893
+ if (comp && source) {
894
+ const seen = /* @__PURE__ */ new Set();
895
+ const collect = (n) => {
896
+ if (seen.has(n)) return;
897
+ seen.add(n);
898
+ const v = source.nodes[n];
899
+ if (v && "children" in v) v.children.forEach(collect);
900
+ };
901
+ collect(comp.rootId);
902
+ for (const [target, patch] of Object.entries(doc.instances[id]?.overrides ?? {})) if (!seen.has(target)) issues.push(diagnostic("invalid-override-target", "Override must target a stable source node in this component", id));
903
+ else try {
904
+ patchNode(source.nodes[target], patch);
905
+ } catch {
906
+ issues.push(diagnostic("invalid-override", "Override fields do not match the source type", id));
907
+ }
908
+ for (const [slotId, children] of Object.entries(doc.instances[id]?.slots ?? {})) {
909
+ const slot = comp.slots[slotId];
910
+ if (!slot) issues.push(diagnostic("invalid-slot", "Instance names an undeclared slot", id));
911
+ else for (const child of children) {
912
+ const n = doc.nodes[child];
913
+ if (slot.accepts.length && (!n || n.type !== "ref" || !slot.accepts.includes(n.componentId))) issues.push(diagnostic("slot-type", "Slot content does not match its accepted symbols", id));
914
+ }
915
+ }
916
+ }
917
+ }
918
+ for (const fill of node.fills ?? []) {
919
+ if (fill.kind === "image" && !doc.assets[fill.assetId]) issues.push(diagnostic("missing-asset", "Image asset is missing; render a placeholder", id, "warning"));
920
+ if (fill.kind === "gradient" && (fill.stops.length < 2 || fill.stops.some((s, i) => i > 0 && s.offset < fill.stops[i - 1].offset))) issues.push(diagnostic("gradient-stops", "Gradient needs ordered stops", id));
921
+ }
922
+ for (const value of [
923
+ node.x,
924
+ node.y,
925
+ node.width.mode === "fixed" ? node.width.value : 0,
926
+ node.height.mode === "fixed" ? node.height.value : 0
927
+ ]) if (typeof value === "object") try {
928
+ if (typeof resolveToken(doc, value.tokenId, theme) !== "number") throw new Error();
929
+ } catch {
930
+ issues.push(diagnostic("token-type", "Geometry token must be numeric", id));
931
+ }
932
+ }
933
+ for (const asset of Object.values(doc.assets)) {
934
+ if (!isConfinedRelativePath(asset.path)) issues.push(diagnostic("unsafe-asset-path", "Asset path must be project-confined and relative"));
935
+ if (context.documentPath) {
936
+ const name = context.documentPath.split("/").at(-1).replace(/\.p4design$/, "");
937
+ if (!asset.path.startsWith(`${name}.assets/`)) issues.push(diagnostic("asset-folder", "Assets must live beside the document in its assets folder"));
938
+ }
939
+ if (context.availableAssets && !context.availableAssets.has(asset.path)) issues.push(diagnostic("missing-asset", "Asset file is unavailable; render a placeholder", void 0, "warning"));
940
+ }
941
+ for (const lib of Object.values(context.manifest?.libraries ?? {})) if (!isConfinedRelativePath(lib.path) || !lib.path.endsWith(".p4design")) issues.push(diagnostic("unsafe-library-path", "Library document path must be project-confined"));
942
+ for (const imported of Object.values(doc.imports)) if (!context.manifest?.libraries[imported.libraryId] || !context.documents?.[imported.libraryId]) issues.push(diagnostic("missing-import", "Declared library is unavailable"));
943
+ const layout = computeLayout(doc, theme, context);
944
+ issues.push(...layout.diagnostics);
945
+ for (const item of layout.items) validateValues(item.node, item.document, item.ownerId);
946
+ return issues.filter((issue, index) => issues.findIndex((v) => v.code === issue.code && v.nodeId === issue.nodeId && v.message === issue.message) === index);
947
+ }
948
+ function semanticDiff(before, after) {
949
+ const changes = [];
950
+ const walk = (a, b, path) => {
951
+ if (a === void 0) {
952
+ changes.push({
953
+ path,
954
+ kind: "added",
955
+ after: b
956
+ });
957
+ return;
958
+ }
959
+ if (b === void 0) {
960
+ changes.push({
961
+ path,
962
+ kind: "removed",
963
+ before: a
964
+ });
965
+ return;
966
+ }
967
+ if (canonicalSerialize(a) === canonicalSerialize(b)) return;
968
+ if (a && b && typeof a === "object" && typeof b === "object" && !Array.isArray(a) && !Array.isArray(b)) {
969
+ const aa = a, bb = b;
970
+ for (const k of [.../* @__PURE__ */ new Set([...Object.keys(aa), ...Object.keys(bb)])].sort()) walk(aa[k], bb[k], `${path}/${k.replaceAll("~", "~0").replaceAll("/", "~1")}`);
971
+ } else changes.push({
972
+ path,
973
+ kind: "changed",
974
+ before: a,
975
+ after: b
976
+ });
977
+ };
978
+ walk(before, after, "");
979
+ return changes;
980
+ }
981
+ //#endregion
982
+ export { documentContentDigest as a, parseDesign as c, reviewState as d, selectLayoutItems as f, diagnostic as i, patchNode as l, validateDesign as m, canonicalSerialize as n, fileHash as o, semanticDiff as p, computeLayout as r, isConfinedRelativePath as s, buildIndexes as t, resolveValue as u };
983
+
984
+ //# sourceMappingURL=runtime-DC_ewLnZ.mjs.map