doxum 0.1.7 → 0.1.9

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 (70) hide show
  1. package/README.md +217 -296
  2. package/dist/contract-BNStLbSE.d.ts +441 -0
  3. package/dist/contract-CIU5FCC1.d.cts +441 -0
  4. package/dist/driver-BlR81Dqg.js +200 -0
  5. package/dist/driver-BlR81Dqg.js.map +1 -0
  6. package/dist/driver-xOIkwrB8.cjs +241 -0
  7. package/dist/driver-xOIkwrB8.cjs.map +1 -0
  8. package/dist/index.cjs +666 -2222
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +10 -20
  11. package/dist/index.d.ts +10 -18
  12. package/dist/index.js +634 -2189
  13. package/dist/index.js.map +1 -1
  14. package/dist/{integration-CZwCwFBS.cjs → integration-C87tjRop.cjs} +4 -9
  15. package/dist/integration-C87tjRop.cjs.map +1 -0
  16. package/dist/{integration-B56u1l9V.js → integration-D5XCBLJ8.js} +2 -7
  17. package/dist/integration-D5XCBLJ8.js.map +1 -0
  18. package/dist/integration.cjs +11 -11
  19. package/dist/integration.d.cts +10 -4
  20. package/dist/integration.d.ts +10 -4
  21. package/dist/integration.js +4 -3
  22. package/dist/issue-DVaGQGeP.js +576 -0
  23. package/dist/issue-DVaGQGeP.js.map +1 -0
  24. package/dist/issue-DhrNdQNg.cjs +797 -0
  25. package/dist/issue-DhrNdQNg.cjs.map +1 -0
  26. package/dist/local-sync.cjs +67 -39
  27. package/dist/local-sync.cjs.map +1 -1
  28. package/dist/local-sync.d.cts +10 -10
  29. package/dist/local-sync.d.ts +10 -10
  30. package/dist/local-sync.js +66 -38
  31. package/dist/local-sync.js.map +1 -1
  32. package/dist/react.cjs +23 -4
  33. package/dist/react.cjs.map +1 -1
  34. package/dist/react.d.cts +8 -3
  35. package/dist/react.d.ts +8 -3
  36. package/dist/react.js +21 -6
  37. package/dist/react.js.map +1 -1
  38. package/dist/store-1Uob0Ghk.cjs +2750 -0
  39. package/dist/store-1Uob0Ghk.cjs.map +1 -0
  40. package/dist/store-CD0KdGsq.d.cts +262 -0
  41. package/dist/store-D7QH6Rzw.js +2511 -0
  42. package/dist/store-D7QH6Rzw.js.map +1 -0
  43. package/dist/store-cp5CpfCy.d.ts +262 -0
  44. package/package.json +1 -1
  45. package/skills/doxum-runtime/SKILL.md +26 -44
  46. package/skills/doxum-runtime/references/guide.en.md +95 -359
  47. package/skills/doxum-runtime/references/guide.zh-CN.md +80 -285
  48. package/skills/doxum-runtime/references/invariants.en.md +42 -165
  49. package/skills/doxum-runtime/references/invariants.zh-CN.md +34 -97
  50. package/skills/doxum-runtime/references/patterns.en.md +73 -225
  51. package/skills/doxum-runtime/references/patterns.zh-CN.md +73 -179
  52. package/dist/chunk-pbuEa-1d.js +0 -13
  53. package/dist/contract-DNZ4D53r.d.ts +0 -563
  54. package/dist/contract-j3SLGAwh.d.cts +0 -563
  55. package/dist/driver-CNxqMVFH.cjs +0 -69
  56. package/dist/driver-CNxqMVFH.cjs.map +0 -1
  57. package/dist/driver-CbzfW5MR.js +0 -46
  58. package/dist/driver-CbzfW5MR.js.map +0 -1
  59. package/dist/integration-B56u1l9V.js.map +0 -1
  60. package/dist/integration-CZwCwFBS.cjs.map +0 -1
  61. package/dist/ownership-CY0nPXGF.cjs +0 -304
  62. package/dist/ownership-CY0nPXGF.cjs.map +0 -1
  63. package/dist/ownership-CduRygE7.js +0 -245
  64. package/dist/ownership-CduRygE7.js.map +0 -1
  65. package/dist/runtime-0mOFbe_H.cjs +0 -2439
  66. package/dist/runtime-0mOFbe_H.cjs.map +0 -1
  67. package/dist/runtime-B22tuj9A.d.cts +0 -150
  68. package/dist/runtime-BFmhzPpZ.d.ts +0 -150
  69. package/dist/runtime-DF1q9Gje.js +0 -2129
  70. package/dist/runtime-DF1q9Gje.js.map +0 -1
