doxum 0.1.6 → 0.1.7

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 (45) hide show
  1. package/README.md +38 -13
  2. package/dist/{contract-I99DUMbN.d.ts → contract-DNZ4D53r.d.ts} +87 -76
  3. package/dist/{contract-uYJTmT8o.d.cts → contract-j3SLGAwh.d.cts} +87 -76
  4. package/dist/index.cjs +221 -349
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +3 -3
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.js +209 -327
  9. package/dist/index.js.map +1 -1
  10. package/dist/{integration-xupBIKjU.js → integration-B56u1l9V.js} +2 -2
  11. package/dist/integration-B56u1l9V.js.map +1 -0
  12. package/dist/{integration-Bs3pOk65.cjs → integration-CZwCwFBS.cjs} +2 -2
  13. package/dist/integration-CZwCwFBS.cjs.map +1 -0
  14. package/dist/integration.cjs +10 -2
  15. package/dist/integration.d.cts +3 -3
  16. package/dist/integration.d.ts +3 -3
  17. package/dist/integration.js +3 -3
  18. package/dist/local-sync.cjs +56 -17
  19. package/dist/local-sync.cjs.map +1 -1
  20. package/dist/local-sync.d.cts +2 -2
  21. package/dist/local-sync.d.ts +2 -2
  22. package/dist/local-sync.js +56 -17
  23. package/dist/local-sync.js.map +1 -1
  24. package/dist/react.cjs +27 -19
  25. package/dist/react.cjs.map +1 -1
  26. package/dist/react.js +27 -19
  27. package/dist/react.js.map +1 -1
  28. package/dist/{runtime-CRijXjYp.cjs → runtime-0mOFbe_H.cjs} +899 -451
  29. package/dist/runtime-0mOFbe_H.cjs.map +1 -0
  30. package/dist/{runtime-CUcvhFNP.d.ts → runtime-B22tuj9A.d.cts} +18 -20
  31. package/dist/{runtime-C_vKHmhy.d.cts → runtime-BFmhzPpZ.d.ts} +18 -20
  32. package/dist/{runtime-BJRXF0gq.js → runtime-DF1q9Gje.js} +823 -429
  33. package/dist/runtime-DF1q9Gje.js.map +1 -0
  34. package/package.json +1 -1
  35. package/skills/doxum-runtime/SKILL.md +2 -1
  36. package/skills/doxum-runtime/references/guide.en.md +18 -18
  37. package/skills/doxum-runtime/references/guide.zh-CN.md +12 -17
  38. package/skills/doxum-runtime/references/invariants.en.md +1 -1
  39. package/skills/doxum-runtime/references/invariants.zh-CN.md +1 -1
  40. package/skills/doxum-runtime/references/patterns.en.md +9 -20
  41. package/skills/doxum-runtime/references/patterns.zh-CN.md +5 -18
  42. package/dist/integration-Bs3pOk65.cjs.map +0 -1
  43. package/dist/integration-xupBIKjU.js.map +0 -1
  44. package/dist/runtime-BJRXF0gq.js.map +0 -1
  45. package/dist/runtime-CRijXjYp.cjs.map +0 -1
@@ -14,10 +14,19 @@ const require_ownership = require("./ownership-CY0nPXGF.cjs");
14
14
  //#region core/src/schema.ts
15
15
  const node = (value) => Object.freeze(value);
16
16
  const field = () => node({ kind: "field" });
