doxum 0.1.21 → 0.1.23

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 (72) hide show
  1. package/README.md +35 -7
  2. package/dist/{advanced-FgdTwu5b.d.ts → advanced-CLJCWkHa.d.cts} +3 -3
  3. package/dist/{advanced-COFJtWMT.js → advanced-CRNHfJBu.js} +482 -446
  4. package/dist/advanced-CRNHfJBu.js.map +1 -0
  5. package/dist/{advanced-BRL5pN0y.cjs → advanced-Cle5g3B4.cjs} +615 -459
  6. package/dist/advanced-Cle5g3B4.cjs.map +1 -0
  7. package/dist/{advanced-DfKc7TZR.d.cts → advanced-d6J61er9.d.ts} +3 -3
  8. package/dist/advanced.cjs +1 -1
  9. package/dist/advanced.d.cts +1 -1
  10. package/dist/advanced.d.ts +1 -1
  11. package/dist/advanced.js +1 -1
  12. package/dist/context-BcLKGfq8.cjs +149 -0
  13. package/dist/context-BcLKGfq8.cjs.map +1 -0
  14. package/dist/context-DFEaDUv5.js +84 -0
  15. package/dist/context-DFEaDUv5.js.map +1 -0
  16. package/dist/{contract-CdmDHRUq.d.cts → contract-SYJFP0lM.d.cts} +62 -88
  17. package/dist/{contract-KG5XnQZt.d.ts → contract-ULPQmiEs.d.ts} +62 -88
  18. package/dist/driver-BD3DQOW6.cjs +382 -0
  19. package/dist/driver-BD3DQOW6.cjs.map +1 -0
  20. package/dist/{driver-CLYJOGDQ.js → driver-yFfvDMX5.js} +155 -26
  21. package/dist/driver-yFfvDMX5.js.map +1 -0
  22. package/dist/index.cjs +2370 -406
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +6 -5
  25. package/dist/index.d.ts +6 -5
  26. package/dist/index.js +2293 -330
  27. package/dist/index.js.map +1 -1
  28. package/dist/local-sync.cjs +5 -5
  29. package/dist/local-sync.cjs.map +1 -1
  30. package/dist/local-sync.d.cts +1 -1
  31. package/dist/local-sync.d.ts +1 -1
  32. package/dist/local-sync.js +2 -2
  33. package/dist/local-sync.js.map +1 -1
  34. package/dist/react.cjs +2 -69
  35. package/dist/react.cjs.map +1 -1
  36. package/dist/react.d.cts +0 -1
  37. package/dist/react.d.ts +1 -2
  38. package/dist/react.js +3 -70
  39. package/dist/react.js.map +1 -1
  40. package/package.json +1 -6
  41. package/skills/doxum-runtime/SKILL.md +7 -1
  42. package/skills/doxum-runtime/references/guide.en.md +17 -6
  43. package/skills/doxum-runtime/references/guide.zh-CN.md +15 -5
  44. package/skills/doxum-runtime/references/invariants.en.md +5 -3
  45. package/skills/doxum-runtime/references/invariants.zh-CN.md +4 -3
  46. package/skills/doxum-runtime/references/projections.en.md +5 -0
  47. package/skills/doxum-runtime/references/projections.zh-CN.md +5 -0
  48. package/dist/access-BeSV5ve-.js +0 -816
  49. package/dist/access-BeSV5ve-.js.map +0 -1
  50. package/dist/access-Bienf28h.cjs +0 -911
  51. package/dist/access-Bienf28h.cjs.map +0 -1
  52. package/dist/advanced-BRL5pN0y.cjs.map +0 -1
  53. package/dist/advanced-COFJtWMT.js.map +0 -1
  54. package/dist/driver-CLYJOGDQ.js.map +0 -1
  55. package/dist/driver-Djr3BVvo.cjs +0 -241
  56. package/dist/driver-Djr3BVvo.cjs.map +0 -1
  57. package/dist/integration-C4Isp8lJ.js +0 -26
  58. package/dist/integration-C4Isp8lJ.js.map +0 -1
  59. package/dist/integration-Z6MsfFI7.cjs +0 -31
  60. package/dist/integration-Z6MsfFI7.cjs.map +0 -1
  61. package/dist/integration.cjs +0 -12
  62. package/dist/integration.d.cts +0 -17
  63. package/dist/integration.d.ts +0 -17
  64. package/dist/integration.js +0 -4
  65. package/dist/issue-DjK1xp5K.cjs +0 -881
  66. package/dist/issue-DjK1xp5K.cjs.map +0 -1
  67. package/dist/issue-W0hT8u8D.js +0 -642
  68. package/dist/issue-W0hT8u8D.js.map +0 -1
  69. package/dist/notification-Bkjau6B6.js +0 -232
  70. package/dist/notification-Bkjau6B6.js.map +0 -1
  71. package/dist/notification-DJfouMEb.cjs +0 -309
  72. package/dist/notification-DJfouMEb.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,105 +1,1026 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_issue = require("./issue-DjK1xp5K.cjs");