@@ -0,0 +1,2511 @@
1
+ import { A as insert$2, C as validate, D as equal, E as afterRemove, I as valid, L as profile, M as keys, O as index, P as move$2, T as isRecord, a as compiledShape, b as contains, g as resolveLocated, i as AddressIndex, k as indexedKeys, m as resolveChild, n as fail, o as contains$1, u as nodeAt, w as isPlainObject, x as parent, y as children } from "./issue-DVaGQGeP.js";
2
+ //#region core/src/schema-value.ts
3
+ var ParseError = class extends TypeError {
4
+ issues;
5
+ constructor(issue) {
6
+ super(`${issue.message} at ${JSON.stringify(issue.address)}`);
7
+ this.name = "ParseError";
8
+ this.issues = Object.freeze([Object.freeze({
9
+ ...issue,
10
+ address: Object.freeze([...issue.address])
11
+ })]);
12
+ }
13
+ };
14
+ const issue = (address, message, code = "invalid-value") => ({
15
+ address,
16
+ message,
17
+ code
18
+ });
19
+ const checkScalar = (validator, value, address, strict) => {
20
+ if (!validator) return strict ? issue(address, "A validator is required to parse an opaque value.", "missing-validator") : void 0;
21
+ try {
22
+ const result = typeof validator === "function" ? validator(value) : validator["~standard"].validate(value);
23
+ if (result && (typeof result === "object" || typeof result === "function") && "then" in result && typeof result.then === "function") return issue(address, "Validators must be synchronous.");
24
+ if (typeof validator !== "function") {
25
+ if (!isRecord(result)) return issue(address, "Malformed validator result.");
26
+ if (Array.isArray(result.issues)) {
27
+ const first = result.issues[0];
28
+ return issue([...address, ...(first?.path ?? []).map((part) => String(typeof part === "object" ? part.key : part))], first?.message ?? "Value validation failed.");
29
+ }
30
+ if (!Object.hasOwn(result, "value")) return issue(address, "Malformed validator result.");
31
+ }
32
+ return;
33
+ } catch (error) {
34
+ return issue(address, error instanceof Error ? error.message : "Value validation failed.");
35
+ }
36
+ };
37
+ const checkKey = (validator, value, address) => {
38
+ if (typeof value !== "string") return issue(address, "Collection keys must be strings.", "invalid-key");
39
+ const failure = checkScalar(validator, value, address, false);
40
+ return failure ? {
41
+ ...failure,
42
+ code: "invalid-key"
43
+ } : void 0;
44
+ };
45
+ const checkValue = (node, value, address = [], strict = false) => {
46
+ if (node.optional && value === void 0) return void 0;
47
+ if (node.kind === "field") return checkScalar(node.validator, value, address, strict);
48
+ if (node.kind === "object" || node.kind === "variant") {
49
+ if (!isPlainObject(value)) return issue(address, "Expected an object.");
50
+ if (node.kind === "variant") {
51
+ const tag = value[node.tag];
52
+ if (!Object.hasOwn(value, node.tag) || typeof tag !== "string" || !Object.hasOwn(node.variants, tag)) return issue([...address, node.tag], "Unknown variant tag.");
53
+ }
54
+ const { members } = compiledShape(node, value);
55
+ for (const key of Reflect.ownKeys(value)) {
56
+ if (node.kind === "variant" && key === node.tag) continue;
57
+ if (typeof key !== "string" || !members.has(key)) return issue([...address, String(key)], "Property is not declared in the object schema.");
58
+ }
59
+ for (const [key, member] of members) {
60
+ const child = member.node;
61
+ if (!Object.prototype.hasOwnProperty.call(value, key) && !child.optional) return issue([...address, key], "Required property is missing.");
62
+ const failure = checkValue(child, value[key], [...address, key], strict);
63
+ if (failure) return failure;
64
+ }
65
+ return;
66
+ }
67
+ if (node.kind === "table" || node.kind === "map") {
68
+ if (!isPlainObject(value)) return issue(address, "Expected a collection object.");
69
+ const entries = node.kind === "table" ? value.byId : value;
70
+ if (!isPlainObject(entries)) return issue(address, "Expected a byId object.");
71
+ if (node.kind === "table") {
72
+ if (!Array.isArray(value.ids) || value.ids.length !== Object.keys(entries).length) return issue(address, "Table ids and entries must agree.");
73
+ const seen = /* @__PURE__ */ new Set();
74
+ for (const id of value.ids) {
75
+ const failure = checkKey(node.key, id, address);
76
+ if (failure) return failure;
77
+ if (seen.has(id) || !Object.prototype.hasOwnProperty.call(entries, id)) return issue(address, "Table ids must be unique and reference existing entries.");
78
+ seen.add(id);
79
+ }
80
+ }
81
+ for (const id of Object.keys(entries)) {
82
+ const failure = checkKey(node.key, id, [...address, id]) ?? checkValue(node.value, entries[id], [...address, id], strict);
83
+ if (failure) return failure;
84
+ }
85
+ return;
86
+ }
87
+ if (node.kind === "list") {
88
+ if (!Array.isArray(value)) return issue(address, "Expected a list.");
89
+ const seen = /* @__PURE__ */ new Set();
90
+ const order = keys(value, node.keyOf);
91
+ for (let i = 0; i < value.length; i++) {
92
+ const failure = checkValue(node.value, value[i], [...address, String(i)], strict);
93
+ if (failure) return failure;
94
+ let key;
95
+ try {
96
+ key = order.at(i);
97
+ } catch {
98
+ return issue(address, "List keyOf failed.");
99
+ }
100
+ if (typeof key !== "string" || seen.has(key)) return issue(address, "List keys must be unique strings.", "invalid-key");
101
+ seen.add(key);
102
+ }
103
+ return;
104
+ }
105
+ if (!validate(value)) return issue(address, "Invalid tree structure.", "invalid-tree");
106
+ if (isRecord(value) && isRecord(value.nodes)) for (const id of Object.keys(value.nodes)) {
107
+ const entry = value.nodes[id];
108
+ if (isRecord(entry) && Object.prototype.hasOwnProperty.call(entry, "value")) {
109
+ const failure = checkValue(node.value, entry.value, [...address, id], strict);
110
+ if (failure) return failure;
111
+ }
112
+ }
113
+ };
114
+ /** Copy editable structure; atomic payloads retain their immutable ownership contract. */
115
+ const copyValue = (node, value) => {
116
+ if (value === void 0 || node.kind === "field") return value;
117
+ profile.copy.structure();
118
+ if ((node.kind === "object" || node.kind === "variant") && isRecord(value)) {
119
+ const result = Object.create(Object.getPrototypeOf(value));
120
+ const { members } = compiledShape(node, value);
121
+ for (const key of Object.getOwnPropertyNames(value)) Object.defineProperty(result, key, {
122
+ value: node.kind === "variant" && key === node.tag ? value[key] : copyValue(members.get(key).node, value[key]),
123
+ writable: true,
124
+ enumerable: Object.getOwnPropertyDescriptor(value, key)?.enumerable,
125
+ configurable: true
126
+ });
127
+ return result;
128
+ }
129
+ if ((node.kind === "table" || node.kind === "map") && isRecord(value)) {
130
+ const entries = node.kind === "table" ? value.byId : value;
131
+ const result = Object.fromEntries(Object.keys(entries).map((id) => [id, copyValue(node.value, entries[id])]));
132
+ return node.kind === "table" ? {
133
+ ids: [...value.ids],
134
+ byId: result
135
+ } : result;
136
+ }
137
+ if (node.kind === "list") return [...value];
138
+ if (node.kind === "tree" && isRecord(value)) {
139
+ const entries = value.nodes;
140
+ return {
141
+ ...value,
142
+ nodes: Object.fromEntries(Object.entries(entries).map(([id, n]) => [id, {
143
+ ...n,
144
+ children: [...n.children]
145
+ }]))
146
+ };
147
+ }
148
+ return value;
149
+ };
150
+ const equalValue = (node, left, right) => {
151
+ if (Object.is(left, right)) return true;
152
+ if (node.kind === "field" || left === void 0 || right === void 0) return false;
153
+ if ((node.kind === "object" || node.kind === "variant") && isRecord(left) && isRecord(right)) {
154
+ if (node.kind === "variant" && left[node.tag] !== right[node.tag]) return false;
155
+ for (const [key, member] of compiledShape(node, left).members) {
156
+ if (Object.hasOwn(left, key) !== Object.hasOwn(right, key)) return false;
157
+ if (!equalValue(member.node, left[key], right[key])) return false;
158
+ }
159
+ return true;
160
+ }
161
+ if (node.kind === "list" && Array.isArray(left) && Array.isArray(right)) return left.length === right.length && left.every((v, i) => equalValue(node.value, v, right[i]));
162
+ if (node.kind === "table" && isRecord(left) && isRecord(right)) return equal(left.ids, right.ids) && equalValue({
163
+ kind: "map",
164
+ value: node.value
165
+ }, left.byId, right.byId);
166
+ if (node.kind === "tree" && isRecord(left) && isRecord(right)) {
167
+ if (left.rootId !== right.rootId) return false;
168
+ const a = left.nodes, b = right.nodes;
169
+ return Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((id) => Object.hasOwn(b, id) && a[id].parentId === b[id].parentId && equal(a[id].children, b[id].children) && Object.hasOwn(a[id], "value") === Object.hasOwn(b[id], "value") && equalValue(node.value, a[id].value, b[id].value));
170
+ }
171
+ if (node.kind === "map" && isRecord(left) && isRecord(right)) {
172
+ const keys = Object.keys(left);
173
+ return keys.length === Object.keys(right).length && keys.every((key) => Object.hasOwn(right, key) && equalValue(node.value, left[key], right[key]));
174
+ }
175
+ return false;
176
+ };
177
+ const parse = (node, input) => {
178
+ const valueNode = node;
179
+ const failure = checkValue(valueNode, input, [], true);
180
+ if (failure) throw new ParseError(failure);
181
+ return copyValue(valueNode, input);
182
+ };
183
+ //#endregion
184
+ //#region core/src/schema.ts
185
+ const node$1 = (value) => Object.freeze(value);
186
+ const field = (validator) => node$1({
187
+ kind: "field",
188
+ ...validator ? { validator } : {}
189
+ });
190
+ const optional = (value) => {
191
+ if (![
192
+ "field",
193
+ "variant",
194
+ "map",
195
+ "list",
196
+ "tree"
197
+ ].includes(value.kind)) throw new TypeError("Only field, variant, map, list and tree nodes support optional presence.");
198
+ return node$1({
199
+ ...value,
200
+ optional: true
201
+ });
202
+ };
203
+ const object = (shape) => node$1({
204
+ kind: "object",
205
+ shape: Object.freeze({ ...shape })
206
+ });
207
+ const variant = (tag, variants) => node$1({
208
+ kind: "variant",
209
+ tag,
210
+ variants: Object.freeze({ ...variants })
211
+ });
212
+ const table = (value, options) => node$1({
213
+ kind: "table",
214
+ value,
215
+ ...options
216
+ });
217
+ const map = (value, options) => node$1({
218
+ kind: "map",
219
+ value,
220
+ ...options
221
+ });
222
+ const list = (value, config) => node$1({
223
+ kind: "list",
224
+ keyOf: config.keyOf,
225
+ value
226
+ });
227
+ const tree = (value) => node$1({
228
+ kind: "tree",
229
+ value
230
+ });
231
+ const paths = /* @__PURE__ */ new WeakMap();
232
+ const pathProxy = (nodes, address, owner) => {
233
+ const proxy = new Proxy({}, { get: (_target, property) => {
234
+ if (typeof property !== "string") return void 0;
235
+ if (nodes.every((node) => node.kind === "table" || node.kind === "map") && property === "item") return (id) => {
236
+ if (typeof id !== "string") throw new TypeError("Collection keys must be strings.");
237
+ for (const node of nodes) if (node.kind === "table" || node.kind === "map") {
238
+ const invalid = checkKey(node.key, id, [...address, id]);
239
+ if (invalid) throw new TypeError(invalid.message);
240
+ }
241
+ return pathProxy(nodes.map((node) => node.value), [...address, id], owner);
242
+ };
243
+ const children = nodes.flatMap((node) => {
244
+ if (node.kind === "object") return Object.hasOwn(node.shape, property) ? [node.shape[property]] : [];
245
+ if (node.kind !== "variant") return [];
246
+ if (property === node.tag) return [field()];
247
+ return Object.values(node.variants).flatMap((branch) => Object.hasOwn(branch.shape, property) ? [branch.shape[property]] : []);
248
+ });
249
+ if (!children.length) throw new TypeError(`Invalid schema path segment: ${property}`);
250
+ return pathProxy(children, [...address, property], owner);
251
+ } });
252
+ paths.set(proxy, {
253
+ nodes,
254
+ address,
255
+ owner
256
+ });
257
+ return proxy;
258
+ };
259
+ const compilePath = (owner, kind, pick) => {
260
+ const scope = {};
261
+ const value = pick(pathProxy([owner], [], scope));
262
+ const selected = typeof value === "object" && value !== null ? paths.get(value) : void 0;
263
+ if (!selected || selected.owner !== scope) throw new TypeError("Selector must return a path from its callback.");
264
+ if (kind === "collection" && !selected.nodes.every((node) => node.kind === "table" || node.kind === "map")) throw new TypeError("Collection selectors require a table or map path.");
265
+ return Object.freeze({
266
+ kind,
267
+ schema: owner,
268
+ address: Object.freeze([...selected.address])
269
+ });
270
+ };
271
+ //#endregion
272
+ //#region core/src/mutation/operations/table.ts
273
+ function create(session, container, entries, position) {
274
+ const { at, node, value } = container;
275
+ if (node.kind !== "table") return fail(at, "invalid-collection", "Expected a table.");
276
+ const table = value;
277
+ if (!valid(table.ids, position)) return fail(at, "invalid-anchor", "Unknown table anchor.");
278
+ const ids = /* @__PURE__ */ new Set();
279
+ for (const entry of entries) {
280
+ if (Object.hasOwn(table.byId, entry.id) || ids.has(entry.id)) return fail(at, "duplicate-entity", "Table key already exists.");
281
+ ids.add(entry.id);
282
+ }
283
+ if (!entries.length) return;
284
+ const index$1 = index(table.ids, position);
285
+ for (const entry of entries) session.writeMember(container, entry.id, entry.value, "set");
286
+ const length = table.ids.length;
287
+ table.ids.length += entries.length;
288
+ table.ids.copyWithin(index$1 + entries.length, index$1, length);
289
+ for (let i = 0; i < entries.length; i++) table.ids[index$1 + i] = entries[i].id;
290
+ }
291
+ function remove$2(session, container, ids) {
292
+ const { at, node, value } = container;
293
+ if (node.kind !== "table") return fail(at, "invalid-collection", "Expected a table.");
294
+ const table = value;
295
+ for (const id of ids) if (!Object.hasOwn(table.byId, id)) return fail(at, "missing-entity", "Table key does not exist.");
296
+ if (!ids.length) return;
297
+ const removed = new Set(ids);
298
+ for (const id of removed) session.writeMember(container, id, void 0, "remove");
299
+ table.ids = table.ids.filter((id) => !removed.has(id));
300
+ }
301
+ //#endregion
302
+ //#region core/src/mutation/operations/list.ts
303
+ function insert$1(session, container, value, position) {
304
+ const { at, node, value: current } = container;
305
+ if (node.kind !== "list") return fail(at, "invalid-list-key", "Expected a list.");
306
+ session.validate(node.value, value, at);
307
+ const id = node.keyOf(value);
308
+ if (typeof id !== "string") return fail(at, "invalid-list-key", "List key must be a string.");
309
+ const items = current, keys$2 = keys(items, node.keyOf);
310
+ if (keys$2.index(id) >= 0) return fail(at, "duplicate-list-item", "List key already exists.");
311
+ if (!valid(keys$2, position)) return fail(at, "invalid-anchor", "Unknown list anchor.");
312
+ session.recorder.order(container);
313
+ session.recorder.member(container, id, session.definition(container, id), -1);
314
+ insert$2(items, index(keys$2, position), value);
315
+ session.invalidate();
316
+ }
317
+ function set$1(session, container, id, value) {
318
+ const { at, node, value: items } = container;
319
+ if (node.kind !== "list") return fail(at, "invalid-list-key", "Expected a list.");
320
+ const index = indexedKeys(items, node.keyOf).index(id);
321
+ if (index < 0) return fail(at, "missing-list-item", "List key does not exist.");
322
+ session.writeMember(container, id, value, "set", index);
323
+ }
324
+ function remove$1(session, container, id) {
325
+ const { at, node, value } = container;
326
+ if (node.kind !== "list") return fail(at, "missing-list-item", "List key does not exist.");
327
+ const index = keys(value, node.keyOf).index(id);
328
+ if (index < 0) return fail(at, "missing-list-item", "List key does not exist.");
329
+ session.writeMember(container, id, void 0, "remove", index);
330
+ }
331
+ //#endregion
332
+ //#region core/src/mutation/operations/tree.ts
333
+ const node = (tree, id) => Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : void 0;
334
+ const positionIndex = (index, length, at) => {
335
+ if (index !== void 0 && (!Number.isInteger(index) || index < 0)) return fail(at, "invalid-tree-index", "Tree index must be nonnegative.");
336
+ return Math.min(index ?? length, length);
337
+ };
338
+ const insert = (session, container, id, value, position) => {
339
+ const { at, node: schema, value: tree } = container;
340
+ if (typeof id !== "string") return fail(at, "invalid-key", "Tree keys must be strings.");
341
+ if (contains(tree, id)) return fail(at, "duplicate-tree-node", "Tree node already exists.");
342
+ const parentId = position?.parentId;
343
+ const parent = parentId === void 0 ? void 0 : node(tree, parentId);
344
+ if (tree.rootId !== void 0 && !parent || tree.rootId === void 0 && parentId !== void 0) return fail(at, "missing-tree-parent", "A nonempty tree requires an existing parent.");
345
+ const index = positionIndex(position?.index, parent?.children.length ?? 0, at);
346
+ session.validate(schema.value, value, at.concat(id));
347
+ session.recorder.tree(container, id);
348
+ if (parentId !== void 0) session.recorder.tree(container, parentId);
349
+ Object.defineProperty(tree.nodes, id, {
350
+ value: {
351
+ ...parentId === void 0 ? {} : { parentId },
352
+ children: [],
353
+ value
354
+ },
355
+ writable: true,
356
+ configurable: true,
357
+ enumerable: true
358
+ });
359
+ if (parent) parent.children.splice(index, 0, id);
360
+ else tree.rootId = id;
361
+ session.invalidate();
362
+ };
363
+ const set = (session, container, id, value) => {
364
+ const { at, node: schema, value: tree } = container;
365
+ const entry = node(tree, id);
366
+ if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
367
+ if (Object.is(entry.value, value) && Object.hasOwn(entry, "value")) return;
368
+ session.validate(schema.value, value, at.concat(id));
369
+ session.recorder.tree(container, id);
370
+ entry.value = value;
371
+ };
372
+ const remove = (session, container, id) => {
373
+ const { at, value: tree } = container;
374
+ const entry = node(tree, id);
375
+ if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
376
+ const ids = [], stack = [id];
377
+ while (stack.length) {
378
+ const current = stack.pop();
379
+ ids.push(current);
380
+ for (const child of tree.nodes[current].children) stack.push(child);
381
+ }
382
+ for (const key of ids) session.recorder.tree(container, key);
383
+ if (entry.parentId !== void 0) session.recorder.tree(container, entry.parentId);
384
+ if (entry.parentId === void 0) delete tree.rootId;
385
+ else {
386
+ const children = tree.nodes[entry.parentId].children;
387
+ children.splice(children.indexOf(id), 1);
388
+ }
389
+ for (const key of ids) delete tree.nodes[key];
390
+ session.invalidate();
391
+ };
392
+ const move$1 = (session, container, id, position) => {
393
+ const { at, value: tree } = container;
394
+ const entry = node(tree, id), parentId = position?.parentId;
395
+ if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
396
+ if (entry.parentId === void 0 || parentId === void 0 || !contains(tree, parentId)) return fail(at, "missing-tree-parent", "Moving requires a non-root node and an existing parent.");
397
+ for (let ancestor = parentId; ancestor !== void 0; ancestor = tree.nodes[ancestor].parentId) if (ancestor === id) return fail(at, "tree-cycle", "Cannot move a node under its descendant.");
398
+ const old = tree.nodes[entry.parentId], next = tree.nodes[parentId];
399
+ const index = positionIndex(position?.index, next.children.length - (old === next ? 1 : 0), at);
400
+ const previous = old.children.indexOf(id);
401
+ if (old === next && previous === index) return;
402
+ session.recorder.tree(container, id);
403
+ session.recorder.tree(container, entry.parentId);
404
+ session.recorder.tree(container, parentId);
405
+ old.children.splice(previous, 1);
406
+ next.children.splice(index, 0, id);
407
+ entry.parentId = parentId;
408
+ session.invalidate();
409
+ };
410
+ //#endregion
411
+ //#region core/src/mutation/operations/order.ts
412
+ function move(session, container, id, position) {
413
+ const { at, node, value } = container;
414
+ if (node.kind !== "table" && node.kind !== "list") return fail(at, "invalid-collection", "Expected an ordered container.");
415
+ const items = node.kind === "table" ? value.ids : value;
416
+ const keys$1 = node.kind === "table" ? items : keys(items, node.keyOf);
417
+ const index = node.kind === "table" ? items.indexOf(id) : keys$1.index(id);
418
+ if (index < 0) return fail(at, "missing-entity", "Ordered key does not exist.");
419
+ if (!valid(keys$1, position)) return fail(at, "invalid-anchor", "Unknown order anchor.");
420
+ const next = afterRemove(keys$1, index, position);
421
+ if (next === index) return;
422
+ session.recorder.order(container);
423
+ move$2(items, index, next);
424
+ session.invalidate();
425
+ }
426
+ //#endregion
427
+ //#region core/src/access/scope.ts
428
+ const locations = /* @__PURE__ */ new WeakMap();
429
+ const locationOf = (value) => locations.get(value);
430
+ const addressOf = (target) => {
431
+ if (target.at) return target.at;
432
+ profile.access("addresses");
433
+ return target.at = addressOf(target.parent).concat(target.key);
434
+ };
435
+ const assertActive = (context) => {
436
+ if (context.active && !context.active()) throw new Error("Document access scope has expired.");
437
+ };
438
+ const collect = (context, at, kind = "value", id) => {
439
+ context.dependencies?.record(kind === "value" ? {
440
+ kind,
441
+ at
442
+ } : {
443
+ kind,
444
+ at,
445
+ ...id === void 0 ? {} : { id }
446
+ });
447
+ };
448
+ const snapshot = (value) => {
449
+ const location = value && typeof value === "object" ? locationOf(value) : void 0;
450
+ if (!location) return value;
451
+ const { context } = location;
452
+ const at = addressOf(location.target);
453
+ profile.access("snapshots");
454
+ collect(context, at);
455
+ const resolved = resolve(context, location.target);
456
+ if (!resolved.node) throw new Error("The selected schema address no longer exists.");
457
+ return copyValue(resolved.node, resolved.value);
458
+ };
459
+ /** TypeScript cannot express asymmetric index signatures for nested collection tools. */
460
+ const assign = (container, key, value) => {
461
+ if (!locationOf(container)) throw new TypeError("assign requires scoped document access.");
462
+ if (typeof key !== "string") throw new TypeError("Document keys must be strings.");
463
+ Reflect.set(container, key, value);
464
+ };
465
+ const resolve = (context, target) => {
466
+ assertActive(context);
467
+ const generation = context.session?.generation ?? 0;
468
+ if (target.generation !== generation) {
469
+ profile.access("resolutions");
470
+ if (!target.parent && !target.at.length) {
471
+ target.node = context.state.schema;
472
+ target.value = context.state.document;
473
+ } else {
474
+ const parent = target.parent && resolve(context, target.parent);
475
+ const location = parent ? resolveChild(parent.node, parent.value, target.key) : resolveLocated(context.state.schema, context.state.document, target.at);
476
+ target.node = location?.node ?? nodeAt(context.state.schema, addressOf(target), context.state.document);
477
+ target.value = location && Object.hasOwn(location.parent, location.key) ? location.parent[location.key] : void 0;
478
+ }
479
+ target.generation = generation;
480
+ }
481
+ return target;
482
+ };
483
+ const createAccess = (context, initial = []) => {
484
+ profile.access("scopes");
485
+ const mutable = () => {
486
+ assertActive(context);
487
+ if (!context.session) throw new TypeError("Cannot modify read-only document access.");
488
+ return context.session;
489
+ };
490
+ const writableContainer = (target, session) => {
491
+ if (target.container?.generation === session.generation && "layout" in target.container) return target.container;
492
+ return target.container = session.bind(addressOf(target), target.node, target.value);
493
+ };
494
+ const childAt = (target, key) => {
495
+ profile.access("addresses");
496
+ return addressOf(target).concat(key);
497
+ };
498
+ const access = (target) => {
499
+ if (target.proxy) return target.proxy;
500
+ const proxy = new Proxy(target, handler);
501
+ profile.access("proxies");
502
+ locations.set(proxy, {
503
+ context,
504
+ target
505
+ });
506
+ target.proxy = proxy;
507
+ return proxy;
508
+ };
509
+ const createTarget = (at, parent, key) => ({
510
+ at,
511
+ parent,
512
+ key,
513
+ node: void 0,
514
+ value: void 0,
515
+ generation: -1,
516
+ children: void 0,
517
+ proxy: void 0
518
+ });
519
+ const childAccess = (target, member, node, value) => {
520
+ const key = typeof member === "string" ? member : member.key;
521
+ if (node.kind === "field" || value === void 0) {
522
+ if (context.dependencies) collect(context, childAt(target, key));
523
+ return value;
524
+ }
525
+ let children = target.children;
526
+ let child;
527
+ if (typeof member !== "string") {
528
+ if (children?.kind !== "fixed" || children.schema !== target.node) children = {
529
+ kind: "fixed",
530
+ schema: target.node,
531
+ slots: new Array(compiledShape(target.node, target.value).members.size)
532
+ };
533
+ child = children.slots[member.slot] ??= createTarget(void 0, target, key);
534
+ } else {
535
+ if (children?.kind !== "dynamic") children = {
536
+ kind: "dynamic",
537
+ keys: /* @__PURE__ */ new Map()
538
+ };
539
+ const retained = children.keys.get(key);
540
+ child = retained ?? createTarget(void 0, target, key);
541
+ if (!retained) children.keys.set(key, child);
542
+ }
543
+ target.children = children;
544
+ if (child.generation === target.generation) return child.proxy;
545
+ child.node = node;
546
+ child.value = value;
547
+ child.generation = target.generation;
548
+ return access(child);
549
+ };
550
+ const collectionValue = (target, node) => {
551
+ const current = resolve(context, target);
552
+ if (current.node !== node || current.value === void 0) throw new TypeError("The collection method belongs to a replaced schema branch.");
553
+ return current.value;
554
+ };
555
+ const writableCollection = (target, node) => {
556
+ const session = mutable();
557
+ collectionValue(target, node);
558
+ return writableContainer(target, session);
559
+ };
560
+ const writableTree = (target, node) => {
561
+ const session = mutable();
562
+ collectionValue(target, node);
563
+ if (target.container?.generation === session.generation && !("layout" in target.container)) return target.container;
564
+ return target.container = session.bindTree(addressOf(target), target.node, target.value);
565
+ };
566
+ const orderedMethod = (target, property, node) => {
567
+ const at = addressOf(target);
568
+ if (property === "ids") return () => {
569
+ const current = collectionValue(target, node);
570
+ collect(context, at, "collection");
571
+ return node.kind === "table" ? [...current.ids] : current.map(node.keyOf);
572
+ };
573
+ if (property === "has") return (id) => {
574
+ const current = collectionValue(target, node);
575
+ collect(context, at, "collection", id);
576
+ return node.kind === "table" ? Object.hasOwn(current.byId, id) : indexedKeys(current, node.keyOf).index(id) >= 0;
577
+ };
578
+ if (property === "get") return (id) => {
579
+ const current = collectionValue(target, node);
580
+ collect(context, at, "collection", id);
581
+ const location = resolveChild(node, current, id);
582
+ if (!location || !Object.hasOwn(location.parent, location.key)) return void 0;
583
+ return childAccess(target, id, location.node, location.parent[location.key]);
584
+ };
585
+ if (property === "move") return (id, position) => {
586
+ const writable = writableCollection(target, node);
587
+ move(context.session, writable, id, position);
588
+ };
589
+ };
590
+ const tableMethod = (target, property, node) => {
591
+ if (property === "create") return (input, position) => {
592
+ const writable = writableCollection(target, node);
593
+ create(context.session, writable, Array.isArray(input) ? input : [input], position);
594
+ };
595
+ if (property === "remove") return (ids) => {
596
+ const writable = writableCollection(target, node);
597
+ remove$2(context.session, writable, typeof ids === "string" ? [ids] : ids);
598
+ };
599
+ return orderedMethod(target, property, node);
600
+ };
601
+ const listMethod = (target, property, node) => {
602
+ const at = addressOf(target);
603
+ if (property === "insert") return (value, position) => {
604
+ const writable = writableCollection(target, node);
605
+ insert$1(context.session, writable, value, position);
606
+ };
607
+ if (property === "remove") return (id) => {
608
+ const writable = writableCollection(target, node);
609
+ remove$1(context.session, writable, id);
610
+ };
611
+ if (property === "set") return (id, value) => {
612
+ const writable = writableCollection(target, node);
613
+ set$1(context.session, writable, id, value);
614
+ };
615
+ if (property === "replace") return (value) => {
616
+ const session = mutable();
617
+ collectionValue(target, node);
618
+ session.replace(at, value);
619
+ };
620
+ return orderedMethod(target, property, node);
621
+ };
622
+ const treeMethod = (target, property, node) => {
623
+ const at = addressOf(target);
624
+ if (property === "insert") return (id, value, position) => {
625
+ const writable = writableTree(target, node);
626
+ insert(context.session, writable, id, value, position);
627
+ };
628
+ if (property === "set") return (id, value) => {
629
+ const writable = writableTree(target, node);
630
+ set(context.session, writable, id, value);
631
+ };
632
+ if (property === "remove") return (id) => {
633
+ const writable = writableTree(target, node);
634
+ remove(context.session, writable, id);
635
+ };
636
+ if (property === "move") return (id, position) => {
637
+ const writable = writableTree(target, node);
638
+ move$1(context.session, writable, id, position);
639
+ };
640
+ if (property === "replace") return (value) => {
641
+ const session = mutable();
642
+ collectionValue(target, node);
643
+ session.replace(at, value);
644
+ };
645
+ const current = () => {
646
+ collect(context, at);
647
+ return collectionValue(target, node);
648
+ };
649
+ if (property === "rootId") return () => current().rootId;
650
+ if (property === "get") return (id) => {
651
+ const value = current();
652
+ return contains(value, id) ? value.nodes[id].value : void 0;
653
+ };
654
+ if (property === "has") return (id) => contains(current(), id);
655
+ if (property === "parent") return (id) => parent(current(), id);
656
+ if (property === "children") return (id) => {
657
+ const children$1 = children(current(), id);
658
+ return children$1 ? [...children$1] : void 0;
659
+ };
660
+ };
661
+ const handler = {
662
+ get: (target, property) => {
663
+ const { node, value } = resolve(context, target);
664
+ if (!node || value === void 0) return void 0;
665
+ if (typeof property !== "string") return void 0;
666
+ if (node.kind === "map") {
667
+ if (context.dependencies) collect(context, addressOf(target), "collection", property);
668
+ if (!Object.hasOwn(value, property)) return void 0;
669
+ if (node.value.kind === "field") {
670
+ if (context.dependencies) collect(context, childAt(target, property));
671
+ return value[property];
672
+ }
673
+ return childAccess(target, property, node.value, value[property]);
674
+ }
675
+ if (node.kind === "table") return tableMethod(target, property, node);
676
+ if (node.kind === "list") return listMethod(target, property, node);
677
+ if (node.kind === "tree") return treeMethod(target, property, node);
678
+ if (node.kind === "variant" && property === node.tag) {
679
+ collect(context, childAt(target, property));
680
+ return value[property];
681
+ }
682
+ const member = compiledShape(node, value)?.members.get(property);
683
+ if (!member) return void 0;
684
+ if (member.field) {
685
+ if (context.dependencies) collect(context, childAt(target, property));
686
+ return value[property];
687
+ }
688
+ return childAccess(target, node.kind === "object" ? member : property, member.node, value[property]);
689
+ },
690
+ set: (target, property, value) => {
691
+ const session = mutable(), { node } = resolve(context, target);
692
+ if (typeof property !== "string" || !node || node.kind !== "object" && node.kind !== "variant" && node.kind !== "map") throw new TypeError("Invalid structural assignment.");
693
+ if (node.kind === "variant" && node.tag === property) throw new TypeError("Variant discriminants are read-only.");
694
+ session.assignMember(writableContainer(target, session), property, value);
695
+ return true;
696
+ },
697
+ deleteProperty: (target, property) => {
698
+ const session = mutable(), { node } = resolve(context, target);
699
+ if (typeof property !== "string" || node?.kind === "variant" && node.tag === property) throw new TypeError("Invalid structural deletion.");
700
+ session.removeMember(writableContainer(target, session), property);
701
+ return true;
702
+ },
703
+ has: (target, property) => {
704
+ const { node, value } = resolve(context, target);
705
+ if (typeof property !== "string") return false;
706
+ collect(context, addressOf(target), node?.kind === "map" ? "collection" : "value", node?.kind === "map" ? property : void 0);
707
+ return value !== void 0 && Object.hasOwn(value, property);
708
+ },
709
+ ownKeys: (target) => {
710
+ const { value } = resolve(context, target);
711
+ if (context.dependencies) collect(context, addressOf(target), "collection");
712
+ return value && typeof value === "object" ? Object.keys(value) : [];
713
+ },
714
+ getOwnPropertyDescriptor: (target, property) => {
715
+ const { value } = resolve(context, target);
716
+ return value && Object.hasOwn(value, property) ? {
717
+ enumerable: true,
718
+ configurable: true
719
+ } : void 0;
720
+ },
721
+ defineProperty: () => {
722
+ assertActive(context);
723
+ throw new TypeError("Use schema assignments.");
724
+ },
725
+ setPrototypeOf: () => {
726
+ assertActive(context);
727
+ throw new TypeError("Document prototypes are immutable.");
728
+ },
729
+ preventExtensions: () => {
730
+ assertActive(context);
731
+ throw new TypeError("Document access cannot be frozen.");
732
+ }
733
+ };
734
+ const root = resolve(context, createTarget(initial, void 0, ""));
735
+ if (!root.node || root.node.kind === "field" || root.value === void 0) {
736
+ if (context.dependencies) collect(context, initial);
737
+ return root.value;
738
+ }
739
+ return access(root);
740
+ };
741
+ const collectionAccess = (context, at) => {
742
+ const value = createAccess(context, at);
743
+ if (value === void 0) return {
744
+ get: () => {
745
+ assertActive(context);
746
+ },
747
+ has: () => {
748
+ assertActive(context);
749
+ return false;
750
+ },
751
+ ids: () => {
752
+ assertActive(context);
753
+ return [];
754
+ }
755
+ };
756
+ if (locations.get(value).target.node?.kind === "table") return value;
757
+ const map = value;
758
+ return {
759
+ get: (id) => map[id],
760
+ has: (id) => id in map,
761
+ ids: () => Object.keys(map)
762
+ };
763
+ };
764
+ //#endregion
765
+ //#region core/src/impact-target.ts
766
+ const address = (target) => "at" in target ? target.at : target.address;
767
+ const id = (target) => target.kind === "collection" && "id" in target ? target.id : void 0;
768
+ const belongs = (target, schema) => !("schema" in target) || target.schema === schema;
769
+ const same = (left, right) => {
770
+ if (left.kind !== right.kind) return false;
771
+ if ("schema" in left && "schema" in right && left.schema !== right.schema) return false;
772
+ const leftAddress = address(left);
773
+ const rightAddress = address(right);
774
+ if (leftAddress.length !== rightAddress.length) return false;
775
+ for (let index = 0; index < leftAddress.length; index += 1) if (leftAddress[index] !== rightAddress[index]) return false;
776
+ return left.kind !== "collection" || id(left) === id(right);
777
+ };
778
+ const indexedAddress = (target) => {
779
+ const key = id(target);
780
+ return key === void 0 ? address(target) : [...address(target), key];
781
+ };
782
+ const affected = (target, values, orders) => {
783
+ const at = indexedAddress(target);
784
+ return id(target) !== void 0 ? values.hasAncestor(at) : values.overlaps(at) || orders.hasDescendant(at);
785
+ };
786
+ /** Targets are classified once; commit matching never builds a reverse impact index. */
787
+ var SubscriptionIndex = class {
788
+ ordinary = new AddressIndex();
789
+ membership = new AddressIndex();
790
+ constructor(schema) {
791
+ this.schema = schema;
792
+ }
793
+ add(target, value) {
794
+ if (belongs(target, this.schema)) (id(target) === void 0 ? this.ordinary : this.membership).add(indexedAddress(target), value);
795
+ }
796
+ delete(target, value) {
797
+ if (belongs(target, this.schema)) (id(target) === void 0 ? this.ordinary : this.membership).delete(indexedAddress(target), value);
798
+ }
799
+ collect(changes, visit) {
800
+ const values = this.ordinary.query(visit);
801
+ const members = this.membership.query(visit, "descendants");
802
+ const orders = this.ordinary.query(visit, "ancestors");
803
+ for (const change of changes.changes) if (change.kind === "reset") {
804
+ values([]);
805
+ members([]);
806
+ } else if (change.kind === "members") {
807
+ values(change.at, change.members);
808
+ members(change.at, change.members);
809
+ if (change.order) orders(change.at);
810
+ } else {
811
+ values(change.at);
812
+ members(change.at);
813
+ }
814
+ }
815
+ clear() {
816
+ this.ordinary.clear();
817
+ this.membership.clear();
818
+ }
819
+ };
820
+ //#endregion
821
+ //#region core/src/impact.ts
822
+ const queries = /* @__PURE__ */ new WeakMap();
823
+ const affectsTarget = (impact, value) => queries.get(impact).affects(value);
824
+ const collectionImpact = (impact, selector) => queries.get(impact).collection(selector);
825
+ const createImpact = (schema, changes) => {
826
+ const reset = changes.changes.some((change) => change.kind === "reset");
827
+ let values;
828
+ let orders;
829
+ const index = () => {
830
+ if (!values) {
831
+ profile.impact.index();
832
+ values = new AddressIndex();
833
+ orders = new AddressIndex();
834
+ for (const change of changes.changes) if (change.kind === "members") {
835
+ for (const member of change.members) values.add(change.at, true, member.key);
836
+ if (change.order) orders.add(change.at, true);
837
+ } else if (change.kind === "tree") values.add(change.at, true);
838
+ }
839
+ return values;
840
+ };
841
+ const cache = /* @__PURE__ */ new Map();
842
+ const implementation = {
843
+ affects(value) {
844
+ profile.impact.affects();
845
+ if (!belongs(value, schema)) return false;
846
+ if (reset) return true;
847
+ return affected(value, index(), orders);
848
+ },
849
+ collection(selector) {
850
+ if (selector.schema !== schema) throw new TypeError("Collection belongs to another root model.");
851
+ const key = JSON.stringify(selector.address), cached = cache.get(key);
852
+ if (cached) return cached;
853
+ if (reset) {
854
+ const result = { kind: "reset" };
855
+ cache.set(key, result);
856
+ return result;
857
+ }
858
+ const at = selector.address;
859
+ const added = /* @__PURE__ */ new Set(), removed = /* @__PURE__ */ new Set(), updated = /* @__PURE__ */ new Set();
860
+ let orderChanged = false;
861
+ for (const change of changes.changes) {
862
+ if (change.kind === "reset") continue;
863
+ if (change.kind === "members") {
864
+ if (change.at.length < at.length && contains$1(change.at, at)) {
865
+ if (change.members.some((member) => member.key === at[change.at.length])) {
866
+ const result = { kind: "reset" };
867
+ cache.set(key, result);
868
+ return result;
869
+ }
870
+ } else if (contains$1(at, change.at)) if (change.at.length === at.length) {
871
+ if (change.order) {
872
+ const positions = new Map(change.order.after.map((id, i) => [id, i]));
873
+ let previous = -1;
874
+ for (const id of change.order.before) {
875
+ const position = positions.get(id);
876
+ if (position === void 0) continue;
877
+ if (position < previous) orderChanged = true;
878
+ previous = position;
879
+ }
880
+ }
881
+ for (const member of change.members) if (member.kind === "added") added.add(member.key);
882
+ else if (member.kind === "removed") removed.add(member.key);
883
+ else updated.add(member.key);
884
+ } else updated.add(change.at[at.length]);
885
+ continue;
886
+ }
887
+ if (change.kind === "tree" && contains$1(change.at, at)) {
888
+ const result = { kind: "reset" };
889
+ cache.set(key, result);
890
+ return result;
891
+ }
892
+ if (!contains$1(at, change.at)) continue;
893
+ if (change.at.length === at.length) continue;
894
+ const id = change.at[at.length];
895
+ updated.add(id);
896
+ }
897
+ added.forEach((id) => updated.delete(id));
898
+ removed.forEach((id) => updated.delete(id));
899
+ const result = {
900
+ kind: "incremental",
901
+ added,
902
+ removed,
903
+ updated,
904
+ orderChanged
905
+ };
906
+ cache.set(key, result);
907
+ return result;
908
+ }
909
+ };
910
+ const impact = Object.freeze({
911
+ kind: reset ? "reset" : "incremental",
912
+ affects: (pick) => implementation.affects(compilePath(schema, "value", pick)),
913
+ collection: (pick) => implementation.collection(compilePath(schema, "collection", pick))
914
+ });
915
+ queries.set(impact, implementation);
916
+ return impact;
917
+ };
918
+ //#endregion
919
+ //#region core/src/runtime/contract.ts
920
+ var DocumentReentrancyError = class extends Error {
921
+ constructor() {
922
+ super("Document writes cannot be re-entered during an update or notification.");
923
+ this.name = "DocumentReentrancyError";
924
+ }
925
+ };
926
+ var DocumentDisposedError = class extends Error {
927
+ constructor() {
928
+ super("Doxum runtime has been disposed.");
929
+ this.name = "DocumentDisposedError";
930
+ }
931
+ };
932
+ var TransactionRejected = class extends Error {
933
+ issues;
934
+ constructor(input) {
935
+ super("Document update rejected.");
936
+ this.name = "TransactionRejected";
937
+ const issues = Array.isArray(input) ? input : [input];
938
+ this.issues = Object.freeze(issues.map((issue) => Object.freeze({
939
+ code: issue.code,
940
+ message: issue.message,
941
+ source: "application",
942
+ ...issue.address === void 0 ? {} : { address: Object.freeze([...issue.address]) }
943
+ })));
944
+ }
945
+ };
946
+ //#endregion
947
+ //#region core/src/runtime/access.ts
948
+ const states = /* @__PURE__ */ new WeakMap();
949
+ const bindRuntimeAccess = (runtime, state) => {
950
+ states.set(runtime, state);
951
+ };
952
+ const accessOf = (runtime) => {
953
+ const state = states.get(runtime);
954
+ if (!state) throw new Error("Unknown Doxum runtime.");
955
+ return state;
956
+ };
957
+ const readWith = (runtime, run, dependencies) => {
958
+ const state = accessOf(runtime);
959
+ if (state.disposed) throw new DocumentDisposedError();
960
+ state.projectionLocks = (state.projectionLocks ?? 0) + 1;
961
+ try {
962
+ return run(createAccess({
963
+ state,
964
+ dependencies
965
+ }));
966
+ } finally {
967
+ state.projectionLocks--;
968
+ }
969
+ };
970
+ //#endregion
971
+ //#region core/src/runtime/notification.ts
972
+ const notifications = /* @__PURE__ */ new WeakMap();
973
+ const readableOwners = /* @__PURE__ */ new WeakMap();
974
+ const bindDocumentReadable = (readable, runtime) => {
975
+ readableOwners.set(readable, runtime);
976
+ };
977
+ const documentReadableOwner = (readable) => readableOwners.get(readable);
978
+ const createNotification = (runtime) => {
979
+ const notification = {
980
+ root: /* @__PURE__ */ new Set(),
981
+ filtered: /* @__PURE__ */ new Set(),
982
+ index: new SubscriptionIndex(runtime.schema),
983
+ cleanups: /* @__PURE__ */ new Set(),
984
+ processors: [],
985
+ candidates: /* @__PURE__ */ new Set(),
986
+ rootSnapshot: [],
987
+ notifying: false
988
+ };
989
+ notifications.set(runtime, notification);
990
+ return notification;
991
+ };
992
+ const notificationOf = (runtime) => {
993
+ const value = notifications.get(runtime);
994
+ if (!value) throw new Error("Unknown Doxum runtime.");
995
+ return value;
996
+ };
997
+ const shareNotification = (source, target) => {
998
+ notifications.set(target, notificationOf(source));
999
+ };
1000
+ const attachProjection = (runtime, processor) => {
1001
+ const notification = notificationOf(runtime);
1002
+ const entry = {
1003
+ processor,
1004
+ active: true
1005
+ };
1006
+ notification.processors.push(entry);
1007
+ return () => {
1008
+ if (!entry.active) return;
1009
+ entry.active = false;
1010
+ if (!notification.notifying) {
1011
+ const index = notification.processors.indexOf(entry);
1012
+ if (index >= 0) notification.processors.splice(index, 1);
1013
+ }
1014
+ };
1015
+ };
1016
+ const subscribeRoot = (notification, listener) => {
1017
+ const entry = {
1018
+ listener,
1019
+ active: true
1020
+ };
1021
+ notification.root.add(entry);
1022
+ return () => {
1023
+ entry.active = false;
1024
+ if (!notification.notifying) notification.root.delete(entry);
1025
+ else notification.cleanups.add(() => {
1026
+ notification.root.delete(entry);
1027
+ });
1028
+ };
1029
+ };
1030
+ const subscribeTargets = (notification, targets, listener) => {
1031
+ const entry = {
1032
+ targets: Object.freeze(targets.slice()),
1033
+ listener,
1034
+ active: true
1035
+ };
1036
+ notification.filtered.add(entry);
1037
+ entry.targets.forEach((value) => notification.index.add(value, entry));
1038
+ const remove = () => {
1039
+ notification.filtered.delete(entry);
1040
+ entry.targets.forEach((value) => notification.index.delete(value, entry));
1041
+ };
1042
+ return () => {
1043
+ if (!entry.active) return;
1044
+ entry.active = false;
1045
+ if (notification.notifying) notification.cleanups.add(remove);
1046
+ else remove();
1047
+ };
1048
+ };
1049
+ const notify = (notification, commit, afterSettle) => {
1050
+ notification.notifying = true;
1051
+ const errors = [];
1052
+ const call = (listener) => {
1053
+ try {
1054
+ listener(commit);
1055
+ } catch (error) {
1056
+ errors.push(Object.freeze({
1057
+ phase: "listener",
1058
+ error
1059
+ }));
1060
+ }
1061
+ };
1062
+ try {
1063
+ const processorCount = notification.processors.length;
1064
+ for (let index = 0; index < processorCount; index += 1) {
1065
+ const entry = notification.processors[index];
1066
+ if (!entry?.active) continue;
1067
+ try {
1068
+ entry.processor.capture(commit);
1069
+ } catch (error) {
1070
+ errors.push(Object.freeze({
1071
+ phase: "processor",
1072
+ error
1073
+ }));
1074
+ }
1075
+ }
1076
+ for (let index = 0; index < processorCount; index += 1) {
1077
+ const entry = notification.processors[index];
1078
+ if (!entry?.active) continue;
1079
+ try {
1080
+ entry.processor.settle();
1081
+ } catch (error) {
1082
+ errors.push(Object.freeze({
1083
+ phase: "processor",
1084
+ error
1085
+ }));
1086
+ }
1087
+ }
1088
+ for (let index = 0; index < processorCount; index += 1) {
1089
+ const entry = notification.processors[index];
1090
+ if (!entry?.active) continue;
1091
+ try {
1092
+ errors.push(...entry.processor.flush());
1093
+ } catch (error) {
1094
+ errors.push(Object.freeze({
1095
+ phase: "flush",
1096
+ error
1097
+ }));
1098
+ }
1099
+ }
1100
+ if (afterSettle) errors.push(...afterSettle());
1101
+ const candidates = notification.candidates;
1102
+ candidates.clear();
1103
+ if (notification.filtered.size) notification.index.collect(commit.changes, (entry) => candidates.add(entry));
1104
+ candidates.forEach((entry) => {
1105
+ if (entry.active) call(entry.listener);
1106
+ });
1107
+ const rootSnapshot = notification.rootSnapshot;
1108
+ rootSnapshot.length = 0;
1109
+ notification.root.forEach((entry) => rootSnapshot.push(entry));
1110
+ for (const entry of rootSnapshot) if (entry.active) call(entry.listener);
1111
+ } finally {
1112
+ notification.notifying = false;
1113
+ let writeIndex = 0;
1114
+ for (const entry of notification.processors) if (entry.active) notification.processors[writeIndex++] = entry;
1115
+ notification.processors.length = writeIndex;
1116
+ notification.cleanups.forEach((cleanup) => cleanup());
1117
+ notification.cleanups.clear();
1118
+ }
1119
+ return Object.freeze(errors);
1120
+ };
1121
+ const subscribeDependencies = (runtime, targets, listener) => subscribeTargets(notificationOf(runtime), targets, listener);
1122
+ const disposeNotification = (notification) => {
1123
+ const attachments = notification.processors.slice();
1124
+ notification.root.clear();
1125
+ notification.filtered.clear();
1126
+ notification.index.clear();
1127
+ notification.cleanups.clear();
1128
+ notification.processors.length = 0;
1129
+ notification.candidates.clear();
1130
+ notification.rootSnapshot.length = 0;
1131
+ const errors = [];
1132
+ attachments.forEach((entry) => {
1133
+ if (!entry.active) return;
1134
+ try {
1135
+ entry.processor.dispose();
1136
+ } catch (error) {
1137
+ errors.push(error);
1138
+ }
1139
+ });
1140
+ if (errors.length) throw new AggregateError(errors, "Projection disposal notification failed.");
1141
+ };
1142
+ //#endregion
1143
+ //#region core/src/projection/definition.ts
1144
+ const definitions = /* @__PURE__ */ new WeakMap();
1145
+ const freezeSources = (sources) => Object.freeze({ ...sources });
1146
+ const define = (definition) => {
1147
+ const handle = Object.freeze({});
1148
+ definitions.set(handle, definition);
1149
+ return handle;
1150
+ };
1151
+ const definitionOf = (projection) => {
1152
+ const definition = definitions.get(projection);
1153
+ if (!definition) throw new TypeError("Unknown projection definition.");
1154
+ return definition;
1155
+ };
1156
+ const input = (initial, options) => define({
1157
+ kind: "input",
1158
+ initial,
1159
+ isEqual: options?.isEqual
1160
+ });
1161
+ function project(first, second, third, fourth) {
1162
+ if (first && typeof first === "object" && "kind" in first && (first.kind === "value" || first.kind === "collection")) {
1163
+ const spec = first;
1164
+ return define({
1165
+ kind: spec.kind === "value" ? "advanced-value" : "advanced-collection",
1166
+ sources: freezeSources(spec.sources),
1167
+ build: spec.build,
1168
+ isEqual: spec.isEqual,
1169
+ name: spec.name
1170
+ });
1171
+ }
1172
+ if (first && typeof first === "object" && "revision" in first && "subscribe" in first) {
1173
+ if ("address" in first) {
1174
+ const document = first;
1175
+ if (!second) return define({
1176
+ kind: "document",
1177
+ document
1178
+ });
1179
+ if (Array.isArray(second)) return define({
1180
+ kind: "document",
1181
+ document,
1182
+ targets: Object.freeze([...second])
1183
+ });
1184
+ if (typeof second !== "function") throw new TypeError("Invalid document projection.");
1185
+ const source = define({
1186
+ kind: "document-collection",
1187
+ document,
1188
+ pick: second
1189
+ });
1190
+ if (!third || typeof third !== "function") return source;
1191
+ return define({
1192
+ kind: "map",
1193
+ source,
1194
+ mapper: third,
1195
+ isEqual: fourth?.isEqual
1196
+ });
1197
+ }
1198
+ return define({
1199
+ kind: "readable",
1200
+ readable: first
1201
+ });
1202
+ }
1203
+ if (second) {
1204
+ if (typeof second !== "function") throw new TypeError("Invalid project declaration.");
1205
+ if (definitions.has(first)) return define({
1206
+ kind: "map",
1207
+ source: first,
1208
+ mapper: second,
1209
+ isEqual: third?.isEqual
1210
+ });
1211
+ return define({
1212
+ kind: "value",
1213
+ sources: freezeSources(first),
1214
+ compute: second,
1215
+ isEqual: third?.isEqual
1216
+ });
1217
+ }
1218
+ throw new TypeError("Invalid project declaration.");
1219
+ }
1220
+ //#endregion
1221
+ //#region core/src/projection/contract.ts
1222
+ var ProjectionError = class extends Error {
1223
+ constructor(phase, identity, revisions, cause) {
1224
+ super(`Projection ${phase} failed: ${identity}`, { cause });
1225
+ this.phase = phase;
1226
+ this.identity = identity;
1227
+ this.revisions = revisions;
1228
+ this.name = "ProjectionError";
1229
+ }
1230
+ };
1231
+ var ProjectionDisposedError = class extends Error {
1232
+ constructor() {
1233
+ super("Projection has been disposed.");
1234
+ this.name = "ProjectionDisposedError";
1235
+ }
1236
+ };
1237
+ //#endregion
1238
+ //#region core/src/projection/scheduler.ts
1239
+ const projectionHandles = /* @__PURE__ */ new WeakSet();
1240
+ const createScheduler = (onError) => {
1241
+ let disposed = false;
1242
+ let depth = 0;
1243
+ let phase = "idle";
1244
+ let sequence = 0;
1245
+ const records = /* @__PURE__ */ new Map();
1246
+ const nodes = /* @__PURE__ */ new Set();
1247
+ const pending = /* @__PURE__ */ new Set();
1248
+ const heap = [];
1249
+ const queued = /* @__PURE__ */ new Set();
1250
+ const completed = [];
1251
+ const emissions = /* @__PURE__ */ new Set();
1252
+ const errors = [];
1253
+ let reportingFailures = [];
1254
+ const guards = /* @__PURE__ */ new Set();
1255
+ const cleanups = /* @__PURE__ */ new Set();
1256
+ const assertActive = () => {
1257
+ if (disposed) throw new ProjectionDisposedError();
1258
+ };
1259
+ const assertIdle = () => {
1260
+ assertActive();
1261
+ if (phase !== "idle") throw new Error("Projection cannot be re-entered during processing or notification.");
1262
+ };
1263
+ const lock = (value) => guards.forEach((guard) => guard(value));
1264
+ const enqueue = (node) => {
1265
+ if (node.disposed || queued.has(node)) return;
1266
+ queued.add(node);
1267
+ profile.projection("scheduledNodes");
1268
+ let i = heap.length;
1269
+ heap.push(node);
1270
+ while (i > 0) {
1271
+ const p = i - 1 >> 1;
1272
+ if (heap[p].order < node.order) break;
1273
+ heap[i] = heap[p];
1274
+ i = p;
1275
+ }
1276
+ heap[i] = node;
1277
+ };
1278
+ const pop = () => {
1279
+ const first = heap[0];
1280
+ const last = heap.pop();
1281
+ if (heap.length) {
1282
+ let i = 0;
1283
+ while (i * 2 + 1 < heap.length) {
1284
+ let c = i * 2 + 1;
1285
+ if (c + 1 < heap.length && heap[c + 1].order < heap[c].order) c++;
1286
+ if (last.order < heap[c].order) break;
1287
+ heap[i] = heap[c];
1288
+ i = c;
1289
+ }
1290
+ heap[i] = last;
1291
+ }
1292
+ return first;
1293
+ };
1294
+ const errorFor = (node, cause, kind = "processor") => new ProjectionError(kind, node.name, node.sources.map((source) => source.revision()), cause);
1295
+ const capture = (source) => {
1296
+ if (disposed) return;
1297
+ if (phase !== "idle") {
1298
+ source.fault = new ProjectionError("source", "reentrant source", [source.revision()], /* @__PURE__ */ new Error("Source changed while projection was running."));
1299
+ errors.push(source.fault);
1300
+ throw source.fault;
1301
+ }
1302
+ pending.add(source);
1303
+ profile.projection("sourceEvents");
1304
+ if (source.fault) errors.push(source.fault);
1305
+ };
1306
+ const settle = () => {
1307
+ if (disposed || depth || phase !== "idle") return;
1308
+ phase = "compute";
1309
+ lock(true);
1310
+ try {
1311
+ pending.forEach((source) => source.consumers.forEach(enqueue));
1312
+ while (heap.length) {
1313
+ const node = pop();
1314
+ if (node.disposed) continue;
1315
+ const wasFaulted = node.fault !== void 0;
1316
+ profile.projection("processedNodes");
1317
+ let changed = false;
1318
+ const blocked = node.sources.find((source) => source.fault || source.disposed);
1319
+ if (blocked) node.fault = errorFor(node, blocked.fault ?? new ProjectionDisposedError(), "blocked");
1320
+ else {
1321
+ const build = node.forced || wasFaulted || node.sources.some((source) => source.reset());
1322
+ try {
1323
+ changed = node.evaluate(build);
1324
+ const failure = node.sources.find((source) => source.fault)?.fault;
1325
+ if (failure) throw failure;
1326
+ node.fault = void 0;
1327
+ } catch (cause) {
1328
+ const error = errorFor(node, cause);
1329
+ errors.push(error);
1330
+ node.fault = error;
1331
+ if (!build) try {
1332
+ changed = node.evaluate(true);
1333
+ const failure = node.sources.find((source) => source.fault)?.fault;
1334
+ if (failure) throw failure;
1335
+ node.fault = void 0;
1336
+ } catch (cause) {
1337
+ node.fault = errorFor(node, cause);
1338
+ errors.push(node.fault);
1339
+ }
1340
+ }
1341
+ }
1342
+ node.forced = false;
1343
+ node.statusChanged = wasFaulted !== (node.fault !== void 0);
1344
+ completed.push(node);
1345
+ if (changed || wasFaulted || node.fault) {
1346
+ profile.projection("publishedNodes");
1347
+ emissions.add(node);
1348
+ node.consumers.forEach(enqueue);
1349
+ }
1350
+ }
1351
+ completed.forEach((node) => node.publish());
1352
+ } finally {
1353
+ lock(false);
1354
+ phase = "idle";
1355
+ }
1356
+ };
1357
+ const flush = () => {
1358
+ if (disposed || depth) return [];
1359
+ if (pending.size || completed.length) profile.projection("flushes");
1360
+ phase = "notify";
1361
+ lock(true);
1362
+ const reportFailures = [];
1363
+ try {
1364
+ emissions.forEach((node) => node.emit((listener) => {
1365
+ try {
1366
+ listener();
1367
+ } catch (cause) {
1368
+ errors.push(errorFor(node, cause, "listener"));
1369
+ }
1370
+ }));
1371
+ for (const error of errors.slice()) try {
1372
+ onError(error);
1373
+ } catch (cause) {
1374
+ reportFailures.push(cause);
1375
+ }
1376
+ return Object.freeze([...errors.map((error) => Object.freeze({
1377
+ phase: error.phase === "listener" ? "listener" : "processor",
1378
+ error
1379
+ })), ...reportFailures.map((error) => Object.freeze({
1380
+ phase: "listener",
1381
+ error
1382
+ }))]);
1383
+ } finally {
1384
+ pending.forEach((source) => source.clear());
1385
+ completed.forEach((node) => node.clear());
1386
+ pending.clear();
1387
+ queued.clear();
1388
+ completed.length = 0;
1389
+ emissions.clear();
1390
+ errors.length = 0;
1391
+ reportingFailures = reportFailures;
1392
+ lock(false);
1393
+ phase = "idle";
1394
+ }
1395
+ };
1396
+ const run = () => {
1397
+ settle();
1398
+ const result = flush();
1399
+ const failures = reportingFailures;
1400
+ reportingFailures = [];
1401
+ if (failures.length) throw new AggregateError(failures, "Projection error reporter failed.");
1402
+ return result;
1403
+ };
1404
+ return {
1405
+ assertActive,
1406
+ assertIdle,
1407
+ capture,
1408
+ settle,
1409
+ flush,
1410
+ run,
1411
+ records,
1412
+ guards,
1413
+ cleanups,
1414
+ get active() {
1415
+ return !disposed;
1416
+ },
1417
+ register(handle, record) {
1418
+ assertIdle();
1419
+ records.set(handle, record);
1420
+ projectionHandles.add(handle);
1421
+ },
1422
+ unregisterSource(handle) {
1423
+ const record = records.get(handle);
1424
+ if (record) {
1425
+ record.disposed = true;
1426
+ pending.delete(record);
1427
+ record.clear();
1428
+ records.delete(handle);
1429
+ }
1430
+ },
1431
+ source(handle) {
1432
+ assertActive();
1433
+ const source = records.get(handle);
1434
+ if (!source || source.disposed) throw new Error("Unknown, disposed, or foreign projection source.");
1435
+ return source;
1436
+ },
1437
+ addNode(node) {
1438
+ nodes.add(node);
1439
+ node.sources.forEach((source) => source.consumers.add(node));
1440
+ },
1441
+ order: () => sequence++,
1442
+ initialize(run) {
1443
+ assertIdle();
1444
+ phase = "compute";
1445
+ lock(true);
1446
+ try {
1447
+ return run();
1448
+ } finally {
1449
+ lock(false);
1450
+ phase = "idle";
1451
+ }
1452
+ },
1453
+ rebuild(node) {
1454
+ assertIdle();
1455
+ if (node.disposed) throw new ProjectionDisposedError();
1456
+ node.forced = true;
1457
+ enqueue(node);
1458
+ run();
1459
+ },
1460
+ disposeNode(node) {
1461
+ if (node.disposed) return;
1462
+ assertIdle();
1463
+ if (node.consumers.size) throw new Error("Cannot dispose a projection node with downstream consumers.");
1464
+ node.disposed = true;
1465
+ node.sources.forEach((source) => source.consumers.delete(node));
1466
+ nodes.delete(node);
1467
+ node.release();
1468
+ for (const [handle, record] of records) if (record === node) records.delete(handle);
1469
+ },
1470
+ batch(callback) {
1471
+ assertIdle();
1472
+ depth++;
1473
+ let failed = false;
1474
+ try {
1475
+ const value = callback();
1476
+ assertSynchronous(value);
1477
+ return value;
1478
+ } catch (error) {
1479
+ failed = true;
1480
+ throw error;
1481
+ } finally {
1482
+ depth--;
1483
+ if (!depth) if (failed) try {
1484
+ run();
1485
+ } catch {}
1486
+ else run();
1487
+ }
1488
+ },
1489
+ dispose() {
1490
+ if (disposed) return;
1491
+ assertIdle();
1492
+ disposed = true;
1493
+ const failures = [];
1494
+ cleanups.forEach((cleanup) => {
1495
+ try {
1496
+ cleanup();
1497
+ } catch (error) {
1498
+ failures.push(error);
1499
+ }
1500
+ });
1501
+ cleanups.clear();
1502
+ nodes.forEach((node) => {
1503
+ node.disposed = true;
1504
+ node.release();
1505
+ node.consumers.clear();
1506
+ });
1507
+ records.forEach((source) => {
1508
+ source.disposed = true;
1509
+ source.consumers.clear();
1510
+ source.clear();
1511
+ });
1512
+ nodes.clear();
1513
+ records.clear();
1514
+ pending.clear();
1515
+ heap.length = 0;
1516
+ completed.length = 0;
1517
+ queued.clear();
1518
+ emissions.clear();
1519
+ guards.clear();
1520
+ errors.length = 0;
1521
+ reportingFailures = [];
1522
+ if (failures.length) throw new AggregateError(failures, "Projection cleanup failed.");
1523
+ },
1524
+ debug: () => ({
1525
+ nodes: nodes.size,
1526
+ sources: records.size - nodes.size,
1527
+ subscriptions: cleanups.size,
1528
+ pending: pending.size + heap.length
1529
+ })
1530
+ };
1531
+ };
1532
+ const assertSynchronous = (value) => {
1533
+ if (value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function") throw new TypeError("Projection callbacks must be synchronous.");
1534
+ };
1535
+ const assertScope = (active) => {
1536
+ if (!active()) throw new Error("Projection reader or writer is no longer active.");
1537
+ };
1538
+ //#endregion
1539
+ //#region core/src/projection/node.ts
1540
+ const createNode = (scheduler, spec, behavior) => {
1541
+ scheduler.assertIdle();
1542
+ const entries = Object.entries(spec.sources).map(([key, handle]) => [key, scheduler.source(handle)]);
1543
+ const order = scheduler.order();
1544
+ const node = {
1545
+ ...behavior,
1546
+ publish: () => {
1547
+ if (node.fault) behavior.clear();
1548
+ else behavior.publish();
1549
+ },
1550
+ order,
1551
+ name: spec.name ? `${spec.name} (${order})` : `projection-${order}`,
1552
+ sources: entries.map(([, source]) => source),
1553
+ consumers: /* @__PURE__ */ new Set(),
1554
+ disposed: false,
1555
+ fault: void 0,
1556
+ forced: false,
1557
+ statusChanged: false,
1558
+ evaluate: (build) => {
1559
+ let active = true;
1560
+ try {
1561
+ const scopeActive = () => active;
1562
+ const inputs = Object.fromEntries(entries.map(([key, source]) => [key, source.context(scopeActive)]));
1563
+ return behavior.evaluate(inputs, build, scopeActive);
1564
+ } finally {
1565
+ active = false;
1566
+ }
1567
+ }
1568
+ };
1569
+ scheduler.initialize(() => {
1570
+ const invalid = node.sources.find((source) => source.fault || source.disposed);
1571
+ if (invalid) throw invalid.fault ?? new ProjectionDisposedError();
1572
+ node.evaluate(true);
1573
+ node.publish();
1574
+ node.clear();
1575
+ });
1576
+ const check = () => {
1577
+ if (node.disposed || !scheduler.active) throw new ProjectionDisposedError();
1578
+ if (node.fault) throw node.fault;
1579
+ };
1580
+ return {
1581
+ node,
1582
+ check,
1583
+ install(handle) {
1584
+ scheduler.register(handle, node);
1585
+ scheduler.addNode(node);
1586
+ },
1587
+ rebuild: () => scheduler.rebuild(node),
1588
+ dispose: () => scheduler.disposeNode(node)
1589
+ };
1590
+ };
1591
+ //#endregion
1592
+ //#region core/src/projection/collection.ts
1593
+ const collectionHandles = /* @__PURE__ */ new WeakSet();
1594
+ const readonlySet = (values) => {
1595
+ const set = new Set(values);
1596
+ const view = Object.freeze({
1597
+ get size() {
1598
+ return set.size;
1599
+ },
1600
+ has: (value) => set.has(value),
1601
+ entries: () => set.entries(),
1602
+ keys: () => set.keys(),
1603
+ values: () => set.values(),
1604
+ [Symbol.iterator]: () => set.values(),
1605
+ forEach: (callback, thisArg) => set.forEach((value) => callback.call(thisArg, value, value, view))
1606
+ });
1607
+ return view;
1608
+ };
1609
+ const createCollection = (scheduler, spec) => {
1610
+ const values = /* @__PURE__ */ new Map();
1611
+ let ids = Object.freeze([]);
1612
+ let staged = /* @__PURE__ */ new Map();
1613
+ let nextIds = ids;
1614
+ let change;
1615
+ let instance;
1616
+ let initialized = false;
1617
+ let reset = false;
1618
+ let revision = 0;
1619
+ let idsRevision = 0;
1620
+ let all;
1621
+ const items = /* @__PURE__ */ new Map();
1622
+ const subscribedItems = /* @__PURE__ */ new Set();
1623
+ const idsListeners = /* @__PURE__ */ new Set();
1624
+ const allListeners = /* @__PURE__ */ new Set();
1625
+ const listeners = /* @__PURE__ */ new Set();
1626
+ const changedKeys = /* @__PURE__ */ new Set();
1627
+ let orderChanged = false;
1628
+ const current = Object.freeze({
1629
+ get: (key) => {
1630
+ owner.check();
1631
+ return values.get(key);
1632
+ },
1633
+ has: (key) => {
1634
+ owner.check();
1635
+ return values.has(key);
1636
+ },
1637
+ ids: () => {
1638
+ owner.check();
1639
+ return ids;
1640
+ }
1641
+ });
1642
+ const hasNext = (key) => staged.has(key) ? staged.get(key).present : values.has(key);
1643
+ const getNext = (key) => {
1644
+ const entry = staged.get(key);
1645
+ return entry ? entry.present ? entry.value : void 0 : values.get(key);
1646
+ };
1647
+ const owner = createNode(scheduler, spec, {
1648
+ evaluate: (sources, build, active) => {
1649
+ staged = /* @__PURE__ */ new Map();
1650
+ nextIds = ids;
1651
+ change = void 0;
1652
+ changedKeys.clear();
1653
+ let explicitOrder;
1654
+ let cleared = false;
1655
+ const nextHas = (key) => staged.has(key) ? staged.get(key).present : !cleared && values.has(key);
1656
+ const nextGet = (key) => {
1657
+ const entry = staged.get(key);
1658
+ return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : values.get(key);
1659
+ };
1660
+ const deriveIds = () => {
1661
+ const result = cleared ? [] : ids.filter(nextHas);
1662
+ for (const [key, entry] of staged) if (entry.present && (cleared || !values.has(key))) result.push(key);
1663
+ return result;
1664
+ };
1665
+ const read = (next) => ({
1666
+ get: (key) => {
1667
+ assertScope(active);
1668
+ return next ? nextGet(key) : values.get(key);
1669
+ },
1670
+ has: (key) => {
1671
+ assertScope(active);
1672
+ return next ? nextHas(key) : values.has(key);
1673
+ },
1674
+ ids: () => {
1675
+ assertScope(active);
1676
+ return next ? Object.freeze(explicitOrder ? explicitOrder.slice() : deriveIds()) : ids;
1677
+ }
1678
+ });
1679
+ const writer = {
1680
+ set: (key, value) => {
1681
+ assertScope(active);
1682
+ if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
1683
+ staged.set(key, {
1684
+ present: true,
1685
+ value
1686
+ });
1687
+ },
1688
+ remove: (key) => {
1689
+ assertScope(active);
1690
+ staged.set(key, { present: false });
1691
+ },
1692
+ order: (order) => {
1693
+ assertScope(active);
1694
+ explicitOrder = order.slice();
1695
+ },
1696
+ replace: (entries) => {
1697
+ assertScope(active);
1698
+ staged.clear();
1699
+ cleared = true;
1700
+ const order = [];
1701
+ for (const [key, value] of entries) {
1702
+ if (staged.has(key)) throw new TypeError("Duplicate projection key.");
1703
+ writer.set(key, value);
1704
+ order.push(key);
1705
+ }
1706
+ explicitOrder = order;
1707
+ }
1708
+ };
1709
+ const input = {
1710
+ sources,
1711
+ previous: read(false),
1712
+ next: read(true),
1713
+ writer
1714
+ };
1715
+ if (build || !instance) {
1716
+ profile.materialized.rebuilt();
1717
+ instance = void 0;
1718
+ cleared = true;
1719
+ const built = spec.build(input);
1720
+ assertSynchronous(built);
1721
+ instance = built;
1722
+ } else {
1723
+ profile.materialized.updated();
1724
+ const result = instance.update(input);
1725
+ assertSynchronous(result);
1726
+ if (result?.kind === "rebuild") {
1727
+ staged.clear();
1728
+ cleared = true;
1729
+ explicitOrder = void 0;
1730
+ instance = void 0;
1731
+ const built = spec.build(input);
1732
+ assertSynchronous(built);
1733
+ instance = built;
1734
+ build = true;
1735
+ }
1736
+ }
1737
+ if (cleared) {
1738
+ for (const key of values.keys()) if (!staged.has(key)) staged.set(key, { present: false });
1739
+ }
1740
+ const added = /* @__PURE__ */ new Set();
1741
+ const removed = /* @__PURE__ */ new Set();
1742
+ const updated = /* @__PURE__ */ new Set();
1743
+ profile.projection("touchedKeys", staged.size);
1744
+ for (const [key, entry] of staged) {
1745
+ const existed = values.has(key);
1746
+ if (entry.present) if (!existed) added.add(key);
1747
+ else if (!(spec.isEqual ?? Object.is)(values.get(key), entry.value)) updated.add(key);
1748
+ else staged.delete(key);
1749
+ else if (existed) removed.add(key);
1750
+ else staged.delete(key);
1751
+ }
1752
+ if (explicitOrder || added.size || removed.size) {
1753
+ const order = explicitOrder ?? [...ids.filter((key) => !removed.has(key)), ...added];
1754
+ if (new Set(order).size !== order.length || order.length !== values.size + added.size - removed.size || order.some((key) => !hasNext(key))) throw new TypeError("Projection order must contain every key exactly once.");
1755
+ nextIds = ids.length === order.length && ids.every((key, i) => key === order[i]) ? ids : Object.freeze(order.slice());
1756
+ }
1757
+ orderChanged = nextIds !== ids;
1758
+ if (added.size || removed.size || updated.size || orderChanged) {
1759
+ let commonOrderChanged = false;
1760
+ if (orderChanged) {
1761
+ const before = ids.filter((key) => !removed.has(key));
1762
+ const after = nextIds.filter((key) => !added.has(key));
1763
+ commonOrderChanged = before.some((key, i) => after[i] !== key);
1764
+ }
1765
+ change = Object.freeze({
1766
+ kind: "incremental",
1767
+ added: readonlySet(added),
1768
+ removed: readonlySet(removed),
1769
+ updated: readonlySet(updated),
1770
+ orderChanged: commonOrderChanged
1771
+ });
1772
+ [
1773
+ ...added,
1774
+ ...removed,
1775
+ ...updated
1776
+ ].forEach((key) => changedKeys.add(key));
1777
+ profile.projection("changedKeys", changedKeys.size);
1778
+ }
1779
+ reset = build;
1780
+ return change !== void 0;
1781
+ },
1782
+ context: (active) => {
1783
+ const input = {
1784
+ get: (key) => {
1785
+ assertScope(active);
1786
+ return getNext(key);
1787
+ },
1788
+ has: (key) => {
1789
+ assertScope(active);
1790
+ return hasNext(key);
1791
+ },
1792
+ ids: () => {
1793
+ assertScope(active);
1794
+ return nextIds;
1795
+ },
1796
+ change,
1797
+ revision: revision + (change ? 1 : 0),
1798
+ reset
1799
+ };
1800
+ return Object.freeze(input);
1801
+ },
1802
+ revision: () => revision,
1803
+ reset: () => reset,
1804
+ publish: () => {
1805
+ for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
1806
+ else values.delete(key);
1807
+ if (change) {
1808
+ if (initialized) revision++;
1809
+ if (orderChanged && initialized) idsRevision++;
1810
+ all = void 0;
1811
+ changedKeys.forEach((key) => {
1812
+ const item = items.get(key);
1813
+ if (item && initialized) item.revision++;
1814
+ });
1815
+ }
1816
+ ids = nextIds;
1817
+ initialized = true;
1818
+ },
1819
+ emit: (call) => {
1820
+ profile.materialized.notification();
1821
+ const fault = owner.node.fault !== void 0;
1822
+ const resetEvent = Object.freeze({ kind: "reset" });
1823
+ Array.from(listeners).forEach((listener) => call(() => listener(change ?? resetEvent)));
1824
+ if (orderChanged || !change || fault || owner.node.statusChanged) Array.from(idsListeners).forEach((listener) => call(listener));
1825
+ Array.from(allListeners).forEach((listener) => call(listener));
1826
+ if (!change || fault || owner.node.statusChanged) for (const item of subscribedItems) Array.from(item.listeners ?? []).forEach((listener) => call(listener));
1827
+ else for (const key of changedKeys) Array.from(items.get(key)?.listeners ?? []).forEach((listener) => call(listener));
1828
+ },
1829
+ clear: () => {
1830
+ staged.clear();
1831
+ nextIds = ids;
1832
+ change = void 0;
1833
+ reset = false;
1834
+ changedKeys.clear();
1835
+ orderChanged = false;
1836
+ },
1837
+ release: () => {
1838
+ values.clear();
1839
+ staged.clear();
1840
+ items.clear();
1841
+ subscribedItems.clear();
1842
+ idsListeners.clear();
1843
+ allListeners.clear();
1844
+ listeners.clear();
1845
+ ids = nextIds = Object.freeze([]);
1846
+ all = void 0;
1847
+ instance = void 0;
1848
+ }
1849
+ });
1850
+ const handle = {
1851
+ current: () => {
1852
+ owner.check();
1853
+ return current;
1854
+ },
1855
+ ids: {
1856
+ current: () => {
1857
+ owner.check();
1858
+ return ids;
1859
+ },
1860
+ revision: () => {
1861
+ owner.check();
1862
+ return idsRevision;
1863
+ },
1864
+ subscribe: (listener) => {
1865
+ owner.check();
1866
+ idsListeners.add(listener);
1867
+ return () => {
1868
+ idsListeners.delete(listener);
1869
+ };
1870
+ }
1871
+ },
1872
+ all: {
1873
+ current: () => {
1874
+ owner.check();
1875
+ if (!all) {
1876
+ profile.collectionView.arrayCopied();
1877
+ all = Object.freeze(ids.map((key) => values.get(key)));
1878
+ }
1879
+ return all;
1880
+ },
1881
+ revision: () => {
1882
+ owner.check();
1883
+ return revision;
1884
+ },
1885
+ subscribe: (listener) => {
1886
+ owner.check();
1887
+ allListeners.add(listener);
1888
+ return () => {
1889
+ allListeners.delete(listener);
1890
+ };
1891
+ }
1892
+ },
1893
+ item: (key) => {
1894
+ owner.check();
1895
+ const old = items.get(key);
1896
+ if (old) return old.readable;
1897
+ const readable = Object.freeze({
1898
+ current: () => {
1899
+ owner.check();
1900
+ return values.get(key);
1901
+ },
1902
+ revision: () => {
1903
+ owner.check();
1904
+ return item.revision;
1905
+ },
1906
+ subscribe: (listener) => {
1907
+ owner.check();
1908
+ const set = item.listeners ??= /* @__PURE__ */ new Set();
1909
+ set.add(listener);
1910
+ subscribedItems.add(item);
1911
+ return () => {
1912
+ set.delete(listener);
1913
+ if (!set.size) subscribedItems.delete(item);
1914
+ };
1915
+ }
1916
+ });
1917
+ const item = {
1918
+ readable,
1919
+ revision: 0
1920
+ };
1921
+ projectionHandles.add(readable);
1922
+ items.set(key, item);
1923
+ return readable;
1924
+ },
1925
+ revision: () => {
1926
+ owner.check();
1927
+ return revision;
1928
+ },
1929
+ subscribe: (listener) => {
1930
+ owner.check();
1931
+ listeners.add(listener);
1932
+ return () => {
1933
+ listeners.delete(listener);
1934
+ };
1935
+ },
1936
+ rebuild: owner.rebuild,
1937
+ dispose: owner.dispose
1938
+ };
1939
+ projectionHandles.add(handle.ids);
1940
+ projectionHandles.add(handle.all);
1941
+ collectionHandles.add(handle);
1942
+ owner.install(handle);
1943
+ return Object.freeze(handle);
1944
+ };
1945
+ //#endregion
1946
+ //#region core/src/projection/source.ts
1947
+ const createSources = (scheduler) => {
1948
+ const documents = /* @__PURE__ */ new Map();
1949
+ const readables = /* @__PURE__ */ new Map();
1950
+ const collections = /* @__PURE__ */ new WeakSet();
1951
+ const document = (runtime) => {
1952
+ scheduler.assertIdle();
1953
+ const state = accessOf(runtime);
1954
+ if (state.disposed) throw new Error("Document has been disposed.");
1955
+ const existing = documents.get(state);
1956
+ if (existing) return existing;
1957
+ const bindings = [];
1958
+ const make = (input) => {
1959
+ const collection = "collection" in input ? input.collection : void 0;
1960
+ const targets = "targets" in input ? input.targets : [input.collection];
1961
+ if (state.disposed) throw new Error("Document has been disposed.");
1962
+ targets.forEach((value) => {
1963
+ if (!belongs(value, state.schema)) throw new Error("Projection target belongs to another schema.");
1964
+ });
1965
+ const old = bindings.find((entry) => Boolean(collection) === collections.has(entry.handle) && entry.targets.length === targets.length && entry.targets.every((value, i) => same(value, targets[i])));
1966
+ if (old) return old.handle;
1967
+ let commits = [];
1968
+ let reset = false;
1969
+ let orderDirty = false;
1970
+ const keys = /* @__PURE__ */ new Set();
1971
+ let candidates;
1972
+ const record = {
1973
+ consumers: /* @__PURE__ */ new Set(),
1974
+ disposed: false,
1975
+ fault: void 0,
1976
+ revision: runtime.revision,
1977
+ reset: () => reset,
1978
+ clear: () => {
1979
+ commits = [];
1980
+ reset = orderDirty = false;
1981
+ keys.clear();
1982
+ candidates = void 0;
1983
+ },
1984
+ context: (active) => {
1985
+ assertScope(active);
1986
+ if (state.disposed) throw new Error("Document has been disposed.");
1987
+ const context = {
1988
+ state,
1989
+ active
1990
+ };
1991
+ const read = collection ? collectionAccess(context, collection.address) : createAccess(context);
1992
+ return Object.freeze({
1993
+ read,
1994
+ revision: runtime.revision(),
1995
+ commits: Object.freeze(commits.slice()),
1996
+ reset: record.reset(),
1997
+ ...collection ? { candidates: candidates ??= Object.freeze({
1998
+ keys: Object.freeze([...keys]),
1999
+ orderDirty
2000
+ }) } : {}
2001
+ });
2002
+ }
2003
+ };
2004
+ const handle = collection ? {} : {
2005
+ collection: (pick) => {
2006
+ scheduler.assertIdle();
2007
+ return make({ collection: compilePath(state.schema, "collection", pick) });
2008
+ },
2009
+ targets: (...selected) => {
2010
+ scheduler.assertIdle();
2011
+ if (!selected.length) throw new TypeError("Expected at least one target.");
2012
+ return make({ targets: selected.map((pick) => compilePath(state.schema, "value", pick)) });
2013
+ }
2014
+ };
2015
+ Object.freeze(handle);
2016
+ if (collection) collections.add(handle);
2017
+ scheduler.register(handle, record);
2018
+ const receive = (commit) => {
2019
+ commits.push(commit);
2020
+ reset ||= commit.impact.kind === "reset";
2021
+ if (collection) {
2022
+ const change = collectionImpact(commit.impact, collection);
2023
+ if (change.kind === "reset") reset = true;
2024
+ else {
2025
+ change.added.forEach((key) => keys.add(key));
2026
+ change.removed.forEach((key) => keys.add(key));
2027
+ change.updated.forEach((key) => keys.add(key));
2028
+ orderDirty ||= Boolean(change.added.size || change.removed.size || change.orderChanged);
2029
+ }
2030
+ candidates = void 0;
2031
+ }
2032
+ };
2033
+ bindings.push({
2034
+ handle,
2035
+ targets,
2036
+ record,
2037
+ receive
2038
+ });
2039
+ return handle;
2040
+ };
2041
+ const handle = make({ targets: [] });
2042
+ const guard = (locked) => {
2043
+ state.projectionLocks = (state.projectionLocks ?? 0) + (locked ? 1 : -1);
2044
+ };
2045
+ scheduler.guards.add(guard);
2046
+ const unsubscribe = attachProjection(runtime, {
2047
+ capture: (commit) => {
2048
+ for (const binding of bindings) if (!binding.targets.length || binding.targets.some((value) => affectsTarget(commit.impact, value))) {
2049
+ binding.receive(commit);
2050
+ scheduler.capture(binding.record);
2051
+ }
2052
+ },
2053
+ settle: scheduler.settle,
2054
+ flush: scheduler.flush,
2055
+ dispose: () => {
2056
+ bindings.forEach(({ record }) => {
2057
+ record.fault = new ProjectionError("source", "document disposed", [runtime.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
2058
+ scheduler.capture(record);
2059
+ });
2060
+ scheduler.run();
2061
+ }
2062
+ });
2063
+ scheduler.cleanups.add(() => {
2064
+ unsubscribe();
2065
+ scheduler.guards.delete(guard);
2066
+ });
2067
+ documents.set(state, handle);
2068
+ return handle;
2069
+ };
2070
+ const valueSource = (initial, equal) => {
2071
+ let value = initial;
2072
+ let previous = initial;
2073
+ let revision = 0;
2074
+ const handle = Object.freeze({});
2075
+ const record = {
2076
+ consumers: /* @__PURE__ */ new Set(),
2077
+ disposed: false,
2078
+ fault: void 0,
2079
+ revision: () => revision,
2080
+ reset: () => false,
2081
+ clear: () => {
2082
+ previous = value;
2083
+ },
2084
+ context: (active) => {
2085
+ assertScope(active);
2086
+ return Object.freeze({
2087
+ value,
2088
+ previous,
2089
+ revision,
2090
+ changed: !equal(previous, value),
2091
+ reset: false
2092
+ });
2093
+ }
2094
+ };
2095
+ scheduler.register(handle, record);
2096
+ const accept = (next) => {
2097
+ scheduler.assertIdle();
2098
+ const recovering = record.fault !== void 0;
2099
+ if (equal(value, next) && !recovering) return;
2100
+ record.fault = void 0;
2101
+ value = next;
2102
+ revision++;
2103
+ scheduler.capture(record);
2104
+ };
2105
+ return {
2106
+ source: handle,
2107
+ accept,
2108
+ set(next) {
2109
+ accept(next);
2110
+ scheduler.run();
2111
+ }
2112
+ };
2113
+ };
2114
+ return {
2115
+ dispose: () => {
2116
+ documents.clear();
2117
+ readables.clear();
2118
+ },
2119
+ document,
2120
+ input: (initial, options) => {
2121
+ const { source, set } = valueSource(initial, options?.isEqual ?? Object.is);
2122
+ return Object.freeze({
2123
+ source,
2124
+ set
2125
+ });
2126
+ },
2127
+ fromReadable: (readable, options) => {
2128
+ scheduler.assertIdle();
2129
+ if (projectionHandles.has(readable)) throw new Error("Projection nodes must be declared directly as sources.");
2130
+ const equal = options?.isEqual ?? Object.is;
2131
+ let bindings = readables.get(readable);
2132
+ const old = bindings?.get(equal);
2133
+ if (old) return old.source;
2134
+ const input = valueSource(readable.current(), equal);
2135
+ if (bindings) {
2136
+ bindings.set(equal, {
2137
+ source: input.source,
2138
+ accept: (value) => input.accept(value)
2139
+ });
2140
+ return input.source;
2141
+ }
2142
+ bindings = new Map([[equal, {
2143
+ source: input.source,
2144
+ accept: (value) => input.accept(value)
2145
+ }]]);
2146
+ const members = bindings;
2147
+ const receive = (settle = true) => {
2148
+ if (!scheduler.active) return;
2149
+ let value;
2150
+ try {
2151
+ value = readable.current();
2152
+ } catch (cause) {
2153
+ for (const member of members.values()) {
2154
+ const record = scheduler.source(member.source);
2155
+ record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
2156
+ scheduler.capture(record);
2157
+ }
2158
+ if (settle) scheduler.run();
2159
+ return;
2160
+ }
2161
+ for (const member of members.values()) try {
2162
+ member.accept(value);
2163
+ } catch (cause) {
2164
+ const record = scheduler.source(member.source);
2165
+ record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
2166
+ scheduler.capture(record);
2167
+ }
2168
+ if (settle) scheduler.run();
2169
+ };
2170
+ let unsubscribe;
2171
+ let detach;
2172
+ try {
2173
+ const owner = documentReadableOwner(readable);
2174
+ if (owner) {
2175
+ document(owner);
2176
+ detach = attachProjection(owner, {
2177
+ capture: () => receive(false),
2178
+ settle: scheduler.settle,
2179
+ flush: scheduler.flush,
2180
+ dispose: () => {
2181
+ for (const member of members.values()) {
2182
+ const record = scheduler.source(member.source);
2183
+ record.fault = new ProjectionError("source", "document readable disposed", [record.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
2184
+ scheduler.capture(record);
2185
+ }
2186
+ scheduler.run();
2187
+ }
2188
+ });
2189
+ }
2190
+ unsubscribe = readable.subscribe(() => receive());
2191
+ input.accept(readable.current());
2192
+ } catch (error) {
2193
+ try {
2194
+ unsubscribe?.();
2195
+ detach?.();
2196
+ } finally {
2197
+ scheduler.unregisterSource(input.source);
2198
+ }
2199
+ throw error;
2200
+ }
2201
+ scheduler.cleanups.add(unsubscribe);
2202
+ if (detach) scheduler.cleanups.add(detach);
2203
+ readables.set(readable, bindings);
2204
+ scheduler.run();
2205
+ return input.source;
2206
+ },
2207
+ assertCollection: (handle) => {
2208
+ scheduler.source(handle);
2209
+ if (!collections.has(handle) && !collectionHandles.has(handle)) throw new Error("map requires a collection source.");
2210
+ }
2211
+ };
2212
+ };
2213
+ //#endregion
2214
+ //#region core/src/projection/value.ts
2215
+ const createValue = (scheduler, spec, options) => {
2216
+ let instance;
2217
+ let value;
2218
+ let next;
2219
+ let previous;
2220
+ let initialized = false;
2221
+ let revision = 0;
2222
+ let changed = false;
2223
+ let reset = false;
2224
+ const listeners = /* @__PURE__ */ new Set();
2225
+ const owner = createNode(scheduler, spec, {
2226
+ evaluate: (sources, build) => {
2227
+ let candidate;
2228
+ if (build || !instance) {
2229
+ profile.materialized.rebuilt();
2230
+ instance = void 0;
2231
+ const built = spec.build(sources);
2232
+ assertSynchronous(built);
2233
+ instance = built;
2234
+ candidate = built.value;
2235
+ } else {
2236
+ profile.materialized.updated();
2237
+ const result = instance.update(sources);
2238
+ assertSynchronous(result);
2239
+ if (result.kind === "rebuild") {
2240
+ instance = void 0;
2241
+ const built = spec.build(sources);
2242
+ assertSynchronous(built);
2243
+ instance = built;
2244
+ candidate = built.value;
2245
+ build = true;
2246
+ } else candidate = result.kind === "changed" ? result.value : value;
2247
+ }
2248
+ previous = value;
2249
+ changed = !initialized || !(options?.isEqual ?? Object.is)(value, candidate);
2250
+ next = changed ? candidate : value;
2251
+ reset = build;
2252
+ return changed;
2253
+ },
2254
+ context: (active) => {
2255
+ assertScope(active);
2256
+ return Object.freeze({
2257
+ value: next,
2258
+ previous,
2259
+ changed,
2260
+ revision: revision + (changed ? 1 : 0),
2261
+ reset
2262
+ });
2263
+ },
2264
+ revision: () => revision,
2265
+ reset: () => reset,
2266
+ publish: () => {
2267
+ if (changed && initialized) revision++;
2268
+ value = next;
2269
+ initialized = true;
2270
+ },
2271
+ emit: (call) => {
2272
+ profile.materialized.notification();
2273
+ Array.from(listeners).forEach((listener) => call(listener));
2274
+ },
2275
+ clear: () => {
2276
+ next = value;
2277
+ previous = value;
2278
+ changed = false;
2279
+ reset = false;
2280
+ },
2281
+ release: () => {
2282
+ listeners.clear();
2283
+ instance = void 0;
2284
+ value = next = previous = void 0;
2285
+ }
2286
+ });
2287
+ const handle = Object.freeze({
2288
+ current: () => {
2289
+ owner.check();
2290
+ return value;
2291
+ },
2292
+ revision: () => {
2293
+ owner.check();
2294
+ return revision;
2295
+ },
2296
+ subscribe: (listener) => {
2297
+ owner.check();
2298
+ listeners.add(listener);
2299
+ return () => {
2300
+ listeners.delete(listener);
2301
+ };
2302
+ },
2303
+ rebuild: owner.rebuild,
2304
+ dispose: owner.dispose
2305
+ });
2306
+ owner.install(handle);
2307
+ return handle;
2308
+ };
2309
+ //#endregion
2310
+ //#region core/src/projection/runtime.ts
2311
+ const debug = /* @__PURE__ */ new WeakMap();
2312
+ const projectionEngineDebug = (runtime) => {
2313
+ const read = debug.get(runtime);
2314
+ if (!read) throw new Error("Unknown projection engine.");
2315
+ return Object.freeze(read());
2316
+ };
2317
+ const createProjectionEngine = (options) => {
2318
+ const scheduler = createScheduler(options.onError);
2319
+ const sources = createSources(scheduler);
2320
+ function value(input, computeOrOptions, options) {
2321
+ if (typeof computeOrOptions !== "function") return createValue(scheduler, input, computeOrOptions);
2322
+ const compute = (input) => {
2323
+ const result = computeOrOptions(input);
2324
+ assertSynchronous(result);
2325
+ return result;
2326
+ };
2327
+ return createValue(scheduler, {
2328
+ sources: input,
2329
+ build: (input) => ({
2330
+ value: compute(input),
2331
+ update: (input) => ({
2332
+ kind: "changed",
2333
+ value: compute(input)
2334
+ })
2335
+ })
2336
+ }, options);
2337
+ }
2338
+ const map = (source, mapper, options) => {
2339
+ sources.assertCollection(source);
2340
+ const calculate = (id, entry) => {
2341
+ const value = mapper(id, entry);
2342
+ assertSynchronous(value);
2343
+ return value;
2344
+ };
2345
+ return createCollection(scheduler, {
2346
+ sources: { source },
2347
+ isEqual: options?.isEqual,
2348
+ build: ({ sources, writer }) => {
2349
+ const input = sources.source;
2350
+ const read = "read" in input ? input.read : input;
2351
+ const ids = read.ids();
2352
+ profile.collectionView.idsScanned(ids.length);
2353
+ for (const id of ids) {
2354
+ profile.collectionView.mapped();
2355
+ writer.set(id, calculate(id, read.get(id)));
2356
+ }
2357
+ writer.order(ids);
2358
+ return { update: ({ sources, writer }) => {
2359
+ const input = sources.source;
2360
+ if (input.reset) return { kind: "rebuild" };
2361
+ const read = "read" in input ? input.read : input;
2362
+ let keys;
2363
+ let structural;
2364
+ if ("candidates" in input) {
2365
+ keys = input.candidates.keys;
2366
+ structural = input.candidates.orderDirty;
2367
+ } else {
2368
+ const change = input.change;
2369
+ if (!change) return;
2370
+ if (change.kind === "reset") return { kind: "rebuild" };
2371
+ keys = new Set([
2372
+ ...change.added,
2373
+ ...change.removed,
2374
+ ...change.updated
2375
+ ]);
2376
+ structural = Boolean(change.added.size || change.removed.size || change.orderChanged);
2377
+ }
2378
+ for (const key of keys) if (!read.has(key)) writer.remove(key);
2379
+ else {
2380
+ profile.collectionView.mapped();
2381
+ writer.set(key, calculate(key, read.get(key)));
2382
+ }
2383
+ if (structural) {
2384
+ const ids = read.ids();
2385
+ profile.collectionView.idsScanned(ids.length);
2386
+ writer.order(ids);
2387
+ }
2388
+ } };
2389
+ }
2390
+ });
2391
+ };
2392
+ const runtime = {
2393
+ document: sources.document,
2394
+ input: sources.input,
2395
+ fromReadable: sources.fromReadable,
2396
+ value,
2397
+ collection: () => (spec) => createCollection(scheduler, spec),
2398
+ map,
2399
+ batch: scheduler.batch,
2400
+ dispose: () => {
2401
+ try {
2402
+ scheduler.dispose();
2403
+ } finally {
2404
+ if (!scheduler.active) sources.dispose();
2405
+ }
2406
+ }
2407
+ };
2408
+ debug.set(runtime, scheduler.debug);
2409
+ return Object.freeze(runtime);
2410
+ };
2411
+ //#endregion
2412
+ //#region core/src/projection/store.ts
2413
+ const stores = /* @__PURE__ */ new WeakMap();
2414
+ const projectionStoreDebug = (store) => {
2415
+ const runtime = stores.get(store);
2416
+ if (!runtime) throw new Error("Unknown projection store.");
2417
+ return projectionEngineDebug(runtime);
2418
+ };
2419
+ const createProjectionStore = (options) => {
2420
+ const runtime = createProjectionEngine(options);
2421
+ const instances = /* @__PURE__ */ new WeakMap();
2422
+ const inputs = /* @__PURE__ */ new WeakMap();
2423
+ const materialize = (projection) => {
2424
+ const old = instances.get(projection);
2425
+ if (old) return old;
2426
+ const definition = definitionOf(projection);
2427
+ let instance;
2428
+ if (definition.kind === "input") {
2429
+ const input = runtime.input(definition.initial, { isEqual: definition.isEqual });
2430
+ inputs.set(projection, input);
2431
+ instance = runtime.value({ input: input.source }, ({ input }) => input.value);
2432
+ } else if (definition.kind === "readable") {
2433
+ const source = runtime.fromReadable(definition.readable, { isEqual: definition.isEqual });
2434
+ instance = runtime.value({ source }, ({ source }) => source.value);
2435
+ } else if (definition.kind === "document") {
2436
+ const document = runtime.document(definition.document);
2437
+ instance = definition.targets?.length ? document.targets(...definition.targets) : document;
2438
+ } else if (definition.kind === "document-collection") instance = runtime.document(definition.document).collection(definition.pick);
2439
+ else if (definition.kind === "map") instance = runtime.map(materialize(definition.source), definition.mapper, { isEqual: definition.isEqual });
2440
+ else {
2441
+ const sources = Object.fromEntries(Object.entries(definition.sources).map(([key, source]) => [key, materialize(source)]));
2442
+ if (definition.kind === "value") instance = runtime.value(sources, (events) => {
2443
+ const values = Object.fromEntries(Object.entries(events).map(([key, event]) => [key, current(event)]));
2444
+ return definition.compute(values);
2445
+ }, { isEqual: definition.isEqual });
2446
+ else if (definition.kind === "advanced-value") instance = runtime.value({
2447
+ sources,
2448
+ build: definition.build,
2449
+ name: definition.name
2450
+ }, { isEqual: definition.isEqual });
2451
+ else instance = runtime.collection()({
2452
+ sources,
2453
+ build: definition.build,
2454
+ isEqual: definition.isEqual,
2455
+ name: definition.name
2456
+ });
2457
+ }
2458
+ instances.set(projection, instance);
2459
+ return instance;
2460
+ };
2461
+ const current = (event) => {
2462
+ if (event && typeof event === "object") {
2463
+ if ("value" in event) return event.value;
2464
+ if ("read" in event) return event.read;
2465
+ }
2466
+ return event;
2467
+ };
2468
+ const store = {
2469
+ get: ((projection) => {
2470
+ const instance = materialize(projection);
2471
+ if ("current" in instance && typeof instance.current === "function") return instance.current();
2472
+ throw new TypeError("Projection is a source and has no published value.");
2473
+ }),
2474
+ set: (projection, value) => {
2475
+ materialize(projection);
2476
+ const target = inputs.get(projection);
2477
+ if (!target) throw new TypeError("Projection is not an input.");
2478
+ target.set(value);
2479
+ },
2480
+ subscribe: ((projection, listener) => {
2481
+ const instance = materialize(projection);
2482
+ if (!("subscribe" in instance)) throw new TypeError("Projection is a source and cannot be subscribed.");
2483
+ return instance.subscribe(listener);
2484
+ }),
2485
+ revision: (projection) => {
2486
+ const instance = materialize(projection);
2487
+ if (!("revision" in instance) || typeof instance.revision !== "function") throw new TypeError("Projection has no published revision.");
2488
+ return instance.revision();
2489
+ },
2490
+ rebuild: (projection) => {
2491
+ const instance = materialize(projection);
2492
+ if (!("rebuild" in instance) || typeof instance.rebuild !== "function") throw new TypeError("Projection cannot be rebuilt.");
2493
+ instance.rebuild();
2494
+ },
2495
+ release: (projection) => {
2496
+ const instance = instances.get(projection);
2497
+ if (!instance) return;
2498
+ if ("dispose" in instance && typeof instance.dispose === "function") instance.dispose();
2499
+ instances.delete(projection);
2500
+ inputs.delete(projection);
2501
+ },
2502
+ batch: runtime.batch,
2503
+ dispose: runtime.dispose
2504
+ };
2505
+ stores.set(store, runtime);
2506
+ return Object.freeze(store);
2507
+ };
2508
+ //#endregion
2509
+ export { object as A, parse as B, assign as C, field as D, compilePath as E, ParseError as F, checkKey as I, checkValue as L, table as M, tree as N, list as O, variant as P, copyValue as R, same as S, snapshot as T, readWith as _, input as a, TransactionRejected as b, createNotification as c, shareNotification as d, subscribeDependencies as f, bindRuntimeAccess as g, accessOf as h, ProjectionError as i, optional as j, map as k, disposeNotification as l, subscribeTargets as m, projectionStoreDebug as n, project as o, subscribeRoot as p, ProjectionDisposedError as r, bindDocumentReadable as s, createProjectionStore as t, notify as u, DocumentDisposedError as v, createAccess as w, createImpact as x, DocumentReentrancyError as y, equalValue as z };
2510
+
2511
+ //# sourceMappingURL=store-D7QH6Rzw.js.map