17
- const optional = (value) => node({
18
- ...value,
19
- optional: true
20
- });
17
+ const optional = (value) => {
18
+ if (![
19
+ "field",
20
+ "variant",
21
+ "dict",
22
+ "list",
23
+ "tree"
24
+ ].includes(value.kind)) throw new TypeError("Only field, variant, dict, list and tree nodes support optional presence.");
25
+ return node({
26
+ ...value,
27
+ optional: true
28
+ });
29
+ };
21
30
  const object = (shape) => node({
22
31
  kind: "object",
23
32
  shape
@@ -27,10 +36,6 @@ const variant = (tag, variants) => node({
27
36
  tag,
28
37
  variants
29
38
  });
30
- const single = (value) => node({
31
- kind: "single",
32
- value
33
- });
34
39
  const table = (value) => node({
35
40
  kind: "table",
36
41
  value
@@ -39,26 +44,55 @@ const map = (value) => node({
39
44
  kind: "map",
40
45
  value
41
46
  });
42
- const record = () => node({ kind: "record" });
43
47
  const dict = () => node({ kind: "dict" });
44
48
  const list = (config) => ({
45
49
  kind: "list",
46
50
  keyOf: config.keyOf
47
51
  });
48
52
  const tree = () => node({ kind: "tree" });
49
- const pathProxy = (address) => new Proxy({ address }, { get: (_target, property) => {
50
- if (property === "address") return address;
51
- if (property === "item") return (id) => pathProxy([...address, id]);
52
- if (typeof property === "symbol") return void 0;
53
- return pathProxy([...address, property]);
54
- } });
53
+ const paths = /* @__PURE__ */ new WeakMap();
54
+ const collectionSchemas = /* @__PURE__ */ new WeakMap();
55
+ const collectionSchema = (selector) => {
56
+ const node = collectionSchemas.get(selector);
57
+ if (!node) throw new TypeError("Expected a schema-owned collection selector.");
58
+ return node;
59
+ };
60
+ const pathProxy = (nodes, address, owner) => {
61
+ const proxy = new Proxy({}, { get: (_target, property) => {
62
+ if (typeof property !== "string") return void 0;
63
+ if (nodes.every((node) => node.kind === "table" || node.kind === "map") && property === "item") return (id) => {
64
+ if (typeof id !== "string") throw new TypeError("Collection keys must be strings.");
65
+ return pathProxy(nodes.map((node) => node.value), [...address, id], owner);
66
+ };
67
+ const children = nodes.flatMap((node) => {
68
+ if (node.kind === "object") return node.shape[property] ? [node.shape[property]] : [];
69
+ if (node.kind !== "variant") return [];
70
+ if (property === node.tag) return [field()];
71
+ return Object.values(node.variants).flatMap((branch) => branch.shape[property] ? [branch.shape[property]] : []);
72
+ });
73
+ if (!children.length) throw new TypeError(`Invalid schema path segment: ${property}`);
74
+ return pathProxy(children, [...address, property], owner);
75
+ } });
76
+ paths.set(proxy, {
77
+ nodes,
78
+ address,
79
+ owner
80
+ });
81
+ return proxy;
82
+ };
55
83
  const select = (owner, kind, pick) => {
56
- const selected = pick(pathProxy([]));
57
- return Object.freeze({
84
+ const scope = {};
85
+ const value = pick(pathProxy([object(owner.shape)], [], scope));
86
+ const selected = typeof value === "object" && value !== null ? paths.get(value) : void 0;
87
+ if (!selected || selected.owner !== scope) throw new TypeError("Selector must return a path from its callback.");
88
+ if (kind === "collection" && !selected.nodes.every((node) => node.kind === "table" || node.kind === "map")) throw new TypeError("Collection selectors require a table or map path.");
89
+ const result = Object.freeze({
58
90
  kind,
59
91
  schema: owner,
60
- address: Object.freeze([...selected.address ?? []])
92
+ address: Object.freeze([...selected.address])
61
93
  });
94
+ if (kind === "collection") collectionSchemas.set(result, selected.nodes[0]);
95
+ return result;
62
96
  };
63
97
  const schema = (shape) => {
64
98
  const result = {
@@ -90,11 +124,6 @@ const hashText = (value, seed) => {
90
124
  };
91
125
  const appendAddressHash = (hash, segment) => hashText(segment, hashText("/", hash));
92
126
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
93
- const unwrap = (node) => {
94
- let current = node;
95
- while (current?.kind === "single") current = current.value;
96
- return current;
97
- };
98
127
  const variantNode = (node, value) => {
99
128
  const tag = isRecord(value) && typeof value[node.tag] === "string" ? value[node.tag] : void 0;
100
129
  return node.variants[String(tag ?? Object.keys(node.variants)[0] ?? "")];
@@ -104,12 +133,12 @@ const schemaRoot$1 = (schema) => ({
104
133
  shape: schema.shape
105
134
  });
106
135
  const step = (nodeInput, value, segment) => {
107
- let node = unwrap(nodeInput);
136
+ let node = nodeInput;
108
137
  if (!node) return {
109
138
  node: void 0,
110
139
  dynamic: false
111
140
  };
112
- if (node.kind === "variant") node = unwrap(variantNode(node, value));
141
+ if (node.kind === "variant") node = variantNode(node, value);
113
142
  if (!node) return {
114
143
  node: void 0,
115
144
  dynamic: false
@@ -122,7 +151,7 @@ const step = (nodeInput, value, segment) => {
122
151
  node: node.value,
123
152
  dynamic: true
124
153
  };
125
- if (node.kind === "record" || node.kind === "dict" || node.kind === "list" || node.kind === "tree") return {
154
+ if (node.kind === "dict" || node.kind === "list" || node.kind === "tree") return {
126
155
  node,
127
156
  dynamic: true
128
157
  };
@@ -173,7 +202,7 @@ const nodeAt = (schema, address, document) => {
173
202
  value = readSegment(value, segment);
174
203
  if (!node) return void 0;
175
204
  }
176
- return unwrap(node);
205
+ return node;
177
206
  };
178
207
  const readSegment = (value, segment) => {
179
208
  if (!isRecord(value) && !Array.isArray(value)) return void 0;
@@ -287,25 +316,93 @@ const debugKey = (address) => {
287
316
  //#endregion
288
317
  //#region core/src/impact-target.ts
289
318
  var impact_target_exports = /* @__PURE__ */ __exportAll({
290
- address: () => address,
319
+ address: () => address$1,
291
320
  belongs: () => belongs,
292
321
  bucket: () => bucket,
293
322
  id: () => id,
294
323
  same: () => same
295
324
  });
296
- const address = (target) => "at" in target ? target.at : target.address;
325
+ const address$1 = (target) => "at" in target ? target.at : target.address;
297
326
  const id = (target) => target.kind === "collection" && "id" in target ? target.id : void 0;
298
327
  const belongs = (target, schema) => !("schema" in target) || target.schema === schema;
299
328
  const same = (left, right) => {
300
329
  if (left.kind !== right.kind) return false;
301
330
  if ("schema" in left && "schema" in right && left.schema !== right.schema) return false;
302
- const leftAddress = address(left);
303
- const rightAddress = address(right);
331
+ const leftAddress = address$1(left);
332
+ const rightAddress = address$1(right);
304
333
  if (leftAddress.length !== rightAddress.length) return false;
305
334
  for (let index = 0; index < leftAddress.length; index += 1) if (leftAddress[index] !== rightAddress[index]) return false;
306
335
  return left.kind !== "collection" || id(left) === id(right);
307
336
  };
308
- const bucket = (target) => address(target)[0];
337
+ const bucket = (target) => address$1(target)[0];
338
+ //#endregion
339
+ //#region core/src/runtime/contract.ts
340
+ var DocumentReentrancyError = class extends Error {
341
+ constructor() {
342
+ super("Document writes cannot be re-entered during an update or notification.");
343
+ this.name = "DocumentReentrancyError";
344
+ }
345
+ };
346
+ var DocumentDisposedError = class extends Error {
347
+ constructor() {
348
+ super("Doxum runtime has been disposed.");
349
+ this.name = "DocumentDisposedError";
350
+ }
351
+ };
352
+ //#endregion
353
+ //#region core/src/mutation/anchor.ts
354
+ const length = (keys) => keys.length;
355
+ const keyAt = (keys, index) => Array.isArray(keys) ? keys[index] : keys.at(index);
356
+ const keyIndex = (keys, key) => Array.isArray(keys) ? keys.indexOf(key) : keys.index(key);
357
+ const keys = (values, keyOf) => ({
358
+ length: values.length,
359
+ at: (index) => index >= 0 && index < values.length ? keyOf(values[index]) : void 0,
360
+ index: (key) => {
361
+ for (let index = 0; index < values.length; index += 1) if (keyOf(values[index]) === key) return index;
362
+ return -1;
363
+ }
364
+ });
365
+ const index = (keys, anchor) => {
366
+ if (!anchor) return length(keys);
367
+ if ("at" in anchor) return anchor.at === "start" ? 0 : length(keys);
368
+ if ("before" in anchor) return keyIndex(keys, anchor.before);
369
+ return keyIndex(keys, anchor.after) + 1;
370
+ };
371
+ const at = (keys, position) => position <= 0 ? { at: "start" } : { after: keyAt(keys, position - 1) };
372
+ const valid = (keys, anchor) => {
373
+ if (!anchor) return true;
374
+ if (typeof anchor !== "object" || anchor === null) return false;
375
+ if (Number("at" in anchor) + Number("before" in anchor) + Number("after" in anchor) !== 1) return false;
376
+ if ("at" in anchor) return anchor.at === "start" || anchor.at === "end";
377
+ return keyIndex(keys, "before" in anchor ? anchor.before : anchor.after) >= 0;
378
+ };
379
+ const afterRemove = (keys, removedIndex, anchor) => {
380
+ const position = index(keys, anchor);
381
+ return position > removedIndex ? position - 1 : position;
382
+ };
383
+ const validPositions = (length, positions) => {
384
+ let previous = -1;
385
+ for (let index = 0; index < positions.length; index += 1) {
386
+ const position = positions[index];
387
+ if (!Number.isInteger(position) || position < 0 || position <= previous || position > length + index) return false;
388
+ previous = position;
389
+ }
390
+ return true;
391
+ };
392
+ const restore = (keys, inserted, positions) => {
393
+ const currentLength = keys.length;
394
+ if (inserted.length !== positions.length || !validPositions(currentLength, positions)) throw new Error("Ordered restore positions are invalid.");
395
+ let readIndex = currentLength - 1;
396
+ let insertedIndex = inserted.length - 1;
397
+ keys.length = currentLength + inserted.length;
398
+ for (let writeIndex = keys.length - 1; writeIndex >= 0; writeIndex -= 1) if (insertedIndex >= 0 && positions[insertedIndex] === writeIndex) {
399
+ keys[writeIndex] = inserted[insertedIndex];
400
+ insertedIndex -= 1;
401
+ } else {
402
+ keys[writeIndex] = keys[readIndex];
403
+ readIndex -= 1;
404
+ }
405
+ };
309
406
  //#endregion
310
407
  //#region core/src/access/reader.ts
311
408
  const schemaRoot = (schema) => object(schema.shape);
@@ -336,12 +433,40 @@ const readerFor = (node, context, address = []) => {
336
433
  collectValue(context, address);
337
434
  return readAt(context, address);
338
435
  } };
339
- if (node.kind === "dict" || node.kind === "record") return { get: () => {
340
- collectValue(context, address);
341
- require_ownership.profile.reader.structuralSnapshot();
342
- return require_ownership.cloneValue(readAt(context, address), "reader");
343
- } };
436
+ if (node.kind === "dict") return {
437
+ get: (key) => {
438
+ collectValue(context, address);
439
+ const value = readAt(context, address);
440
+ return require_ownership.cloneValue(require_ownership.isRecord(value) && Object.prototype.hasOwnProperty.call(value, key) ? value[key] : void 0, "reader");
441
+ },
442
+ has: (key) => {
443
+ collectValue(context, address);
444
+ const value = readAt(context, address);
445
+ return require_ownership.isRecord(value) && Object.prototype.hasOwnProperty.call(value, key);
446
+ },
447
+ keys: () => {
448
+ collectValue(context, address);
449
+ const value = readAt(context, address);
450
+ return require_ownership.isRecord(value) ? Object.keys(value) : [];
451
+ },
452
+ values: () => {
453
+ collectValue(context, address);
454
+ require_ownership.profile.reader.structuralSnapshot();
455
+ return require_ownership.cloneValue(readAt(context, address) ?? {}, "reader");
456
+ }
457
+ };
344
458
  if (node.kind === "list") return {
459
+ get: (key) => {
460
+ collectValue(context, address);
461
+ const value = readAt(context, address);
462
+ if (!Array.isArray(value)) return void 0;
463
+ return require_ownership.cloneValue(value[keys(value, node.keyOf).index(key)], "reader");
464
+ },
465
+ has: (key) => {
466
+ collectValue(context, address);
467
+ const value = readAt(context, address);
468
+ return Array.isArray(value) && keys(value, node.keyOf).index(key) >= 0;
469
+ },
345
470
  values: () => {
346
471
  collectValue(context, address);
347
472
  const value = readAt(context, address);
@@ -419,14 +544,10 @@ const readerFor = (node, context, address = []) => {
419
544
  }
420
545
  };
421
546
  }
422
- if (node.kind === "single") return readerFor(node.value, context, address);
423
- if (node.kind === "variant") return new Proxy({}, { get: (_target, property) => {
424
- if (typeof property === "symbol") return void 0;
425
- const value = readAt(context, address);
426
- const tag = require_ownership.isRecord(value) && typeof value[node.tag] === "string" ? String(value[node.tag]) : void 0;
427
- const child = (tag ? node.variants[tag] : void 0)?.shape[property];
428
- return child ? readerFor(child, context, [...address, property]) : void 0;
429
- } });
547
+ if (node.kind === "variant") return { get: () => {
548
+ collectValue(context, address);
549
+ return require_ownership.cloneValue(readAt(context, address), "reader");
550
+ } };
430
551
  let children;
431
552
  return new Proxy({}, { get: (_target, property) => {
432
553
  if (typeof property === "symbol") return void 0;
@@ -448,19 +569,191 @@ const documentReader = (schema, root, active, dependencies) => {
448
569
  });
449
570
  };
450
571
  //#endregion
451
- //#region core/src/runtime/contract.ts
452
- var DocumentReentrancyError = class extends Error {
453
- constructor() {
454
- super("Document writes cannot be re-entered during an update or notification.");
455
- this.name = "DocumentReentrancyError";
572
+ //#region core/src/mutation/footprint.ts
573
+ const sameAddress = (left, right) => left.length === right.length && left.every((segment, index) => segment === right[index]);
574
+ const containsAddress = (parent, child) => parent.length <= child.length && parent.every((segment, index) => segment === child[index]);
575
+ const address = (value) => Array.isArray(value) && value.every((segment) => typeof segment === "string") ? Object.freeze(value.slice()) : void 0;
576
+ const targetKey = (target) => {
577
+ const at = target.at.join("\0");
578
+ if (target.kind === "dictionary-key" || target.kind === "list-item") return `${target.kind}:${at}:${target.key}`;
579
+ if (target.kind === "entity" || target.kind === "tree-node") return `${target.kind}:${at}:${target.id}`;
580
+ return `${target.kind}:${at}`;
581
+ };
582
+ const freezeTarget = (target) => Object.freeze({
583
+ ...target,
584
+ at: Object.freeze(target.at.slice())
585
+ });
586
+ const add = (targets, target) => {
587
+ const stable = freezeTarget(target);
588
+ targets.set(targetKey(stable), stable);
589
+ };
590
+ const commandFootprint = (operations) => {
591
+ const targets = /* @__PURE__ */ new Map();
592
+ for (const operation of operations) switch (operation.type) {
593
+ case "field.set":
594
+ case "field.clear":
595
+ case "value.clear":
596
+ case "variant.replace":
597
+ add(targets, {
598
+ kind: "value",
599
+ at: operation.at
600
+ });
601
+ break;
602
+ case "dict.replace":
603
+ add(targets, {
604
+ kind: "dictionary",
605
+ at: operation.at
606
+ });
607
+ break;
608
+ case "dict.set":
609
+ case "dict.delete":
610
+ add(targets, {
611
+ kind: "dictionary-key",
612
+ at: operation.at,
613
+ key: operation.key
614
+ });
615
+ break;
616
+ case "entity.create":
617
+ add(targets, {
618
+ kind: "collection",
619
+ at: operation.at
620
+ });
621
+ for (const entry of operation.entries) add(targets, {
622
+ kind: "entity",
623
+ at: operation.at,
624
+ id: entry.id
625
+ });
626
+ break;
627
+ case "entity.remove":
628
+ add(targets, {
629
+ kind: "collection",
630
+ at: operation.at
631
+ });
632
+ for (const id of operation.ids) add(targets, {
633
+ kind: "entity",
634
+ at: operation.at,
635
+ id
636
+ });
637
+ break;
638
+ case "entity.move":
639
+ add(targets, {
640
+ kind: "collection",
641
+ at: operation.at
642
+ });
643
+ add(targets, {
644
+ kind: "entity",
645
+ at: operation.at,
646
+ id: operation.id
647
+ });
648
+ break;
649
+ case "list.insert":
650
+ case "list.move":
651
+ case "list.remove":
652
+ add(targets, {
653
+ kind: "list",
654
+ at: operation.at
655
+ });
656
+ add(targets, {
657
+ kind: "list-item",
658
+ at: operation.at,
659
+ key: operation.key
660
+ });
661
+ break;
662
+ case "list.replace":
663
+ add(targets, {
664
+ kind: "list",
665
+ at: operation.at
666
+ });
667
+ for (const key of operation.keys) add(targets, {
668
+ kind: "list-item",
669
+ at: operation.at,
670
+ key
671
+ });
672
+ break;
673
+ case "tree.insert":
674
+ case "tree.move":
675
+ case "tree.remove":
676
+ case "tree.set":
677
+ add(targets, {
678
+ kind: "tree",
679
+ at: operation.at
680
+ });
681
+ add(targets, {
682
+ kind: "tree-node",
683
+ at: operation.at,
684
+ id: operation.treeNodeId
685
+ });
686
+ break;
687
+ case "tree.replace":
688
+ add(targets, {
689
+ kind: "tree",
690
+ at: operation.at
691
+ });
692
+ break;
456
693
  }
694
+ return Object.freeze(Array.from(targets.values()));
457
695
  };
458
- var DocumentDisposedError = class extends Error {
459
- constructor() {
460
- super("Doxum runtime has been disposed.");
461
- this.name = "DocumentDisposedError";
696
+ const overlapsValueAndEntity = (value, entity) => containsAddress([...entity.at, entity.id], value.at) || containsAddress(value.at, [...entity.at, entity.id]);
697
+ const targetsOverlap = (left, right) => {
698
+ if (left.kind === "value" && right.kind === "value") return containsAddress(left.at, right.at) || containsAddress(right.at, left.at);
699
+ if (left.kind === "value" && right.kind === "entity") return overlapsValueAndEntity(left, right);
700
+ if (left.kind === "entity" && right.kind === "value") return overlapsValueAndEntity(right, left);
701
+ if (left.kind === "dictionary" && right.kind === "dictionary") return sameAddress(left.at, right.at);
702
+ if (left.kind === "dictionary" && right.kind === "dictionary-key") return sameAddress(left.at, right.at);
703
+ if (left.kind === "dictionary-key" && right.kind === "dictionary") return sameAddress(left.at, right.at);
704
+ if (left.kind === "dictionary-key" && right.kind === "dictionary-key") return sameAddress(left.at, right.at) && left.key === right.key;
705
+ if (left.kind === "collection" && right.kind === "collection") return sameAddress(left.at, right.at);
706
+ if (left.kind === "collection" && right.kind === "entity") return sameAddress(left.at, right.at);
707
+ if (left.kind === "entity" && right.kind === "collection") return sameAddress(left.at, right.at);
708
+ if (left.kind === "entity" && right.kind === "entity") return sameAddress(left.at, right.at) && left.id === right.id;
709
+ if (left.kind === "list" && (right.kind === "list" || right.kind === "list-item")) return sameAddress(left.at, right.at);
710
+ if (right.kind === "list" && left.kind === "list-item") return sameAddress(left.at, right.at);
711
+ if (left.kind === "list-item" && right.kind === "list-item") return sameAddress(left.at, right.at) && left.key === right.key;
712
+ if (left.kind === "tree" && (right.kind === "tree" || right.kind === "tree-node")) return sameAddress(left.at, right.at);
713
+ if (right.kind === "tree" && left.kind === "tree-node") return sameAddress(left.at, right.at);
714
+ if (left.kind === "tree-node" && right.kind === "tree-node") return sameAddress(left.at, right.at) && left.id === right.id;
715
+ return false;
716
+ };
717
+ const footprintsOverlap = (left, right) => left.some((leftTarget) => right.some((rightTarget) => targetsOverlap(leftTarget, rightTarget)));
718
+ const decodedTarget = (value) => {
719
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
720
+ const record = value;
721
+ const at = address(record.at);
722
+ if (!at || typeof record.kind !== "string") return void 0;
723
+ switch (record.kind) {
724
+ case "value":
725
+ case "dictionary":
726
+ case "collection":
727
+ case "list":
728
+ case "tree": return freezeTarget({
729
+ kind: record.kind,
730
+ at
731
+ });
732
+ case "dictionary-key":
733
+ case "list-item": return typeof record.key === "string" ? freezeTarget({
734
+ kind: record.kind,
735
+ at,
736
+ key: record.key
737
+ }) : void 0;
738
+ case "entity":
739
+ case "tree-node": return typeof record.id === "string" ? freezeTarget({
740
+ kind: record.kind,
741
+ at,
742
+ id: record.id
743
+ }) : void 0;
744
+ default: return;
462
745
  }
463
746
  };
747
+ const decodeCommandFootprint = (value) => {
748
+ if (!Array.isArray(value)) return void 0;
749
+ const targets = /* @__PURE__ */ new Map();
750
+ for (const entry of value) {
751
+ const target = decodedTarget(entry);
752
+ if (!target) return void 0;
753
+ targets.set(targetKey(target), target);
754
+ }
755
+ return Object.freeze(Array.from(targets.values()));
756
+ };
464
757
  //#endregion
465
758
  //#region core/src/runtime/access.ts
466
759
  const states = /* @__PURE__ */ new WeakMap();
@@ -487,6 +780,11 @@ const readWith = (runtime, run, dependencies) => {
487
780
  //#region core/src/runtime/notification.ts
488
781
  const ROOT_BUCKET = Symbol("document-root");
489
782
  const notifications = /* @__PURE__ */ new WeakMap();
783
+ const readableOwners = /* @__PURE__ */ new WeakMap();
784
+ const bindDocumentReadable = (readable, runtime) => {
785
+ readableOwners.set(readable, runtime);
786
+ };
787
+ const documentReadableOwner = (readable) => readableOwners.get(readable);
490
788
  const key = (value) => bucket(value) ?? ROOT_BUCKET;
491
789
  const createNotification = (runtime) => {
492
790
  const notification = {
@@ -561,7 +859,7 @@ const subscribeTargets = (notification, targets, listener) => {
561
859
  });
562
860
  };
563
861
  };
564
- const notify = (notification, commit) => {
862
+ const notify = (notification, commit, afterSettle) => {
565
863
  notification.notifying = true;
566
864
  const errors = [];
567
865
  const call = (listener) => {
@@ -612,6 +910,7 @@ const notify = (notification, commit) => {
612
910
  }));
613
911
  }
614
912
  }
913
+ if (afterSettle) errors.push(...afterSettle());
615
914
  const candidates = notification.candidates;
616
915
  candidates.clear();
617
916
  if (commit.impact.kind === "reset") notification.filtered.forEach((entry) => candidates.add(entry));
@@ -985,239 +1284,34 @@ const assertScope = (active) => {
985
1284
  if (!active()) throw new Error("Projection reader or writer is no longer active.");
986
1285
  };
987
1286
  //#endregion
988
- //#region core/src/projection/source.ts
989
- const createSources = (scheduler) => {
990
- const documents = /* @__PURE__ */ new Map();
991
- const readables = /* @__PURE__ */ new Map();
992
- const collections = /* @__PURE__ */ new WeakMap();
993
- const document = (runtime) => {
994
- scheduler.assertIdle();
995
- const state = accessOf(runtime);
996
- if (state.disposed) throw new Error("Document has been disposed.");
997
- const existing = documents.get(state);
998
- if (existing) return existing;
999
- const bindings = [];
1000
- const make = (targets, collection) => {
1001
- if (state.disposed) throw new Error("Document has been disposed.");
1002
- targets.forEach((value) => {
1003
- if (!belongs(value, state.schema)) throw new Error("Projection target belongs to another schema.");
1004
- });
1005
- const old = bindings.find((entry) => Boolean(collection) === collections.has(entry.handle) && entry.targets.length === targets.length && entry.targets.every((value, i) => same(value, targets[i])));
1006
- if (old) return old.handle;
1007
- let commits = [];
1008
- const record = {
1009
- consumers: /* @__PURE__ */ new Set(),
1010
- disposed: false,
1011
- fault: void 0,
1012
- revision: runtime.revision,
1013
- reset: () => commits.some((commit) => commit.kind === "replace"),
1014
- clear: () => {
1015
- commits = [];
1016
- },
1017
- context: (active) => {
1018
- assertScope(active);
1019
- if (state.disposed) throw new Error("Document has been disposed.");
1020
- const read = collection ? readerFor(nodeAt(state.schema, collection.address), {
1021
- root: () => state.document,
1022
- active
1023
- }, collection.address) : documentReader(state.schema, () => state.document, active);
1024
- return Object.freeze({
1025
- read,
1026
- target: collection,
1027
- revision: runtime.revision(),
1028
- commits: Object.freeze(commits.slice()),
1029
- reset: record.reset()
1030
- });
1031
- }
1032
- };
1033
- const handle = collection ? {} : {
1034
- collection: (pick) => {
1035
- scheduler.assertIdle();
1036
- const selector = state.schema.collection(pick);
1037
- const node = nodeAt(state.schema, selector.address);
1038
- if (node?.kind !== "table" && node?.kind !== "map") throw new TypeError("Expected a table or map collection path.");
1039
- return make([selector], selector);
1040
- },
1041
- targets: (...selected) => {
1042
- scheduler.assertIdle();
1043
- if (!selected.length) throw new TypeError("Expected at least one target.");
1044
- return make(selected);
1045
- }
1046
- };
1047
- Object.freeze(handle);
1048
- bindings.push({
1049
- handle,
1050
- targets,
1051
- record
1052
- });
1053
- if (collection) collections.set(handle, {
1054
- source: handle,
1055
- target: collection
1056
- });
1057
- scheduler.register(handle, record);
1058
- receivers.set(record, (commit) => {
1059
- commits.push(commit);
1060
- });
1061
- return handle;
1062
- };
1063
- const receivers = /* @__PURE__ */ new Map();
1064
- const handle = make([]);
1065
- const guard = (locked) => {
1066
- state.projectionLocks = (state.projectionLocks ?? 0) + (locked ? 1 : -1);
1067
- };
1068
- scheduler.guards.add(guard);
1069
- const unsubscribe = attachProjection(runtime, {
1070
- capture: (commit) => {
1071
- for (const binding of bindings) if (!binding.targets.length || binding.targets.some(commit.impact.affects)) {
1072
- receivers.get(binding.record)(commit);
1073
- scheduler.capture(binding.record);
1074
- }
1075
- },
1076
- settle: scheduler.settle,
1077
- flush: scheduler.flush,
1078
- dispose: () => {
1079
- bindings.forEach(({ record }) => {
1080
- record.fault = new ProjectionError("source", "document disposed", [runtime.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
1081
- scheduler.capture(record);
1082
- });
1083
- scheduler.run();
1287
+ //#region core/src/projection/node.ts
1288
+ const createNode = (scheduler, spec, behavior) => {
1289
+ scheduler.assertIdle();
1290
+ const entries = Object.entries(spec.sources).map(([key, handle]) => [key, scheduler.source(handle)]);
1291
+ const order = scheduler.order();
1292
+ const node = {
1293
+ ...behavior,
1294
+ publish: () => {
1295
+ if (node.fault) behavior.clear();
1296
+ else behavior.publish();
1297
+ },
1298
+ order,
1299
+ name: spec.name ? `${spec.name} (${order})` : `projection-${order}`,
1300
+ sources: entries.map(([, source]) => source),
1301
+ consumers: /* @__PURE__ */ new Set(),
1302
+ disposed: false,
1303
+ fault: void 0,
1304
+ forced: false,
1305
+ statusChanged: false,
1306
+ evaluate: (build) => {
1307
+ let active = true;
1308
+ try {
1309
+ const inputs = Object.fromEntries(entries.map(([key, source]) => [key, source.context(() => active)]));
1310
+ return behavior.evaluate(inputs, build, () => active);
1311
+ } finally {
1312
+ active = false;
1084
1313
  }
1085
- });
1086
- scheduler.cleanups.add(() => {
1087
- unsubscribe();
1088
- scheduler.guards.delete(guard);
1089
- });
1090
- documents.set(state, handle);
1091
- return handle;
1092
- };
1093
- const valueSource = (initial, equal) => {
1094
- let value = initial;
1095
- let previous = initial;
1096
- let revision = 0;
1097
- const handle = Object.freeze({});
1098
- const record = {
1099
- consumers: /* @__PURE__ */ new Set(),
1100
- disposed: false,
1101
- fault: void 0,
1102
- revision: () => revision,
1103
- reset: () => false,
1104
- clear: () => {
1105
- previous = value;
1106
- },
1107
- context: (active) => {
1108
- assertScope(active);
1109
- return Object.freeze({
1110
- value,
1111
- previous,
1112
- revision,
1113
- changed: !equal(previous, value),
1114
- reset: false
1115
- });
1116
- }
1117
- };
1118
- scheduler.register(handle, record);
1119
- const accept = (next) => {
1120
- scheduler.assertIdle();
1121
- const recovering = record.fault !== void 0;
1122
- if (equal(value, next) && !recovering) return;
1123
- record.fault = void 0;
1124
- value = next;
1125
- revision++;
1126
- scheduler.capture(record);
1127
- };
1128
- return {
1129
- source: handle,
1130
- accept,
1131
- set(next) {
1132
- accept(next);
1133
- scheduler.run();
1134
- }
1135
- };
1136
- };
1137
- return {
1138
- dispose: () => {
1139
- documents.clear();
1140
- readables.clear();
1141
- },
1142
- document,
1143
- input: (initial, options) => {
1144
- const { source, set } = valueSource(initial, options?.isEqual ?? Object.is);
1145
- return Object.freeze({
1146
- source,
1147
- set
1148
- });
1149
- },
1150
- fromReadable: (readable, options) => {
1151
- scheduler.assertIdle();
1152
- if (projectionHandles.has(readable)) throw new Error("Projection nodes must be declared directly as sources.");
1153
- const old = readables.get(readable);
1154
- if (old) return old;
1155
- const input = valueSource(readable.current(), options?.isEqual ?? Object.is);
1156
- const receive = () => {
1157
- if (!scheduler.active) return;
1158
- try {
1159
- input.accept(readable.current());
1160
- } catch (cause) {
1161
- const record = scheduler.source(input.source);
1162
- record.fault = new ProjectionError("source", "external readable", [readable.revision()], cause);
1163
- scheduler.capture(record);
1164
- }
1165
- scheduler.run();
1166
- };
1167
- let unsubscribe;
1168
- try {
1169
- unsubscribe = readable.subscribe(receive);
1170
- input.accept(readable.current());
1171
- } catch (error) {
1172
- try {
1173
- unsubscribe?.();
1174
- } finally {
1175
- scheduler.unregisterSource(input.source);
1176
- }
1177
- throw error;
1178
- }
1179
- scheduler.cleanups.add(unsubscribe);
1180
- readables.set(readable, input.source);
1181
- scheduler.run();
1182
- return input.source;
1183
- },
1184
- collectionTarget: (handle) => {
1185
- scheduler.source(handle);
1186
- const value = collections.get(handle);
1187
- if (!value) throw new Error("map requires a document collection source.");
1188
- return value.target;
1189
- }
1190
- };
1191
- };
1192
- //#endregion
1193
- //#region core/src/projection/node.ts
1194
- const createNode = (scheduler, spec, behavior) => {
1195
- scheduler.assertIdle();
1196
- const entries = Object.entries(spec.sources).map(([key, handle]) => [key, scheduler.source(handle)]);
1197
- const order = scheduler.order();
1198
- const node = {
1199
- ...behavior,
1200
- publish: () => {
1201
- if (node.fault) behavior.clear();
1202
- else behavior.publish();
1203
- },
1204
- order,
1205
- name: spec.name ? `${spec.name} (${order})` : `projection-${order}`,
1206
- sources: entries.map(([, source]) => source),
1207
- consumers: /* @__PURE__ */ new Set(),
1208
- disposed: false,
1209
- fault: void 0,
1210
- forced: false,
1211
- statusChanged: false,
1212
- evaluate: (build) => {
1213
- let active = true;
1214
- try {
1215
- const inputs = Object.fromEntries(entries.map(([key, source]) => [key, source.context(() => active)]));
1216
- return behavior.evaluate(inputs, build, () => active);
1217
- } finally {
1218
- active = false;
1219
- }
1220
- }
1314
+ }
1221
1315
  };
1222
1316
  scheduler.initialize(() => {
1223
1317
  const invalid = node.sources.find((source) => source.fault || source.disposed);
@@ -1242,103 +1336,8 @@ const createNode = (scheduler, spec, behavior) => {
1242
1336
  };
1243
1337
  };
1244
1338
  //#endregion
1245
- //#region core/src/projection/value.ts
1246
- const createValue = (scheduler, spec) => {
1247
- let instance;
1248
- let value;
1249
- let next;
1250
- let previous;
1251
- let initialized = false;
1252
- let revision = 0;
1253
- let changed = false;
1254
- let reset = false;
1255
- const listeners = /* @__PURE__ */ new Set();
1256
- const owner = createNode(scheduler, spec, {
1257
- evaluate: (sources, build) => {
1258
- let candidate;
1259
- if (build || !instance) {
1260
- require_ownership.profile.materialized.rebuilt();
1261
- instance = void 0;
1262
- const built = spec.build(sources);
1263
- assertSynchronous(built);
1264
- instance = built;
1265
- candidate = built.value;
1266
- } else {
1267
- require_ownership.profile.materialized.updated();
1268
- const result = instance.update(sources);
1269
- assertSynchronous(result);
1270
- if (result.kind === "rebuild") {
1271
- instance = void 0;
1272
- const built = spec.build(sources);
1273
- assertSynchronous(built);
1274
- instance = built;
1275
- candidate = built.value;
1276
- build = true;
1277
- } else candidate = result.kind === "changed" ? result.value : value;
1278
- }
1279
- previous = value;
1280
- changed = !initialized || !(spec.isEqual ?? Object.is)(value, candidate);
1281
- next = changed ? candidate : value;
1282
- reset = build;
1283
- return changed;
1284
- },
1285
- context: (active) => {
1286
- assertScope(active);
1287
- return Object.freeze({
1288
- value: next,
1289
- previous,
1290
- changed,
1291
- revision: revision + (changed ? 1 : 0),
1292
- reset
1293
- });
1294
- },
1295
- revision: () => revision,
1296
- reset: () => reset,
1297
- publish: () => {
1298
- if (changed && initialized) revision++;
1299
- value = next;
1300
- initialized = true;
1301
- },
1302
- emit: (call) => {
1303
- require_ownership.profile.materialized.notification();
1304
- Array.from(listeners).forEach((listener) => call(listener));
1305
- },
1306
- clear: () => {
1307
- next = value;
1308
- previous = value;
1309
- changed = false;
1310
- reset = false;
1311
- },
1312
- release: () => {
1313
- listeners.clear();
1314
- instance = void 0;
1315
- value = next = previous = void 0;
1316
- }
1317
- });
1318
- const handle = Object.freeze({
1319
- current: () => {
1320
- owner.check();
1321
- return value;
1322
- },
1323
- revision: () => {
1324
- owner.check();
1325
- return revision;
1326
- },
1327
- subscribe: (listener) => {
1328
- owner.check();
1329
- listeners.add(listener);
1330
- return () => {
1331
- listeners.delete(listener);
1332
- };
1333
- },
1334
- rebuild: owner.rebuild,
1335
- dispose: owner.dispose
1336
- });
1337
- owner.install(handle);
1338
- return handle;
1339
- };
1340
- //#endregion
1341
1339
  //#region core/src/projection/collection.ts
1340
+ const collectionHandles = /* @__PURE__ */ new WeakSet();
1342
1341
  const readonlySet = (values) => {
1343
1342
  const set = new Set(values);
1344
1343
  const view = Object.freeze({
@@ -1666,68 +1665,463 @@ const createCollection = (scheduler, spec) => {
1666
1665
  };
1667
1666
  projectionHandles.add(handle.ids);
1668
1667
  projectionHandles.add(handle.all);
1668
+ collectionHandles.add(handle);
1669
1669
  owner.install(handle);
1670
1670
  return Object.freeze(handle);
1671
1671
  };
1672
1672
  //#endregion
1673
- //#region core/src/projection/runtime.ts
1674
- const debug = /* @__PURE__ */ new WeakMap();
1675
- const projectionDebug = (runtime) => {
1676
- const read = debug.get(runtime);
1677
- if (!read) throw new Error("Unknown projection runtime.");
1678
- return Object.freeze(read());
1679
- };
1680
- const createProjectionRuntime = (options) => {
1681
- const scheduler = createScheduler(options.onError);
1682
- const sources = createSources(scheduler);
1683
- const runtime = {
1684
- document: sources.document,
1685
- input: sources.input,
1686
- fromReadable: sources.fromReadable,
1687
- value: (spec) => createValue(scheduler, spec),
1688
- collection: (spec) => createCollection(scheduler, spec),
1689
- map: (source, mapper, options) => {
1690
- sources.collectionTarget(source);
1691
- return createCollection(scheduler, {
1692
- sources: { source },
1693
- isEqual: options?.isEqual,
1694
- build: ({ sources, writer }) => {
1695
- const ids = sources.source.read.ids();
1696
- require_ownership.profile.collectionView.idsScanned(ids.length);
1697
- for (const id of ids) {
1698
- require_ownership.profile.collectionView.mapped();
1699
- writer.set(id, mapper(id, sources.source.read.get(id)));
1673
+ //#region core/src/projection/source.ts
1674
+ const createSources = (scheduler) => {
1675
+ const documents = /* @__PURE__ */ new Map();
1676
+ const readables = /* @__PURE__ */ new Map();
1677
+ const collections = /* @__PURE__ */ new WeakSet();
1678
+ const document = (runtime) => {
1679
+ scheduler.assertIdle();
1680
+ const state = accessOf(runtime);
1681
+ if (state.disposed) throw new Error("Document has been disposed.");
1682
+ const existing = documents.get(state);
1683
+ if (existing) return existing;
1684
+ const bindings = [];
1685
+ const make = (targets, collection) => {
1686
+ if (state.disposed) throw new Error("Document has been disposed.");
1687
+ targets.forEach((value) => {
1688
+ if (!belongs(value, state.schema)) throw new Error("Projection target belongs to another schema.");
1689
+ });
1690
+ const old = bindings.find((entry) => Boolean(collection) === collections.has(entry.handle) && entry.targets.length === targets.length && entry.targets.every((value, i) => same(value, targets[i])));
1691
+ if (old) return old.handle;
1692
+ let commits = [];
1693
+ let reset = false;
1694
+ let orderDirty = false;
1695
+ const keys = /* @__PURE__ */ new Set();
1696
+ let candidates;
1697
+ const record = {
1698
+ consumers: /* @__PURE__ */ new Set(),
1699
+ disposed: false,
1700
+ fault: void 0,
1701
+ revision: runtime.revision,
1702
+ reset: () => reset,
1703
+ clear: () => {
1704
+ commits = [];
1705
+ reset = orderDirty = false;
1706
+ keys.clear();
1707
+ candidates = void 0;
1708
+ },
1709
+ context: (active) => {
1710
+ assertScope(active);
1711
+ if (state.disposed) throw new Error("Document has been disposed.");
1712
+ const read = collection ? readerFor(nodeAt(state.schema, collection.address, state.document) ?? collectionSchema(collection), {
1713
+ root: () => state.document,
1714
+ active
1715
+ }, collection.address) : documentReader(state.schema, () => state.document, active);
1716
+ return Object.freeze({
1717
+ read,
1718
+ target: collection,
1719
+ revision: runtime.revision(),
1720
+ commits: Object.freeze(commits.slice()),
1721
+ reset: record.reset(),
1722
+ ...collection ? { candidates: candidates ??= Object.freeze({
1723
+ keys: Object.freeze([...keys]),
1724
+ orderDirty
1725
+ }) } : {}
1726
+ });
1727
+ }
1728
+ };
1729
+ const handle = collection ? {} : {
1730
+ collection: (pick) => {
1731
+ scheduler.assertIdle();
1732
+ const selector = state.schema.collection(pick);
1733
+ return make([selector], selector);
1734
+ },
1735
+ targets: (...selected) => {
1736
+ scheduler.assertIdle();
1737
+ if (!selected.length) throw new TypeError("Expected at least one target.");
1738
+ return make(selected);
1739
+ }
1740
+ };
1741
+ Object.freeze(handle);
1742
+ bindings.push({
1743
+ handle,
1744
+ targets,
1745
+ record
1746
+ });
1747
+ if (collection) collections.add(handle);
1748
+ scheduler.register(handle, record);
1749
+ receivers.set(record, (commit) => {
1750
+ commits.push(commit);
1751
+ reset ||= commit.kind === "replace";
1752
+ if (collection) {
1753
+ const change = commit.impact.collection(collection);
1754
+ if (change.kind === "reset") reset = true;
1755
+ else {
1756
+ change.added.forEach((key) => keys.add(key));
1757
+ change.removed.forEach((key) => keys.add(key));
1758
+ change.updated.forEach((key) => keys.add(key));
1759
+ orderDirty ||= Boolean(change.added.size || change.removed.size || change.orderChanged);
1700
1760
  }
1701
- writer.order(ids);
1702
- return { update: ({ sources, writer }) => {
1703
- const input = sources.source;
1704
- const keys = /* @__PURE__ */ new Set();
1705
- let structural = false;
1706
- for (const commit of input.commits) {
1707
- const impact = commit.impact.collection(input.target);
1708
- if (impact.kind === "reset") return { kind: "rebuild" };
1709
- impact.added.forEach((key) => keys.add(key));
1710
- impact.removed.forEach((key) => keys.add(key));
1711
- impact.updated.forEach((key) => keys.add(key));
1712
- structural ||= Boolean(impact.added.size || impact.removed.size || impact.orderChanged);
1713
- }
1714
- for (const key of keys) {
1715
- const entry = input.read.get(key);
1716
- if (!entry) writer.remove(key);
1717
- else {
1718
- require_ownership.profile.collectionView.mapped();
1719
- writer.set(key, mapper(key, entry));
1720
- }
1721
- }
1722
- if (structural) {
1723
- const ids = input.read.ids();
1724
- require_ownership.profile.collectionView.idsScanned(ids.length);
1725
- writer.order(ids);
1726
- }
1727
- } };
1761
+ candidates = void 0;
1728
1762
  }
1729
1763
  });
1764
+ return handle;
1765
+ };
1766
+ const receivers = /* @__PURE__ */ new Map();
1767
+ const handle = make([]);
1768
+ const guard = (locked) => {
1769
+ state.projectionLocks = (state.projectionLocks ?? 0) + (locked ? 1 : -1);
1770
+ };
1771
+ scheduler.guards.add(guard);
1772
+ const unsubscribe = attachProjection(runtime, {
1773
+ capture: (commit) => {
1774
+ for (const binding of bindings) if (!binding.targets.length || binding.targets.some(commit.impact.affects)) {
1775
+ receivers.get(binding.record)(commit);
1776
+ scheduler.capture(binding.record);
1777
+ }
1778
+ },
1779
+ settle: scheduler.settle,
1780
+ flush: scheduler.flush,
1781
+ dispose: () => {
1782
+ bindings.forEach(({ record }) => {
1783
+ record.fault = new ProjectionError("source", "document disposed", [runtime.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
1784
+ scheduler.capture(record);
1785
+ });
1786
+ scheduler.run();
1787
+ }
1788
+ });
1789
+ scheduler.cleanups.add(() => {
1790
+ unsubscribe();
1791
+ scheduler.guards.delete(guard);
1792
+ });
1793
+ documents.set(state, handle);
1794
+ return handle;
1795
+ };
1796
+ const valueSource = (initial, equal) => {
1797
+ let value = initial;
1798
+ let previous = initial;
1799
+ let revision = 0;
1800
+ const handle = Object.freeze({});
1801
+ const record = {
1802
+ consumers: /* @__PURE__ */ new Set(),
1803
+ disposed: false,
1804
+ fault: void 0,
1805
+ revision: () => revision,
1806
+ reset: () => false,
1807
+ clear: () => {
1808
+ previous = value;
1809
+ },
1810
+ context: (active) => {
1811
+ assertScope(active);
1812
+ return Object.freeze({
1813
+ value,
1814
+ previous,
1815
+ revision,
1816
+ changed: !equal(previous, value),
1817
+ reset: false
1818
+ });
1819
+ }
1820
+ };
1821
+ scheduler.register(handle, record);
1822
+ const accept = (next) => {
1823
+ scheduler.assertIdle();
1824
+ const recovering = record.fault !== void 0;
1825
+ if (equal(value, next) && !recovering) return;
1826
+ record.fault = void 0;
1827
+ value = next;
1828
+ revision++;
1829
+ scheduler.capture(record);
1830
+ };
1831
+ return {
1832
+ source: handle,
1833
+ accept,
1834
+ set(next) {
1835
+ accept(next);
1836
+ scheduler.run();
1837
+ }
1838
+ };
1839
+ };
1840
+ return {
1841
+ dispose: () => {
1842
+ documents.clear();
1843
+ readables.clear();
1844
+ },
1845
+ document,
1846
+ input: (initial, options) => {
1847
+ const { source, set } = valueSource(initial, options?.isEqual ?? Object.is);
1848
+ return Object.freeze({
1849
+ source,
1850
+ set
1851
+ });
1852
+ },
1853
+ fromReadable: (readable, options) => {
1854
+ scheduler.assertIdle();
1855
+ if (projectionHandles.has(readable)) throw new Error("Projection nodes must be declared directly as sources.");
1856
+ const equal = options?.isEqual ?? Object.is;
1857
+ let bindings = readables.get(readable);
1858
+ const old = bindings?.get(equal);
1859
+ if (old) return old.source;
1860
+ const input = valueSource(readable.current(), equal);
1861
+ if (bindings) {
1862
+ bindings.set(equal, {
1863
+ source: input.source,
1864
+ accept: (value) => input.accept(value)
1865
+ });
1866
+ return input.source;
1867
+ }
1868
+ bindings = new Map([[equal, {
1869
+ source: input.source,
1870
+ accept: (value) => input.accept(value)
1871
+ }]]);
1872
+ const members = bindings;
1873
+ const receive = (settle = true) => {
1874
+ if (!scheduler.active) return;
1875
+ let value;
1876
+ try {
1877
+ value = readable.current();
1878
+ } catch (cause) {
1879
+ for (const member of members.values()) {
1880
+ const record = scheduler.source(member.source);
1881
+ record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
1882
+ scheduler.capture(record);
1883
+ }
1884
+ if (settle) scheduler.run();
1885
+ return;
1886
+ }
1887
+ for (const member of members.values()) try {
1888
+ member.accept(value);
1889
+ } catch (cause) {
1890
+ const record = scheduler.source(member.source);
1891
+ record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
1892
+ scheduler.capture(record);
1893
+ }
1894
+ if (settle) scheduler.run();
1895
+ };
1896
+ let unsubscribe;
1897
+ let detach;
1898
+ try {
1899
+ const owner = documentReadableOwner(readable);
1900
+ if (owner) {
1901
+ document(owner);
1902
+ detach = attachProjection(owner, {
1903
+ capture: () => receive(false),
1904
+ settle: scheduler.settle,
1905
+ flush: scheduler.flush,
1906
+ dispose: () => {
1907
+ for (const member of members.values()) {
1908
+ const record = scheduler.source(member.source);
1909
+ record.fault = new ProjectionError("source", "document readable disposed", [record.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
1910
+ scheduler.capture(record);
1911
+ }
1912
+ scheduler.run();
1913
+ }
1914
+ });
1915
+ }
1916
+ unsubscribe = readable.subscribe(() => receive());
1917
+ input.accept(readable.current());
1918
+ } catch (error) {
1919
+ try {
1920
+ unsubscribe?.();
1921
+ detach?.();
1922
+ } finally {
1923
+ scheduler.unregisterSource(input.source);
1924
+ }
1925
+ throw error;
1926
+ }
1927
+ scheduler.cleanups.add(unsubscribe);
1928
+ if (detach) scheduler.cleanups.add(detach);
1929
+ readables.set(readable, bindings);
1930
+ scheduler.run();
1931
+ return input.source;
1932
+ },
1933
+ assertCollection: (handle) => {
1934
+ scheduler.source(handle);
1935
+ if (!collections.has(handle) && !collectionHandles.has(handle)) throw new Error("map requires a collection source.");
1936
+ }
1937
+ };
1938
+ };
1939
+ //#endregion
1940
+ //#region core/src/projection/value.ts
1941
+ const createValue = (scheduler, spec, options) => {
1942
+ let instance;
1943
+ let value;
1944
+ let next;
1945
+ let previous;
1946
+ let initialized = false;
1947
+ let revision = 0;
1948
+ let changed = false;
1949
+ let reset = false;
1950
+ const listeners = /* @__PURE__ */ new Set();
1951
+ const owner = createNode(scheduler, spec, {
1952
+ evaluate: (sources, build) => {
1953
+ let candidate;
1954
+ if (build || !instance) {
1955
+ require_ownership.profile.materialized.rebuilt();
1956
+ instance = void 0;
1957
+ const built = spec.build(sources);
1958
+ assertSynchronous(built);
1959
+ instance = built;
1960
+ candidate = built.value;
1961
+ } else {
1962
+ require_ownership.profile.materialized.updated();
1963
+ const result = instance.update(sources);
1964
+ assertSynchronous(result);
1965
+ if (result.kind === "rebuild") {
1966
+ instance = void 0;
1967
+ const built = spec.build(sources);
1968
+ assertSynchronous(built);
1969
+ instance = built;
1970
+ candidate = built.value;
1971
+ build = true;
1972
+ } else candidate = result.kind === "changed" ? result.value : value;
1973
+ }
1974
+ previous = value;
1975
+ changed = !initialized || !(options?.isEqual ?? Object.is)(value, candidate);
1976
+ next = changed ? candidate : value;
1977
+ reset = build;
1978
+ return changed;
1979
+ },
1980
+ context: (active) => {
1981
+ assertScope(active);
1982
+ return Object.freeze({
1983
+ value: next,
1984
+ previous,
1985
+ changed,
1986
+ revision: revision + (changed ? 1 : 0),
1987
+ reset
1988
+ });
1730
1989
  },
1990
+ revision: () => revision,
1991
+ reset: () => reset,
1992
+ publish: () => {
1993
+ if (changed && initialized) revision++;
1994
+ value = next;
1995
+ initialized = true;
1996
+ },
1997
+ emit: (call) => {
1998
+ require_ownership.profile.materialized.notification();
1999
+ Array.from(listeners).forEach((listener) => call(listener));
2000
+ },
2001
+ clear: () => {
2002
+ next = value;
2003
+ previous = value;
2004
+ changed = false;
2005
+ reset = false;
2006
+ },
2007
+ release: () => {
2008
+ listeners.clear();
2009
+ instance = void 0;
2010
+ value = next = previous = void 0;
2011
+ }
2012
+ });
2013
+ const handle = Object.freeze({
2014
+ current: () => {
2015
+ owner.check();
2016
+ return value;
2017
+ },
2018
+ revision: () => {
2019
+ owner.check();
2020
+ return revision;
2021
+ },
2022
+ subscribe: (listener) => {
2023
+ owner.check();
2024
+ listeners.add(listener);
2025
+ return () => {
2026
+ listeners.delete(listener);
2027
+ };
2028
+ },
2029
+ rebuild: owner.rebuild,
2030
+ dispose: owner.dispose
2031
+ });
2032
+ owner.install(handle);
2033
+ return handle;
2034
+ };
2035
+ //#endregion
2036
+ //#region core/src/projection/runtime.ts
2037
+ const debug = /* @__PURE__ */ new WeakMap();
2038
+ const projectionDebug = (runtime) => {
2039
+ const read = debug.get(runtime);
2040
+ if (!read) throw new Error("Unknown projection runtime.");
2041
+ return Object.freeze(read());
2042
+ };
2043
+ const createProjectionRuntime = (options) => {
2044
+ const scheduler = createScheduler(options.onError);
2045
+ const sources = createSources(scheduler);
2046
+ function value(input, computeOrOptions, options) {
2047
+ if (typeof computeOrOptions !== "function") return createValue(scheduler, input, computeOrOptions);
2048
+ const compute = (input) => {
2049
+ const result = computeOrOptions(input);
2050
+ assertSynchronous(result);
2051
+ return result;
2052
+ };
2053
+ return createValue(scheduler, {
2054
+ sources: input,
2055
+ build: (input) => ({
2056
+ value: compute(input),
2057
+ update: (input) => ({
2058
+ kind: "changed",
2059
+ value: compute(input)
2060
+ })
2061
+ })
2062
+ }, options);
2063
+ }
2064
+ const map = (source, mapper, options) => {
2065
+ sources.assertCollection(source);
2066
+ const calculate = (id, entry) => {
2067
+ const value = mapper(id, entry);
2068
+ assertSynchronous(value);
2069
+ return value;
2070
+ };
2071
+ return createCollection(scheduler, {
2072
+ sources: { source },
2073
+ isEqual: options?.isEqual,
2074
+ build: ({ sources, writer }) => {
2075
+ const input = sources.source;
2076
+ const read = "read" in input ? input.read : input;
2077
+ const ids = read.ids();
2078
+ require_ownership.profile.collectionView.idsScanned(ids.length);
2079
+ for (const id of ids) {
2080
+ require_ownership.profile.collectionView.mapped();
2081
+ writer.set(id, calculate(id, read.get(id)));
2082
+ }
2083
+ writer.order(ids);
2084
+ return { update: ({ sources, writer }) => {
2085
+ const input = sources.source;
2086
+ if (input.reset) return { kind: "rebuild" };
2087
+ const read = "read" in input ? input.read : input;
2088
+ let keys;
2089
+ let structural;
2090
+ if ("candidates" in input) {
2091
+ keys = input.candidates.keys;
2092
+ structural = input.candidates.orderDirty;
2093
+ } else {
2094
+ const change = input.change;
2095
+ if (!change) return;
2096
+ if (change.kind === "reset") return { kind: "rebuild" };
2097
+ keys = new Set([
2098
+ ...change.added,
2099
+ ...change.removed,
2100
+ ...change.updated
2101
+ ]);
2102
+ structural = Boolean(change.added.size || change.removed.size || change.orderChanged);
2103
+ }
2104
+ for (const key of keys) if (!read.has(key)) writer.remove(key);
2105
+ else {
2106
+ require_ownership.profile.collectionView.mapped();
2107
+ writer.set(key, calculate(key, read.get(key)));
2108
+ }
2109
+ if (structural) {
2110
+ const ids = read.ids();
2111
+ require_ownership.profile.collectionView.idsScanned(ids.length);
2112
+ writer.order(ids);
2113
+ }
2114
+ } };
2115
+ }
2116
+ });
2117
+ };
2118
+ const runtime = {
2119
+ document: sources.document,
2120
+ input: sources.input,
2121
+ fromReadable: sources.fromReadable,
2122
+ value,
2123
+ collection: () => (spec) => createCollection(scheduler, spec),
2124
+ map,
1731
2125
  batch: scheduler.batch,
1732
2126
  dispose: () => {
1733
2127
  try {
@@ -1774,7 +2168,19 @@ Object.defineProperty(exports, "accessOf", {
1774
2168
  Object.defineProperty(exports, "address", {
1775
2169
  enumerable: true,
1776
2170
  get: function() {
1777
- return address;
2171
+ return address$1;
2172
+ }
2173
+ });
2174
+ Object.defineProperty(exports, "afterRemove", {
2175
+ enumerable: true,
2176
+ get: function() {
2177
+ return afterRemove;
2178
+ }
2179
+ });
2180
+ Object.defineProperty(exports, "at", {
2181
+ enumerable: true,
2182
+ get: function() {
2183
+ return at;
1778
2184
  }
1779
2185
  });
1780
2186
  Object.defineProperty(exports, "belongs", {
@@ -1783,12 +2189,24 @@ Object.defineProperty(exports, "belongs", {
1783
2189
  return belongs;
1784
2190
  }
1785
2191
  });
2192
+ Object.defineProperty(exports, "bindDocumentReadable", {
2193
+ enumerable: true,
2194
+ get: function() {
2195
+ return bindDocumentReadable;
2196
+ }
2197
+ });
1786
2198
  Object.defineProperty(exports, "bindRuntimeAccess", {
1787
2199
  enumerable: true,
1788
2200
  get: function() {
1789
2201
  return bindRuntimeAccess;
1790
2202
  }
1791
2203
  });
2204
+ Object.defineProperty(exports, "commandFootprint", {
2205
+ enumerable: true,
2206
+ get: function() {
2207
+ return commandFootprint;
2208
+ }
2209
+ });
1792
2210
  Object.defineProperty(exports, "contains", {
1793
2211
  enumerable: true,
1794
2212
  get: function() {
@@ -1813,6 +2231,12 @@ Object.defineProperty(exports, "debugKey", {
1813
2231
  return debugKey;
1814
2232
  }
1815
2233
  });
2234
+ Object.defineProperty(exports, "decodeCommandFootprint", {
2235
+ enumerable: true,
2236
+ get: function() {
2237
+ return decodeCommandFootprint;
2238
+ }
2239
+ });
1816
2240
  Object.defineProperty(exports, "dict", {
1817
2241
  enumerable: true,
1818
2242
  get: function() {
@@ -1837,6 +2261,12 @@ Object.defineProperty(exports, "field", {
1837
2261
  return field;
1838
2262
  }
1839
2263
  });
2264
+ Object.defineProperty(exports, "footprintsOverlap", {
2265
+ enumerable: true,
2266
+ get: function() {
2267
+ return footprintsOverlap;
2268
+ }
2269
+ });
1840
2270
  Object.defineProperty(exports, "id", {
1841
2271
  enumerable: true,
1842
2272
  get: function() {
@@ -1849,6 +2279,18 @@ Object.defineProperty(exports, "impact_target_exports", {
1849
2279
  return impact_target_exports;
1850
2280
  }
1851
2281
  });
2282
+ Object.defineProperty(exports, "index", {
2283
+ enumerable: true,
2284
+ get: function() {
2285
+ return index;
2286
+ }
2287
+ });
2288
+ Object.defineProperty(exports, "keys", {
2289
+ enumerable: true,
2290
+ get: function() {
2291
+ return keys;
2292
+ }
2293
+ });
1852
2294
  Object.defineProperty(exports, "list", {
1853
2295
  enumerable: true,
1854
2296
  get: function() {
@@ -1909,12 +2351,6 @@ Object.defineProperty(exports, "readWith", {
1909
2351
  return readWith;
1910
2352
  }
1911
2353
  });
1912
- Object.defineProperty(exports, "record", {
1913
- enumerable: true,
1914
- get: function() {
1915
- return record;
1916
- }
1917
- });
1918
2354
  Object.defineProperty(exports, "resolveAddress", {
1919
2355
  enumerable: true,
1920
2356
  get: function() {
@@ -1927,6 +2363,12 @@ Object.defineProperty(exports, "resolveLocated", {
1927
2363
  return resolveLocated;
1928
2364
  }
1929
2365
  });
2366
+ Object.defineProperty(exports, "restore", {
2367
+ enumerable: true,
2368
+ get: function() {
2369
+ return restore;
2370
+ }
2371
+ });
1930
2372
  Object.defineProperty(exports, "same", {
1931
2373
  enumerable: true,
1932
2374
  get: function() {
@@ -1951,12 +2393,6 @@ Object.defineProperty(exports, "shareNotification", {
1951
2393
  return shareNotification;
1952
2394
  }
1953
2395
  });
1954
- Object.defineProperty(exports, "single", {
1955
- enumerable: true,
1956
- get: function() {
1957
- return single;
1958
- }
1959
- });
1960
2396
  Object.defineProperty(exports, "subscribeRoot", {
1961
2397
  enumerable: true,
1962
2398
  get: function() {
@@ -1981,6 +2417,18 @@ Object.defineProperty(exports, "tree", {
1981
2417
  return tree;
1982
2418
  }
1983
2419
  });
2420
+ Object.defineProperty(exports, "valid", {
2421
+ enumerable: true,
2422
+ get: function() {
2423
+ return valid;
2424
+ }
2425
+ });
2426
+ Object.defineProperty(exports, "validPositions", {
2427
+ enumerable: true,
2428
+ get: function() {
2429
+ return validPositions;
2430
+ }
2431
+ });
1984
2432
  Object.defineProperty(exports, "variant", {
1985
2433
  enumerable: true,
1986
2434
  get: function() {
@@ -1988,4 +2436,4 @@ Object.defineProperty(exports, "variant", {
1988
2436
  }
1989
2437
  });
1990
2438
 
1991
- //# sourceMappingURL=runtime-CRijXjYp.cjs.map
2439
+ //# sourceMappingURL=runtime-0mOFbe_H.cjs.map