3
- const require_access = require("./access-Bienf28h.cjs");
4
- const require_advanced = require("./advanced-BRL5pN0y.cjs");
5
- const require_notification = require("./notification-DJfouMEb.cjs");
6
- const require_driver = require("./driver-Djr3BVvo.cjs");
2
+ const require_advanced = require("./advanced-Cle5g3B4.cjs");
3
+ const require_context = require("./context-BcLKGfq8.cjs");
4
+ const require_driver = require("./driver-BD3DQOW6.cjs");
5
+ //#region core/src/address/resolve.ts
6
+ const variantNode = (node, value) => {
7
+ const tag = require_context.isRecord(value) && typeof value[node.tag] === "string" ? value[node.tag] : void 0;
8
+ return node.variants[String(tag ?? Object.keys(node.variants)[0] ?? "")];
9
+ };
10
+ const step = (nodeInput, value, segment) => {
11
+ let node = nodeInput;
12
+ if (!node) return void 0;
13
+ if (node.kind === "variant") node = variantNode(node, value);
14
+ if (!node) return void 0;
15
+ if (node.kind === "object") return Object.hasOwn(node.shape, segment) ? node.shape[segment] : void 0;
16
+ if (node.kind === "table" || node.kind === "map") return node.value;
17
+ if (node.kind === "list") return node.value;
18
+ };
19
+ const nodeAt = (schema, address, document) => {
20
+ let node = schema;
21
+ let value = document;
22
+ for (const segment of address) {
23
+ const resolved = step(node, value, segment);
24
+ value = readSegment(value, segment, node);
25
+ node = resolved;
26
+ if (!node) return void 0;
27
+ }
28
+ return node;
29
+ };
30
+ const readSegment = (value, segment, node) => {
31
+ if (!require_context.isRecord(value) && !Array.isArray(value)) return void 0;
32
+ if (node?.kind === "list" && Array.isArray(value)) return value[require_advanced.indexedKeys(value, node.keyOf).index(segment)];
33
+ if (node?.kind === "table" && require_context.isRecord(value) && require_context.isRecord(value.byId)) return Object.hasOwn(value.byId, segment) ? value.byId[segment] : void 0;
34
+ return Object.prototype.hasOwnProperty.call(value, segment) ? value[segment] : void 0;
35
+ };
36
+ const resolveContainer = (owner, generation, at, node, value) => {
37
+ if (!node || !require_context.isRecord(value) && !Array.isArray(value)) return void 0;
38
+ const parent = node.kind === "table" && require_context.isRecord(value) ? value.byId : value;
39
+ if (!require_context.isRecord(parent) && !Array.isArray(parent)) return void 0;
40
+ if (node.kind === "tree") return void 0;
41
+ const layout = require_advanced.compiledLayout(node, value);
42
+ if (!layout) return void 0;
43
+ return {
44
+ owner,
45
+ generation,
46
+ at,
47
+ node,
48
+ parent,
49
+ value,
50
+ layout
51
+ };
52
+ };
53
+ const resolveTreeContainer = (owner, generation, at, node, value) => node?.kind === "tree" && require_context.is(value) ? {
54
+ owner,
55
+ generation,
56
+ at,
57
+ node,
58
+ value
59
+ } : void 0;
60
+ const memberKey = (container, key) => container.node.kind === "list" ? require_advanced.indexedKeys(container.parent, container.node.keyOf).index(key) : key;
61
+ /** One recent path per transaction; structural writes invalidate all retained locations. */
62
+ const createAddressResolver = (schema, root) => {
63
+ const prefix = [];
64
+ return {
65
+ container: (owner, generation, at) => {
66
+ if (!at.length) return resolveContainer(owner, generation, at, schema, root);
67
+ const location = resolveWithPrefix(schema, root, at, prefix);
68
+ if (!location || !Object.hasOwn(location.parent, location.key)) return void 0;
69
+ return resolveContainer(owner, generation, at, location.node, location.parent[location.key]);
70
+ },
71
+ invalidate: () => {
72
+ prefix.length = 0;
73
+ }
74
+ };
75
+ };
76
+ /** A subtree root is a value, not a fabricated member of an ObjectNode. */
77
+ const resolveValue = (schema, root, address, prefix) => {
78
+ if (!address.length) return {
79
+ node: schema,
80
+ value: root
81
+ };
82
+ const location = resolveWithPrefix(schema, root, address, prefix);
83
+ return location && {
84
+ node: location.node,
85
+ value: Object.hasOwn(location.parent, location.key) ? location.parent[location.key] : void 0
86
+ };
87
+ };
88
+ /** Resolves schema and document location in one address traversal. */
89
+ const resolveLocated = (schema, root, address) => resolveWithPrefix(schema, root, address);
90
+ const resolveWithPrefix = (schema, root, address, prefix) => {
91
+ if (address.length === 0) return void 0;
92
+ let node = schema;
93
+ let current = root;
94
+ let start = 0;
95
+ if (prefix) {
96
+ while (start < prefix.length && start < address.length - 1 && prefix[start].segment === address[start]) start++;
97
+ prefix.length = start;
98
+ if (start) {
99
+ const retained = prefix[start - 1];
100
+ node = retained.node;
101
+ current = retained.value;
102
+ }
103
+ }
104
+ let cacheable = true;
105
+ for (let index = start; index < address.length - 1; index += 1) {
106
+ require_advanced.profile.address.schemaStep();
107
+ require_advanced.profile.address.documentStep();
108
+ const resolved = step(node, current, address[index]);
109
+ if (!resolved) return void 0;
110
+ if (node?.kind !== "object" && node?.kind !== "table" && node?.kind !== "map") cacheable = false;
111
+ current = readSegment(current, address[index], node);
112
+ node = resolved;
113
+ if (prefix && cacheable) prefix.push({
114
+ segment: address[index],
115
+ node: resolved,
116
+ value: current
117
+ });
118
+ if (!node) return void 0;
119
+ }
120
+ const last = address[address.length - 1];
121
+ return resolveChild(node, current, last);
122
+ };
123
+ /** Resolves one member from an already current schema/container pair. */
124
+ const resolveChild = (node, current, last) => {
125
+ require_advanced.profile.address.schemaStep();
126
+ require_advanced.profile.address.documentStep();
127
+ const resolved = step(node, current, last);
128
+ if (!resolved || !require_context.isRecord(current) && !Array.isArray(current)) return void 0;
129
+ if (node?.kind === "table" && require_context.isRecord(current) && require_context.isRecord(current.byId)) return {
130
+ node: resolved,
131
+ parent: current.byId,
132
+ key: last
133
+ };
134
+ return {
135
+ node: resolved,
136
+ parent: current,
137
+ key: node?.kind === "list" && Array.isArray(current) ? require_advanced.indexedKeys(current, node.keyOf).index(last) : last
138
+ };
139
+ };
140
+ //#endregion
141
+ //#region core/src/mutation/operations/map.ts
142
+ function put(session, container, id, value) {
143
+ if (container.node.kind !== "map") return require_driver.fail(container.at, "invalid-collection", "Expected a map.");
144
+ session.writeMember(container, id, value, "set");
145
+ }
146
+ function remove$3(session, container, id) {
147
+ if (container.node.kind !== "map") return require_driver.fail(container.at, "invalid-collection", "Expected a map.");
148
+ session.writeMember(container, id, void 0, "remove");
149
+ }
150
+ //#endregion
151
+ //#region core/src/order/anchor.ts
152
+ const length = (keys) => keys.length;
153
+ const keyIndex = (keys, key) => Array.isArray(keys) ? keys.indexOf(key) : keys.index(key);
154
+ /** Plan one relative move against the order that remains after removing the selection. */
155
+ const moveSelection = (current, selection, position) => {
156
+ if (!selection.length) return {
157
+ kind: "ok",
158
+ order: current
159
+ };
160
+ const selected = /* @__PURE__ */ new Set();
161
+ for (const key of selection) {
162
+ if (selected.has(key)) return { kind: "duplicate" };
163
+ selected.add(key);
164
+ }
165
+ const moved = [];
166
+ const remaining = [];
167
+ for (const key of current) (selected.has(key) ? moved : remaining).push(key);
168
+ if (moved.length !== selected.size) return { kind: "missing" };
169
+ if (!valid(remaining, position)) return { kind: "invalid-anchor" };
170
+ const insertion = index(remaining, position);
171
+ return {
172
+ kind: "ok",
173
+ order: [
174
+ ...remaining.slice(0, insertion),
175
+ ...moved,
176
+ ...remaining.slice(insertion)
177
+ ]
178
+ };
179
+ };
180
+ const index = (keys, anchor) => {
181
+ if (!anchor) return length(keys);
182
+ if ("at" in anchor) return anchor.at === "start" ? 0 : length(keys);
183
+ if ("before" in anchor) return keyIndex(keys, anchor.before);
184
+ return keyIndex(keys, anchor.after) + 1;
185
+ };
186
+ const valid = (keys, anchor) => {
187
+ if (!anchor) return true;
188
+ if (typeof anchor !== "object" || anchor === null) return false;
189
+ if (Number("at" in anchor) + Number("before" in anchor) + Number("after" in anchor) !== 1) return false;
190
+ if ("at" in anchor) return anchor.at === "start" || anchor.at === "end";
191
+ return keyIndex(keys, "before" in anchor ? anchor.before : anchor.after) >= 0;
192
+ };
193
+ //#endregion
194
+ //#region core/src/mutation/operations/table.ts
195
+ function create(session, container, entries, position) {
196
+ const { at, node, value } = container;
197
+ if (node.kind !== "table") return require_driver.fail(at, "invalid-collection", "Expected a table.");
198
+ const table = value;
199
+ if (!valid(table.ids, position)) return require_driver.fail(at, "invalid-anchor", "Unknown table anchor.");
200
+ const ids = /* @__PURE__ */ new Set();
201
+ for (const entry of entries) {
202
+ if (Object.hasOwn(table.byId, entry.id) || ids.has(entry.id)) return require_driver.fail(at, "duplicate-entity", "Table key already exists.");
203
+ ids.add(entry.id);
204
+ }
205
+ if (!entries.length) return;
206
+ const index$1 = index(table.ids, position);
207
+ for (const entry of entries) session.writeMember(container, entry.id, entry.value, "set");
208
+ const length = table.ids.length;
209
+ table.ids.length += entries.length;
210
+ table.ids.copyWithin(index$1 + entries.length, index$1, length);
211
+ for (let i = 0; i < entries.length; i++) table.ids[index$1 + i] = entries[i].id;
212
+ }
213
+ function remove$2(session, container, ids) {
214
+ const { at, node, value } = container;
215
+ if (node.kind !== "table") return require_driver.fail(at, "invalid-collection", "Expected a table.");
216
+ const table = value;
217
+ for (const id of ids) if (!Object.hasOwn(table.byId, id)) return require_driver.fail(at, "missing-entity", "Table key does not exist.");
218
+ if (!ids.length) return;
219
+ const removed = new Set(ids);
220
+ for (const id of removed) session.writeMember(container, id, void 0, "remove");
221
+ table.ids = table.ids.filter((id) => !removed.has(id));
222
+ }
223
+ function replace$3(session, container, id, value) {
224
+ const { at, node, value: tableValue } = container;
225
+ if (node.kind !== "table") return require_driver.fail(at, "invalid-collection", "Expected a table.");
226
+ const table = tableValue;
227
+ if (!Object.hasOwn(table.byId, id)) return require_driver.fail(at.concat(id), "missing-entity", "Table key does not exist.");
228
+ session.writeMember(container, id, value, "set");
229
+ }
230
+ //#endregion
231
+ //#region core/src/mutation/operations/list.ts
232
+ function insert$1(session, container, value, position) {
233
+ const { at, node, value: current } = container;
234
+ if (node.kind !== "list") return require_driver.fail(at, "invalid-list-key", "Expected a list.");
235
+ session.validate(node.value, value, at);
236
+ const id = node.keyOf(value);
237
+ if (typeof id !== "string") return require_driver.fail(at, "invalid-list-key", "List key must be a string.");
238
+ const items = current, keys$1 = require_advanced.keys(items, node.keyOf);
239
+ if (keys$1.index(id) >= 0) return require_driver.fail(at, "duplicate-list-item", "List key already exists.");
240
+ if (!valid(keys$1, position)) return require_driver.fail(at, "invalid-anchor", "Unknown list anchor.");
241
+ session.recorder.order(container);
242
+ session.recorder.member(container, id, session.definition(container, id), -1);
243
+ require_advanced.insert(items, index(keys$1, position), value);
244
+ session.invalidate();
245
+ }
246
+ function replace$2(session, container, id, value) {
247
+ const { at, node, value: items } = container;
248
+ if (node.kind !== "list") return require_driver.fail(at, "invalid-list-key", "Expected a list.");
249
+ const index = require_advanced.indexedKeys(items, node.keyOf).index(id);
250
+ if (index < 0) return require_driver.fail(at, "missing-list-item", "List key does not exist.");
251
+ session.writeMember(container, id, value, "set", index);
252
+ }
253
+ function remove$1(session, container, id) {
254
+ const { at, node, value } = container;
255
+ if (node.kind !== "list") return require_driver.fail(at, "missing-list-item", "List key does not exist.");
256
+ const index = require_advanced.keys(value, node.keyOf).index(id);
257
+ if (index < 0) return require_driver.fail(at, "missing-list-item", "List key does not exist.");
258
+ session.writeMember(container, id, void 0, "remove", index);
259
+ }
260
+ //#endregion
261
+ //#region core/src/mutation/operations/tree.ts
262
+ const node$1 = (tree, id) => Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : void 0;
263
+ const positionIndex = (index, length, at) => {
264
+ if (index !== void 0 && (!Number.isInteger(index) || index < 0)) return require_driver.fail(at, "invalid-tree-index", "Tree index must be nonnegative.");
265
+ return Math.min(index ?? length, length);
266
+ };
267
+ const insert = (session, container, id, value, position) => {
268
+ const { at, node: schema, value: tree } = container;
269
+ if (typeof id !== "string") return require_driver.fail(at, "invalid-key", "Tree keys must be strings.");
270
+ if (require_context.contains(tree, id)) return require_driver.fail(at, "duplicate-tree-node", "Tree node already exists.");
271
+ const parentId = position?.parentId;
272
+ const parent = parentId === void 0 ? void 0 : node$1(tree, parentId);
273
+ if (tree.rootId !== void 0 && !parent || tree.rootId === void 0 && parentId !== void 0) return require_driver.fail(at, "missing-tree-parent", "A nonempty tree requires an existing parent.");
274
+ const index = positionIndex(position?.index, parent?.children.length ?? 0, at);
275
+ session.validate(schema.value, value, at.concat(id));
276
+ session.recorder.tree(container, id);
277
+ if (parentId !== void 0) session.recorder.tree(container, parentId);
278
+ Object.defineProperty(tree.nodes, id, {
279
+ value: {
280
+ ...parentId === void 0 ? {} : { parentId },
281
+ children: [],
282
+ value
283
+ },
284
+ writable: true,
285
+ configurable: true,
286
+ enumerable: true
287
+ });
288
+ if (parent) parent.children.splice(index, 0, id);
289
+ else tree.rootId = id;
290
+ session.invalidate();
291
+ };
292
+ const replace$1 = (session, container, id, value) => {
293
+ const { at, node: schema, value: tree } = container;
294
+ const entry = node$1(tree, id);
295
+ if (!entry) return require_driver.fail(at, "missing-tree-node", "Tree node does not exist.");
296
+ if (Object.is(entry.value, value) && Object.hasOwn(entry, "value")) return;
297
+ session.validate(schema.value, value, at.concat(id));
298
+ session.recorder.tree(container, id);
299
+ entry.value = value;
300
+ };
301
+ const remove = (session, container, id) => {
302
+ const { at, value: tree } = container;
303
+ const entry = node$1(tree, id);
304
+ if (!entry) return require_driver.fail(at, "missing-tree-node", "Tree node does not exist.");
305
+ const ids = [], stack = [id];
306
+ while (stack.length) {
307
+ const current = stack.pop();
308
+ ids.push(current);
309
+ for (const child of tree.nodes[current].children) stack.push(child);
310
+ }
311
+ for (const key of ids) session.recorder.tree(container, key);
312
+ if (entry.parentId !== void 0) session.recorder.tree(container, entry.parentId);
313
+ if (entry.parentId === void 0) delete tree.rootId;
314
+ else {
315
+ const children = tree.nodes[entry.parentId].children;
316
+ children.splice(children.indexOf(id), 1);
317
+ }
318
+ for (const key of ids) delete tree.nodes[key];
319
+ session.invalidate();
320
+ };
321
+ const move$1 = (session, container, id, position) => {
322
+ const { at, value: tree } = container;
323
+ const entry = node$1(tree, id), parentId = position?.parentId;
324
+ if (!entry) return require_driver.fail(at, "missing-tree-node", "Tree node does not exist.");
325
+ if (entry.parentId === void 0 || parentId === void 0 || !require_context.contains(tree, parentId)) return require_driver.fail(at, "missing-tree-parent", "Moving requires a non-root node and an existing parent.");
326
+ for (let ancestor = parentId; ancestor !== void 0; ancestor = tree.nodes[ancestor].parentId) if (ancestor === id) return require_driver.fail(at, "tree-cycle", "Cannot move a node under its descendant.");
327
+ const old = tree.nodes[entry.parentId], next = tree.nodes[parentId];
328
+ const index = positionIndex(position?.index, next.children.length - (old === next ? 1 : 0), at);
329
+ const previous = old.children.indexOf(id);
330
+ if (old === next && previous === index) return;
331
+ session.recorder.tree(container, id);
332
+ session.recorder.tree(container, entry.parentId);
333
+ session.recorder.tree(container, parentId);
334
+ old.children.splice(previous, 1);
335
+ next.children.splice(index, 0, id);
336
+ entry.parentId = parentId;
337
+ session.invalidate();
338
+ };
339
+ //#endregion
340
+ //#region core/src/mutation/operations/order.ts
341
+ const assertOrdered = (container) => {
342
+ const { at, node } = container;
343
+ if (node.kind !== "table" && node.kind !== "list") return require_driver.fail(at, "invalid-collection", "Expected an ordered container.");
344
+ return node;
345
+ };
346
+ const plannedOrder = (at, current, selection, position) => {
347
+ const plan = moveSelection(current, selection, position);
348
+ switch (plan.kind) {
349
+ case "ok": return plan.order;
350
+ case "duplicate": return require_driver.fail(at, "invalid-key", "Moved keys must be unique.");
351
+ case "missing": return require_driver.fail(at, "missing-entity", "Ordered key does not exist.");
352
+ case "invalid-anchor": return require_driver.fail(at, "invalid-anchor", "Unknown order anchor.");
353
+ }
354
+ };
355
+ function move(session, container, ids, position) {
356
+ const node = assertOrdered(container);
357
+ const selection = typeof ids === "string" ? [ids] : ids;
358
+ if (!selection.length) return;
359
+ if (node.kind === "table") {
360
+ const order = container.value.ids;
361
+ const next = plannedOrder(container.at, order, selection, position);
362
+ if (require_advanced.equal(order, next)) return;
363
+ session.recorder.order(container);
364
+ require_advanced.installKeys(order, next);
365
+ } else {
366
+ const items = container.value;
367
+ const current = require_advanced.listSequence(items, node.keyOf);
368
+ const next = plannedOrder(container.at, current.order, selection, position);
369
+ if (require_advanced.equal(current.order, next)) return;
370
+ session.recorder.order(container);
371
+ require_advanced.installList(items, node.keyOf, current, next);
372
+ }
373
+ session.invalidate();
374
+ }
375
+ function reorder(session, container, next) {
376
+ const node = assertOrdered(container);
377
+ if (node.kind === "table") {
378
+ const current = container.value.ids;
379
+ if (!require_advanced.matches(next, current)) return require_driver.fail(container.at, "invalid-collection", "Order must contain every key exactly once.");
380
+ if (require_advanced.equal(current, next)) return;
381
+ session.recorder.order(container);
382
+ require_advanced.installKeys(current, next);
383
+ } else {
384
+ const items = container.value;
385
+ const current = require_advanced.listSequence(items, node.keyOf);
386
+ if (!require_advanced.matches(next, current.order)) return require_driver.fail(container.at, "invalid-collection", "Order must contain every key exactly once.");
387
+ if (require_advanced.equal(current.order, next)) return;
388
+ session.recorder.order(container);
389
+ require_advanced.installList(items, node.keyOf, current, next);
390
+ }
391
+ session.invalidate();
392
+ }
393
+ //#endregion
394
+ //#region core/src/impact/target.ts
395
+ const treeRootSegment = "rootId";
396
+ const treeNodesSegment = "nodes";
397
+ const ordinaryNamespace = "\0value";
398
+ const treeNamespace = "\0tree";
399
+ const tree$1 = (at, selection) => ({
400
+ kind: "value",
401
+ at,
402
+ tree: selection
403
+ });
404
+ const address = (target) => "at" in target ? target.at : target.address;
405
+ const id = (target) => target.kind === "collection" && "id" in target ? target.id : void 0;
406
+ const belongs = (target, schema) => !("schema" in target) || target.schema === schema;
407
+ const treeTarget = (target) => "tree" in target ? target.tree : void 0;
408
+ const sameTreeTarget = (left, right) => {
409
+ if (!left || !right) return left === right;
410
+ if (left.kind !== right.kind) return false;
411
+ return left.kind !== "node" || right.kind === "node" && left.id === right.id;
412
+ };
413
+ const same = (left, right) => {
414
+ if ("schema" in left && "schema" in right && left.schema !== right.schema) return false;
415
+ if (!sameTreeTarget(treeTarget(left), treeTarget(right))) return false;
416
+ if (left.kind !== right.kind) return false;
417
+ const leftAddress = address(left);
418
+ const rightAddress = address(right);
419
+ if (leftAddress.length !== rightAddress.length) return false;
420
+ for (let index = 0; index < leftAddress.length; index += 1) if (leftAddress[index] !== rightAddress[index]) return false;
421
+ return left.kind !== "collection" || id(left) === id(right);
422
+ };
423
+ const indexedAddress = (target) => {
424
+ const tree = treeTarget(target);
425
+ if (tree?.kind === "root") return [
426
+ treeNamespace,
427
+ ...address(target),
428
+ treeRootSegment
429
+ ];
430
+ if (tree?.kind === "nodes") return [
431
+ treeNamespace,
432
+ ...address(target),
433
+ treeNodesSegment
434
+ ];
435
+ if (tree?.kind === "node") return [
436
+ treeNamespace,
437
+ ...address(target),
438
+ treeNodesSegment,
439
+ tree.id
440
+ ];
441
+ const key = id(target);
442
+ return key === void 0 ? [ordinaryNamespace, ...address(target)] : [
443
+ ordinaryNamespace,
444
+ ...address(target),
445
+ key
446
+ ];
447
+ };
448
+ const visitChangedLocations = (changes, value, order) => {
449
+ for (const change of changes.changes) {
450
+ if (change.kind === "reset") continue;
451
+ if (change.kind === "members") {
452
+ for (const member of change.members) {
453
+ value([
454
+ ordinaryNamespace,
455
+ ...change.at,
456
+ member.key
457
+ ]);
458
+ value([
459
+ treeNamespace,
460
+ ...change.at,
461
+ member.key
462
+ ]);
463
+ }
464
+ if (change.order) order([ordinaryNamespace, ...change.at]);
465
+ continue;
466
+ }
467
+ value([ordinaryNamespace, ...change.at]);
468
+ if (change.before !== change.after) value([
469
+ treeNamespace,
470
+ ...change.at,
471
+ treeRootSegment
472
+ ]);
473
+ for (const node of change.nodes) value([
474
+ treeNamespace,
475
+ ...change.at,
476
+ treeNodesSegment,
477
+ node.id
478
+ ]);
479
+ }
480
+ };
481
+ const affected = (target, values, orders) => {
482
+ const at = indexedAddress(target);
483
+ return id(target) !== void 0 ? values.hasAncestor(at) : values.overlaps(at) || orders.hasDescendant(at);
484
+ };
485
+ /** Targets are classified once; commit matching never builds a reverse impact index. */
486
+ var SubscriptionIndex = class {
487
+ ordinary = new require_driver.AddressIndex();
488
+ membership = new require_driver.AddressIndex();
489
+ constructor(schema) {
490
+ this.schema = schema;
491
+ }
492
+ add(target, value) {
493
+ if (!belongs(target, this.schema)) return;
494
+ (id(target) === void 0 ? this.ordinary : this.membership).add(indexedAddress(target), value);
495
+ }
496
+ delete(target, value) {
497
+ if (!belongs(target, this.schema)) return;
498
+ (id(target) === void 0 ? this.ordinary : this.membership).delete(indexedAddress(target), value);
499
+ }
500
+ collect(changes, visit) {
501
+ const values = this.ordinary.query(visit);
502
+ const members = this.membership.query(visit, "descendants");
503
+ const orders = this.ordinary.query(visit, "ancestors");
504
+ if (changes.changes.some((change) => change.kind === "reset")) {
505
+ values([]);
506
+ members([]);
507
+ return;
508
+ }
509
+ visitChangedLocations(changes, (at) => {
510
+ values(at);
511
+ members(at);
512
+ }, orders);
513
+ }
514
+ clear() {
515
+ this.ordinary.clear();
516
+ this.membership.clear();
517
+ }
518
+ };
519
+ //#endregion
520
+ //#region core/src/access/scope.ts
521
+ const locations = /* @__PURE__ */ new WeakMap();
522
+ const locationOf = (value) => locations.get(value);
523
+ const addressOf = (target) => {
524
+ if (target.at) return target.at;
525
+ require_advanced.profile.access("addresses");
526
+ return target.at = addressOf(target.parent).concat(target.key);
527
+ };
528
+ const assertActive = (context) => {
529
+ if (context.active && !context.active()) throw new Error("Document access scope has expired.");
530
+ };
531
+ const collect = (context, at, kind = "value", id) => {
532
+ context.dependencies?.record(kind === "value" ? {
533
+ kind,
534
+ at
535
+ } : {
536
+ kind,
537
+ at,
538
+ ...id === void 0 ? {} : { id }
539
+ });
540
+ };
541
+ const collectTree = (context, at, selection) => context.dependencies?.record(tree$1(at, selection));
542
+ const snapshot = (value) => {
543
+ const location = value && typeof value === "object" ? locationOf(value) : void 0;
544
+ if (!location) return value;
545
+ const { context } = location;
546
+ const at = addressOf(location.target);
547
+ require_advanced.profile.access("snapshots");
548
+ collect(context, at);
549
+ const resolved = resolve(context, location.target);
550
+ if (!resolved.node) throw new Error("The selected schema address no longer exists.");
551
+ return require_advanced.copyValue(resolved.node, resolved.value);
552
+ };
553
+ const replace = (container, key, value) => {
554
+ if (!locationOf(container)) throw new TypeError("replace requires scoped document access.");
555
+ if (typeof key !== "string") throw new TypeError("Document keys must be strings.");
556
+ Reflect.set(container, key, value);
557
+ };
558
+ const resolve = (context, target) => {
559
+ assertActive(context);
560
+ const generation = context.session?.generation ?? 0;
561
+ if (target.generation !== generation) {
562
+ require_advanced.profile.access("resolutions");
563
+ if (!target.parent && !target.at.length) {
564
+ target.node = context.state.schema;
565
+ target.value = context.state.document;
566
+ } else {
567
+ const parent = target.parent && resolve(context, target.parent);
568
+ const location = parent ? resolveChild(parent.node, parent.value, target.key) : resolveLocated(context.state.schema, context.state.document, target.at);
569
+ target.node = location?.node ?? nodeAt(context.state.schema, addressOf(target), context.state.document);
570
+ target.value = location && Object.hasOwn(location.parent, location.key) ? location.parent[location.key] : void 0;
571
+ }
572
+ target.generation = generation;
573
+ }
574
+ return target;
575
+ };
576
+ const createAccess = (context, initial = []) => {
577
+ require_advanced.profile.access("scopes");
578
+ const mutable = () => {
579
+ assertActive(context);
580
+ if (!context.session) throw new TypeError("Cannot modify read-only document access.");
581
+ return context.session;
582
+ };
583
+ const writableContainer = (target, session) => {
584
+ if (target.container?.generation === session.generation && "layout" in target.container) return target.container;
585
+ return target.container = session.bind(addressOf(target), target.node, target.value);
586
+ };
587
+ const childAt = (target, key) => {
588
+ require_advanced.profile.access("addresses");
589
+ return addressOf(target).concat(key);
590
+ };
591
+ const access = (target) => {
592
+ if (target.proxy) return target.proxy;
593
+ const proxy = new Proxy(target, handler);
594
+ require_advanced.profile.access("proxies");
595
+ locations.set(proxy, {
596
+ context,
597
+ target
598
+ });
599
+ target.proxy = proxy;
600
+ return proxy;
601
+ };
602
+ const createTarget = (at, parent, key) => ({
603
+ at,
604
+ parent,
605
+ key,
606
+ node: void 0,
607
+ value: void 0,
608
+ generation: -1,
609
+ children: void 0,
610
+ proxy: void 0
611
+ });
612
+ const childAccess = (target, member, node, value) => {
613
+ const key = typeof member === "string" ? member : member.key;
614
+ if (node.kind === "field" || value === void 0) {
615
+ if (context.dependencies) collect(context, childAt(target, key));
616
+ return value;
617
+ }
618
+ let children = target.children;
619
+ let child;
620
+ if (typeof member !== "string") {
621
+ if (children?.kind !== "fixed" || children.schema !== target.node) children = {
622
+ kind: "fixed",
623
+ schema: target.node,
624
+ slots: new Array(require_advanced.compiledShape(target.node, target.value).members.size)
625
+ };
626
+ child = children.slots[member.slot] ??= createTarget(void 0, target, key);
627
+ } else {
628
+ if (children?.kind !== "dynamic") children = {
629
+ kind: "dynamic",
630
+ keys: /* @__PURE__ */ new Map()
631
+ };
632
+ const retained = children.keys.get(key);
633
+ child = retained ?? createTarget(void 0, target, key);
634
+ if (!retained) children.keys.set(key, child);
635
+ }
636
+ target.children = children;
637
+ if (child.generation === target.generation) return child.proxy;
638
+ child.node = node;
639
+ child.value = value;
640
+ child.generation = target.generation;
641
+ return access(child);
642
+ };
643
+ const collectionValue = (target, node) => {
644
+ const current = resolve(context, target);
645
+ if (current.node !== node || current.value === void 0) throw new TypeError("The collection method belongs to a replaced schema branch.");
646
+ return current.value;
647
+ };
648
+ const writableCollection = (target, node) => {
649
+ const session = mutable();
650
+ collectionValue(target, node);
651
+ return writableContainer(target, session);
652
+ };
653
+ const writableTree = (target, node) => {
654
+ const session = mutable();
655
+ collectionValue(target, node);
656
+ if (target.container?.generation === session.generation && !("layout" in target.container)) return target.container;
657
+ return target.container = session.bindTree(addressOf(target), target.node, target.value);
658
+ };
659
+ const replaceCollection = (target, node, value) => {
660
+ const session = mutable();
661
+ collectionValue(target, node);
662
+ session.replace(addressOf(target), value);
663
+ };
664
+ const mapMethod = (target, property, node) => {
665
+ if (property === "ids") return () => {
666
+ const current = collectionValue(target, node);
667
+ if (context.dependencies) collect(context, addressOf(target), "collection");
668
+ return Object.keys(current);
669
+ };
670
+ if (property === "has") return (id) => {
671
+ const current = collectionValue(target, node);
672
+ if (context.dependencies) collect(context, addressOf(target), "collection", id);
673
+ return Object.hasOwn(current, id);
674
+ };
675
+ if (property === "get") return (id) => {
676
+ const current = collectionValue(target, node);
677
+ if (context.dependencies) collect(context, addressOf(target), "collection", id);
678
+ if (!Object.hasOwn(current, id)) return void 0;
679
+ if (node.value.kind === "field") {
680
+ if (context.dependencies) collect(context, childAt(target, id));
681
+ return current[id];
682
+ }
683
+ return childAccess(target, id, node.value, current[id]);
684
+ };
685
+ if (property === "put") return (id, value) => {
686
+ const writable = writableCollection(target, node);
687
+ put(context.session, writable, id, value);
688
+ };
689
+ if (property === "remove") return (id) => {
690
+ const writable = writableCollection(target, node);
691
+ remove$3(context.session, writable, id);
692
+ };
693
+ if (property === "replace") return (value) => replaceCollection(target, node, value);
694
+ };
695
+ const orderedMethod = (target, property, node) => {
696
+ const at = addressOf(target);
697
+ if (property === "ids") return () => {
698
+ const current = collectionValue(target, node);
699
+ collect(context, at, "collection");
700
+ return node.kind === "table" ? [...current.ids] : current.map(node.keyOf);
701
+ };
702
+ if (property === "has") return (id) => {
703
+ const current = collectionValue(target, node);
704
+ collect(context, at, "collection", id);
705
+ return node.kind === "table" ? Object.hasOwn(current.byId, id) : require_advanced.indexedKeys(current, node.keyOf).index(id) >= 0;
706
+ };
707
+ if (property === "get") return (id) => {
708
+ const current = collectionValue(target, node);
709
+ collect(context, at, "collection", id);
710
+ const location = resolveChild(node, current, id);
711
+ if (!location || !Object.hasOwn(location.parent, location.key)) return void 0;
712
+ return childAccess(target, id, location.node, location.parent[location.key]);
713
+ };
714
+ if (property === "move") return (ids, position) => {
715
+ const writable = writableCollection(target, node);
716
+ move(context.session, writable, ids, position);
717
+ };
718
+ if (property === "reorder") return (ids) => {
719
+ const writable = writableCollection(target, node);
720
+ reorder(context.session, writable, ids);
721
+ };
722
+ };
723
+ const tableMethod = (target, property, node) => {
724
+ if (property === "create") return (input, valueOrPosition, position) => {
725
+ const writable = writableCollection(target, node);
726
+ create(context.session, writable, Array.isArray(input) ? input : [{
727
+ id: input,
728
+ value: valueOrPosition
729
+ }], Array.isArray(input) ? valueOrPosition : position);
730
+ };
731
+ if (property === "remove") return (ids) => {
732
+ const writable = writableCollection(target, node);
733
+ remove$2(context.session, writable, typeof ids === "string" ? [ids] : ids);
734
+ };
735
+ if (property === "replace") return (...args) => {
736
+ const [valueOrId, value] = args;
737
+ if (args.length === 1) return replaceCollection(target, node, valueOrId);
738
+ const writable = writableCollection(target, node);
739
+ replace$3(context.session, writable, valueOrId, value);
740
+ };
741
+ return orderedMethod(target, property, node);
742
+ };
743
+ const listMethod = (target, property, node) => {
744
+ if (property === "insert") return (value, position) => {
745
+ const writable = writableCollection(target, node);
746
+ insert$1(context.session, writable, value, position);
747
+ };
748
+ if (property === "remove") return (id) => {
749
+ const writable = writableCollection(target, node);
750
+ remove$1(context.session, writable, id);
751
+ };
752
+ if (property === "replace") return (...args) => {
753
+ const [valueOrId, value] = args;
754
+ if (args.length === 1) return replaceCollection(target, node, valueOrId);
755
+ const writable = writableCollection(target, node);
756
+ replace$2(context.session, writable, valueOrId, value);
757
+ };
758
+ return orderedMethod(target, property, node);
759
+ };
760
+ const treeMethod = (target, property, node) => {
761
+ const at = addressOf(target);
762
+ if (property === "insert") return (id, value, position) => {
763
+ const writable = writableTree(target, node);
764
+ insert(context.session, writable, id, value, position);
765
+ };
766
+ if (property === "remove") return (id) => {
767
+ const writable = writableTree(target, node);
768
+ remove(context.session, writable, id);
769
+ };
770
+ if (property === "move") return (id, position) => {
771
+ const writable = writableTree(target, node);
772
+ move$1(context.session, writable, id, position);
773
+ };
774
+ if (property === "replace") return (...args) => {
775
+ const [valueOrId, value] = args;
776
+ if (args.length === 1) return replaceCollection(target, node, valueOrId);
777
+ const writable = writableTree(target, node);
778
+ replace$1(context.session, writable, valueOrId, value);
779
+ };
780
+ const current = () => collectionValue(target, node);
781
+ if (property === "rootId") return () => {
782
+ collectTree(context, at, { kind: "root" });
783
+ return current().rootId;
784
+ };
785
+ if (property === "get") return (id) => {
786
+ collectTree(context, at, {
787
+ kind: "node",
788
+ id
789
+ });
790
+ const value = current();
791
+ return require_context.contains(value, id) ? value.nodes[id].value : void 0;
792
+ };
793
+ if (property === "has") return (id) => {
794
+ collectTree(context, at, {
795
+ kind: "node",
796
+ id
797
+ });
798
+ return require_context.contains(current(), id);
799
+ };
800
+ if (property === "parent") return (id) => {
801
+ collectTree(context, at, {
802
+ kind: "node",
803
+ id
804
+ });
805
+ return require_context.parent(current(), id);
806
+ };
807
+ if (property === "children") return (id) => {
808
+ collectTree(context, at, {
809
+ kind: "node",
810
+ id
811
+ });
812
+ const children$1 = require_context.children(current(), id);
813
+ return children$1 ? [...children$1] : void 0;
814
+ };
815
+ };
816
+ const handler = {
817
+ get: (target, property) => {
818
+ const { node, value } = resolve(context, target);
819
+ if (!node || value === void 0) return void 0;
820
+ if (typeof property !== "string") return void 0;
821
+ if (node.kind === "map") return mapMethod(target, property, node);
822
+ if (node.kind === "table") return tableMethod(target, property, node);
823
+ if (node.kind === "list") return listMethod(target, property, node);
824
+ if (node.kind === "tree") return treeMethod(target, property, node);
825
+ if (node.kind === "variant" && property === node.tag) {
826
+ collect(context, childAt(target, property));
827
+ return value[property];
828
+ }
829
+ const member = require_advanced.compiledShape(node, value)?.members.get(property);
830
+ if (!member) return void 0;
831
+ if (member.field) {
832
+ if (context.dependencies) collect(context, childAt(target, property));
833
+ return value[property];
834
+ }
835
+ return childAccess(target, node.kind === "object" ? member : property, member.node, value[property]);
836
+ },
837
+ set: (target, property, value) => {
838
+ const session = mutable(), { node } = resolve(context, target);
839
+ if (typeof property !== "string" || !node || node.kind !== "object" && node.kind !== "variant") throw new TypeError("Invalid structural assignment.");
840
+ if (node.kind === "variant" && node.tag === property) throw new TypeError("Variant discriminants are read-only.");
841
+ session.assignMember(writableContainer(target, session), property, value);
842
+ return true;
843
+ },
844
+ deleteProperty: (target, property) => {
845
+ const session = mutable(), { node } = resolve(context, target);
846
+ if (typeof property !== "string" || node?.kind === "map" || node?.kind === "variant" && node.tag === property) throw new TypeError("Invalid structural deletion.");
847
+ session.removeMember(writableContainer(target, session), property);
848
+ return true;
849
+ },
850
+ has: (target, property) => {
851
+ const { node, value } = resolve(context, target);
852
+ if (typeof property !== "string") return false;
853
+ if (node?.kind === "map") return false;
854
+ collect(context, addressOf(target));
855
+ return value !== void 0 && Object.hasOwn(value, property);
856
+ },
857
+ ownKeys: (target) => {
858
+ const { node, value } = resolve(context, target);
859
+ if (node?.kind === "map") return [];
860
+ if (context.dependencies) collect(context, addressOf(target));
861
+ return value && typeof value === "object" ? Object.keys(value) : [];
862
+ },
863
+ getOwnPropertyDescriptor: (target, property) => {
864
+ const { node, value } = resolve(context, target);
865
+ if (node?.kind === "map") return void 0;
866
+ return value && Object.hasOwn(value, property) ? {
867
+ enumerable: true,
868
+ configurable: true
869
+ } : void 0;
870
+ },
871
+ defineProperty: () => {
872
+ assertActive(context);
873
+ throw new TypeError("Use schema assignments.");
874
+ },
875
+ setPrototypeOf: () => {
876
+ assertActive(context);
877
+ throw new TypeError("Document prototypes are immutable.");
878
+ },
879
+ preventExtensions: () => {
880
+ assertActive(context);
881
+ throw new TypeError("Document access cannot be frozen.");
882
+ }
883
+ };
884
+ const root = resolve(context, createTarget(initial, void 0, ""));
885
+ if (!root.node || root.node.kind === "field" || root.value === void 0) {
886
+ if (context.dependencies) collect(context, initial);
887
+ return root.value;
888
+ }
889
+ return access(root);
890
+ };
891
+ //#endregion
892
+ //#region core/src/address/relation.ts
893
+ const contains = (parent, child) => {
894
+ require_advanced.profile.address.prefixComparison();
895
+ if (parent.length > child.length) return false;
896
+ for (let index = 0; index < parent.length; index += 1) {
897
+ require_advanced.profile.address.segmentCompared();
898
+ if (parent[index] !== child[index]) return false;
899
+ }
900
+ return true;
901
+ };
902
+ //#endregion
7
903
  //#region core/src/impact.ts
8
- const queries = /* @__PURE__ */ new WeakMap();
9
- const affectsTarget = (impact, value) => queries.get(impact).affects(value);
10
- const collectionImpact = (impact, selector) => queries.get(impact).collection(selector);
11
904
  const createImpact = (schema, changes) => {
12
905
  const reset = changes.changes.some((change) => change.kind === "reset");
13
906
  let values;
14
907
  let orders;
15
908
  const index = () => {
16
909
  if (!values) {
17
- require_issue.profile.impact.index();
18
- values = new require_issue.AddressIndex();
19
- orders = new require_issue.AddressIndex();
20
- for (const change of changes.changes) if (change.kind === "members") {
21
- for (const member of change.members) values.add(change.at, true, member.key);
22
- if (change.order) orders.add(change.at, true);
23
- } else if (change.kind === "tree") values.add(change.at, true);
910
+ require_advanced.profile.impact.index();
911
+ values = new require_driver.AddressIndex();
912
+ orders = new require_driver.AddressIndex();
913
+ visitChangedLocations(changes, (at) => values.add(at, true), (at) => orders.add(at, true));
24
914
  }
25
915
  return values;
26
916
  };
27
917
  const cache = /* @__PURE__ */ new Map();
28
- const implementation = {
29
- affects(value) {
30
- require_issue.profile.impact.affects();
31
- if (!require_notification.belongs(value, schema)) return false;
32
- if (reset) return true;
33
- return require_notification.affected(value, index(), orders);
34
- },
35
- collection(selector) {
36
- if (selector.schema !== schema) throw new TypeError("Collection belongs to another root model.");
37
- const key = JSON.stringify(selector.address), cached = cache.get(key);
38
- if (cached) return cached;
39
- if (reset) {
40
- const result = { kind: "reset" };
41
- cache.set(key, result);
42
- return result;
43
- }
44
- const at = selector.address;
45
- const added = /* @__PURE__ */ new Set(), removed = /* @__PURE__ */ new Set(), updated = /* @__PURE__ */ new Set();
46
- let orderChanged = false;
47
- for (const change of changes.changes) {
48
- if (change.kind === "reset") continue;
49
- if (change.kind === "members") {
50
- if (change.at.length < at.length && require_issue.contains(change.at, at)) {
51
- if (change.members.some((member) => member.key === at[change.at.length])) {
52
- const result = { kind: "reset" };
53
- cache.set(key, result);
54
- return result;
55
- }
56
- } else if (require_issue.contains(at, change.at)) if (change.at.length === at.length) {
57
- if (change.order) {
58
- const positions = new Map(change.order.after.map((id, i) => [id, i]));
59
- let previous = -1;
60
- for (const id of change.order.before) {
61
- const position = positions.get(id);
62
- if (position === void 0) continue;
63
- if (position < previous) orderChanged = true;
64
- previous = position;
65
- }
918
+ const affects = (value) => {
919
+ require_advanced.profile.impact.affects();
920
+ if (!belongs(value, schema)) return false;
921
+ if (reset) return true;
922
+ return affected(value, index(), orders);
923
+ };
924
+ const collection = (selector) => {
925
+ if (selector.schema !== schema) throw new TypeError("Collection belongs to another root model.");
926
+ const key = JSON.stringify(indexedAddress(selector)), cached = cache.get(key);
927
+ if (cached) return cached;
928
+ if (reset) {
929
+ const result = { kind: "reset" };
930
+ cache.set(key, result);
931
+ return result;
932
+ }
933
+ const at = selector.address;
934
+ const tree = selector.tree;
935
+ const added = /* @__PURE__ */ new Set(), removed = /* @__PURE__ */ new Set(), updated = /* @__PURE__ */ new Set();
936
+ let orderChanged = false;
937
+ for (const change of changes.changes) {
938
+ if (change.kind === "reset") continue;
939
+ if (change.kind === "members") {
940
+ if (change.at.length < at.length && contains(change.at, at)) {
941
+ if (change.members.some((member) => member.key === at[change.at.length])) {
942
+ const result = { kind: "reset" };
943
+ cache.set(key, result);
944
+ return result;
945
+ }
946
+ } else if (contains(at, change.at)) if (change.at.length === at.length) {
947
+ if (change.order) {
948
+ const positions = new Map(change.order.after.map((id, i) => [id, i]));
949
+ let previous = -1;
950
+ for (const id of change.order.before) {
951
+ const position = positions.get(id);
952
+ if (position === void 0) continue;
953
+ if (position < previous) orderChanged = true;
954
+ previous = position;
66
955
  }
67
- for (const member of change.members) if (member.kind === "added") added.add(member.key);
68
- else if (member.kind === "removed") removed.add(member.key);
69
- else updated.add(member.key);
70
- } else updated.add(change.at[at.length]);
956
+ }
957
+ for (const member of change.members) if (member.kind === "added") added.add(member.key);
958
+ else if (member.kind === "removed") removed.add(member.key);
959
+ else updated.add(member.key);
960
+ } else updated.add(change.at[at.length]);
961
+ continue;
962
+ }
963
+ if (change.kind === "tree" && contains(change.at, at)) {
964
+ if (tree?.kind === "nodes" && change.at.length === at.length && contains(at, change.at)) {
965
+ for (const node of change.nodes) if (node.kind === "added") added.add(node.id);
966
+ else if (node.kind === "removed") removed.add(node.id);
967
+ else updated.add(node.id);
71
968
  continue;
72
969
  }
73
- if (change.kind === "tree" && require_issue.contains(change.at, at)) {
74
- const result = { kind: "reset" };
75
- cache.set(key, result);
76
- return result;
77
- }
78
- if (!require_issue.contains(at, change.at)) continue;
79
- if (change.at.length === at.length) continue;
80
- const id = change.at[at.length];
81
- updated.add(id);
970
+ const result = { kind: "reset" };
971
+ cache.set(key, result);
972
+ return result;
82
973
  }
83
- added.forEach((id) => updated.delete(id));
84
- removed.forEach((id) => updated.delete(id));
85
- const result = {
86
- kind: "incremental",
87
- added,
88
- removed,
89
- updated,
90
- orderChanged
91
- };
92
- cache.set(key, result);
93
- return result;
974
+ if (!contains(at, change.at)) continue;
975
+ if (change.at.length === at.length) continue;
976
+ const id = change.at[at.length];
977
+ updated.add(id);
94
978
  }
979
+ added.forEach((id) => updated.delete(id));
980
+ removed.forEach((id) => updated.delete(id));
981
+ const result = {
982
+ kind: "incremental",
983
+ added,
984
+ removed,
985
+ updated,
986
+ orderChanged
987
+ };
988
+ cache.set(key, result);
989
+ return result;
95
990
  };
96
- const impact = Object.freeze({
991
+ return Object.freeze({
97
992
  kind: reset ? "reset" : "incremental",
98
- affects: (pick) => implementation.affects(require_advanced.compilePath(schema, "value", pick)),
99
- collection: (pick) => implementation.collection(require_advanced.compilePath(schema, "collection", pick))
993
+ affects: (pick) => affects(require_advanced.compilePath(schema, "value", pick)),
994
+ collection: (pick) => collection(require_advanced.compilePath(schema, "collection", pick))
100
995
  });
101
- queries.set(impact, implementation);
102
- return impact;
996
+ };
997
+ //#endregion
998
+ //#region core/src/runtime/contract.ts
999
+ var DocumentReentrancyError = class extends Error {
1000
+ constructor() {
1001
+ super("Document writes cannot be re-entered during an update or notification.");
1002
+ this.name = "DocumentReentrancyError";
1003
+ }
1004
+ };
1005
+ var DocumentDisposedError = class extends Error {
1006
+ constructor() {
1007
+ super("Doxum runtime has been disposed.");
1008
+ this.name = "DocumentDisposedError";
1009
+ }
1010
+ };
1011
+ var TransactionRejected = class extends Error {
1012
+ issues;
1013
+ constructor(input) {
1014
+ super("Document update rejected.");
1015
+ this.name = "TransactionRejected";
1016
+ const issues = Array.isArray(input) ? input : [input];
1017
+ this.issues = Object.freeze(issues.map((issue) => Object.freeze({
1018
+ code: issue.code,
1019
+ message: issue.message,
1020
+ source: "application",
1021
+ ...issue.address === void 0 ? {} : { address: Object.freeze([...issue.address]) }
1022
+ })));
1023
+ }
103
1024
  };
104
1025
  //#endregion
105
1026
  //#region core/src/history.ts
@@ -190,7 +1111,7 @@ const createHistory = (input) => {
190
1111
  current: () => state,
191
1112
  revision: () => revision,
192
1113
  subscribe: (listener) => {
193
- if (disposed) throw new require_access.DocumentDisposedError();
1114
+ if (disposed) throw new DocumentDisposedError();
194
1115
  listeners.add(listener);
195
1116
  return () => {
196
1117
  listeners.delete(listener);
@@ -272,25 +1193,18 @@ const createHistory = (input) => {
272
1193
  const installMember = (parent, key, present, value) => {
273
1194
  if (Array.isArray(parent)) {
274
1195
  const index = Number(key);
275
- if (present) if (index < 0) require_issue.insert(parent, parent.length, value);
1196
+ if (present) if (index < 0) require_advanced.insert(parent, parent.length, value);
276
1197
  else parent[index] = value;
277
- else if (index >= 0) require_issue.remove(parent, index);
278
- } else if (!present) delete parent[key];
279
- else if (Object.hasOwn(parent, key)) parent[key] = value;
280
- else Object.defineProperty(parent, key, {
281
- value,
282
- writable: true,
283
- configurable: true,
284
- enumerable: true
285
- });
1198
+ else if (index >= 0) require_advanced.remove(parent, index);
1199
+ } else require_context.installOwn(parent, key, present, value);
286
1200
  };
287
1201
  const orderOf = (node, value) => {
288
- if (node.kind === "list") return require_issue.toArray(require_issue.indexedKeys(value, node.keyOf));
1202
+ if (node.kind === "list") return require_advanced.toArray(require_advanced.indexedKeys(value, node.keyOf));
289
1203
  if (node.kind === "table") return [...value.ids];
290
1204
  throw new Error("Order requires a table or list.");
291
1205
  };
292
1206
  const installOrder = (node, value, order) => {
293
- if (node.kind === "list") require_issue.install(value, node.keyOf, order);
1207
+ if (node.kind === "list") require_advanced.install(value, node.keyOf, order);
294
1208
  else if (node.kind === "table") value.ids = [...order];
295
1209
  else throw new Error("Order requires a table or list.");
296
1210
  };
@@ -309,15 +1223,11 @@ const forgetMember = (group, key) => {
309
1223
  }
310
1224
  };
311
1225
  const emptyGroup = (group) => !group.order && (group.layout === "fixed" ? !group.members.some(Boolean) : !group.members?.size);
312
- const copyNode = (node) => ({
313
- ...node,
314
- children: [...node.children]
315
- });
316
1226
  const transition = (node, key, beforePresent, before, afterPresent, after) => {
317
1227
  if (beforePresent === afterPresent && (!beforePresent || Object.is(before, after))) return;
318
- if (node.kind !== "field" && beforePresent === afterPresent && require_access.equalValue(node, before, after)) return;
319
- const value = afterPresent ? node.kind === "field" ? after : require_access.copyValue(node, after) : void 0;
320
- require_issue.profile.recorder("transitions");
1228
+ if (node.kind !== "field" && beforePresent === afterPresent && require_advanced.equalValue(node, before, after)) return;
1229
+ const value = afterPresent ? node.kind === "field" ? after : require_advanced.copyValue(node, after) : void 0;
1230
+ require_advanced.profile.recorder("transitions");
321
1231
  return !beforePresent ? {
322
1232
  key,
323
1233
  kind: "added",
@@ -358,7 +1268,7 @@ const diffMember = (changes, members, node, at, key, beforePresent, before, afte
358
1268
  const left = before, right = after;
359
1269
  const childAt = at.concat(key);
360
1270
  const children = [];
361
- for (const [child, definition] of require_issue.compiledShape(node, before).members) diffMember(changes, children, definition.node, childAt, child, Object.hasOwn(left, child), left[child], Object.hasOwn(right, child), right[child]);
1271
+ for (const [child, definition] of require_advanced.compiledShape(node, before).members) diffMember(changes, children, definition.node, childAt, child, Object.hasOwn(left, child), left[child], Object.hasOwn(right, child), right[child]);
362
1272
  publishMembers(changes, childAt, children);
363
1273
  return;
364
1274
  }
@@ -371,8 +1281,8 @@ const diffMember = (changes, members, node, at, key, beforePresent, before, afte
371
1281
  for (const id of Object.keys(left)) diffMember(changes, children, node.value, childAt, id, true, left[id], Object.hasOwn(right, id), right[id]);
372
1282
  for (const id of Object.keys(right)) if (!Object.hasOwn(left, id)) diffMember(changes, children, node.value, childAt, id, false, void 0, true, right[id]);
373
1283
  let order;
374
- if (node.kind === "table" && !require_issue.equal(a.ids, b.ids)) {
375
- require_issue.profile.recorder("publishedOrderItems", b.ids.length);
1284
+ if (node.kind === "table" && !require_advanced.equal(a.ids, b.ids)) {
1285
+ require_advanced.profile.recorder("publishedOrderItems", b.ids.length);
376
1286
  order = {
377
1287
  before: a.ids,
378
1288
  after: [...b.ids]
@@ -389,26 +1299,26 @@ const restore = (fact, schema, root, skip = 0) => {
389
1299
  const at = skip ? fact.at.slice(skip) : fact.at;
390
1300
  if (fact.kind === "member") {
391
1301
  const member = fact.member;
392
- const value = member.present ? require_access.copyValue(member.node, member.value) : void 0;
1302
+ const value = member.present ? require_advanced.copyValue(member.node, member.value) : void 0;
393
1303
  if (!at.length) return value;
394
- const location = require_issue.resolveLocated(schema, root, at);
1304
+ const location = resolveLocated(schema, root, at);
395
1305
  if (!location) throw new Error("Cannot restore an unresolved member.");
396
1306
  installMember(location.parent, location.key, member.present, value);
397
1307
  return root;
398
1308
  }
399
- const location = require_issue.resolveValue(schema, root, at);
1309
+ const location = resolveValue(schema, root, at);
400
1310
  if (!location) throw new Error("Cannot restore an unresolved subtree.");
401
1311
  if (fact.kind === "order") installOrder(location.node, location.value, fact.group.order);
402
1312
  else {
403
1313
  const tree = location.value;
404
1314
  if (fact.before === null) delete tree.rootId;
405
1315
  else tree.rootId = fact.before;
406
- for (const [id, node] of fact.nodes) installMember(tree.nodes, id, node !== void 0, node && copyNode(node));
1316
+ for (const [id, node] of fact.nodes) installMember(tree.nodes, id, node !== void 0, node && require_advanced.copyTreeNode(node));
407
1317
  }
408
1318
  return root;
409
1319
  };
410
1320
  const reconstructBefore = (node, current, records, offset) => {
411
- let before = require_access.copyValue(node, current);
1321
+ let before = require_advanced.copyValue(node, current);
412
1322
  for (let i = records.length - 1; i >= 0; i--) if (records[i].kind !== "order") before = restore(records[i], node, before, offset);
413
1323
  for (const record of records) if (record.kind === "order") before = restore(record, node, before, offset);
414
1324
  return before;
@@ -417,7 +1327,7 @@ const sealMembers = (fact, changes) => {
417
1327
  const members = [];
418
1328
  for (const member of fact.members?.values() ?? []) {
419
1329
  if (!member) continue;
420
- const key = require_issue.memberKey(fact, member.key);
1330
+ const key = memberKey(fact, member.key);
421
1331
  const present = Object.hasOwn(fact.parent, key);
422
1332
  const value = fact.parent[key];
423
1333
  if (member.node.kind === "field") {
@@ -427,10 +1337,10 @@ const sealMembers = (fact, changes) => {
427
1337
  }
428
1338
  let order;
429
1339
  if (fact.order) {
430
- const current = fact.node.kind === "list" ? require_issue.indexedKeys(fact.value, fact.node.keyOf) : fact.value.ids;
431
- if (!require_issue.equal(fact.order, current)) {
1340
+ const current = fact.node.kind === "list" ? require_advanced.indexedKeys(fact.value, fact.node.keyOf) : fact.value.ids;
1341
+ if (!require_advanced.equal(fact.order, current)) {
432
1342
  const after = orderOf(fact.node, fact.value);
433
- require_issue.profile.recorder("publishedOrderItems", after.length);
1343
+ require_advanced.profile.recorder("publishedOrderItems", after.length);
434
1344
  order = {
435
1345
  before: fact.order,
436
1346
  after
@@ -439,20 +1349,19 @@ const sealMembers = (fact, changes) => {
439
1349
  }
440
1350
  publishMembers(changes, fact.at, members, order);
441
1351
  };
442
- const sealTree = (fact, state, changes) => {
443
- const location = require_issue.resolveValue(state.schema, state.document, fact.at);
1352
+ const sealTree = (fact, canonical, changes) => {
1353
+ const location = resolveValue(canonical.schema, canonical.document, fact.at);
444
1354
  const tree = location?.value;
445
- const node = location?.node;
446
- if (node?.kind !== "tree") throw new Error("Tree schema disappeared before sealing.");
1355
+ if ((location?.node)?.kind !== "tree") throw new Error("Tree schema disappeared before sealing.");
447
1356
  const nodes = [];
448
1357
  for (const [id, before] of fact.nodes) {
449
1358
  const after = Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : void 0;
450
1359
  if (!before && !after) continue;
451
- if (before && after && before.parentId === after.parentId && require_issue.equal(before.children, after.children) && Object.hasOwn(before, "value") === Object.hasOwn(after, "value") && require_access.equalValue(node.value, before.value, after.value)) continue;
1360
+ if (before && after && require_advanced.equalTreeNode(before, after)) continue;
452
1361
  nodes.push(!before ? {
453
1362
  id,
454
1363
  kind: "added",
455
- after: copyNode(after)
1364
+ after: require_advanced.copyTreeNode(after)
456
1365
  } : !after ? {
457
1366
  id,
458
1367
  kind: "removed",
@@ -461,7 +1370,7 @@ const sealTree = (fact, state, changes) => {
461
1370
  id,
462
1371
  kind: "updated",
463
1372
  before,
464
- after: copyNode(after)
1373
+ after: require_advanced.copyTreeNode(after)
465
1374
  });
466
1375
  }
467
1376
  const after = tree.rootId ?? null;
@@ -484,12 +1393,12 @@ var ChangeRecorder = class {
484
1393
  this.state = state;
485
1394
  }
486
1395
  indexGroups() {
487
- const index = this.index ??= new require_issue.AddressIndex();
1396
+ const index = this.index ??= new require_driver.AddressIndex();
488
1397
  if (!this.indexedGroups) {
489
1398
  this.indexedGroups = true;
490
1399
  for (const fact of this.facts) if (fact.kind === "members") {
491
1400
  index.add(fact.at, fact);
492
- require_issue.profile.recorder("indexedGroups");
1401
+ require_advanced.profile.recorder("indexedGroups");
493
1402
  }
494
1403
  }
495
1404
  return index;
@@ -514,7 +1423,7 @@ var ChangeRecorder = class {
514
1423
  }
515
1424
  return;
516
1425
  }
517
- if (fact.order && require_issue.contains(at, fact.at)) children.push({
1426
+ if (fact.order && contains(at, fact.at)) children.push({
518
1427
  kind: "order",
519
1428
  at: fact.at,
520
1429
  group: fact
@@ -522,14 +1431,14 @@ var ChangeRecorder = class {
522
1431
  for (const member of fact.members?.values() ?? []) {
523
1432
  if (!member) continue;
524
1433
  const memberAt = fact.at.concat(member.key);
525
- if (require_issue.contains(at, memberAt)) children.push({
1434
+ if (contains(at, memberAt)) children.push({
526
1435
  kind: "member",
527
1436
  at: memberAt,
528
1437
  group: fact,
529
1438
  member
530
1439
  });
531
1440
  }
532
- } else if (require_issue.contains(at, fact.at)) children.push(fact);
1441
+ } else if (contains(at, fact.at)) children.push(fact);
533
1442
  })(at);
534
1443
  if (present) value = reconstructBefore(node, value, children, at.length);
535
1444
  this.releaseCovered(children);
@@ -538,7 +1447,7 @@ var ChangeRecorder = class {
538
1447
  releaseCovered(children) {
539
1448
  let changedGroups;
540
1449
  for (const child of children) {
541
- require_issue.profile.recorder("absorbed");
1450
+ require_advanced.profile.recorder("absorbed");
542
1451
  if (child.kind === "member") {
543
1452
  const { group, member } = child;
544
1453
  forgetMember(group, member.key);
@@ -554,11 +1463,11 @@ var ChangeRecorder = class {
554
1463
  this.facts.add(fact);
555
1464
  if (fact.kind === "members") {
556
1465
  this.groups.set(fact.parent, fact);
557
- require_issue.profile.recorder("groups");
1466
+ require_advanced.profile.recorder("groups");
558
1467
  if (!this.indexedGroups) return;
559
- require_issue.profile.recorder("indexedGroups");
1468
+ require_advanced.profile.recorder("indexedGroups");
560
1469
  }
561
- (this.index ??= new require_issue.AddressIndex()).add(fact.at, fact);
1470
+ (this.index ??= new require_driver.AddressIndex()).add(fact.at, fact);
562
1471
  }
563
1472
  unregister(fact) {
564
1473
  this.facts.delete(fact);
@@ -586,14 +1495,14 @@ var ChangeRecorder = class {
586
1495
  if (group.layout === "dynamic") (group.members ??= /* @__PURE__ */ new Map()).set(key, member);
587
1496
  else if (definition.kind === "fixed") group.members[definition.slot] = member;
588
1497
  else throw new Error("A fixed member group requires a fixed schema member.");
589
- require_issue.profile.recorder("facts");
1498
+ require_advanced.profile.recorder("facts");
590
1499
  }
591
1500
  reset() {
592
1501
  if (this.resetBefore) return;
593
1502
  const value = this.absorb([], this.state.schema, this.state.document, true);
594
1503
  this.resetBefore = { value };
595
1504
  this.groups = /* @__PURE__ */ new WeakMap();
596
- require_issue.profile.recorder("facts");
1505
+ require_advanced.profile.recorder("facts");
597
1506
  }
598
1507
  createGroup(container) {
599
1508
  const group = container.layout.kind === "fixed" ? {
@@ -618,18 +1527,18 @@ var ChangeRecorder = class {
618
1527
  return group;
619
1528
  }
620
1529
  order(container) {
621
- require_issue.profile.recorder("orderCaptures");
1530
+ require_advanced.profile.recorder("orderCaptures");
622
1531
  let group = this.groups.get(container.parent);
623
1532
  if (group?.order || this.covered(container.at)) return;
624
1533
  group ??= this.createGroup(container);
625
1534
  group.order = orderOf(container.node, container.value);
626
- require_issue.profile.recorder("orderSnapshots");
627
- require_issue.profile.recorder("orderItems", group.order.length);
1535
+ require_advanced.profile.recorder("orderSnapshots");
1536
+ require_advanced.profile.recorder("orderItems", group.order.length);
628
1537
  }
629
1538
  tree(container, id) {
630
1539
  const { at, value: tree } = container;
631
1540
  if (this.covered(at)) return;
632
- const index = this.index ??= new require_issue.AddressIndex();
1541
+ const index = this.index ??= new require_driver.AddressIndex();
633
1542
  let fact;
634
1543
  for (const item of index.exact(at) ?? []) if (item.kind === "tree") fact = item;
635
1544
  if (!fact) {
@@ -642,8 +1551,8 @@ var ChangeRecorder = class {
642
1551
  this.register(fact);
643
1552
  }
644
1553
  if (id !== void 0 && !fact.nodes.has(id)) {
645
- require_issue.profile.recorder("treeNodes");
646
- fact.nodes.set(id, Object.hasOwn(tree.nodes, id) ? copyNode(tree.nodes[id]) : void 0);
1554
+ require_advanced.profile.recorder("treeNodes");
1555
+ fact.nodes.set(id, Object.hasOwn(tree.nodes, id) ? require_advanced.copyTreeNode(tree.nodes[id]) : void 0);
647
1556
  }
648
1557
  }
649
1558
  rollback() {
@@ -656,7 +1565,7 @@ var ChangeRecorder = class {
656
1565
  const fact = facts[i];
657
1566
  if (fact.kind === "members") for (const member of fact.members?.values() ?? []) {
658
1567
  if (!member) continue;
659
- installMember(fact.parent, require_issue.memberKey(fact, member.key), member.present, member.present ? require_access.copyValue(member.node, member.value) : void 0);
1568
+ installMember(fact.parent, memberKey(fact, member.key), member.present, member.present ? require_advanced.copyValue(member.node, member.value) : void 0);
660
1569
  }
661
1570
  else if (fact.kind === "tree") restore(fact, this.state.schema, this.state.document);
662
1571
  }
@@ -664,19 +1573,19 @@ var ChangeRecorder = class {
664
1573
  }
665
1574
  seal() {
666
1575
  if (this.resetBefore) {
667
- if (require_access.equalValue(this.state.schema, this.resetBefore.value, this.state.document)) return require_driver.sealChanges([]);
668
- require_issue.profile.recorder("sealed");
1576
+ if (require_advanced.equalValue(this.state.schema, this.resetBefore.value, this.state.document)) return require_driver.sealChanges([]);
1577
+ require_advanced.profile.recorder("sealed");
669
1578
  return require_driver.sealChanges([{
670
1579
  kind: "reset",
671
1580
  before: this.resetBefore.value,
672
- after: require_access.copyValue(this.state.schema, this.state.document)
1581
+ after: require_advanced.copyValue(this.state.schema, this.state.document)
673
1582
  }]);
674
1583
  }
675
1584
  const changes = [];
676
1585
  for (const fact of this.facts) if (fact.kind === "members") sealMembers(fact, changes);
677
1586
  else sealTree(fact, this.state, changes);
678
1587
  const result = require_driver.sealChanges(changes);
679
- require_issue.profile.recorder("sealed", result.changes.length);
1588
+ require_advanced.profile.recorder("sealed", result.changes.length);
680
1589
  return result;
681
1590
  }
682
1591
  };
@@ -692,25 +1601,25 @@ var MutationSession = class {
692
1601
  constructor(state) {
693
1602
  this.state = state;
694
1603
  this.recorder = new ChangeRecorder(state);
695
- this.resolver = require_issue.createAddressResolver(state.schema, state.document);
1604
+ this.resolver = createAddressResolver(state.schema, state.document);
696
1605
  this.resolvedRoot = state.document;
697
1606
  }
698
1607
  validate(node, value, at) {
699
- const issue = require_access.checkValue(node, value, at);
700
- if (issue) require_issue.invalidValue(issue);
1608
+ const failure = require_advanced.checkValue(node, value, at);
1609
+ if (failure) require_driver.invalidValue(failure);
701
1610
  }
702
1611
  resolveContainer(at) {
703
- return this.resolver.container(this.identity, this.generation, at) ?? require_issue.fail(at, "invalid-address", "Container does not exist.");
1612
+ return this.resolver.container(this.identity, this.generation, at) ?? require_driver.fail(at, "invalid-address", "Container does not exist.");
704
1613
  }
705
1614
  /** Bind access-resolved canonical facts without walking the address again. */
706
1615
  bind(at, node, value) {
707
- return require_issue.resolveContainer(this.identity, this.generation, at, node, value) ?? require_issue.fail(at, "invalid-address", "Container does not exist.");
1616
+ return resolveContainer(this.identity, this.generation, at, node, value) ?? require_driver.fail(at, "invalid-address", "Container does not exist.");
708
1617
  }
709
1618
  bindTree(at, node, value) {
710
- return require_issue.resolveTreeContainer(this.identity, this.generation, at, node, value) ?? require_issue.fail(at, "invalid-tree", "Expected a tree.");
1619
+ return resolveTreeContainer(this.identity, this.generation, at, node, value) ?? require_driver.fail(at, "invalid-tree", "Expected a tree.");
711
1620
  }
712
1621
  resolveTree(at) {
713
- const location = require_issue.resolveValue(this.state.schema, this.state.document, at);
1622
+ const location = resolveValue(this.state.schema, this.state.document, at);
714
1623
  return this.bindTree(at, location?.node, location?.value);
715
1624
  }
716
1625
  replace(at, value) {
@@ -718,7 +1627,7 @@ var MutationSession = class {
718
1627
  this.validate(this.state.schema, value, at);
719
1628
  if (Object.is(this.state.document, value)) return;
720
1629
  this.recorder.reset();
721
- this.state.document = require_access.copyValue(this.state.schema, value);
1630
+ this.state.document = require_advanced.copyValue(this.state.schema, value);
722
1631
  this.invalidate();
723
1632
  return;
724
1633
  }
@@ -727,24 +1636,24 @@ var MutationSession = class {
727
1636
  assignMember(container, key, value) {
728
1637
  container = this.refresh(container);
729
1638
  const member = this.definition(container, key);
730
- if (container.layout.kind === "fixed" && member.node.kind !== "field" && member.node.kind !== "variant" && !member.node.optional) return require_issue.fail(container.at.concat(key), "invalid-value", "Replace fields, variants, or collection entries; edit object members individually.");
731
- this.writeLocatedMember(container, key, member, require_issue.memberKey(container, key), value, "set");
1639
+ if (container.layout.kind === "fixed" && member.node.kind !== "field" && member.node.kind !== "variant" && !member.node.optional) return require_driver.fail(container.at.concat(key), "invalid-value", "Replace fields, variants, or collection entries; edit object members individually.");
1640
+ this.writeLocatedMember(container, key, member, memberKey(container, key), value, "set");
732
1641
  }
733
1642
  removeMember(container, key) {
734
1643
  this.writeMember(this.refresh(container), key, void 0, "remove");
735
1644
  }
736
1645
  refresh(container) {
737
- if (container.owner !== this.identity) return require_issue.fail(container.at, "invalid-address", "Container belongs to another mutation session.");
1646
+ if (container.owner !== this.identity) return require_driver.fail(container.at, "invalid-address", "Container belongs to another mutation session.");
738
1647
  return container.generation === this.generation ? container : this.resolveContainer(container.at);
739
1648
  }
740
1649
  definition(container, key) {
741
1650
  const layout = container.layout;
742
1651
  const member = layout.kind === "dynamic" ? layout.entry : layout.members.get(key);
743
- if (!member) return require_issue.fail(container.at.concat(key), "invalid-address", "Address does not exist.");
1652
+ if (!member) return require_driver.fail(container.at.concat(key), "invalid-address", "Address does not exist.");
744
1653
  return member;
745
1654
  }
746
1655
  /** Consume a current command-local container; bulk writes retain its storage. */
747
- writeMember(container, key, value, operation, physicalKey = require_issue.memberKey(container, key)) {
1656
+ writeMember(container, key, value, operation, physicalKey = memberKey(container, key)) {
748
1657
  const member = this.definition(container, key);
749
1658
  return this.writeLocatedMember(container, key, member, physicalKey, value, operation);
750
1659
  }
@@ -753,27 +1662,27 @@ var MutationSession = class {
753
1662
  const parentNode = container.node;
754
1663
  const node = member.node;
755
1664
  const present = operation === "set";
756
- if (parentNode.kind === "variant" && parentNode.tag === key) return require_issue.fail(container.at.concat(key), "invalid-value", "Variant discriminants are read-only.");
1665
+ if (parentNode.kind === "variant" && parentNode.tag === key) return require_driver.fail(container.at.concat(key), "invalid-value", "Variant discriminants are read-only.");
757
1666
  const entry = container.layout.kind === "dynamic";
758
- if (!present && !node.optional && !entry) return require_issue.fail(container.at.concat(key), "required-field", "A required value cannot be removed.");
1667
+ if (!present && !node.optional && !entry) return require_driver.fail(container.at.concat(key), "required-field", "A required value cannot be removed.");
759
1668
  const existed = Object.hasOwn(container.parent, physicalKey);
760
1669
  const previous = container.parent[physicalKey];
761
1670
  if (existed === present && (!present || Object.is(previous, value))) return false;
762
1671
  if (parentNode.kind === "map" || parentNode.kind === "table") {
763
- const issue = require_access.checkKey(parentNode.key, key, []);
764
- if (issue) return require_issue.fail(container.at.concat(key, ...issue.address), "invalid-key", issue.message);
1672
+ const failure = require_advanced.checkKey(parentNode.key, key, []);
1673
+ if (failure) return require_driver.fail(container.at.concat(key, ...failure.address), "invalid-key", failure.message);
765
1674
  }
766
1675
  if (present) {
767
1676
  if (node.kind === "field") {
768
- const issue = require_access.checkValue(node, value, []);
769
- if (issue) return require_issue.invalidValue(issue, container.at.concat(key, ...issue.address));
1677
+ const failure = require_advanced.checkValue(node, value, []);
1678
+ if (failure) return require_driver.invalidValue(failure, container.at.concat(key, ...failure.address));
770
1679
  } else this.validate(node, value, container.at.concat(key));
771
- if (parentNode.kind === "list" && parentNode.keyOf(value) !== key) return require_issue.fail(container.at.concat(key), "invalid-list-key", "Replacing an item must retain its addressed key.");
1680
+ if (parentNode.kind === "list" && parentNode.keyOf(value) !== key) return require_driver.fail(container.at.concat(key), "invalid-list-key", "Replacing an item must retain its addressed key.");
772
1681
  }
773
1682
  const membershipChanged = entry && existed !== present;
774
1683
  if (membershipChanged && (parentNode.kind === "list" || parentNode.kind === "table")) this.recorder.order(container);
775
1684
  this.recorder.member(container, key, member, physicalKey);
776
- const next = present ? node.kind === "field" ? value : require_access.copyValue(node, value) : void 0;
1685
+ const next = present ? node.kind === "field" ? value : require_advanced.copyValue(node, value) : void 0;
777
1686
  installMember(container.parent, physicalKey, present, next);
778
1687
  if (node.kind !== "field" || membershipChanged) this.invalidate();
779
1688
  return membershipChanged;
@@ -782,7 +1691,7 @@ var MutationSession = class {
782
1691
  this.generation++;
783
1692
  if (this.resolvedRoot !== this.state.document) {
784
1693
  this.resolvedRoot = this.state.document;
785
- this.resolver = require_issue.createAddressResolver(this.state.schema, this.state.document);
1694
+ this.resolver = createAddressResolver(this.state.schema, this.state.document);
786
1695
  } else this.resolver.invalidate();
787
1696
  }
788
1697
  finish() {
@@ -802,7 +1711,7 @@ function apply(session, changes, direction) {
802
1711
  const container = session.resolveContainer(change.at);
803
1712
  const { node, value: current } = container;
804
1713
  if (change.order) {
805
- if (node.kind !== "table" && node.kind !== "list") return require_issue.fail(change.at, "invalid-changes", "Order requires an ordered container.");
1714
+ if (node.kind !== "table" && node.kind !== "list") return require_driver.fail(change.at, "invalid-changes", "Order requires an ordered container.");
806
1715
  session.recorder.order(container);
807
1716
  }
808
1717
  let membershipChanged = false;
@@ -813,12 +1722,12 @@ function apply(session, changes, direction) {
813
1722
  }
814
1723
  if (change.order) {
815
1724
  const keys = node.kind === "table" ? Object.keys(container.parent) : orderOf(node, current);
816
- if (!require_issue.matches(change.order[side], keys)) return require_issue.fail(change.at, "invalid-changes", "Order must contain exactly the resulting keys.");
1725
+ if (!require_advanced.matches(change.order[side], keys)) return require_driver.fail(change.at, "invalid-changes", "Order must contain exactly the resulting keys.");
817
1726
  installOrder(node, current, change.order[side]);
818
1727
  session.invalidate();
819
1728
  } else if (membershipChanged && node.kind === "table") {
820
1729
  const ids = current.ids;
821
- if (!require_issue.matches(ids, Object.keys(container.parent))) return require_issue.fail(change.at, "invalid-changes", "Table membership changes require a matching order.");
1730
+ if (!require_advanced.matches(ids, Object.keys(container.parent))) return require_driver.fail(change.at, "invalid-changes", "Table membership changes require a matching order.");
822
1731
  }
823
1732
  } else {
824
1733
  const container = session.resolveTree(change.at);
@@ -830,31 +1739,195 @@ function apply(session, changes, direction) {
830
1739
  else current.rootId = root;
831
1740
  for (const item of change.nodes) {
832
1741
  const next = side === "after" ? item.kind !== "removed" ? item.after : void 0 : item.kind !== "added" ? item.before : void 0;
833
- installMember(current.nodes, item.id, next !== void 0, next && {
834
- ...next,
835
- children: [...next.children]
836
- });
1742
+ if (next !== void 0) {
1743
+ const diagnostic = require_advanced.checkTreePayload(node.value, next, change.at.concat(item.id));
1744
+ if (diagnostic) require_driver.invalidValue(diagnostic);
1745
+ }
1746
+ installMember(current.nodes, item.id, next !== void 0, next && require_advanced.copyTreeNode(next));
837
1747
  }
838
- session.validate(node, current, change.at);
1748
+ if (!require_context.validate(current)) return require_driver.fail(change.at, "invalid-tree", "Invalid tree structure.");
839
1749
  session.invalidate();
840
1750
  }
841
1751
  }
842
1752
  //#endregion
1753
+ //#region core/src/runtime/notification.ts
1754
+ const attachProjection = (notification, processor) => {
1755
+ const entry = {
1756
+ processor,
1757
+ active: true
1758
+ };
1759
+ notification.processors.push(entry);
1760
+ return () => {
1761
+ if (!entry.active) return;
1762
+ entry.active = false;
1763
+ if (!notification.notifying) {
1764
+ const index = notification.processors.indexOf(entry);
1765
+ if (index >= 0) notification.processors.splice(index, 1);
1766
+ }
1767
+ };
1768
+ };
1769
+ const addRoot = (notification, listener) => {
1770
+ const entry = {
1771
+ listener,
1772
+ active: true
1773
+ };
1774
+ notification.root.add(entry);
1775
+ return () => {
1776
+ entry.active = false;
1777
+ if (!notification.notifying) notification.root.delete(entry);
1778
+ else notification.cleanups.add(() => {
1779
+ notification.root.delete(entry);
1780
+ });
1781
+ };
1782
+ };
1783
+ const addFiltered = (notification, targets, listener) => {
1784
+ const entry = {
1785
+ targets: Object.freeze(targets.slice()),
1786
+ listener,
1787
+ active: true
1788
+ };
1789
+ notification.filtered.add(entry);
1790
+ entry.targets.forEach((value) => notification.index.add(value, entry));
1791
+ const remove = () => {
1792
+ notification.filtered.delete(entry);
1793
+ entry.targets.forEach((value) => notification.index.delete(value, entry));
1794
+ };
1795
+ return () => {
1796
+ if (!entry.active) return;
1797
+ entry.active = false;
1798
+ if (notification.notifying) notification.cleanups.add(remove);
1799
+ else remove();
1800
+ };
1801
+ };
1802
+ const publishState = (notification, commit, afterSettle) => {
1803
+ notification.notifying = true;
1804
+ const errors = [];
1805
+ const call = (listener) => {
1806
+ try {
1807
+ listener(commit);
1808
+ } catch (error) {
1809
+ errors.push(Object.freeze({
1810
+ phase: "listener",
1811
+ error
1812
+ }));
1813
+ }
1814
+ };
1815
+ try {
1816
+ const processorCount = notification.processors.length;
1817
+ for (let index = 0; index < processorCount; index += 1) {
1818
+ const entry = notification.processors[index];
1819
+ if (!entry?.active) continue;
1820
+ try {
1821
+ entry.processor.capture(commit);
1822
+ } catch (error) {
1823
+ errors.push(Object.freeze({
1824
+ phase: "processor",
1825
+ error
1826
+ }));
1827
+ }
1828
+ }
1829
+ for (let index = 0; index < processorCount; index += 1) {
1830
+ const entry = notification.processors[index];
1831
+ if (!entry?.active) continue;
1832
+ try {
1833
+ entry.processor.settle();
1834
+ } catch (error) {
1835
+ errors.push(Object.freeze({
1836
+ phase: "processor",
1837
+ error
1838
+ }));
1839
+ }
1840
+ }
1841
+ for (let index = 0; index < processorCount; index += 1) {
1842
+ const entry = notification.processors[index];
1843
+ if (!entry?.active) continue;
1844
+ try {
1845
+ errors.push(...entry.processor.flush());
1846
+ } catch (error) {
1847
+ errors.push(Object.freeze({
1848
+ phase: "flush",
1849
+ error
1850
+ }));
1851
+ }
1852
+ }
1853
+ if (afterSettle) errors.push(...afterSettle());
1854
+ const candidates = notification.candidates;
1855
+ candidates.clear();
1856
+ if (notification.filtered.size) notification.index.collect(commit.changes, (entry) => candidates.add(entry));
1857
+ candidates.forEach((entry) => {
1858
+ if (entry.active) call(entry.listener);
1859
+ });
1860
+ const rootSnapshot = notification.rootSnapshot;
1861
+ rootSnapshot.length = 0;
1862
+ notification.root.forEach((entry) => rootSnapshot.push(entry));
1863
+ for (const entry of rootSnapshot) if (entry.active) call(entry.listener);
1864
+ } finally {
1865
+ notification.notifying = false;
1866
+ let writeIndex = 0;
1867
+ for (const entry of notification.processors) if (entry.active) notification.processors[writeIndex++] = entry;
1868
+ notification.processors.length = writeIndex;
1869
+ notification.cleanups.forEach((cleanup) => cleanup());
1870
+ notification.cleanups.clear();
1871
+ }
1872
+ return Object.freeze(errors);
1873
+ };
1874
+ const disposeState = (notification) => {
1875
+ const attachments = notification.processors.slice();
1876
+ notification.root.clear();
1877
+ notification.filtered.clear();
1878
+ notification.index.clear();
1879
+ notification.cleanups.clear();
1880
+ notification.processors.length = 0;
1881
+ notification.candidates.clear();
1882
+ notification.rootSnapshot.length = 0;
1883
+ const errors = [];
1884
+ attachments.forEach((entry) => {
1885
+ if (!entry.active) return;
1886
+ try {
1887
+ entry.processor.dispose();
1888
+ } catch (error) {
1889
+ errors.push(error);
1890
+ }
1891
+ });
1892
+ if (errors.length) throw new AggregateError(errors, "Projection disposal notification failed.");
1893
+ };
1894
+ const createNotificationCenter = (schema) => {
1895
+ const state = {
1896
+ root: /* @__PURE__ */ new Set(),
1897
+ filtered: /* @__PURE__ */ new Set(),
1898
+ index: new SubscriptionIndex(schema),
1899
+ cleanups: /* @__PURE__ */ new Set(),
1900
+ processors: [],
1901
+ candidates: /* @__PURE__ */ new Set(),
1902
+ rootSnapshot: [],
1903
+ notifying: false
1904
+ };
1905
+ return Object.freeze({
1906
+ subscribe: (listener) => addRoot(state, listener),
1907
+ subscribeTargets: (targets, listener) => addFiltered(state, targets, listener),
1908
+ attachProjection: (processor) => attachProjection(state, processor),
1909
+ publish: (commit, afterSettle) => publishState(state, commit, afterSettle),
1910
+ dispose: () => disposeState(state)
1911
+ });
1912
+ };
1913
+ //#endregion
843
1914
  //#region core/src/runtime.ts
844
1915
  const createDocument = (input) => {
845
1916
  if (input.schema.kind !== "object") throw new TypeError("Document schema must be a root object node.");
846
- const invalid = require_access.checkValue(input.schema, input.initial);
847
- if (invalid) throw new require_access.ParseError(invalid);
1917
+ const invalid = require_advanced.checkValue(input.schema, input.initial);
1918
+ if (invalid) throw new require_advanced.ParseError(invalid);
848
1919
  const state = {
849
1920
  schema: input.schema,
850
- document: require_access.copyValue(input.schema, input.initial),
851
- disposed: false
1921
+ document: require_advanced.copyValue(input.schema, input.initial),
1922
+ disposed: false,
1923
+ projectionLocks: 0
852
1924
  };
853
1925
  let revision = 0, busy = false;
854
- let runtime, notification;
1926
+ const notifications = createNotificationCenter(input.schema);
1927
+ let runtime, context;
855
1928
  const idle = () => {
856
- if (state.disposed) throw new require_access.DocumentDisposedError();
857
- if (busy || require_access.accessOf(runtime).projectionLocks) throw new require_access.DocumentReentrancyError();
1929
+ if (state.disposed) throw new DocumentDisposedError();
1930
+ if (busy || state.projectionLocks) throw new DocumentReentrancyError();
858
1931
  };
859
1932
  const writable = (intent) => {
860
1933
  idle();
@@ -893,7 +1966,7 @@ const createDocument = (input) => {
893
1966
  return {
894
1967
  status: "committed",
895
1968
  commit,
896
- observerErrors: require_notification.notify(notification, commit, history.flush)
1969
+ observerErrors: notifications.publish(commit, history.flush)
897
1970
  };
898
1971
  };
899
1972
  function mutate(intent, recordHistory, execute) {
@@ -907,12 +1980,12 @@ const createDocument = (input) => {
907
1980
  changes = session.finish();
908
1981
  } catch (error) {
909
1982
  session.rollback();
910
- if (error instanceof require_issue.MutationRejected) return {
1983
+ if (error instanceof require_driver.MutationRejected) return {
911
1984
  status: "rejected",
912
1985
  issues: [error.issue],
913
1986
  revision
914
1987
  };
915
- if (intent.kind === "update" && error instanceof require_access.TransactionRejected) return {
1988
+ if (intent.kind === "update" && error instanceof TransactionRejected) return {
916
1989
  status: "rejected",
917
1990
  issues: error.issues,
918
1991
  revision
@@ -930,93 +2003,567 @@ const createDocument = (input) => {
930
2003
  }
931
2004
  runtime = {
932
2005
  schema: input.schema,
933
- address: {
934
- resolve: (at) => require_issue.resolveAddress(input.schema, at, state.document),
935
- read: (at) => require_issue.read(state.document, at, input.schema),
936
- contains: require_issue.contains,
937
- overlaps: require_issue.overlaps,
938
- debugKey: require_issue.debugKey
2006
+ revision: () => revision,
2007
+ update: (run, options) => {
2008
+ const source = options?.source ?? "local";
2009
+ let value;
2010
+ const result = mutate({
2011
+ kind: "update",
2012
+ source
2013
+ }, options?.history ?? true, (session) => {
2014
+ value = run(createAccess({
2015
+ state,
2016
+ session
2017
+ }));
2018
+ if (value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function") throw new TypeError("Document updates must be synchronous.");
2019
+ });
2020
+ return result.status === "rejected" ? result : {
2021
+ ...result,
2022
+ value
2023
+ };
2024
+ },
2025
+ apply: (inputChanges, options) => mutate({
2026
+ kind: "apply",
2027
+ source: options?.source ?? "local"
2028
+ }, options?.history ?? true, (session) => {
2029
+ if (!options || options.expectedRevision !== revision) require_driver.fail([], "baseline-mismatch", "apply requires the current expectedRevision.");
2030
+ apply(session, require_driver.decodeChanges(inputChanges), "forward");
2031
+ }),
2032
+ replace: (value, options) => {
2033
+ const source = options?.source ?? "system";
2034
+ return mutate({
2035
+ kind: "replace",
2036
+ source
2037
+ }, source !== "remote", (session) => session.replace([], value));
2038
+ },
2039
+ snapshot: () => {
2040
+ if (state.disposed) throw new DocumentDisposedError();
2041
+ return require_advanced.copyValue(input.schema, state.document);
2042
+ },
2043
+ subscribe: ((pick, listener) => {
2044
+ if (state.disposed) throw new DocumentDisposedError();
2045
+ if (!listener) return notifications.subscribe(pick);
2046
+ const picks = Array.isArray(pick) ? pick : [pick];
2047
+ if (!picks.length) throw new TypeError("Expected at least one subscription path.");
2048
+ return notifications.subscribeTargets(picks.map((p) => require_advanced.compilePath(input.schema, "value", p)), listener);
2049
+ }),
2050
+ history: history.api,
2051
+ dispose: () => {
2052
+ if (state.disposed) return;
2053
+ idle();
2054
+ state.disposed = true;
2055
+ try {
2056
+ notifications.dispose();
2057
+ } finally {
2058
+ history.dispose();
2059
+ context.driver = void 0;
2060
+ }
2061
+ }
2062
+ };
2063
+ context = {
2064
+ state,
2065
+ owner: runtime,
2066
+ notifications,
2067
+ driver: void 0,
2068
+ bypassDepth: 0
2069
+ };
2070
+ require_context.bindContext(runtime, context);
2071
+ require_context.bindContext(history.api, context);
2072
+ return runtime;
2073
+ };
2074
+ //#endregion
2075
+ //#region core/src/runtime/readable.ts
2076
+ const asReadable = (runtime) => {
2077
+ const readable = {
2078
+ revision: runtime.revision,
2079
+ subscribe: runtime.subscribe
2080
+ };
2081
+ require_context.bindContext(readable, require_context.contextOf(runtime));
2082
+ return Object.freeze(readable);
2083
+ };
2084
+ //#endregion
2085
+ //#region core/src/access/dependency.ts
2086
+ const createDependencyTracker = () => {
2087
+ const targets = [];
2088
+ return {
2089
+ record: (value) => {
2090
+ if (!targets.some((entry) => same(entry, value))) targets.push(value);
2091
+ },
2092
+ snapshot: () => Object.freeze(targets.slice())
2093
+ };
2094
+ };
2095
+ //#endregion
2096
+ //#region core/src/runtime/access.ts
2097
+ const readWith = (runtime, run, dependencies) => {
2098
+ const state = require_context.contextOf(runtime).state;
2099
+ if (state.disposed) throw new DocumentDisposedError();
2100
+ state.projectionLocks += 1;
2101
+ try {
2102
+ return run(createAccess({
2103
+ state,
2104
+ dependencies
2105
+ }));
2106
+ } finally {
2107
+ state.projectionLocks--;
2108
+ }
2109
+ };
2110
+ //#endregion
2111
+ //#region core/src/runtime/select.ts
2112
+ const read = (document, selector) => readWith(document, selector);
2113
+ const sameDependencySet = (left, right) => {
2114
+ if (left.length !== right.length) return false;
2115
+ for (let index = 0; index < left.length; index += 1) if (!same(left[index], right[index])) return false;
2116
+ return true;
2117
+ };
2118
+ const select = (document, selector, equality = Object.is) => {
2119
+ const context = require_context.contextOf(document);
2120
+ let initialized = false;
2121
+ let value;
2122
+ let selectedRevision = 0;
2123
+ let documentRevision = -1;
2124
+ let dependencies = Object.freeze([]);
2125
+ let unsubscribeDocument;
2126
+ const listeners = /* @__PURE__ */ new Set();
2127
+ const evaluate = () => {
2128
+ const tracker = createDependencyTracker();
2129
+ const next = readWith(document, selector, tracker);
2130
+ const nextDependencies = tracker.snapshot();
2131
+ const changed = !initialized || !equality(value, next);
2132
+ const rebound = !sameDependencySet(dependencies, nextDependencies);
2133
+ if (changed) {
2134
+ if (initialized) selectedRevision += 1;
2135
+ value = next;
2136
+ }
2137
+ dependencies = nextDependencies;
2138
+ initialized = true;
2139
+ documentRevision = document.revision();
2140
+ return {
2141
+ changed,
2142
+ rebound
2143
+ };
2144
+ };
2145
+ const installSubscription = () => {
2146
+ unsubscribeDocument?.();
2147
+ unsubscribeDocument = dependencies.length > 0 ? context.notifications.subscribeTargets(dependencies, () => {
2148
+ const result = evaluate();
2149
+ if (result.rebound) installSubscription();
2150
+ if (result.changed) for (const listener of [...listeners]) listener();
2151
+ }) : void 0;
2152
+ };
2153
+ const ensureCurrent = () => {
2154
+ if (context.state.disposed) throw new DocumentDisposedError();
2155
+ if (!initialized || !unsubscribeDocument && documentRevision !== document.revision()) evaluate();
2156
+ return value;
2157
+ };
2158
+ return Object.freeze({
2159
+ current: ensureCurrent,
2160
+ revision: () => {
2161
+ ensureCurrent();
2162
+ return selectedRevision;
2163
+ },
2164
+ subscribe: (listener) => {
2165
+ ensureCurrent();
2166
+ listeners.add(listener);
2167
+ if (listeners.size === 1) installSubscription();
2168
+ return () => {
2169
+ listeners.delete(listener);
2170
+ if (!listeners.size) {
2171
+ unsubscribeDocument?.();
2172
+ unsubscribeDocument = void 0;
2173
+ }
2174
+ };
2175
+ }
2176
+ });
2177
+ };
2178
+ //#endregion
2179
+ //#region core/src/projection/collection/change.ts
2180
+ const commonOrderChanged = (before, after, added, removed) => {
2181
+ const beforeCommon = before.filter((key) => !removed.has(key));
2182
+ const afterCommon = after.filter((key) => !added.has(key));
2183
+ return beforeCommon.some((key, index) => afterCommon[index] !== key);
2184
+ };
2185
+ /** Builds the canonical incremental CollectionChange from already-known net transitions. */
2186
+ const createCollectionChange = (input) => {
2187
+ const addedKeys = new Set(input.added.map((entry) => entry.key));
2188
+ const removedKeys = new Set(input.removed.map((entry) => entry.key));
2189
+ const orderChanged = input.beforeOrder !== input.afterOrder && commonOrderChanged(input.beforeOrder, input.afterOrder, addedKeys, removedKeys);
2190
+ if (!input.added.length && !input.updated.length && !input.removed.length && !orderChanged) return void 0;
2191
+ return Object.freeze({
2192
+ kind: "incremental",
2193
+ added: Object.freeze([...input.added]),
2194
+ updated: Object.freeze([...input.updated]),
2195
+ removed: Object.freeze([...input.removed]),
2196
+ ...orderChanged ? { order: Object.freeze({
2197
+ before: Object.freeze([...input.beforeOrder]),
2198
+ after: Object.freeze([...input.afterOrder])
2199
+ }) } : {}
2200
+ });
2201
+ };
2202
+ /** Exact fallback diff between two published map-like snapshots. */
2203
+ const diffCollection = (previous, current, isEqual = Object.is) => {
2204
+ const added = [];
2205
+ const updated = [];
2206
+ const removed = [];
2207
+ for (const [key, before] of previous) if (!current.has(key)) removed.push({
2208
+ key,
2209
+ before
2210
+ });
2211
+ else {
2212
+ const after = current.get(key);
2213
+ if (!isEqual(before, after)) updated.push({
2214
+ key,
2215
+ before,
2216
+ after
2217
+ });
2218
+ }
2219
+ for (const [key, after] of current) if (!previous.has(key)) added.push({
2220
+ key,
2221
+ after
2222
+ });
2223
+ return createCollectionChange({
2224
+ added,
2225
+ updated,
2226
+ removed,
2227
+ beforeOrder: [...previous.keys()],
2228
+ afterOrder: [...current.keys()]
2229
+ });
2230
+ };
2231
+ const collectionChangedKeys = (change) => change.kind === "reset" ? /* @__PURE__ */ new Set() : new Set([
2232
+ ...change.added.map((entry) => entry.key),
2233
+ ...change.updated.map((entry) => entry.key),
2234
+ ...change.removed.map((entry) => entry.key)
2235
+ ]);
2236
+ const collectionHasStructuralChange = (change) => change.kind === "reset" || Boolean(change.added.length || change.removed.length || change.order);
2237
+ const collectionHasAnyChange = (change) => change.kind === "reset" || Boolean(change.added.length || change.updated.length || change.removed.length || change.order);
2238
+ //#endregion
2239
+ //#region core/src/projection/collection/index.ts
2240
+ const height = (node) => node?.height ?? 0;
2241
+ const node = (key, value, left, right) => {
2242
+ require_advanced.profile.collectionIndex.node();
2243
+ return Object.freeze({
2244
+ key,
2245
+ value,
2246
+ left,
2247
+ right,
2248
+ height: Math.max(height(left), height(right)) + 1
2249
+ });
2250
+ };
2251
+ const rotateLeft = (current) => {
2252
+ const right = current.right;
2253
+ return node(right.key, right.value, node(current.key, current.value, current.left, right.left), right.right);
2254
+ };
2255
+ const rotateRight = (current) => {
2256
+ const left = current.left;
2257
+ return node(left.key, left.value, left.left, node(current.key, current.value, left.right, current.right));
2258
+ };
2259
+ const rebalance = (current) => {
2260
+ const balance = height(current.left) - height(current.right);
2261
+ if (balance > 1) {
2262
+ if (height(current.left.left) < height(current.left.right)) return rotateRight(node(current.key, current.value, rotateLeft(current.left), current.right));
2263
+ return rotateRight(current);
2264
+ }
2265
+ if (balance < -1) {
2266
+ if (height(current.right.right) < height(current.right.left)) return rotateLeft(node(current.key, current.value, current.left, rotateRight(current.right)));
2267
+ return rotateLeft(current);
2268
+ }
2269
+ return current;
2270
+ };
2271
+ const setNode = (current, key, value) => {
2272
+ if (!current) return node(key, value);
2273
+ if (key === current.key) return node(key, value, current.left, current.right);
2274
+ if (key < current.key) return rebalance(node(current.key, current.value, setNode(current.left, key, value), current.right));
2275
+ return rebalance(node(current.key, current.value, current.left, setNode(current.right, key, value)));
2276
+ };
2277
+ const minNode = (current) => current.left ? minNode(current.left) : current;
2278
+ const removeNode = (current, key) => {
2279
+ if (!current) return void 0;
2280
+ if (key < current.key) return rebalance(node(current.key, current.value, removeNode(current.left, key), current.right));
2281
+ if (key > current.key) return rebalance(node(current.key, current.value, current.left, removeNode(current.right, key)));
2282
+ if (!current.left) return current.right;
2283
+ if (!current.right) return current.left;
2284
+ const next = minNode(current.right);
2285
+ return rebalance(node(next.key, next.value, current.left, removeNode(current.right, next.key)));
2286
+ };
2287
+ const fromSorted = (entries, start = 0, end = entries.length) => {
2288
+ if (start >= end) return void 0;
2289
+ const middle = start + end >> 1;
2290
+ const [key, value] = entries[middle];
2291
+ return node(key, value, fromSorted(entries, start, middle), fromSorted(entries, middle + 1, end));
2292
+ };
2293
+ /** Immutable keyed lookup used to keep published collection snapshots durable across revisions. */
2294
+ var PersistentKeyedIndex = class PersistentKeyedIndex {
2295
+ constructor(root) {
2296
+ this.root = root;
2297
+ }
2298
+ static empty() {
2299
+ return new PersistentKeyedIndex();
2300
+ }
2301
+ static from(entries) {
2302
+ const sorted = [...entries].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
2303
+ require_advanced.profile.collectionIndex.build(sorted.length);
2304
+ return new PersistentKeyedIndex(fromSorted(sorted));
2305
+ }
2306
+ get(key) {
2307
+ let current = this.root;
2308
+ while (current) {
2309
+ if (key === current.key) return current.value;
2310
+ current = key < current.key ? current.left : current.right;
2311
+ }
2312
+ }
2313
+ has(key) {
2314
+ let current = this.root;
2315
+ while (current) {
2316
+ if (key === current.key) return true;
2317
+ current = key < current.key ? current.left : current.right;
2318
+ }
2319
+ return false;
2320
+ }
2321
+ set(key, value) {
2322
+ return new PersistentKeyedIndex(setNode(this.root, key, value));
2323
+ }
2324
+ remove(key) {
2325
+ return new PersistentKeyedIndex(removeNode(this.root, key));
2326
+ }
2327
+ };
2328
+ //#endregion
2329
+ //#region core/src/projection/output/collection.ts
2330
+ const createPublishedCollection = () => {
2331
+ const values = /* @__PURE__ */ new Map();
2332
+ let ids = Object.freeze([]);
2333
+ let index = PersistentKeyedIndex.empty();
2334
+ return {
2335
+ get: (key) => values.get(key),
2336
+ has: (key) => values.has(key),
2337
+ keys: () => values.keys(),
2338
+ size: () => values.size,
2339
+ ids: () => ids,
2340
+ read: (check) => {
2341
+ const snapshotIndex = index;
2342
+ const snapshotIds = ids;
2343
+ return Object.freeze({
2344
+ get: (key) => {
2345
+ check();
2346
+ return snapshotIndex.get(key);
2347
+ },
2348
+ has: (key) => {
2349
+ check();
2350
+ return snapshotIndex.has(key);
2351
+ },
2352
+ ids: () => {
2353
+ check();
2354
+ return snapshotIds;
2355
+ }
2356
+ });
2357
+ },
2358
+ publish: (staged, nextIds, reset) => {
2359
+ if (reset) {
2360
+ const entries = [];
2361
+ for (const [key, entry] of staged) if (entry.present) entries.push([key, entry.value]);
2362
+ index = PersistentKeyedIndex.from(entries);
2363
+ values.clear();
2364
+ } else for (const [key, entry] of staged) index = entry.present ? index.set(key, entry.value) : index.remove(key);
2365
+ for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
2366
+ else values.delete(key);
2367
+ ids = nextIds;
2368
+ },
2369
+ release: () => {
2370
+ values.clear();
2371
+ ids = Object.freeze([]);
2372
+ index = PersistentKeyedIndex.empty();
2373
+ }
2374
+ };
2375
+ };
2376
+ /** Owns staged keyed intent, exact transitions and published collection state. */
2377
+ const createCollectionOutput = () => {
2378
+ const published = createPublishedCollection();
2379
+ let staged = /* @__PURE__ */ new Map();
2380
+ let nextIds = published.ids();
2381
+ let change;
2382
+ let initialized = false;
2383
+ let reset = false;
2384
+ let revision = 0;
2385
+ let explicitOrder;
2386
+ let cleared = false;
2387
+ const listeners = /* @__PURE__ */ new Set();
2388
+ const hasNext = (key) => staged.has(key) ? staged.get(key).present : !cleared && published.has(key);
2389
+ const getNext = (key) => {
2390
+ const entry = staged.get(key);
2391
+ return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : published.get(key);
2392
+ };
2393
+ const deriveIds = () => {
2394
+ const result = cleared ? [] : published.ids().filter(hasNext);
2395
+ for (const [key, entry] of staged) if (entry.present && (cleared || !published.has(key))) result.push(key);
2396
+ return result;
2397
+ };
2398
+ const begin = (active, initialize) => {
2399
+ staged = /* @__PURE__ */ new Map();
2400
+ nextIds = published.ids();
2401
+ change = void 0;
2402
+ explicitOrder = void 0;
2403
+ cleared = initialize;
2404
+ reset = initialize;
2405
+ const read = (next) => ({
2406
+ get: (key) => {
2407
+ require_advanced.assertScope(active);
2408
+ return next ? getNext(key) : published.get(key);
2409
+ },
2410
+ has: (key) => {
2411
+ require_advanced.assertScope(active);
2412
+ return next ? hasNext(key) : published.has(key);
2413
+ },
2414
+ ids: () => {
2415
+ require_advanced.assertScope(active);
2416
+ return next ? Object.freeze(explicitOrder ? [...explicitOrder] : [...deriveIds()]) : published.ids();
2417
+ }
2418
+ });
2419
+ return {
2420
+ previous: read(false),
2421
+ next: read(true),
2422
+ output: {
2423
+ set: (key, value) => {
2424
+ require_advanced.assertScope(active);
2425
+ if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
2426
+ staged.set(key, {
2427
+ present: true,
2428
+ value
2429
+ });
2430
+ },
2431
+ remove: (key) => {
2432
+ require_advanced.assertScope(active);
2433
+ staged.set(key, { present: false });
2434
+ },
2435
+ order: (order) => {
2436
+ require_advanced.assertScope(active);
2437
+ explicitOrder = Object.freeze([...order]);
2438
+ }
2439
+ }
2440
+ };
2441
+ };
2442
+ const seal = (nextReset, isEqual) => {
2443
+ if (cleared) {
2444
+ for (const key of published.keys()) if (!staged.has(key)) staged.set(key, { present: false });
2445
+ }
2446
+ require_advanced.profile.projection("touchedKeys", staged.size);
2447
+ const added = /* @__PURE__ */ new Set();
2448
+ const removed = /* @__PURE__ */ new Set();
2449
+ const updated = /* @__PURE__ */ new Set();
2450
+ for (const [key, entry] of staged) {
2451
+ const existed = published.has(key);
2452
+ if (entry.present) {
2453
+ if (!existed) added.add(key);
2454
+ else if (!isEqual(published.get(key), entry.value)) updated.add(key);
2455
+ else if (!nextReset) staged.delete(key);
2456
+ } else if (existed) removed.add(key);
2457
+ else staged.delete(key);
2458
+ }
2459
+ const ids = published.ids();
2460
+ if (explicitOrder || added.size || removed.size) {
2461
+ const order = explicitOrder ?? [...ids.filter((key) => !removed.has(key)), ...added];
2462
+ if (new Set(order).size !== order.length || order.length !== published.size() + added.size - removed.size || order.some((key) => !hasNext(key))) throw new TypeError("Projection order must contain every key exactly once.");
2463
+ nextIds = ids.length === order.length && ids.every((key, index) => key === order[index]) ? ids : Object.freeze([...order]);
2464
+ } else nextIds = ids;
2465
+ const changedKeys = new Set([
2466
+ ...added,
2467
+ ...removed,
2468
+ ...updated
2469
+ ]);
2470
+ require_advanced.profile.projection("changedKeys", changedKeys.size);
2471
+ const addedEntries = [];
2472
+ const updatedEntries = [];
2473
+ const removedEntries = [];
2474
+ for (const key of changedKeys) {
2475
+ const beforePresent = published.has(key);
2476
+ const afterPresent = hasNext(key);
2477
+ if (beforePresent === afterPresent) {
2478
+ if (!beforePresent || isEqual(published.get(key), getNext(key))) continue;
2479
+ }
2480
+ if (!beforePresent) addedEntries.push({
2481
+ key,
2482
+ after: getNext(key)
2483
+ });
2484
+ else if (!afterPresent) removedEntries.push({
2485
+ key,
2486
+ before: published.get(key)
2487
+ });
2488
+ else updatedEntries.push({
2489
+ key,
2490
+ before: published.get(key),
2491
+ after: getNext(key)
2492
+ });
2493
+ }
2494
+ change = nextReset ? Object.freeze({ kind: "reset" }) : createCollectionChange({
2495
+ added: addedEntries,
2496
+ updated: updatedEntries,
2497
+ removed: removedEntries,
2498
+ beforeOrder: ids,
2499
+ afterOrder: nextIds
2500
+ });
2501
+ reset = nextReset;
2502
+ return change !== void 0;
2503
+ };
2504
+ const context = (active, cause) => Object.freeze({
2505
+ kind: "collection",
2506
+ read: {
2507
+ get: (key) => {
2508
+ require_advanced.assertScope(active);
2509
+ return getNext(key);
2510
+ },
2511
+ has: (key) => {
2512
+ require_advanced.assertScope(active);
2513
+ return hasNext(key);
2514
+ },
2515
+ ids: () => {
2516
+ require_advanced.assertScope(active);
2517
+ return nextIds;
2518
+ }
939
2519
  },
2520
+ change,
2521
+ revision,
2522
+ reset,
2523
+ cause
2524
+ });
2525
+ return {
2526
+ begin,
2527
+ seal,
2528
+ context,
2529
+ current: (check) => published.read(check),
940
2530
  revision: () => revision,
941
- update: (run, options) => {
942
- const source = options?.source ?? "local";
943
- let value;
944
- const result = mutate({
945
- kind: "update",
946
- source
947
- }, options?.history ?? true, (session) => {
948
- value = run(require_access.createAccess({
949
- state,
950
- session
951
- }));
952
- if (value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function") throw new TypeError("Document updates must be synchronous.");
953
- });
954
- return result.status === "rejected" ? result : {
955
- ...result,
956
- value
957
- };
2531
+ reset: () => reset,
2532
+ publish: () => {
2533
+ if (change) published.publish(staged, nextIds, change.kind === "reset");
2534
+ if (change && initialized) revision++;
2535
+ initialized = true;
958
2536
  },
959
- apply: (inputChanges, options) => mutate({
960
- kind: "apply",
961
- source: options?.source ?? "local"
962
- }, options?.history ?? true, (session) => {
963
- if (!options || options.expectedRevision !== revision) require_issue.fail([], "baseline-mismatch", "apply requires the current expectedRevision.");
964
- apply(session, require_driver.decodeChanges(inputChanges), "forward");
965
- }),
966
- replace: (value, options) => {
967
- const source = options?.source ?? "system";
968
- return mutate({
969
- kind: "replace",
970
- source
971
- }, source !== "remote", (session) => session.replace([], value));
2537
+ emit: (call) => {
2538
+ const resetEvent = Object.freeze({ kind: "reset" });
2539
+ Array.from(listeners).forEach((listener) => call(() => listener(change ?? resetEvent)));
972
2540
  },
973
- snapshot: () => {
974
- if (state.disposed) throw new require_access.DocumentDisposedError();
975
- return require_access.copyValue(input.schema, state.document);
2541
+ clear: () => {
2542
+ staged.clear();
2543
+ nextIds = published.ids();
2544
+ change = void 0;
2545
+ reset = false;
2546
+ explicitOrder = void 0;
2547
+ cleared = false;
976
2548
  },
977
- subscribe: ((pick, listener) => {
978
- if (state.disposed) throw new require_access.DocumentDisposedError();
979
- if (!listener) return require_notification.subscribeRoot(notification, pick);
980
- const picks = Array.isArray(pick) ? pick : [pick];
981
- if (!picks.length) throw new TypeError("Expected at least one subscription path.");
982
- return require_notification.subscribeTargets(notification, picks.map((p) => require_advanced.compilePath(input.schema, "value", p)), listener);
983
- }),
984
- history: history.api,
985
- dispose: () => {
986
- if (state.disposed) return;
987
- idle();
988
- state.disposed = true;
989
- try {
990
- require_notification.disposeNotification(notification);
991
- } finally {
992
- history.dispose();
993
- require_driver.disposeRuntimeDriver(runtime);
994
- }
995
- }
996
- };
997
- require_access.bindRuntimeAccess(runtime, state);
998
- require_driver.bindRuntimeDriver(runtime);
999
- notification = require_notification.createNotification(runtime);
1000
- require_notification.bindDocumentReadable(history.api, runtime);
1001
- return runtime;
1002
- };
1003
- //#endregion
1004
- //#region core/src/runtime/readable.ts
1005
- const asReadable = (runtime) => {
1006
- const readable = {
1007
- address: runtime.address,
1008
- revision: runtime.revision,
1009
- subscribe: runtime.subscribe
2549
+ release: () => {
2550
+ published.release();
2551
+ staged.clear();
2552
+ listeners.clear();
2553
+ nextIds = Object.freeze([]);
2554
+ change = void 0;
2555
+ initialized = false;
2556
+ revision = 0;
2557
+ reset = false;
2558
+ explicitOrder = void 0;
2559
+ cleared = false;
2560
+ },
2561
+ subscribe: (listener) => listeners.add(listener),
2562
+ unsubscribe: (listener) => listeners.delete(listener)
1010
2563
  };
1011
- require_access.bindRuntimeAccess(readable, require_access.accessOf(runtime));
1012
- require_notification.shareNotification(runtime, readable);
1013
- return Object.freeze(readable);
1014
2564
  };
1015
2565
  //#endregion
1016
- //#region core/src/projection/select.ts
1017
- const read = (runtime, selector) => require_access.readWith(runtime, selector);
1018
- //#endregion
1019
- //#region core/src/projection/value-output.ts
2566
+ //#region core/src/projection/output/value.ts
1020
2567
  const unset = Symbol("projection-value-unset");
1021
2568
  /** Owns staged and published state for one scalar Projection output. */
1022
2569
  const createValueOutput = () => {
@@ -1085,7 +2632,7 @@ const createValueOutput = () => {
1085
2632
  initialized = true;
1086
2633
  },
1087
2634
  emit: (call) => {
1088
- require_issue.profile.materialized.notification();
2635
+ require_advanced.profile.materialized.notification();
1089
2636
  Array.from(listeners).forEach((listener) => call(listener));
1090
2637
  },
1091
2638
  clear: () => {
@@ -1108,7 +2655,7 @@ const createValueOutput = () => {
1108
2655
  };
1109
2656
  };
1110
2657
  //#endregion
1111
- //#region core/src/projection/processor.ts
2658
+ //#region core/src/projection/graph/processor.ts
1112
2659
  /** Materializes every derive/incremental/group definition through one processor lifecycle. */
1113
2660
  const createProcessor = (scheduler, definition, dependencies) => {
1114
2661
  scheduler.assertIdle();
@@ -1148,7 +2695,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
1148
2695
  }
1149
2696
  };
1150
2697
  }
1151
- const state = require_advanced.createCollectionOutput();
2698
+ const state = createCollectionOutput();
1152
2699
  return {
1153
2700
  kind: "collection",
1154
2701
  definition: outputDefinition,
@@ -1202,7 +2749,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
1202
2749
  const sources = Object.freeze(dependencies.map((output) => output.context(scopeActive)));
1203
2750
  let outputs = begin(scopeActive, reset);
1204
2751
  if (!instance) instance = definition.create();
1205
- require_issue.profile.materialized[reset ? "rebuilt" : "updated"]();
2752
+ require_advanced.profile.materialized[reset ? "rebuilt" : "updated"]();
1206
2753
  let result = instance.evaluate({
1207
2754
  reset,
1208
2755
  cause: runCause,
@@ -1270,7 +2817,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
1270
2817
  return processor;
1271
2818
  };
1272
2819
  //#endregion
1273
- //#region core/src/projection/projection-readable.ts
2820
+ //#region core/src/projection/readable/selection.ts
1274
2821
  let activeTracker;
1275
2822
  const trackKey = (key) => {
1276
2823
  activeTracker?.keys.add(key);
@@ -1321,9 +2868,10 @@ const trackedMap = (source) => {
1321
2868
  };
1322
2869
  const selectionAffects = (selection, change) => {
1323
2870
  if (!change || change.kind === "reset") return true;
1324
- if (selection.all && (change.added.length || change.removed.length || change.updated.length || change.order)) return true;
1325
- if (selection.structure && (change.added.length || change.removed.length || change.order)) return true;
1326
- for (const key of selection.keys) if (change.added.some((entry) => entry.key === key) || change.removed.some((entry) => entry.key === key) || change.updated.some((entry) => entry.key === key)) return true;
2871
+ if (selection.all && collectionHasAnyChange(change)) return true;
2872
+ if (selection.structure && collectionHasStructuralChange(change)) return true;
2873
+ const changedKeys = collectionChangedKeys(change);
2874
+ for (const key of selection.keys) if (changedKeys.has(key)) return true;
1327
2875
  return false;
1328
2876
  };
1329
2877
  const createDirectReadable = (source) => Object.freeze({
@@ -1391,7 +2939,7 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
1391
2939
  if (!change && previousCollection) {
1392
2940
  const current = source.current();
1393
2941
  if (isMapLike(current)) {
1394
- change = require_advanced.collectionChangeBetween(previousCollection, current);
2942
+ change = diffCollection(previousCollection, current);
1395
2943
  previousCollection = current;
1396
2944
  if (!change) {
1397
2945
  sourceRevision = source.revision();
@@ -1430,12 +2978,321 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
1430
2978
  });
1431
2979
  };
1432
2980
  //#endregion
1433
- //#region core/src/projection/source.ts
2981
+ //#region core/src/projection/source/materialization.ts
2982
+ const createDocumentDirty = () => ({
2983
+ replace: false,
2984
+ order: false,
2985
+ treeRoot: false,
2986
+ treeNodes: /* @__PURE__ */ new Set(),
2987
+ children: /* @__PURE__ */ new Map()
2988
+ });
2989
+ const clearDocumentDirty = (dirty) => {
2990
+ dirty.replace = false;
2991
+ dirty.order = false;
2992
+ dirty.treeRoot = false;
2993
+ dirty.treeNodes.clear();
2994
+ dirty.children.clear();
2995
+ };
2996
+ const hasDocumentDirty = (dirty) => dirty.replace || dirty.order || dirty.treeRoot || dirty.treeNodes.size > 0 || dirty.children.size > 0;
2997
+ const locate = (dirty, path) => {
2998
+ let current = dirty;
2999
+ for (const key of path) {
3000
+ if (current.replace) return void 0;
3001
+ let child = current.children.get(key);
3002
+ if (!child) {
3003
+ child = createDocumentDirty();
3004
+ current.children.set(key, child);
3005
+ }
3006
+ current = child;
3007
+ }
3008
+ return current;
3009
+ };
3010
+ const markDocumentReplace = (dirty, path) => {
3011
+ const current = locate(dirty, path);
3012
+ if (!current) return;
3013
+ current.replace = true;
3014
+ current.order = false;
3015
+ current.treeRoot = false;
3016
+ current.treeNodes.clear();
3017
+ current.children.clear();
3018
+ };
3019
+ const markDocumentOrder = (dirty, path) => {
3020
+ const current = locate(dirty, path);
3021
+ if (current && !current.replace) current.order = true;
3022
+ };
3023
+ const markDocumentTree = (dirty, path, root, nodes) => {
3024
+ const current = locate(dirty, path);
3025
+ if (!current || current.replace) return;
3026
+ current.treeRoot ||= root;
3027
+ for (const id of nodes) current.treeNodes.add(id);
3028
+ };
3029
+ const equivalent = (node, left, right) => node.kind === "field" ? Object.is(left, right) : require_advanced.equalValue(node, left, right);
3030
+ const replaceValue = (node, previous, current) => equivalent(node, previous, current) ? previous : require_advanced.copyValue(node, current);
3031
+ const cloneRecord = (value) => {
3032
+ require_advanced.profile.copy.shallowRecord(Object.getOwnPropertyNames(value).length);
3033
+ return Object.create(Object.getPrototypeOf(value), Object.getOwnPropertyDescriptors(value));
3034
+ };
3035
+ const materializeRecordChild = (node, previous, current, key, dirty) => {
3036
+ const before = Object.hasOwn(previous, key);
3037
+ const after = Object.hasOwn(current, key);
3038
+ if (!before && !after) return { changed: false };
3039
+ if (!after) return {
3040
+ changed: true,
3041
+ present: false,
3042
+ value: void 0
3043
+ };
3044
+ const next = before ? materializeDocumentValue(node, previous[key], current[key], dirty) : require_advanced.copyValue(node, current[key]);
3045
+ if (before && next === previous[key]) return { changed: false };
3046
+ return {
3047
+ changed: true,
3048
+ present: true,
3049
+ value: next
3050
+ };
3051
+ };
3052
+ const materializeObject = (node, previous, current, dirty) => {
3053
+ if (node.kind === "variant" && previous[node.tag] !== current[node.tag]) return replaceValue(node, previous, current);
3054
+ const layout = require_advanced.compiledShape(node, current);
3055
+ if (!layout) return replaceValue(node, previous, current);
3056
+ let result;
3057
+ for (const [key, childDirty] of dirty.children) {
3058
+ const member = layout.members.get(key);
3059
+ if (!member) return replaceValue(node, previous, current);
3060
+ const child = materializeRecordChild(member.node, previous, current, key, childDirty);
3061
+ if (!child.changed) continue;
3062
+ result ??= cloneRecord(previous);
3063
+ require_context.installOwn(result, key, child.present, child.value);
3064
+ }
3065
+ return result ?? previous;
3066
+ };
3067
+ const materializeMap = (node, previous, current, dirty) => {
3068
+ let result;
3069
+ for (const [key, childDirty] of dirty.children) {
3070
+ const child = materializeRecordChild(node.value, previous, current, key, childDirty);
3071
+ if (!child.changed) continue;
3072
+ result ??= cloneRecord(previous);
3073
+ require_context.installOwn(result, key, child.present, child.value);
3074
+ }
3075
+ return result ?? previous;
3076
+ };
3077
+ const materializeTable = (node, previous, current, dirty) => {
3078
+ if (!require_context.isRecord(previous.byId) || !require_context.isRecord(current.byId)) return replaceValue(node, previous, current);
3079
+ const beforeById = previous.byId;
3080
+ const afterById = current.byId;
3081
+ let result;
3082
+ let nextById;
3083
+ for (const [key, childDirty] of dirty.children) {
3084
+ const child = materializeRecordChild(node.value, beforeById, afterById, key, childDirty);
3085
+ if (!child.changed) continue;
3086
+ nextById ??= cloneRecord(beforeById);
3087
+ require_context.installOwn(nextById, key, child.present, child.value);
3088
+ }
3089
+ if (nextById) {
3090
+ result = cloneRecord(previous);
3091
+ require_context.installOwn(result, "byId", true, nextById);
3092
+ }
3093
+ if (dirty.order) {
3094
+ const beforeIds = previous.ids;
3095
+ const afterIds = current.ids;
3096
+ if (!Array.isArray(beforeIds) || !Array.isArray(afterIds)) return replaceValue(node, previous, current);
3097
+ if (!require_advanced.equal(beforeIds, afterIds)) {
3098
+ result ??= cloneRecord(previous);
3099
+ require_context.installOwn(result, "ids", true, [...afterIds]);
3100
+ }
3101
+ }
3102
+ return result ?? previous;
3103
+ };
3104
+ const materializeList = (node, previous, current, dirty) => {
3105
+ if (dirty.order) {
3106
+ const before = require_advanced.listSequence(previous, node.keyOf);
3107
+ const after = require_advanced.listSequence(current, node.keyOf);
3108
+ let changed = !require_advanced.equal(before.order, after.order);
3109
+ const result = new Array(after.order.length);
3110
+ for (let index = 0; index < after.order.length; index++) {
3111
+ const key = after.order[index];
3112
+ const currentValue = after.values.get(key);
3113
+ if (!before.values.has(key)) {
3114
+ changed = true;
3115
+ result[index] = currentValue;
3116
+ continue;
3117
+ }
3118
+ const previousValue = before.values.get(key);
3119
+ const childDirty = dirty.children.get(key);
3120
+ const next = childDirty ? materializeDocumentValue(node.value, previousValue, currentValue, childDirty) : previousValue;
3121
+ changed ||= next !== previousValue;
3122
+ result[index] = next;
3123
+ }
3124
+ return changed ? result : previous;
3125
+ }
3126
+ let result;
3127
+ const before = require_advanced.indexedKeys(previous, node.keyOf);
3128
+ const after = require_advanced.indexedKeys(current, node.keyOf);
3129
+ for (const [key, childDirty] of dirty.children) {
3130
+ const previousIndex = before.index(key);
3131
+ const currentIndex = after.index(key);
3132
+ if (previousIndex < 0 || currentIndex < 0) return replaceValue(node, previous, current);
3133
+ const next = materializeDocumentValue(node.value, previous[previousIndex], current[currentIndex], childDirty);
3134
+ if (next === previous[previousIndex] && previousIndex === currentIndex) continue;
3135
+ result ??= [...previous];
3136
+ result[currentIndex] = next;
3137
+ }
3138
+ return result ?? previous;
3139
+ };
3140
+ const materializeTree = (node, previous, current, dirty) => {
3141
+ if (!require_context.isRecord(previous.nodes) || !require_context.isRecord(current.nodes)) return replaceValue(node, previous, current);
3142
+ let result;
3143
+ if (dirty.treeRoot) {
3144
+ const beforePresent = Object.hasOwn(previous, "rootId");
3145
+ const afterPresent = Object.hasOwn(current, "rootId");
3146
+ if (beforePresent !== afterPresent || !Object.is(previous.rootId, current.rootId)) {
3147
+ result = cloneRecord(previous);
3148
+ require_context.installOwn(result, "rootId", afterPresent, current.rootId);
3149
+ }
3150
+ }
3151
+ let nodes;
3152
+ for (const id of dirty.treeNodes) {
3153
+ const beforePresent = Object.hasOwn(previous.nodes, id);
3154
+ const afterPresent = Object.hasOwn(current.nodes, id);
3155
+ const before = beforePresent ? previous.nodes[id] : void 0;
3156
+ const after = afterPresent ? current.nodes[id] : void 0;
3157
+ if (beforePresent === afterPresent && require_advanced.equalTreeNode(before, after)) continue;
3158
+ nodes ??= cloneRecord(previous.nodes);
3159
+ require_context.installOwn(nodes, id, afterPresent, after && require_advanced.copyTreeNode(after));
3160
+ }
3161
+ if (nodes) {
3162
+ result ??= cloneRecord(previous);
3163
+ require_context.installOwn(result, "nodes", true, nodes);
3164
+ }
3165
+ return result ?? previous;
3166
+ };
3167
+ const materializeDocumentValue = (node, previous, current, dirty) => {
3168
+ if (!hasDocumentDirty(dirty)) return previous;
3169
+ if (dirty.replace || previous === void 0 || current === void 0 || node.kind === "field") return replaceValue(node, previous, current);
3170
+ if (node.kind === "object" || node.kind === "variant") {
3171
+ if (!require_context.isRecord(previous) || !require_context.isRecord(current)) return replaceValue(node, previous, current);
3172
+ return materializeObject(node, previous, current, dirty);
3173
+ }
3174
+ if (node.kind === "map") {
3175
+ if (!require_context.isRecord(previous) || !require_context.isRecord(current)) return replaceValue(node, previous, current);
3176
+ return materializeMap(node, previous, current, dirty);
3177
+ }
3178
+ if (node.kind === "table") {
3179
+ if (!require_context.isRecord(previous) || !require_context.isRecord(current)) return replaceValue(node, previous, current);
3180
+ return materializeTable(node, previous, current, dirty);
3181
+ }
3182
+ if (node.kind === "list") {
3183
+ if (!Array.isArray(previous) || !Array.isArray(current)) return replaceValue(node, previous, current);
3184
+ return materializeList(node, previous, current, dirty);
3185
+ }
3186
+ if (!require_context.isRecord(previous) || !require_context.isRecord(current)) return replaceValue(node, previous, current);
3187
+ return materializeTree(node, previous, current, dirty);
3188
+ };
3189
+ //#endregion
3190
+ //#region core/src/projection/source/dirty.ts
3191
+ const createCollectionPending = () => ({
3192
+ reset: false,
3193
+ order: false,
3194
+ entries: /* @__PURE__ */ new Map()
3195
+ });
3196
+ const resetCollectionPending = (pending) => {
3197
+ pending.reset = true;
3198
+ pending.order = false;
3199
+ pending.entries.clear();
3200
+ };
3201
+ const clearCollectionPending = (pending) => {
3202
+ pending.reset = false;
3203
+ pending.order = false;
3204
+ pending.entries.clear();
3205
+ };
3206
+ const dirtyEntry = (pending, key) => {
3207
+ let dirty = pending.entries.get(key);
3208
+ if (!dirty) {
3209
+ dirty = createDocumentDirty();
3210
+ pending.entries.set(key, dirty);
3211
+ }
3212
+ return dirty;
3213
+ };
3214
+ const mergeValueChanges = (selector, dirty, changes) => {
3215
+ if (selector.tree) return;
3216
+ const at = selector.address;
3217
+ for (const change of changes.changes) {
3218
+ if (change.kind === "reset") {
3219
+ markDocumentReplace(dirty, []);
3220
+ return;
3221
+ }
3222
+ if (change.kind === "members") {
3223
+ for (const member of change.members) {
3224
+ const changed = [...change.at, member.key];
3225
+ if (contains(changed, at)) markDocumentReplace(dirty, []);
3226
+ else if (contains(at, changed)) markDocumentReplace(dirty, changed.slice(at.length));
3227
+ }
3228
+ if (change.order && contains(at, change.at)) markDocumentOrder(dirty, change.at.slice(at.length));
3229
+ continue;
3230
+ }
3231
+ if (contains(at, change.at)) markDocumentTree(dirty, change.at.slice(at.length), change.before !== change.after, change.nodes.map((node) => node.id));
3232
+ }
3233
+ };
3234
+ const mergeCollectionChanges = (selector, pending, changes) => {
3235
+ if (pending.reset) return;
3236
+ const at = selector.address;
3237
+ for (const change of changes.changes) {
3238
+ if (change.kind === "reset") {
3239
+ resetCollectionPending(pending);
3240
+ return;
3241
+ }
3242
+ if (selector.tree?.kind === "nodes") {
3243
+ if (change.kind === "members") {
3244
+ for (const member of change.members) if (contains([...change.at, member.key], at)) {
3245
+ resetCollectionPending(pending);
3246
+ return;
3247
+ }
3248
+ } else if (change.at.length === at.length && contains(change.at, at)) for (const node of change.nodes) {
3249
+ markDocumentReplace(dirtyEntry(pending, node.id), []);
3250
+ pending.order ||= node.kind !== "updated";
3251
+ }
3252
+ else if (contains(change.at, at)) {
3253
+ resetCollectionPending(pending);
3254
+ return;
3255
+ }
3256
+ continue;
3257
+ }
3258
+ if (change.kind === "members") {
3259
+ for (const member of change.members) {
3260
+ const changed = [...change.at, member.key];
3261
+ if (contains(changed, at)) {
3262
+ resetCollectionPending(pending);
3263
+ return;
3264
+ }
3265
+ if (!contains(at, changed)) continue;
3266
+ const relative = changed.slice(at.length);
3267
+ if (!relative.length) {
3268
+ resetCollectionPending(pending);
3269
+ return;
3270
+ }
3271
+ markDocumentReplace(dirtyEntry(pending, relative[0]), relative.slice(1));
3272
+ if (relative.length === 1 && member.kind !== "updated") pending.order = true;
3273
+ }
3274
+ if (change.order && contains(at, change.at)) {
3275
+ const relative = change.at.slice(at.length);
3276
+ if (!relative.length) pending.order = true;
3277
+ else markDocumentOrder(dirtyEntry(pending, relative[0]), relative.slice(1));
3278
+ }
3279
+ continue;
3280
+ }
3281
+ if (!contains(at, change.at)) continue;
3282
+ const relative = change.at.slice(at.length);
3283
+ if (!relative.length) {
3284
+ resetCollectionPending(pending);
3285
+ return;
3286
+ }
3287
+ markDocumentTree(dirtyEntry(pending, relative[0]), relative.slice(1), change.before !== change.after, change.nodes.map((node) => node.id));
3288
+ }
3289
+ };
3290
+ //#endregion
3291
+ //#region core/src/projection/source/boundary.ts
1434
3292
  const sourceError = (source, cause) => new require_advanced.ProjectionError("source", source.name, [source.outputs[0].revision()], cause);
1435
- const createValueBoundary = (scheduler, name, initial, equality) => {
3293
+ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearSource) => {
1436
3294
  const state = createValueOutput();
1437
3295
  let boundary;
1438
- let latest = initial;
1439
3296
  let pendingReset = false;
1440
3297
  let pendingCause;
1441
3298
  let contextCause;
@@ -1477,7 +3334,8 @@ const createValueBoundary = (scheduler, name, initial, equality) => {
1477
3334
  contextCause = cause;
1478
3335
  let active = true;
1479
3336
  try {
1480
- state.begin(() => active, pendingReset).output.set(latest);
3337
+ const evaluation = state.begin(() => active, pendingReset);
3338
+ evaluation.output.set(prepare(evaluation.previous));
1481
3339
  const changed = state.seal(pendingReset, equality);
1482
3340
  recovering = false;
1483
3341
  return {
@@ -1490,11 +3348,13 @@ const createValueBoundary = (scheduler, name, initial, equality) => {
1490
3348
  },
1491
3349
  publish: state.publish,
1492
3350
  clear: () => {
3351
+ const failed = boundary.fault !== void 0;
1493
3352
  state.clear();
1494
3353
  pendingReset = false;
1495
3354
  pendingCause = void 0;
1496
3355
  contextCause = void 0;
1497
3356
  recovering = false;
3357
+ clearSource(failed);
1498
3358
  },
1499
3359
  release: () => {
1500
3360
  cleanup?.();
@@ -1530,9 +3390,6 @@ const createValueBoundary = (scheduler, name, initial, equality) => {
1530
3390
  return {
1531
3391
  producer: boundary,
1532
3392
  output,
1533
- setLatest: (value) => {
1534
- latest = value;
1535
- },
1536
3393
  mark,
1537
3394
  fail,
1538
3395
  detach: (next) => {
@@ -1540,8 +3397,35 @@ const createValueBoundary = (scheduler, name, initial, equality) => {
1540
3397
  }
1541
3398
  };
1542
3399
  };
1543
- const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
1544
- const state = require_advanced.createCollectionOutput();
3400
+ /** Applies source hints to an output draft; exact net change semantics remain in the output. */
3401
+ const stageCollectionRead = (state, read, active, options) => {
3402
+ const evaluation = state.begin(active, options.reset);
3403
+ if (options.reset) {
3404
+ const ids = read.ids();
3405
+ require_advanced.profile.collectionView.idsScanned(ids.length);
3406
+ for (const key of ids) {
3407
+ require_advanced.profile.collectionView.mapped();
3408
+ evaluation.output.set(key, read.get(key));
3409
+ }
3410
+ evaluation.output.order(ids);
3411
+ } else {
3412
+ const previous = state.current(() => void 0);
3413
+ const keys = options.candidates ? [...new Set(options.candidates)] : [...new Set([...previous.ids(), ...read.ids()])];
3414
+ if (!options.candidates) require_advanced.profile.collectionView.idsScanned(keys.length);
3415
+ for (const key of keys) if (read.has(key)) {
3416
+ require_advanced.profile.collectionView.mapped();
3417
+ evaluation.output.set(key, read.get(key));
3418
+ } else evaluation.output.remove(key);
3419
+ if (options.orderMayChange || !options.candidates) {
3420
+ const ids = read.ids();
3421
+ if (options.candidates) require_advanced.profile.collectionView.idsScanned(ids.length);
3422
+ evaluation.output.order(ids);
3423
+ }
3424
+ }
3425
+ return state.seal(options.reset, options.isEqual ?? Object.is);
3426
+ };
3427
+ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSource) => {
3428
+ const state = createCollectionOutput();
1545
3429
  let boundary;
1546
3430
  let pendingReset = false;
1547
3431
  let pendingCause;
@@ -1587,7 +3471,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
1587
3471
  contextCause = cause;
1588
3472
  let active = true;
1589
3473
  try {
1590
- const changed = require_advanced.stageCollectionRead(state, readLatest(() => active), () => active, {
3474
+ const changed = stageCollectionRead(state, readLatest(() => active, state.current(() => void 0)), () => active, {
1591
3475
  reset: pendingReset,
1592
3476
  ...fullScan ? {} : { candidates },
1593
3477
  orderMayChange,
@@ -1604,6 +3488,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
1604
3488
  },
1605
3489
  publish: state.publish,
1606
3490
  clear: () => {
3491
+ const failed = boundary.fault !== void 0;
1607
3492
  state.clear();
1608
3493
  pendingReset = false;
1609
3494
  pendingCause = void 0;
@@ -1612,6 +3497,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
1612
3497
  fullScan = false;
1613
3498
  orderMayChange = false;
1614
3499
  candidates.clear();
3500
+ clearSource(failed);
1615
3501
  },
1616
3502
  release: () => {
1617
3503
  cleanup?.();
@@ -1623,7 +3509,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
1623
3509
  scheduler.initialize(() => {
1624
3510
  let active = true;
1625
3511
  try {
1626
- require_advanced.stageCollectionRead(state, readLatest(() => active), () => active, {
3512
+ stageCollectionRead(state, readLatest(() => active, state.current(() => void 0)), () => active, {
1627
3513
  reset: true,
1628
3514
  isEqual: equality
1629
3515
  });
@@ -1659,33 +3545,41 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
1659
3545
  }
1660
3546
  };
1661
3547
  };
1662
- const createSourceRegistry = (scheduler) => {
3548
+ //#endregion
3549
+ //#region core/src/projection/source/document.ts
3550
+ const documentRootTarget = Object.freeze({
3551
+ kind: "value",
3552
+ at: Object.freeze([])
3553
+ });
3554
+ const createDocumentSourceRegistry = (scheduler) => {
1663
3555
  const documents = /* @__PURE__ */ new Map();
1664
- const readables = /* @__PURE__ */ new Map();
1665
- const externalValues = /* @__PURE__ */ new WeakMap();
1666
- const externalCollections = /* @__PURE__ */ new WeakMap();
1667
- const documentConnection = (runtime) => {
1668
- const state = require_access.accessOf(runtime);
3556
+ const connection = (runtime) => {
3557
+ const context = require_context.contextOf(runtime);
3558
+ const state = context.state;
1669
3559
  const cached = documents.get(state);
1670
3560
  if (cached) return cached;
1671
3561
  if (state.disposed) throw new Error("Document has been disposed.");
1672
3562
  const bindings = /* @__PURE__ */ new Set();
3563
+ const index = new SubscriptionIndex(state.schema);
3564
+ const candidates = /* @__PURE__ */ new Set();
1673
3565
  const guard = (locked) => {
1674
- state.projectionLocks = (state.projectionLocks ?? 0) + (locked ? 1 : -1);
3566
+ state.projectionLocks += locked ? 1 : -1;
1675
3567
  };
1676
3568
  scheduler.guards.add(guard);
1677
3569
  let closed = false;
1678
3570
  let detach = () => void 0;
1679
- const connection = {
3571
+ const documentConnection = {
1680
3572
  add(binding) {
1681
3573
  if (closed) throw new Error("Document projection connection is closed.");
1682
3574
  bindings.add(binding);
3575
+ index.add(binding.target, binding);
1683
3576
  let active = true;
1684
3577
  return () => {
1685
3578
  if (!active) return;
1686
3579
  active = false;
1687
3580
  bindings.delete(binding);
1688
- if (!bindings.size) connection.close();
3581
+ index.delete(binding.target, binding);
3582
+ if (!bindings.size) documentConnection.close();
1689
3583
  };
1690
3584
  },
1691
3585
  close() {
@@ -1693,13 +3587,17 @@ const createSourceRegistry = (scheduler) => {
1693
3587
  closed = true;
1694
3588
  detach();
1695
3589
  bindings.clear();
3590
+ candidates.clear();
3591
+ index.clear();
1696
3592
  scheduler.guards.delete(guard);
1697
3593
  documents.delete(state);
1698
3594
  }
1699
3595
  };
1700
- detach = require_notification.attachProjection(runtime, {
3596
+ detach = context.notifications.attachProjection({
1701
3597
  capture: (commit) => {
1702
- for (const binding of bindings) if (binding.matches(commit)) binding.capture(commit);
3598
+ candidates.clear();
3599
+ index.collect(commit.changes, (binding) => candidates.add(binding));
3600
+ candidates.forEach((binding) => binding.capture(commit));
1703
3601
  },
1704
3602
  settle: scheduler.settle,
1705
3603
  flush: scheduler.flush,
@@ -1708,20 +3606,175 @@ const createSourceRegistry = (scheduler) => {
1708
3606
  scheduler.run();
1709
3607
  }
1710
3608
  });
1711
- documents.set(state, connection);
1712
- return connection;
3609
+ documents.set(state, documentConnection);
3610
+ return documentConnection;
3611
+ };
3612
+ const materialize = (definition) => {
3613
+ if (definition.source.kind !== "document") throw new Error("Invalid document source.");
3614
+ const { document, selector } = definition.source;
3615
+ const state = require_context.contextOf(document).state;
3616
+ const current = () => resolveValue(state.schema, state.document, selector.address);
3617
+ const disposed = () => {
3618
+ if (state.disposed) throw new Error("Document has been disposed.");
3619
+ };
3620
+ if (selector.kind === "value") {
3621
+ const dirty = createDocumentDirty();
3622
+ const read = (previous) => {
3623
+ disposed();
3624
+ const resolved = current();
3625
+ const node = resolved?.node;
3626
+ const value = resolved?.value;
3627
+ if (selector.tree?.kind === "root") return node?.kind === "tree" && require_context.isRecord(value) && typeof value.rootId === "string" ? value.rootId : void 0;
3628
+ if (selector.tree?.kind === "node") {
3629
+ if (node?.kind !== "tree" || !require_context.isRecord(value) || !require_context.isRecord(value.nodes)) return void 0;
3630
+ const entry = Object.hasOwn(value.nodes, selector.tree.id) ? value.nodes[selector.tree.id] : void 0;
3631
+ if (require_advanced.equalTreeNode(previous, entry)) return previous;
3632
+ return entry ? require_advanced.copyTreeNode(entry) : void 0;
3633
+ }
3634
+ if (!node) return void 0;
3635
+ return previous === void 0 ? require_advanced.copyValue(node, value) : materializeDocumentValue(node, previous, value, dirty);
3636
+ };
3637
+ const initial = read();
3638
+ let boundary;
3639
+ boundary = createValueBoundary(scheduler, "document source", initial, Object.is, (previous) => read(previous), (failed) => {
3640
+ if (failed) markDocumentReplace(dirty, []);
3641
+ else clearDocumentDirty(dirty);
3642
+ });
3643
+ const remove = connection(document).add({
3644
+ target: selector,
3645
+ capture: (commit) => {
3646
+ try {
3647
+ mergeValueChanges(selector, dirty, commit.changes);
3648
+ boundary.mark({ reset: commit.changes.changes.some((change) => change.kind === "reset") });
3649
+ } catch (cause) {
3650
+ boundary.fail(cause);
3651
+ }
3652
+ },
3653
+ dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
3654
+ });
3655
+ boundary.detach(remove);
3656
+ return boundary;
3657
+ }
3658
+ const pending = createCollectionPending();
3659
+ const read = (active, previous) => {
3660
+ disposed();
3661
+ const resolved = current();
3662
+ const node = resolved?.node;
3663
+ const value = resolved?.value;
3664
+ const treeNodes = selector.tree?.kind === "nodes";
3665
+ const entryNode = !treeNodes && node ? require_advanced.collectionEntryNode(node) : void 0;
3666
+ const snapshots = /* @__PURE__ */ new Map();
3667
+ const replacement = createDocumentDirty();
3668
+ if (pending.reset) markDocumentReplace(replacement, []);
3669
+ const ids = () => {
3670
+ require_advanced.assertScope(active);
3671
+ if (treeNodes) return node?.kind === "tree" && require_context.isRecord(value) && require_context.isRecord(value.nodes) ? Object.freeze(Object.keys(value.nodes)) : Object.freeze([]);
3672
+ if (node?.kind === "map" && require_context.isRecord(value)) return Object.freeze(Object.keys(value));
3673
+ if (node?.kind === "table" && require_context.isRecord(value) && Array.isArray(value.ids)) return Object.freeze([...value.ids]);
3674
+ if (node?.kind === "list" && Array.isArray(value)) return Object.freeze(require_advanced.toArray(require_advanced.indexedKeys(value, node.keyOf)));
3675
+ return Object.freeze([]);
3676
+ };
3677
+ const has = (key) => {
3678
+ require_advanced.assertScope(active);
3679
+ if (treeNodes) return Boolean(node?.kind === "tree" && require_context.isRecord(value) && require_context.isRecord(value.nodes) && Object.hasOwn(value.nodes, key));
3680
+ if (node?.kind === "map" && require_context.isRecord(value)) return Object.hasOwn(value, key);
3681
+ if (node?.kind === "table" && require_context.isRecord(value) && require_context.isRecord(value.byId)) return Object.hasOwn(value.byId, key);
3682
+ if (node?.kind === "list" && Array.isArray(value)) return require_advanced.indexedKeys(value, node.keyOf).index(key) >= 0;
3683
+ return false;
3684
+ };
3685
+ const raw = (key) => {
3686
+ if (treeNodes) {
3687
+ if (node?.kind !== "tree" || !require_context.isRecord(value) || !require_context.isRecord(value.nodes)) return void 0;
3688
+ return Object.hasOwn(value.nodes, key) ? value.nodes[key] : void 0;
3689
+ }
3690
+ if (node?.kind === "map" && require_context.isRecord(value)) return Object.hasOwn(value, key) ? value[key] : void 0;
3691
+ if (node?.kind === "table" && require_context.isRecord(value) && require_context.isRecord(value.byId)) return Object.hasOwn(value.byId, key) ? value.byId[key] : void 0;
3692
+ if (node?.kind === "list" && Array.isArray(value)) {
3693
+ const index = require_advanced.indexedKeys(value, node.keyOf).index(key);
3694
+ return index < 0 ? void 0 : value[index];
3695
+ }
3696
+ };
3697
+ return Object.freeze({
3698
+ get: (key) => {
3699
+ require_advanced.assertScope(active);
3700
+ if (!snapshots.has(key)) if (!has(key)) snapshots.set(key, void 0);
3701
+ else {
3702
+ const current = raw(key);
3703
+ if (treeNodes) {
3704
+ const entry = current;
3705
+ const before = previous.has(key) ? previous.get(key) : void 0;
3706
+ snapshots.set(key, require_advanced.equalTreeNode(before, entry) ? before : require_advanced.copyTreeNode(entry));
3707
+ } else if (!entryNode) snapshots.set(key, current);
3708
+ else if (!previous.has(key)) snapshots.set(key, require_advanced.copyValue(entryNode, current));
3709
+ else {
3710
+ const dirty = pending.entries.get(key) ?? (pending.reset ? replacement : void 0);
3711
+ snapshots.set(key, dirty ? materializeDocumentValue(entryNode, previous.get(key), current, dirty) : previous.get(key));
3712
+ }
3713
+ }
3714
+ return snapshots.get(key);
3715
+ },
3716
+ has,
3717
+ ids
3718
+ });
3719
+ };
3720
+ let boundary;
3721
+ boundary = createCollectionBoundary(scheduler, "document collection source", read, Object.is, (failed) => {
3722
+ if (failed) resetCollectionPending(pending);
3723
+ else clearCollectionPending(pending);
3724
+ });
3725
+ const remove = connection(document).add({
3726
+ target: selector,
3727
+ capture: (commit) => {
3728
+ try {
3729
+ mergeCollectionChanges(selector, pending, commit.changes);
3730
+ boundary.mark({
3731
+ reset: pending.reset,
3732
+ ...pending.reset ? {} : { candidates: pending.entries.keys() },
3733
+ orderMayChange: pending.order
3734
+ });
3735
+ } catch (cause) {
3736
+ boundary.fail(cause);
3737
+ }
3738
+ },
3739
+ dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
3740
+ });
3741
+ boundary.detach(remove);
3742
+ return boundary;
3743
+ };
3744
+ return {
3745
+ materialize,
3746
+ attachRoot(runtime, handlers) {
3747
+ return connection(runtime).add({
3748
+ target: documentRootTarget,
3749
+ capture: handlers.capture,
3750
+ dispose: handlers.dispose
3751
+ });
3752
+ },
3753
+ dispose() {
3754
+ documents.forEach((document) => document.close());
3755
+ documents.clear();
3756
+ }
1713
3757
  };
3758
+ };
3759
+ //#endregion
3760
+ //#region core/src/projection/source/registry.ts
3761
+ const createSourceRegistry = (scheduler) => {
3762
+ const documents = createDocumentSourceRegistry(scheduler);
3763
+ const readables = /* @__PURE__ */ new Map();
3764
+ const externalValues = /* @__PURE__ */ new WeakMap();
3765
+ const externalCollections = /* @__PURE__ */ new WeakMap();
1714
3766
  const valueInput = (definition) => {
1715
3767
  if (definition.source.kind !== "value-input") throw new Error("Invalid value input source.");
1716
3768
  const source = definition.source;
1717
- const boundary = createValueBoundary(scheduler, "projection input", source.initial, source.equality);
3769
+ let latest = source.initial;
3770
+ const boundary = createValueBoundary(scheduler, "projection input", source.initial, source.equality, () => latest, () => void 0);
1718
3771
  return {
1719
3772
  ...boundary,
1720
3773
  write: {
1721
3774
  kind: "value",
1722
3775
  set(value) {
1723
3776
  scheduler.assertIdle();
1724
- boundary.setLatest(value);
3777
+ latest = value;
1725
3778
  boundary.mark();
1726
3779
  scheduler.run();
1727
3780
  }
@@ -1731,7 +3784,7 @@ const createSourceRegistry = (scheduler) => {
1731
3784
  const collectionInput = (definition) => {
1732
3785
  if (definition.source.kind !== "collection-input") throw new Error("Invalid collection input source.");
1733
3786
  const values = new Map(definition.source.initial);
1734
- const boundary = createCollectionBoundary(scheduler, "projection collection input", () => require_advanced.mapRead(values), definition.output.equality);
3787
+ const boundary = createCollectionBoundary(scheduler, "projection collection input", () => require_advanced.mapRead(values), definition.output.equality, () => void 0);
1735
3788
  return {
1736
3789
  ...boundary,
1737
3790
  write: {
@@ -1807,103 +3860,15 @@ const createSourceRegistry = (scheduler) => {
1807
3860
  }
1808
3861
  };
1809
3862
  };
1810
- const documentValue = (definition) => {
1811
- if (definition.source.kind !== "document-value") throw new Error("Invalid document value source.");
1812
- const { document, selector } = definition.source;
1813
- const state = require_access.accessOf(document);
1814
- const address = selector?.address ?? Object.freeze([]);
1815
- const schemaNode = () => require_issue.nodeAt(state.schema, address, state.document);
1816
- const read = () => {
1817
- if (state.disposed) throw new Error("Document has been disposed.");
1818
- const node = schemaNode();
1819
- if (!node) return void 0;
1820
- return require_access.copyValue(node, selector ? require_issue.read(state.document, address, state.schema) : state.document);
1821
- };
1822
- const boundary = createValueBoundary(scheduler, "document source", read(), (left, right) => {
1823
- const node = schemaNode();
1824
- return node ? require_access.equalValue(node, left, right) : Object.is(left, right);
1825
- });
1826
- const remove = documentConnection(document).add({
1827
- matches: (commit) => !selector || affectsTarget(commit.impact, selector),
1828
- capture: (commit) => {
1829
- try {
1830
- boundary.setLatest(read());
1831
- boundary.mark({ reset: commit.impact.kind === "reset" });
1832
- } catch (cause) {
1833
- boundary.fail(cause);
1834
- }
1835
- },
1836
- dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
1837
- });
1838
- boundary.detach(remove);
1839
- return boundary;
1840
- };
1841
- const documentCollection = (definition) => {
1842
- if (definition.source.kind !== "document-collection") throw new Error("Invalid document collection source.");
1843
- const { document, selector } = definition.source;
1844
- const state = require_access.accessOf(document);
1845
- const read = (active) => {
1846
- if (state.disposed) throw new Error("Document has been disposed.");
1847
- const raw = require_access.collectionAccess({
1848
- state,
1849
- active
1850
- }, selector.address);
1851
- const snapshots = /* @__PURE__ */ new Map();
1852
- return Object.freeze({
1853
- get: (key) => {
1854
- require_advanced.assertScope(active);
1855
- if (!snapshots.has(key)) snapshots.set(key, require_access.snapshot(raw.get(key)));
1856
- return snapshots.get(key);
1857
- },
1858
- has: (key) => {
1859
- require_advanced.assertScope(active);
1860
- return raw.has(key);
1861
- },
1862
- ids: () => {
1863
- require_advanced.assertScope(active);
1864
- return raw.ids();
1865
- }
1866
- });
1867
- };
1868
- const collectionNode = () => require_issue.nodeAt(state.schema, selector.address, state.document);
1869
- const boundary = createCollectionBoundary(scheduler, "document collection source", read, (left, right) => {
1870
- const node = collectionNode();
1871
- const entry = node && require_advanced.collectionEntryNode(node);
1872
- return entry ? require_access.equalValue(entry, left, right) : Object.is(left, right);
1873
- });
1874
- const remove = documentConnection(document).add({
1875
- matches: (commit) => affectsTarget(commit.impact, selector),
1876
- capture: (commit) => {
1877
- const impact = collectionImpact(commit.impact, selector);
1878
- if (impact.kind === "reset") {
1879
- boundary.mark({ reset: true });
1880
- return;
1881
- }
1882
- const candidates = new Set([
1883
- ...impact.added,
1884
- ...impact.updated,
1885
- ...impact.removed
1886
- ]);
1887
- boundary.mark({
1888
- candidates,
1889
- orderMayChange: Boolean(impact.added.size || impact.removed.size || impact.orderChanged)
1890
- });
1891
- },
1892
- dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
1893
- });
1894
- boundary.detach(remove);
1895
- return boundary;
1896
- };
1897
3863
  const readable = (definition) => {
1898
3864
  if (definition.source.kind !== "readable") throw new Error("Invalid Readable source.");
1899
3865
  const { readable, equality } = definition.source;
1900
- let initial = readable.current();
1901
- const boundary = createValueBoundary(scheduler, "external readable", initial, equality);
3866
+ let latest = readable.current();
3867
+ const boundary = createValueBoundary(scheduler, "external readable", latest, equality, () => latest, () => void 0);
1902
3868
  const member = {
1903
3869
  boundary,
1904
3870
  receive(value, metadata) {
1905
- initial = value;
1906
- boundary.setLatest(value);
3871
+ latest = value;
1907
3872
  boundary.mark(metadata);
1908
3873
  }
1909
3874
  };
@@ -1933,9 +3898,8 @@ const createSourceRegistry = (scheduler) => {
1933
3898
  readables.delete(readable);
1934
3899
  }
1935
3900
  };
1936
- const owner = require_notification.documentReadableOwner(readable);
1937
- if (owner) detachDocument = documentConnection(owner).add({
1938
- matches: () => true,
3901
+ const owner = require_context.contextOf(readable, false)?.owner;
3902
+ if (owner) detachDocument = documents.attachRoot(owner, {
1939
3903
  capture: () => receive(false),
1940
3904
  dispose: () => {
1941
3905
  members.forEach((next) => next.boundary.fail(/* @__PURE__ */ new Error("Document has been disposed.")));
@@ -1955,11 +3919,12 @@ const createSourceRegistry = (scheduler) => {
1955
3919
  const externalValue = (definition) => {
1956
3920
  if (definition.source.kind !== "external-value") throw new Error("Invalid external value source.");
1957
3921
  const { source, equality } = definition.source;
1958
- const boundary = createValueBoundary(scheduler, "external source", source.current(), equality);
3922
+ let latest = source.current();
3923
+ const boundary = createValueBoundary(scheduler, "external source", latest, equality, () => latest, () => void 0);
1959
3924
  const member = {
1960
3925
  boundary,
1961
3926
  receive(value, metadata) {
1962
- boundary.setLatest(value);
3927
+ latest = value;
1963
3928
  boundary.mark(metadata);
1964
3929
  }
1965
3930
  };
@@ -2013,7 +3978,7 @@ const createSourceRegistry = (scheduler) => {
2013
3978
  has: (key) => current.has(key),
2014
3979
  ids: () => current.ids()
2015
3980
  });
2016
- }, definition.output.equality);
3981
+ }, definition.output.equality, () => void 0);
2017
3982
  let connection = externalCollections.get(source);
2018
3983
  if (!connection) {
2019
3984
  const members = /* @__PURE__ */ new Set();
@@ -2076,17 +4041,15 @@ const createSourceRegistry = (scheduler) => {
2076
4041
  switch (definition.source.kind) {
2077
4042
  case "value-input": return valueInput(definition);
2078
4043
  case "collection-input": return collectionInput(definition);
2079
- case "document-value": return documentValue(definition);
2080
- case "document-collection": return documentCollection(definition);
4044
+ case "document": return documents.materialize(definition);
2081
4045
  case "readable": return readable(definition);
2082
4046
  case "external-value": return externalValue(definition);
2083
4047
  case "external-collection": return externalCollection(definition);
2084
4048
  }
2085
4049
  },
2086
4050
  dispose() {
2087
- documents.forEach((connection) => connection.close());
4051
+ documents.dispose();
2088
4052
  readables.forEach((connection) => connection.close());
2089
- documents.clear();
2090
4053
  readables.clear();
2091
4054
  }
2092
4055
  };
@@ -2330,12 +4293,12 @@ const createProjectionRuntime = (options) => {
2330
4293
  });
2331
4294
  };
2332
4295
  //#endregion
2333
- exports.DocumentDisposedError = require_access.DocumentDisposedError;
2334
- exports.DocumentReentrancyError = require_access.DocumentReentrancyError;
2335
- exports.ParseError = require_access.ParseError;
4296
+ exports.DocumentDisposedError = DocumentDisposedError;
4297
+ exports.DocumentReentrancyError = DocumentReentrancyError;
4298
+ exports.ParseError = require_advanced.ParseError;
2336
4299
  exports.ProjectionDisposedError = require_advanced.ProjectionDisposedError;
2337
4300
  exports.ProjectionError = require_advanced.ProjectionError;
2338
- exports.TransactionRejected = require_access.TransactionRejected;
4301
+ exports.TransactionRejected = TransactionRejected;
2339
4302
  exports.asReadable = asReadable;
2340
4303
  exports.createDocument = createDocument;
2341
4304
  exports.createProjectionRuntime = createProjectionRuntime;
@@ -2347,10 +4310,11 @@ exports.map = require_advanced.map;
2347
4310
  exports.object = require_advanced.object;
2348
4311
  exports.observe = require_advanced.observe;
2349
4312
  exports.optional = require_advanced.optional;
2350
- exports.parse = require_access.parse;
4313
+ exports.parse = require_advanced.parse;
2351
4314
  exports.read = read;
2352
- exports.replace = require_access.replace;
2353
- exports.snapshot = require_access.snapshot;
4315
+ exports.replace = replace;
4316
+ exports.select = select;
4317
+ exports.snapshot = snapshot;
2354
4318
  exports.table = require_advanced.table;
2355
4319
  exports.tree = require_advanced.tree;
2356
4320
  exports.variant = require_advanced.variant;