doxum 0.1.11 → 0.1.13

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 (64) hide show
  1. package/README.md +43 -33
  2. package/dist/advanced.cjs +116 -0
  3. package/dist/advanced.cjs.map +1 -0
  4. package/dist/advanced.d.cts +42 -0
  5. package/dist/advanced.d.ts +42 -0
  6. package/dist/advanced.js +115 -0
  7. package/dist/advanced.js.map +1 -0
  8. package/dist/{contract-CeAnEPBA.d.ts → contract-BT53Gg94.d.ts} +5 -4
  9. package/dist/{contract-DtGVSXSK.d.cts → contract-XmKnroJq.d.cts} +5 -4
  10. package/dist/definition-177L4Amk.cjs +898 -0
  11. package/dist/definition-177L4Amk.cjs.map +1 -0
  12. package/dist/definition-BlMeHwdz.d.ts +98 -0
  13. package/dist/definition-DXrKMVs7.js +785 -0
  14. package/dist/definition-DXrKMVs7.js.map +1 -0
  15. package/dist/definition-kxnWUvX9.d.cts +98 -0
  16. package/dist/{driver-xOIkwrB8.cjs → driver-BCyfqxsl.cjs} +1 -1
  17. package/dist/{driver-xOIkwrB8.cjs.map → driver-BCyfqxsl.cjs.map} +1 -1
  18. package/dist/{driver-BlR81Dqg.js → driver-CSnhMjbP.js} +1 -1
  19. package/dist/{driver-BlR81Dqg.js.map → driver-CSnhMjbP.js.map} +1 -1
  20. package/dist/index.cjs +31 -29
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +5 -4
  23. package/dist/index.d.ts +5 -4
  24. package/dist/index.js +7 -6
  25. package/dist/index.js.map +1 -1
  26. package/dist/{integration-VTZ3ICsO.js → integration-Bco8ZB16.js} +7 -3
  27. package/dist/integration-Bco8ZB16.js.map +1 -0
  28. package/dist/integration-CEUn96tx.cjs +52 -0
  29. package/dist/integration-CEUn96tx.cjs.map +1 -0
  30. package/dist/integration.cjs +7 -3
  31. package/dist/integration.d.cts +7 -3
  32. package/dist/integration.d.ts +7 -3
  33. package/dist/integration.js +3 -3
  34. package/dist/local-sync.cjs +1 -1
  35. package/dist/local-sync.d.cts +1 -1
  36. package/dist/local-sync.d.ts +1 -1
  37. package/dist/local-sync.js +1 -1
  38. package/dist/react.cjs +66 -12
  39. package/dist/react.cjs.map +1 -1
  40. package/dist/react.d.cts +8 -6
  41. package/dist/react.d.ts +8 -6
  42. package/dist/react.js +66 -12
  43. package/dist/react.js.map +1 -1
  44. package/dist/store-CDeqZEE3.d.cts +28 -0
  45. package/dist/{store-DHFXfWSz.js → store-CtgvCVFg.js} +585 -917
  46. package/dist/store-CtgvCVFg.js.map +1 -0
  47. package/dist/{store-DU2u-qFH.cjs → store-DcDpDgjk.cjs} +595 -987
  48. package/dist/store-DcDpDgjk.cjs.map +1 -0
  49. package/dist/store-P5dJ7TVe.d.ts +28 -0
  50. package/package.json +6 -1
  51. package/skills/doxum-runtime/SKILL.md +5 -5
  52. package/skills/doxum-runtime/references/guide.en.md +12 -12
  53. package/skills/doxum-runtime/references/guide.zh-CN.md +9 -11
  54. package/skills/doxum-runtime/references/patterns.en.md +12 -17
  55. package/skills/doxum-runtime/references/patterns.zh-CN.md +12 -16
  56. package/skills/doxum-runtime/references/projections.en.md +56 -190
  57. package/skills/doxum-runtime/references/projections.zh-CN.md +49 -171
  58. package/dist/integration-BKgGuodm.cjs +0 -30
  59. package/dist/integration-BKgGuodm.cjs.map +0 -1
  60. package/dist/integration-VTZ3ICsO.js.map +0 -1
  61. package/dist/store-Bs0C0rNd.d.ts +0 -262
  62. package/dist/store-CJuwJl_K.d.cts +0 -262
  63. package/dist/store-DHFXfWSz.js.map +0 -1
  64. package/dist/store-DU2u-qFH.cjs.map +0 -1
@@ -1,189 +1,8 @@
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
1
+ import { L as profile, f as read, i as AddressIndex, o as contains } from "./issue-DVaGQGeP.js";
2
+ import { d as createAccess, h as checkKey, o as definitionOf, p as snapshot, u as collectionAccess } from "./definition-DXrKMVs7.js";
184
3
  //#region core/src/schema.ts
185
- const node$1 = (value) => Object.freeze(value);
186
- const field = (validator) => node$1({
4
+ const node = (value) => Object.freeze(value);
5
+ const field = (validator) => node({
187
6
  kind: "field",
188
7
  ...validator ? { validator } : {}
189
8
  });
@@ -195,36 +14,36 @@ const optional = (value) => {
195
14
  "list",
196
15
  "tree"
197
16
  ].includes(value.kind)) throw new TypeError("Only field, variant, map, list and tree nodes support optional presence.");
198
- return node$1({
17
+ return node({
199
18
  ...value,
200
19
  optional: true
201
20
  });
202
21
  };
203
- const object = (shape) => node$1({
22
+ const object = (shape) => node({
204
23
  kind: "object",
205
24
  shape: Object.freeze({ ...shape })
206
25
  });
207
- const variant = (tag, variants) => node$1({
26
+ const variant = (tag, variants) => node({
208
27
  kind: "variant",
209
28
  tag,
210
29
  variants: Object.freeze({ ...variants })
211
30
  });
212
- const table = (value, options) => node$1({
31
+ const table = (value, options) => node({
213
32
  kind: "table",
214
33
  value,
215
34
  ...options
216
35
  });
217
- const map = (value, options) => node$1({
36
+ const map = (value, options) => node({
218
37
  kind: "map",
219
38
  value,
220
39
  ...options
221
40
  });
222
- const list = (value, config) => node$1({
41
+ const list = (value, config) => node({
223
42
  kind: "list",
224
43
  keyOf: config.keyOf,
225
44
  value
226
45
  });
227
- const tree = (value) => node$1({
46
+ const tree = (value) => node({
228
47
  kind: "tree",
229
48
  value
230
49
  });
@@ -269,542 +88,6 @@ const compilePath = (owner, kind, pick) => {
269
88
  });
270
89
  };
271
90
  //#endregion
272
- //#region core/src/mutation/operations/map.ts
273
- function put(session, container, id, value) {
274
- if (container.node.kind !== "map") return fail(container.at, "invalid-collection", "Expected a map.");
275
- session.writeMember(container, id, value, "set");
276
- }
277
- function remove$3(session, container, id) {
278
- if (container.node.kind !== "map") return fail(container.at, "invalid-collection", "Expected a map.");
279
- session.writeMember(container, id, void 0, "remove");
280
- }
281
- //#endregion
282
- //#region core/src/mutation/operations/table.ts
283
- function create(session, container, entries, position) {
284
- const { at, node, value } = container;
285
- if (node.kind !== "table") return fail(at, "invalid-collection", "Expected a table.");
286
- const table = value;
287
- if (!valid(table.ids, position)) return fail(at, "invalid-anchor", "Unknown table anchor.");
288
- const ids = /* @__PURE__ */ new Set();
289
- for (const entry of entries) {
290
- if (Object.hasOwn(table.byId, entry.id) || ids.has(entry.id)) return fail(at, "duplicate-entity", "Table key already exists.");
291
- ids.add(entry.id);
292
- }
293
- if (!entries.length) return;
294
- const index$1 = index(table.ids, position);
295
- for (const entry of entries) session.writeMember(container, entry.id, entry.value, "set");
296
- const length = table.ids.length;
297
- table.ids.length += entries.length;
298
- table.ids.copyWithin(index$1 + entries.length, index$1, length);
299
- for (let i = 0; i < entries.length; i++) table.ids[index$1 + i] = entries[i].id;
300
- }
301
- function remove$2(session, container, ids) {
302
- const { at, node, value } = container;
303
- if (node.kind !== "table") return fail(at, "invalid-collection", "Expected a table.");
304
- const table = value;
305
- for (const id of ids) if (!Object.hasOwn(table.byId, id)) return fail(at, "missing-entity", "Table key does not exist.");
306
- if (!ids.length) return;
307
- const removed = new Set(ids);
308
- for (const id of removed) session.writeMember(container, id, void 0, "remove");
309
- table.ids = table.ids.filter((id) => !removed.has(id));
310
- }
311
- function replace$3(session, container, id, value) {
312
- const { at, node, value: tableValue } = container;
313
- if (node.kind !== "table") return fail(at, "invalid-collection", "Expected a table.");
314
- const table = tableValue;
315
- if (!Object.hasOwn(table.byId, id)) return fail(at.concat(id), "missing-entity", "Table key does not exist.");
316
- session.writeMember(container, id, value, "set");
317
- }
318
- //#endregion
319
- //#region core/src/mutation/operations/list.ts
320
- function insert$1(session, container, value, position) {
321
- const { at, node, value: current } = container;
322
- if (node.kind !== "list") return fail(at, "invalid-list-key", "Expected a list.");
323
- session.validate(node.value, value, at);
324
- const id = node.keyOf(value);
325
- if (typeof id !== "string") return fail(at, "invalid-list-key", "List key must be a string.");
326
- const items = current, keys$2 = keys(items, node.keyOf);
327
- if (keys$2.index(id) >= 0) return fail(at, "duplicate-list-item", "List key already exists.");
328
- if (!valid(keys$2, position)) return fail(at, "invalid-anchor", "Unknown list anchor.");
329
- session.recorder.order(container);
330
- session.recorder.member(container, id, session.definition(container, id), -1);
331
- insert$2(items, index(keys$2, position), value);
332
- session.invalidate();
333
- }
334
- function replace$2(session, container, id, value) {
335
- const { at, node, value: items } = container;
336
- if (node.kind !== "list") return fail(at, "invalid-list-key", "Expected a list.");
337
- const index = indexedKeys(items, node.keyOf).index(id);
338
- if (index < 0) return fail(at, "missing-list-item", "List key does not exist.");
339
- session.writeMember(container, id, value, "set", index);
340
- }
341
- function remove$1(session, container, id) {
342
- const { at, node, value } = container;
343
- if (node.kind !== "list") return fail(at, "missing-list-item", "List key does not exist.");
344
- const index = keys(value, node.keyOf).index(id);
345
- if (index < 0) return fail(at, "missing-list-item", "List key does not exist.");
346
- session.writeMember(container, id, void 0, "remove", index);
347
- }
348
- //#endregion
349
- //#region core/src/mutation/operations/tree.ts
350
- const node = (tree, id) => Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : void 0;
351
- const positionIndex = (index, length, at) => {
352
- if (index !== void 0 && (!Number.isInteger(index) || index < 0)) return fail(at, "invalid-tree-index", "Tree index must be nonnegative.");
353
- return Math.min(index ?? length, length);
354
- };
355
- const insert = (session, container, id, value, position) => {
356
- const { at, node: schema, value: tree } = container;
357
- if (typeof id !== "string") return fail(at, "invalid-key", "Tree keys must be strings.");
358
- if (contains(tree, id)) return fail(at, "duplicate-tree-node", "Tree node already exists.");
359
- const parentId = position?.parentId;
360
- const parent = parentId === void 0 ? void 0 : node(tree, parentId);
361
- 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.");
362
- const index = positionIndex(position?.index, parent?.children.length ?? 0, at);
363
- session.validate(schema.value, value, at.concat(id));
364
- session.recorder.tree(container, id);
365
- if (parentId !== void 0) session.recorder.tree(container, parentId);
366
- Object.defineProperty(tree.nodes, id, {
367
- value: {
368
- ...parentId === void 0 ? {} : { parentId },
369
- children: [],
370
- value
371
- },
372
- writable: true,
373
- configurable: true,
374
- enumerable: true
375
- });
376
- if (parent) parent.children.splice(index, 0, id);
377
- else tree.rootId = id;
378
- session.invalidate();
379
- };
380
- const replace$1 = (session, container, id, value) => {
381
- const { at, node: schema, value: tree } = container;
382
- const entry = node(tree, id);
383
- if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
384
- if (Object.is(entry.value, value) && Object.hasOwn(entry, "value")) return;
385
- session.validate(schema.value, value, at.concat(id));
386
- session.recorder.tree(container, id);
387
- entry.value = value;
388
- };
389
- const remove = (session, container, id) => {
390
- const { at, value: tree } = container;
391
- const entry = node(tree, id);
392
- if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
393
- const ids = [], stack = [id];
394
- while (stack.length) {
395
- const current = stack.pop();
396
- ids.push(current);
397
- for (const child of tree.nodes[current].children) stack.push(child);
398
- }
399
- for (const key of ids) session.recorder.tree(container, key);
400
- if (entry.parentId !== void 0) session.recorder.tree(container, entry.parentId);
401
- if (entry.parentId === void 0) delete tree.rootId;
402
- else {
403
- const children = tree.nodes[entry.parentId].children;
404
- children.splice(children.indexOf(id), 1);
405
- }
406
- for (const key of ids) delete tree.nodes[key];
407
- session.invalidate();
408
- };
409
- const move$1 = (session, container, id, position) => {
410
- const { at, value: tree } = container;
411
- const entry = node(tree, id), parentId = position?.parentId;
412
- if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
413
- 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.");
414
- 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.");
415
- const old = tree.nodes[entry.parentId], next = tree.nodes[parentId];
416
- const index = positionIndex(position?.index, next.children.length - (old === next ? 1 : 0), at);
417
- const previous = old.children.indexOf(id);
418
- if (old === next && previous === index) return;
419
- session.recorder.tree(container, id);
420
- session.recorder.tree(container, entry.parentId);
421
- session.recorder.tree(container, parentId);
422
- old.children.splice(previous, 1);
423
- next.children.splice(index, 0, id);
424
- entry.parentId = parentId;
425
- session.invalidate();
426
- };
427
- //#endregion
428
- //#region core/src/mutation/operations/order.ts
429
- function move(session, container, id, position) {
430
- const { at, node, value } = container;
431
- if (node.kind !== "table" && node.kind !== "list") return fail(at, "invalid-collection", "Expected an ordered container.");
432
- const items = node.kind === "table" ? value.ids : value;
433
- const keys$1 = node.kind === "table" ? items : keys(items, node.keyOf);
434
- const index = node.kind === "table" ? items.indexOf(id) : keys$1.index(id);
435
- if (index < 0) return fail(at, "missing-entity", "Ordered key does not exist.");
436
- if (!valid(keys$1, position)) return fail(at, "invalid-anchor", "Unknown order anchor.");
437
- const next = afterRemove(keys$1, index, position);
438
- if (next === index) return;
439
- session.recorder.order(container);
440
- move$2(items, index, next);
441
- session.invalidate();
442
- }
443
- //#endregion
444
- //#region core/src/access/scope.ts
445
- const locations = /* @__PURE__ */ new WeakMap();
446
- const locationOf = (value) => locations.get(value);
447
- const addressOf = (target) => {
448
- if (target.at) return target.at;
449
- profile.access("addresses");
450
- return target.at = addressOf(target.parent).concat(target.key);
451
- };
452
- const assertActive = (context) => {
453
- if (context.active && !context.active()) throw new Error("Document access scope has expired.");
454
- };
455
- const collect = (context, at, kind = "value", id) => {
456
- context.dependencies?.record(kind === "value" ? {
457
- kind,
458
- at
459
- } : {
460
- kind,
461
- at,
462
- ...id === void 0 ? {} : { id }
463
- });
464
- };
465
- const snapshot = (value) => {
466
- const location = value && typeof value === "object" ? locationOf(value) : void 0;
467
- if (!location) return value;
468
- const { context } = location;
469
- const at = addressOf(location.target);
470
- profile.access("snapshots");
471
- collect(context, at);
472
- const resolved = resolve(context, location.target);
473
- if (!resolved.node) throw new Error("The selected schema address no longer exists.");
474
- return copyValue(resolved.node, resolved.value);
475
- };
476
- const replace = (container, key, value) => {
477
- if (!locationOf(container)) throw new TypeError("replace requires scoped document access.");
478
- if (typeof key !== "string") throw new TypeError("Document keys must be strings.");
479
- Reflect.set(container, key, value);
480
- };
481
- const resolve = (context, target) => {
482
- assertActive(context);
483
- const generation = context.session?.generation ?? 0;
484
- if (target.generation !== generation) {
485
- profile.access("resolutions");
486
- if (!target.parent && !target.at.length) {
487
- target.node = context.state.schema;
488
- target.value = context.state.document;
489
- } else {
490
- const parent = target.parent && resolve(context, target.parent);
491
- const location = parent ? resolveChild(parent.node, parent.value, target.key) : resolveLocated(context.state.schema, context.state.document, target.at);
492
- target.node = location?.node ?? nodeAt(context.state.schema, addressOf(target), context.state.document);
493
- target.value = location && Object.hasOwn(location.parent, location.key) ? location.parent[location.key] : void 0;
494
- }
495
- target.generation = generation;
496
- }
497
- return target;
498
- };
499
- const createAccess = (context, initial = []) => {
500
- profile.access("scopes");
501
- const mutable = () => {
502
- assertActive(context);
503
- if (!context.session) throw new TypeError("Cannot modify read-only document access.");
504
- return context.session;
505
- };
506
- const writableContainer = (target, session) => {
507
- if (target.container?.generation === session.generation && "layout" in target.container) return target.container;
508
- return target.container = session.bind(addressOf(target), target.node, target.value);
509
- };
510
- const childAt = (target, key) => {
511
- profile.access("addresses");
512
- return addressOf(target).concat(key);
513
- };
514
- const access = (target) => {
515
- if (target.proxy) return target.proxy;
516
- const proxy = new Proxy(target, handler);
517
- profile.access("proxies");
518
- locations.set(proxy, {
519
- context,
520
- target
521
- });
522
- target.proxy = proxy;
523
- return proxy;
524
- };
525
- const createTarget = (at, parent, key) => ({
526
- at,
527
- parent,
528
- key,
529
- node: void 0,
530
- value: void 0,
531
- generation: -1,
532
- children: void 0,
533
- proxy: void 0
534
- });
535
- const childAccess = (target, member, node, value) => {
536
- const key = typeof member === "string" ? member : member.key;
537
- if (node.kind === "field" || value === void 0) {
538
- if (context.dependencies) collect(context, childAt(target, key));
539
- return value;
540
- }
541
- let children = target.children;
542
- let child;
543
- if (typeof member !== "string") {
544
- if (children?.kind !== "fixed" || children.schema !== target.node) children = {
545
- kind: "fixed",
546
- schema: target.node,
547
- slots: new Array(compiledShape(target.node, target.value).members.size)
548
- };
549
- child = children.slots[member.slot] ??= createTarget(void 0, target, key);
550
- } else {
551
- if (children?.kind !== "dynamic") children = {
552
- kind: "dynamic",
553
- keys: /* @__PURE__ */ new Map()
554
- };
555
- const retained = children.keys.get(key);
556
- child = retained ?? createTarget(void 0, target, key);
557
- if (!retained) children.keys.set(key, child);
558
- }
559
- target.children = children;
560
- if (child.generation === target.generation) return child.proxy;
561
- child.node = node;
562
- child.value = value;
563
- child.generation = target.generation;
564
- return access(child);
565
- };
566
- const collectionValue = (target, node) => {
567
- const current = resolve(context, target);
568
- if (current.node !== node || current.value === void 0) throw new TypeError("The collection method belongs to a replaced schema branch.");
569
- return current.value;
570
- };
571
- const writableCollection = (target, node) => {
572
- const session = mutable();
573
- collectionValue(target, node);
574
- return writableContainer(target, session);
575
- };
576
- const writableTree = (target, node) => {
577
- const session = mutable();
578
- collectionValue(target, node);
579
- if (target.container?.generation === session.generation && !("layout" in target.container)) return target.container;
580
- return target.container = session.bindTree(addressOf(target), target.node, target.value);
581
- };
582
- const replaceCollection = (target, node, value) => {
583
- const session = mutable();
584
- collectionValue(target, node);
585
- session.replace(addressOf(target), value);
586
- };
587
- const mapMethod = (target, property, node) => {
588
- if (property === "ids") return () => {
589
- const current = collectionValue(target, node);
590
- if (context.dependencies) collect(context, addressOf(target), "collection");
591
- return Object.keys(current);
592
- };
593
- if (property === "has") return (id) => {
594
- const current = collectionValue(target, node);
595
- if (context.dependencies) collect(context, addressOf(target), "collection", id);
596
- return Object.hasOwn(current, id);
597
- };
598
- if (property === "get") return (id) => {
599
- const current = collectionValue(target, node);
600
- if (context.dependencies) collect(context, addressOf(target), "collection", id);
601
- if (!Object.hasOwn(current, id)) return void 0;
602
- if (node.value.kind === "field") {
603
- if (context.dependencies) collect(context, childAt(target, id));
604
- return current[id];
605
- }
606
- return childAccess(target, id, node.value, current[id]);
607
- };
608
- if (property === "put") return (id, value) => {
609
- const writable = writableCollection(target, node);
610
- put(context.session, writable, id, value);
611
- };
612
- if (property === "remove") return (id) => {
613
- const writable = writableCollection(target, node);
614
- remove$3(context.session, writable, id);
615
- };
616
- if (property === "replace") return (value) => replaceCollection(target, node, value);
617
- };
618
- const orderedMethod = (target, property, node) => {
619
- const at = addressOf(target);
620
- if (property === "ids") return () => {
621
- const current = collectionValue(target, node);
622
- collect(context, at, "collection");
623
- return node.kind === "table" ? [...current.ids] : current.map(node.keyOf);
624
- };
625
- if (property === "has") return (id) => {
626
- const current = collectionValue(target, node);
627
- collect(context, at, "collection", id);
628
- return node.kind === "table" ? Object.hasOwn(current.byId, id) : indexedKeys(current, node.keyOf).index(id) >= 0;
629
- };
630
- if (property === "get") return (id) => {
631
- const current = collectionValue(target, node);
632
- collect(context, at, "collection", id);
633
- const location = resolveChild(node, current, id);
634
- if (!location || !Object.hasOwn(location.parent, location.key)) return void 0;
635
- return childAccess(target, id, location.node, location.parent[location.key]);
636
- };
637
- if (property === "move") return (id, position) => {
638
- const writable = writableCollection(target, node);
639
- move(context.session, writable, id, position);
640
- };
641
- };
642
- const tableMethod = (target, property, node) => {
643
- if (property === "create") return (input, valueOrPosition, position) => {
644
- const writable = writableCollection(target, node);
645
- create(context.session, writable, Array.isArray(input) ? input : [{
646
- id: input,
647
- value: valueOrPosition
648
- }], Array.isArray(input) ? valueOrPosition : position);
649
- };
650
- if (property === "remove") return (ids) => {
651
- const writable = writableCollection(target, node);
652
- remove$2(context.session, writable, typeof ids === "string" ? [ids] : ids);
653
- };
654
- if (property === "replace") return (...args) => {
655
- const [valueOrId, value] = args;
656
- if (args.length === 1) return replaceCollection(target, node, valueOrId);
657
- const writable = writableCollection(target, node);
658
- replace$3(context.session, writable, valueOrId, value);
659
- };
660
- return orderedMethod(target, property, node);
661
- };
662
- const listMethod = (target, property, node) => {
663
- if (property === "insert") return (value, position) => {
664
- const writable = writableCollection(target, node);
665
- insert$1(context.session, writable, value, position);
666
- };
667
- if (property === "remove") return (id) => {
668
- const writable = writableCollection(target, node);
669
- remove$1(context.session, writable, id);
670
- };
671
- if (property === "replace") return (...args) => {
672
- const [valueOrId, value] = args;
673
- if (args.length === 1) return replaceCollection(target, node, valueOrId);
674
- const writable = writableCollection(target, node);
675
- replace$2(context.session, writable, valueOrId, value);
676
- };
677
- return orderedMethod(target, property, node);
678
- };
679
- const treeMethod = (target, property, node) => {
680
- const at = addressOf(target);
681
- if (property === "insert") return (id, value, position) => {
682
- const writable = writableTree(target, node);
683
- insert(context.session, writable, id, value, position);
684
- };
685
- if (property === "remove") return (id) => {
686
- const writable = writableTree(target, node);
687
- remove(context.session, writable, id);
688
- };
689
- if (property === "move") return (id, position) => {
690
- const writable = writableTree(target, node);
691
- move$1(context.session, writable, id, position);
692
- };
693
- if (property === "replace") return (...args) => {
694
- const [valueOrId, value] = args;
695
- if (args.length === 1) return replaceCollection(target, node, valueOrId);
696
- const writable = writableTree(target, node);
697
- replace$1(context.session, writable, valueOrId, value);
698
- };
699
- const current = () => {
700
- collect(context, at);
701
- return collectionValue(target, node);
702
- };
703
- if (property === "rootId") return () => current().rootId;
704
- if (property === "get") return (id) => {
705
- const value = current();
706
- return contains(value, id) ? value.nodes[id].value : void 0;
707
- };
708
- if (property === "has") return (id) => contains(current(), id);
709
- if (property === "parent") return (id) => parent(current(), id);
710
- if (property === "children") return (id) => {
711
- const children$1 = children(current(), id);
712
- return children$1 ? [...children$1] : void 0;
713
- };
714
- };
715
- const handler = {
716
- get: (target, property) => {
717
- const { node, value } = resolve(context, target);
718
- if (!node || value === void 0) return void 0;
719
- if (typeof property !== "string") return void 0;
720
- if (node.kind === "map") return mapMethod(target, property, node);
721
- if (node.kind === "table") return tableMethod(target, property, node);
722
- if (node.kind === "list") return listMethod(target, property, node);
723
- if (node.kind === "tree") return treeMethod(target, property, node);
724
- if (node.kind === "variant" && property === node.tag) {
725
- collect(context, childAt(target, property));
726
- return value[property];
727
- }
728
- const member = compiledShape(node, value)?.members.get(property);
729
- if (!member) return void 0;
730
- if (member.field) {
731
- if (context.dependencies) collect(context, childAt(target, property));
732
- return value[property];
733
- }
734
- return childAccess(target, node.kind === "object" ? member : property, member.node, value[property]);
735
- },
736
- set: (target, property, value) => {
737
- const session = mutable(), { node } = resolve(context, target);
738
- if (typeof property !== "string" || !node || node.kind !== "object" && node.kind !== "variant") throw new TypeError("Invalid structural assignment.");
739
- if (node.kind === "variant" && node.tag === property) throw new TypeError("Variant discriminants are read-only.");
740
- session.assignMember(writableContainer(target, session), property, value);
741
- return true;
742
- },
743
- deleteProperty: (target, property) => {
744
- const session = mutable(), { node } = resolve(context, target);
745
- if (typeof property !== "string" || node?.kind === "map" || node?.kind === "variant" && node.tag === property) throw new TypeError("Invalid structural deletion.");
746
- session.removeMember(writableContainer(target, session), property);
747
- return true;
748
- },
749
- has: (target, property) => {
750
- const { node, value } = resolve(context, target);
751
- if (typeof property !== "string") return false;
752
- if (node?.kind === "map") return false;
753
- collect(context, addressOf(target));
754
- return value !== void 0 && Object.hasOwn(value, property);
755
- },
756
- ownKeys: (target) => {
757
- const { node, value } = resolve(context, target);
758
- if (node?.kind === "map") return [];
759
- if (context.dependencies) collect(context, addressOf(target));
760
- return value && typeof value === "object" ? Object.keys(value) : [];
761
- },
762
- getOwnPropertyDescriptor: (target, property) => {
763
- const { node, value } = resolve(context, target);
764
- if (node?.kind === "map") return void 0;
765
- return value && Object.hasOwn(value, property) ? {
766
- enumerable: true,
767
- configurable: true
768
- } : void 0;
769
- },
770
- defineProperty: () => {
771
- assertActive(context);
772
- throw new TypeError("Use schema assignments.");
773
- },
774
- setPrototypeOf: () => {
775
- assertActive(context);
776
- throw new TypeError("Document prototypes are immutable.");
777
- },
778
- preventExtensions: () => {
779
- assertActive(context);
780
- throw new TypeError("Document access cannot be frozen.");
781
- }
782
- };
783
- const root = resolve(context, createTarget(initial, void 0, ""));
784
- if (!root.node || root.node.kind === "field" || root.value === void 0) {
785
- if (context.dependencies) collect(context, initial);
786
- return root.value;
787
- }
788
- return access(root);
789
- };
790
- const collectionAccess = (context, at) => {
791
- const value = createAccess(context, at);
792
- if (value === void 0) return {
793
- get: () => {
794
- assertActive(context);
795
- },
796
- has: () => {
797
- assertActive(context);
798
- return false;
799
- },
800
- ids: () => {
801
- assertActive(context);
802
- return [];
803
- }
804
- };
805
- return value;
806
- };
807
- //#endregion
808
91
  //#region core/src/impact-target.ts
809
92
  const address = (target) => "at" in target ? target.at : target.address;
810
93
  const id = (target) => target.kind === "collection" && "id" in target ? target.id : void 0;
@@ -904,13 +187,13 @@ const createImpact = (schema, changes) => {
904
187
  for (const change of changes.changes) {
905
188
  if (change.kind === "reset") continue;
906
189
  if (change.kind === "members") {
907
- if (change.at.length < at.length && contains$1(change.at, at)) {
190
+ if (change.at.length < at.length && contains(change.at, at)) {
908
191
  if (change.members.some((member) => member.key === at[change.at.length])) {
909
192
  const result = { kind: "reset" };
910
193
  cache.set(key, result);
911
194
  return result;
912
195
  }
913
- } else if (contains$1(at, change.at)) if (change.at.length === at.length) {
196
+ } else if (contains(at, change.at)) if (change.at.length === at.length) {
914
197
  if (change.order) {
915
198
  const positions = new Map(change.order.after.map((id, i) => [id, i]));
916
199
  let previous = -1;
@@ -927,12 +210,12 @@ const createImpact = (schema, changes) => {
927
210
  } else updated.add(change.at[at.length]);
928
211
  continue;
929
212
  }
930
- if (change.kind === "tree" && contains$1(change.at, at)) {
213
+ if (change.kind === "tree" && contains(change.at, at)) {
931
214
  const result = { kind: "reset" };
932
215
  cache.set(key, result);
933
216
  return result;
934
217
  }
935
- if (!contains$1(at, change.at)) continue;
218
+ if (!contains(at, change.at)) continue;
936
219
  if (change.at.length === at.length) continue;
937
220
  const id = change.at[at.length];
938
221
  updated.add(id);
@@ -1183,84 +466,6 @@ const disposeNotification = (notification) => {
1183
466
  if (errors.length) throw new AggregateError(errors, "Projection disposal notification failed.");
1184
467
  };
1185
468
  //#endregion
1186
- //#region core/src/projection/definition.ts
1187
- const definitions = /* @__PURE__ */ new WeakMap();
1188
- const freezeSources = (sources) => Object.freeze({ ...sources });
1189
- const define = (definition) => {
1190
- const handle = Object.freeze({});
1191
- definitions.set(handle, definition);
1192
- return handle;
1193
- };
1194
- const definitionOf = (projection) => {
1195
- const definition = definitions.get(projection);
1196
- if (!definition) throw new TypeError("Unknown projection definition.");
1197
- return definition;
1198
- };
1199
- const input = (initial, options) => define({
1200
- kind: "input",
1201
- initial,
1202
- isEqual: options?.isEqual
1203
- });
1204
- function project(first, second, third, fourth) {
1205
- if (first && typeof first === "object" && "kind" in first && (first.kind === "value" || first.kind === "collection")) {
1206
- const spec = first;
1207
- return define({
1208
- kind: spec.kind === "value" ? "advanced-value" : "advanced-collection",
1209
- sources: freezeSources(spec.sources),
1210
- build: spec.build,
1211
- isEqual: spec.isEqual,
1212
- name: spec.name
1213
- });
1214
- }
1215
- if (first && typeof first === "object" && "revision" in first && "subscribe" in first) {
1216
- if ("address" in first) {
1217
- const document = first;
1218
- if (!second) return define({
1219
- kind: "document",
1220
- document
1221
- });
1222
- if (Array.isArray(second)) return define({
1223
- kind: "document",
1224
- document,
1225
- targets: Object.freeze([...second])
1226
- });
1227
- if (typeof second !== "function") throw new TypeError("Invalid document projection.");
1228
- const source = define({
1229
- kind: "document-collection",
1230
- document,
1231
- pick: second
1232
- });
1233
- if (!third || typeof third !== "function") return source;
1234
- return define({
1235
- kind: "map",
1236
- source,
1237
- mapper: third,
1238
- isEqual: fourth?.isEqual
1239
- });
1240
- }
1241
- return define({
1242
- kind: "readable",
1243
- readable: first
1244
- });
1245
- }
1246
- if (second) {
1247
- if (typeof second !== "function") throw new TypeError("Invalid project declaration.");
1248
- if (definitions.has(first)) return define({
1249
- kind: "map",
1250
- source: first,
1251
- mapper: second,
1252
- isEqual: third?.isEqual
1253
- });
1254
- return define({
1255
- kind: "value",
1256
- sources: freezeSources(first),
1257
- compute: second,
1258
- isEqual: third?.isEqual
1259
- });
1260
- }
1261
- throw new TypeError("Invalid project declaration.");
1262
- }
1263
- //#endregion
1264
469
  //#region core/src/projection/contract.ts
1265
470
  var ProjectionError = class extends Error {
1266
471
  constructor(phase, identity, revisions, cause) {
@@ -1283,6 +488,8 @@ const projectionHandles = /* @__PURE__ */ new WeakSet();
1283
488
  const createScheduler = (onError) => {
1284
489
  let disposed = false;
1285
490
  let depth = 0;
491
+ let batchSequence = 0;
492
+ let activeBatch;
1286
493
  let phase = "idle";
1287
494
  let sequence = 0;
1288
495
  const records = /* @__PURE__ */ new Map();
@@ -1444,6 +651,36 @@ const createScheduler = (onError) => {
1444
651
  if (failures.length) throw new AggregateError(failures, "Projection error reporter failed.");
1445
652
  return result;
1446
653
  };
654
+ const batch = (optionsOrCallback, maybeCallback) => {
655
+ assertIdle();
656
+ const options = typeof optionsOrCallback === "function" ? void 0 : optionsOrCallback;
657
+ const callback = typeof optionsOrCallback === "function" ? optionsOrCallback : maybeCallback;
658
+ if (!callback) throw new TypeError("Projection batch requires a callback.");
659
+ if (depth === 0) activeBatch = Object.freeze({
660
+ id: ++batchSequence,
661
+ ...options?.cause === void 0 ? {} : { cause: options.cause }
662
+ });
663
+ depth++;
664
+ let failed = false;
665
+ try {
666
+ const value = callback();
667
+ assertSynchronous(value);
668
+ return value;
669
+ } catch (error) {
670
+ failed = true;
671
+ throw error;
672
+ } finally {
673
+ depth--;
674
+ if (!depth) try {
675
+ if (failed) try {
676
+ run();
677
+ } catch {}
678
+ else run();
679
+ } finally {
680
+ activeBatch = void 0;
681
+ }
682
+ }
683
+ };
1447
684
  return {
1448
685
  assertActive,
1449
686
  assertIdle,
@@ -1510,25 +747,8 @@ const createScheduler = (onError) => {
1510
747
  node.release();
1511
748
  for (const [handle, record] of records) if (record === node) records.delete(handle);
1512
749
  },
1513
- batch(callback) {
1514
- assertIdle();
1515
- depth++;
1516
- let failed = false;
1517
- try {
1518
- const value = callback();
1519
- assertSynchronous(value);
1520
- return value;
1521
- } catch (error) {
1522
- failed = true;
1523
- throw error;
1524
- } finally {
1525
- depth--;
1526
- if (!depth) if (failed) try {
1527
- run();
1528
- } catch {}
1529
- else run();
1530
- }
1531
- },
750
+ batch,
751
+ batchContext: () => activeBatch,
1532
752
  dispose() {
1533
753
  if (disposed) return;
1534
754
  assertIdle();
@@ -1660,6 +880,9 @@ const createCollection = (scheduler, spec) => {
1660
880
  let reset = false;
1661
881
  let revision = 0;
1662
882
  let idsRevision = 0;
883
+ let batch;
884
+ let cause;
885
+ let publishedTransitions = Object.freeze([]);
1663
886
  let all;
1664
887
  const items = /* @__PURE__ */ new Map();
1665
888
  const subscribedItems = /* @__PURE__ */ new Set();
@@ -1689,6 +912,9 @@ const createCollection = (scheduler, spec) => {
1689
912
  };
1690
913
  const owner = createNode(scheduler, spec, {
1691
914
  evaluate: (sources, build, active) => {
915
+ const metadata = Object.values(sources).find((value) => value && typeof value === "object" && ("batch" in value && value.batch !== void 0 || "cause" in value && value.cause !== void 0));
916
+ batch = scheduler.batchContext() ?? metadata?.batch;
917
+ cause = metadata?.cause ?? batch?.cause;
1692
918
  staged = /* @__PURE__ */ new Map();
1693
919
  nextIds = ids;
1694
920
  change = void 0;
@@ -1819,10 +1045,29 @@ const createCollection = (scheduler, spec) => {
1819
1045
  ].forEach((key) => changedKeys.add(key));
1820
1046
  profile.projection("changedKeys", changedKeys.size);
1821
1047
  }
1048
+ publishedTransitions = Object.freeze([...changedKeys].flatMap((key) => {
1049
+ const beforePresent = values.has(key);
1050
+ const afterPresent = hasNext(key);
1051
+ if (beforePresent === afterPresent) {
1052
+ if (!beforePresent) return [];
1053
+ if ((spec.isEqual ?? Object.is)(values.get(key), getNext(key))) return [];
1054
+ }
1055
+ return [{
1056
+ key,
1057
+ kind: !beforePresent ? "added" : !afterPresent ? "removed" : "updated",
1058
+ before: beforePresent ? values.get(key) : void 0,
1059
+ after: afterPresent ? getNext(key) : void 0
1060
+ }];
1061
+ }));
1822
1062
  reset = build;
1823
1063
  return change !== void 0;
1824
1064
  },
1825
1065
  context: (active) => {
1066
+ const transitions = (keys) => {
1067
+ assertScope(active);
1068
+ const selected = keys ? new Set(keys) : void 0;
1069
+ return Object.freeze(publishedTransitions.filter((transition) => !selected || selected.has(transition.key)));
1070
+ };
1826
1071
  const input = {
1827
1072
  get: (key) => {
1828
1073
  assertScope(active);
@@ -1836,9 +1081,26 @@ const createCollection = (scheduler, spec) => {
1836
1081
  assertScope(active);
1837
1082
  return nextIds;
1838
1083
  },
1084
+ previous: Object.freeze({
1085
+ get: (key) => {
1086
+ assertScope(active);
1087
+ return values.get(key);
1088
+ },
1089
+ has: (key) => {
1090
+ assertScope(active);
1091
+ return values.has(key);
1092
+ },
1093
+ ids: () => {
1094
+ assertScope(active);
1095
+ return ids;
1096
+ }
1097
+ }),
1839
1098
  change,
1840
1099
  revision: revision + (change ? 1 : 0),
1841
- reset
1100
+ reset,
1101
+ transitions,
1102
+ batch,
1103
+ cause
1842
1104
  };
1843
1105
  return Object.freeze(input);
1844
1106
  },
@@ -1875,6 +1137,9 @@ const createCollection = (scheduler, spec) => {
1875
1137
  change = void 0;
1876
1138
  reset = false;
1877
1139
  changedKeys.clear();
1140
+ publishedTransitions = Object.freeze([]);
1141
+ batch = void 0;
1142
+ cause = void 0;
1878
1143
  orderChanged = false;
1879
1144
  },
1880
1145
  release: () => {
@@ -1991,6 +1256,7 @@ const createSources = (scheduler) => {
1991
1256
  const documents = /* @__PURE__ */ new Map();
1992
1257
  const readables = /* @__PURE__ */ new Map();
1993
1258
  const collections = /* @__PURE__ */ new WeakSet();
1259
+ const externalSources = /* @__PURE__ */ new WeakMap();
1994
1260
  const document = (runtime) => {
1995
1261
  scheduler.assertIdle();
1996
1262
  const state = accessOf(runtime);
@@ -1998,6 +1264,7 @@ const createSources = (scheduler) => {
1998
1264
  const existing = documents.get(state);
1999
1265
  if (existing) return existing;
2000
1266
  const bindings = [];
1267
+ let batch;
2001
1268
  const make = (input) => {
2002
1269
  const collection = "collection" in input ? input.collection : void 0;
2003
1270
  const targets = "targets" in input ? input.targets : [input.collection];
@@ -2021,6 +1288,7 @@ const createSources = (scheduler) => {
2021
1288
  clear: () => {
2022
1289
  commits = [];
2023
1290
  reset = orderDirty = false;
1291
+ batch = void 0;
2024
1292
  keys.clear();
2025
1293
  candidates = void 0;
2026
1294
  },
@@ -2037,6 +1305,8 @@ const createSources = (scheduler) => {
2037
1305
  revision: runtime.revision(),
2038
1306
  commits: Object.freeze(commits.slice()),
2039
1307
  reset: record.reset(),
1308
+ batch,
1309
+ cause: batch?.cause,
2040
1310
  ...collection ? { candidates: candidates ??= Object.freeze({
2041
1311
  keys: Object.freeze([...keys]),
2042
1312
  orderDirty
@@ -2090,6 +1360,7 @@ const createSources = (scheduler) => {
2090
1360
  capture: (commit) => {
2091
1361
  for (const binding of bindings) if (!binding.targets.length || binding.targets.some((value) => affectsTarget(commit.impact, value))) {
2092
1362
  binding.receive(commit);
1363
+ batch = scheduler.batchContext();
2093
1364
  scheduler.capture(binding.record);
2094
1365
  }
2095
1366
  },
@@ -2114,15 +1385,25 @@ const createSources = (scheduler) => {
2114
1385
  let value = initial;
2115
1386
  let previous = initial;
2116
1387
  let revision = 0;
1388
+ let detail;
1389
+ let cause;
1390
+ let batch;
1391
+ let reset = false;
1392
+ let pending = false;
2117
1393
  const handle = Object.freeze({});
2118
1394
  const record = {
2119
1395
  consumers: /* @__PURE__ */ new Set(),
2120
1396
  disposed: false,
2121
1397
  fault: void 0,
2122
1398
  revision: () => revision,
2123
- reset: () => false,
1399
+ reset: () => reset,
2124
1400
  clear: () => {
2125
1401
  previous = value;
1402
+ pending = false;
1403
+ detail = void 0;
1404
+ cause = void 0;
1405
+ batch = void 0;
1406
+ reset = false;
2126
1407
  },
2127
1408
  context: (active) => {
2128
1409
  assertScope(active);
@@ -2131,19 +1412,29 @@ const createSources = (scheduler) => {
2131
1412
  previous,
2132
1413
  revision,
2133
1414
  changed: !equal(previous, value),
2134
- reset: false
1415
+ reset,
1416
+ detail,
1417
+ cause,
1418
+ batch
2135
1419
  });
2136
1420
  }
2137
1421
  };
2138
1422
  scheduler.register(handle, record);
2139
- const accept = (next) => {
1423
+ const accept = (next, metadata) => {
2140
1424
  scheduler.assertIdle();
2141
1425
  const recovering = record.fault !== void 0;
2142
- if (equal(value, next) && !recovering) return;
1426
+ const eventful = metadata !== void 0 && (metadata.reset === true || metadata.revision !== void 0 && metadata.revision !== revision || metadata.detail !== void 0 || metadata.cause !== void 0 || metadata.batch !== void 0);
1427
+ if (equal(value, next) && !recovering && !eventful) return;
2143
1428
  record.fault = void 0;
1429
+ if (!pending) previous = value;
2144
1430
  value = next;
2145
- revision++;
1431
+ revision = metadata?.revision ?? revision + 1;
1432
+ detail = metadata?.detail;
1433
+ cause = metadata?.cause ?? cause;
1434
+ batch = scheduler.batchContext() ?? metadata?.batch ?? batch;
1435
+ reset ||= metadata?.reset ?? false;
2146
1436
  scheduler.capture(record);
1437
+ pending = true;
2147
1438
  };
2148
1439
  return {
2149
1440
  source: handle,
@@ -2154,12 +1445,217 @@ const createSources = (scheduler) => {
2154
1445
  }
2155
1446
  };
2156
1447
  };
1448
+ const externalValueSource = (port, equal) => {
1449
+ scheduler.assertIdle();
1450
+ const old = externalSources.get(port);
1451
+ if (old) return old;
1452
+ const input = valueSource(port.current(), equal);
1453
+ const fail = (cause) => {
1454
+ const record = scheduler.source(input.source);
1455
+ record.fault = new ProjectionError("source", "external source", [record.revision()], cause);
1456
+ scheduler.capture(record);
1457
+ };
1458
+ let unsubscribe;
1459
+ try {
1460
+ unsubscribe = port.subscribe((event) => {
1461
+ try {
1462
+ input.accept(event.value, {
1463
+ revision: event.revision,
1464
+ reset: event.reset,
1465
+ detail: event.detail,
1466
+ cause: event.cause,
1467
+ batch: event.batch
1468
+ });
1469
+ } catch (cause) {
1470
+ fail(cause);
1471
+ }
1472
+ scheduler.run();
1473
+ });
1474
+ } catch (cause) {
1475
+ unsubscribe?.();
1476
+ scheduler.unregisterSource(input.source);
1477
+ throw cause;
1478
+ }
1479
+ scheduler.cleanups.add(() => unsubscribe?.());
1480
+ externalSources.set(port, input.source);
1481
+ return input.source;
1482
+ };
1483
+ const externalCollectionSource = (port) => {
1484
+ scheduler.assertIdle();
1485
+ const old = externalSources.get(port);
1486
+ if (old) return old;
1487
+ let read = port.current();
1488
+ let previous = read;
1489
+ let revision = port.revision();
1490
+ let change;
1491
+ let reset = false;
1492
+ let detail;
1493
+ let cause;
1494
+ let batch;
1495
+ let publishedTransitions = Object.freeze([]);
1496
+ let transitionKeys = /* @__PURE__ */ new Set();
1497
+ let pending = false;
1498
+ const handle = Object.freeze({});
1499
+ const record = {
1500
+ consumers: /* @__PURE__ */ new Set(),
1501
+ disposed: false,
1502
+ fault: void 0,
1503
+ revision: () => revision,
1504
+ reset: () => reset,
1505
+ clear: () => {
1506
+ previous = read;
1507
+ pending = false;
1508
+ change = void 0;
1509
+ reset = false;
1510
+ detail = void 0;
1511
+ cause = void 0;
1512
+ batch = void 0;
1513
+ publishedTransitions = Object.freeze([]);
1514
+ transitionKeys.clear();
1515
+ },
1516
+ context: (active) => {
1517
+ assertScope(active);
1518
+ const currentRead = Object.freeze({
1519
+ get: (key) => {
1520
+ assertScope(active);
1521
+ return read.get(key);
1522
+ },
1523
+ has: (key) => {
1524
+ assertScope(active);
1525
+ return read.has(key);
1526
+ },
1527
+ ids: () => {
1528
+ assertScope(active);
1529
+ return read.ids();
1530
+ }
1531
+ });
1532
+ const previousRead = Object.freeze({
1533
+ get: (key) => {
1534
+ assertScope(active);
1535
+ return previous.get(key);
1536
+ },
1537
+ has: (key) => {
1538
+ assertScope(active);
1539
+ return previous.has(key);
1540
+ },
1541
+ ids: () => {
1542
+ assertScope(active);
1543
+ return previous.ids();
1544
+ }
1545
+ });
1546
+ return Object.freeze({
1547
+ ...currentRead,
1548
+ previous: previousRead,
1549
+ change,
1550
+ revision,
1551
+ reset,
1552
+ transitions: (keys) => {
1553
+ assertScope(active);
1554
+ const selected = keys ? new Set(keys) : void 0;
1555
+ return Object.freeze(publishedTransitions.filter((transition) => !selected || selected.has(transition.key)));
1556
+ },
1557
+ detail,
1558
+ cause,
1559
+ batch
1560
+ });
1561
+ }
1562
+ };
1563
+ scheduler.register(handle, record);
1564
+ collections.add(handle);
1565
+ const fail = (error) => {
1566
+ record.fault = new ProjectionError("source", "external collection source", [revision], error);
1567
+ scheduler.capture(record);
1568
+ };
1569
+ const addTransitionKeys = (event) => {
1570
+ event.transitions().forEach((transition) => transitionKeys.add(transition.key));
1571
+ const impact = event.change;
1572
+ if (impact?.kind === "incremental") {
1573
+ impact.added.forEach((key) => transitionKeys.add(key));
1574
+ impact.removed.forEach((key) => transitionKeys.add(key));
1575
+ impact.updated.forEach((key) => transitionKeys.add(key));
1576
+ }
1577
+ };
1578
+ const recomputeTransitions = () => {
1579
+ const transitions = [];
1580
+ for (const key of transitionKeys) {
1581
+ const beforePresent = previous.has(key);
1582
+ const afterPresent = read.has(key);
1583
+ const before = beforePresent ? previous.get(key) : void 0;
1584
+ const after = afterPresent ? read.get(key) : void 0;
1585
+ if (beforePresent === afterPresent && Object.is(before, after)) continue;
1586
+ transitions.push({
1587
+ key,
1588
+ kind: !beforePresent ? "added" : !afterPresent ? "removed" : "updated",
1589
+ before,
1590
+ after
1591
+ });
1592
+ }
1593
+ publishedTransitions = Object.freeze(transitions);
1594
+ };
1595
+ const deriveChange = () => {
1596
+ if (reset) return Object.freeze({ kind: "reset" });
1597
+ const added = /* @__PURE__ */ new Set();
1598
+ const removed = /* @__PURE__ */ new Set();
1599
+ const updated = /* @__PURE__ */ new Set();
1600
+ publishedTransitions.forEach((transition) => {
1601
+ if (transition.kind === "added") added.add(transition.key);
1602
+ else if (transition.kind === "removed") removed.add(transition.key);
1603
+ else updated.add(transition.key);
1604
+ });
1605
+ const beforeIds = previous.ids();
1606
+ const afterIds = read.ids();
1607
+ const beforeCommon = beforeIds.filter((key) => read.has(key));
1608
+ const afterCommon = afterIds.filter((key) => previous.has(key));
1609
+ const orderChanged = beforeCommon.length !== afterCommon.length || beforeCommon.some((key, index) => afterCommon[index] !== key);
1610
+ if (!added.size && !removed.size && !updated.size && !orderChanged) return void 0;
1611
+ return Object.freeze({
1612
+ kind: "incremental",
1613
+ added,
1614
+ removed,
1615
+ updated,
1616
+ orderChanged
1617
+ });
1618
+ };
1619
+ let unsubscribe;
1620
+ try {
1621
+ unsubscribe = port.subscribe((event) => {
1622
+ try {
1623
+ scheduler.assertIdle();
1624
+ if (!pending) previous = event.previous;
1625
+ read = port.current();
1626
+ revision = event.revision;
1627
+ addTransitionKeys(event);
1628
+ reset ||= event.reset || event.change?.kind === "reset";
1629
+ detail = event.detail;
1630
+ cause = event.cause ?? cause;
1631
+ batch = scheduler.batchContext() ?? event.batch;
1632
+ recomputeTransitions();
1633
+ change = deriveChange();
1634
+ record.fault = void 0;
1635
+ scheduler.capture(record);
1636
+ pending = true;
1637
+ } catch (error) {
1638
+ fail(error);
1639
+ }
1640
+ scheduler.run();
1641
+ });
1642
+ } catch (error) {
1643
+ unsubscribe?.();
1644
+ scheduler.unregisterSource(handle);
1645
+ throw error;
1646
+ }
1647
+ scheduler.cleanups.add(() => unsubscribe?.());
1648
+ externalSources.set(port, handle);
1649
+ return handle;
1650
+ };
2157
1651
  return {
2158
1652
  dispose: () => {
2159
1653
  documents.clear();
2160
1654
  readables.clear();
2161
1655
  },
2162
1656
  document,
1657
+ fromSource: (source, options) => externalValueSource(source, options?.isEqual ?? Object.is),
1658
+ fromCollectionSource: (source) => externalCollectionSource(source),
2163
1659
  input: (initial, options) => {
2164
1660
  const { source, set } = valueSource(initial, options?.isEqual ?? Object.is);
2165
1661
  return Object.freeze({
@@ -2264,9 +1760,14 @@ const createValue = (scheduler, spec, options) => {
2264
1760
  let revision = 0;
2265
1761
  let changed = false;
2266
1762
  let reset = false;
1763
+ let batch;
1764
+ let cause;
2267
1765
  const listeners = /* @__PURE__ */ new Set();
2268
1766
  const owner = createNode(scheduler, spec, {
2269
1767
  evaluate: (sources, build) => {
1768
+ const metadata = Object.values(sources).find((value) => value && typeof value === "object" && ("batch" in value && value.batch !== void 0 || "cause" in value && value.cause !== void 0));
1769
+ batch = scheduler.batchContext() ?? metadata?.batch;
1770
+ cause = metadata?.cause ?? batch?.cause;
2270
1771
  let candidate;
2271
1772
  if (build || !instance) {
2272
1773
  profile.materialized.rebuilt();
@@ -2301,7 +1802,9 @@ const createValue = (scheduler, spec, options) => {
2301
1802
  previous,
2302
1803
  changed,
2303
1804
  revision: revision + (changed ? 1 : 0),
2304
- reset
1805
+ reset,
1806
+ cause,
1807
+ batch
2305
1808
  });
2306
1809
  },
2307
1810
  revision: () => revision,
@@ -2320,6 +1823,8 @@ const createValue = (scheduler, spec, options) => {
2320
1823
  previous = value;
2321
1824
  changed = false;
2322
1825
  reset = false;
1826
+ batch = void 0;
1827
+ cause = void 0;
2323
1828
  },
2324
1829
  release: () => {
2325
1830
  listeners.clear();
@@ -2351,13 +1856,7 @@ const createValue = (scheduler, spec, options) => {
2351
1856
  };
2352
1857
  //#endregion
2353
1858
  //#region core/src/projection/runtime.ts
2354
- const debug = /* @__PURE__ */ new WeakMap();
2355
- const projectionEngineDebug = (runtime) => {
2356
- const read = debug.get(runtime);
2357
- if (!read) throw new Error("Unknown projection engine.");
2358
- return Object.freeze(read());
2359
- };
2360
- const createProjectionEngine = (options) => {
1859
+ const createRuntimeExecutor = (options) => {
2361
1860
  const scheduler = createScheduler(options.onError);
2362
1861
  const sources = createSources(scheduler);
2363
1862
  function value(input, computeOrOptions, options) {
@@ -2434,6 +1933,8 @@ const createProjectionEngine = (options) => {
2434
1933
  };
2435
1934
  const runtime = {
2436
1935
  document: sources.document,
1936
+ fromSource: (source, options) => sources.fromSource(source, options),
1937
+ fromCollectionSource: (source) => sources.fromCollectionSource(source),
2437
1938
  input: sources.input,
2438
1939
  fromReadable: sources.fromReadable,
2439
1940
  value,
@@ -2448,107 +1949,274 @@ const createProjectionEngine = (options) => {
2448
1949
  }
2449
1950
  }
2450
1951
  };
2451
- debug.set(runtime, scheduler.debug);
2452
1952
  return Object.freeze(runtime);
2453
1953
  };
2454
1954
  //#endregion
2455
1955
  //#region core/src/projection/store.ts
2456
- const stores = /* @__PURE__ */ new WeakMap();
2457
- const projectionStoreDebug = (store) => {
2458
- const runtime = stores.get(store);
2459
- if (!runtime) throw new Error("Unknown projection store.");
2460
- return projectionEngineDebug(runtime);
1956
+ const runtimeStates = /* @__PURE__ */ new WeakMap();
1957
+ let activeTracker;
1958
+ const trackKey = (key) => {
1959
+ activeTracker?.keys.add(key);
1960
+ };
1961
+ const trackAll = (structure = false) => {
1962
+ if (!activeTracker) return;
1963
+ activeTracker.all = true;
1964
+ activeTracker.structure ||= structure;
2461
1965
  };
2462
- const createProjectionStore = (options) => {
2463
- const runtime = createProjectionEngine(options);
1966
+ const mapView = (read) => {
1967
+ const keys = read.ids();
1968
+ const source = /* @__PURE__ */ new Map();
1969
+ for (const key of keys) source.set(key, read.get(key));
1970
+ const view = {
1971
+ get: (key) => {
1972
+ trackKey(key);
1973
+ return source.get(key);
1974
+ },
1975
+ has: (key) => {
1976
+ trackKey(key);
1977
+ return source.has(key);
1978
+ },
1979
+ get size() {
1980
+ trackAll(true);
1981
+ return source.size;
1982
+ },
1983
+ keys: () => {
1984
+ trackAll(true);
1985
+ return source.keys();
1986
+ },
1987
+ values: () => {
1988
+ trackAll(true);
1989
+ return source.values();
1990
+ },
1991
+ entries: () => {
1992
+ trackAll(true);
1993
+ return source.entries();
1994
+ },
1995
+ forEach: (callback, thisArg) => {
1996
+ trackAll(true);
1997
+ source.forEach((value, key) => callback.call(thisArg, value, key, view));
1998
+ },
1999
+ [Symbol.iterator]: () => {
2000
+ trackAll(true);
2001
+ return source[Symbol.iterator]();
2002
+ }
2003
+ };
2004
+ return Object.freeze(view);
2005
+ };
2006
+ const isMapLike = (value) => value instanceof Map || value !== null && typeof value === "object" && typeof value.get === "function" && typeof value.keys === "function" && typeof value.entries === "function";
2007
+ const mapRead = (value) => ({
2008
+ get: (key) => value.get(key),
2009
+ has: (key) => value.has(key),
2010
+ ids: () => Object.freeze([...value.keys()])
2011
+ });
2012
+ const mapChange = (previous, current) => {
2013
+ const added = /* @__PURE__ */ new Set();
2014
+ const removed = /* @__PURE__ */ new Set();
2015
+ const updated = /* @__PURE__ */ new Set();
2016
+ for (const key of previous.keys()) if (!current.has(key)) removed.add(key);
2017
+ else if (!Object.is(previous.get(key), current.get(key))) updated.add(key);
2018
+ for (const key of current.keys()) if (!previous.has(key)) added.add(key);
2019
+ const before = [...previous.keys()].filter((key) => current.has(key));
2020
+ const after = [...current.keys()].filter((key) => previous.has(key));
2021
+ const orderChanged = before.length !== after.length || before.some((key, index) => key !== after[index]);
2022
+ if (!added.size && !removed.size && !updated.size && !orderChanged) return void 0;
2023
+ return {
2024
+ kind: "incremental",
2025
+ added,
2026
+ removed,
2027
+ updated,
2028
+ orderChanged
2029
+ };
2030
+ };
2031
+ const currentValue = (event) => {
2032
+ if (!event || typeof event !== "object") return event;
2033
+ if ("value" in event) return event.value;
2034
+ if ("read" in event) return snapshot(event.read);
2035
+ if ("ids" in event && typeof event.ids === "function") return mapView(event);
2036
+ return event;
2037
+ };
2038
+ const sourceOf = (instance) => instance;
2039
+ const createProjectionRuntime = (options) => {
2040
+ const runtime = createRuntimeExecutor({ onError: options?.onError ?? (() => void 0) });
2464
2041
  const instances = /* @__PURE__ */ new WeakMap();
2465
2042
  const inputs = /* @__PURE__ */ new WeakMap();
2043
+ const collections = /* @__PURE__ */ new WeakMap();
2466
2044
  const materialize = (projection) => {
2467
2045
  const old = instances.get(projection);
2468
2046
  if (old) return old;
2469
2047
  const definition = definitionOf(projection);
2470
2048
  let instance;
2471
- if (definition.kind === "input") {
2472
- const input = runtime.input(definition.initial, { isEqual: definition.isEqual });
2473
- inputs.set(projection, input);
2474
- instance = runtime.value({ input: input.source }, ({ input }) => input.value);
2475
- } else if (definition.kind === "readable") {
2476
- const source = runtime.fromReadable(definition.readable, { isEqual: definition.isEqual });
2477
- instance = runtime.value({ source }, ({ source }) => source.value);
2478
- } else if (definition.kind === "document") {
2479
- const document = runtime.document(definition.document);
2480
- instance = definition.targets?.length ? document.targets(...definition.targets) : document;
2481
- } else if (definition.kind === "document-collection") instance = runtime.document(definition.document).collection(definition.pick);
2482
- else if (definition.kind === "map") instance = runtime.map(materialize(definition.source), definition.mapper, { isEqual: definition.isEqual });
2483
- else {
2484
- const sources = Object.fromEntries(Object.entries(definition.sources).map(([key, source]) => [key, materialize(source)]));
2485
- if (definition.kind === "value") instance = runtime.value(sources, (events) => {
2486
- const values = Object.fromEntries(Object.entries(events).map(([key, event]) => [key, current(event)]));
2487
- return definition.compute(values);
2488
- }, { isEqual: definition.isEqual });
2489
- else if (definition.kind === "advanced-value") instance = runtime.value({
2490
- sources,
2491
- build: definition.build,
2492
- name: definition.name
2493
- }, { isEqual: definition.isEqual });
2494
- else instance = runtime.collection()({
2495
- sources,
2496
- build: definition.build,
2497
- isEqual: definition.isEqual,
2498
- name: definition.name
2499
- });
2049
+ switch (definition.kind) {
2050
+ case "input": {
2051
+ const input = runtime.input(definition.initial, { isEqual: definition.isEqual });
2052
+ inputs.set(projection, input);
2053
+ instance = runtime.value({ input: input.source }, ({ input }) => input.value);
2054
+ break;
2055
+ }
2056
+ case "readable": {
2057
+ const source = runtime.fromReadable(definition.readable, { isEqual: definition.isEqual });
2058
+ instance = runtime.value({ source }, ({ source }) => source.value);
2059
+ break;
2060
+ }
2061
+ case "source-value":
2062
+ {
2063
+ const source = runtime.fromSource(definition.source);
2064
+ instance = runtime.value({ source }, ({ source }) => source.value);
2065
+ }
2066
+ break;
2067
+ case "source-collection": {
2068
+ const source = runtime.fromCollectionSource(definition.source);
2069
+ instance = runtime.map(source, (_key, value) => value);
2070
+ break;
2071
+ }
2072
+ case "document": {
2073
+ const document = runtime.document(definition.document);
2074
+ if (!definition.selector) {
2075
+ instance = runtime.value({ document }, ({ document }) => snapshot(document.read));
2076
+ break;
2077
+ }
2078
+ const state = accessOf(definition.document);
2079
+ try {
2080
+ const collection = document.collection(definition.selector);
2081
+ instance = runtime.map(collection, (_id, entry) => snapshot(entry));
2082
+ break;
2083
+ } catch {
2084
+ const selected = document.targets(definition.selector);
2085
+ const address = compilePath(state.schema, "value", definition.selector).address;
2086
+ instance = runtime.value({ selected }, () => {
2087
+ return read(snapshot(state.document), address, state.schema);
2088
+ });
2089
+ }
2090
+ break;
2091
+ }
2092
+ case "derive": {
2093
+ const sources = Object.fromEntries(definition.dependencies.map((dependency, index) => [`d${index}`, sourceOf(materialize(dependency))]));
2094
+ instance = runtime.value(sources, (values) => definition.compute(...Object.values(values).map(currentValue)), { isEqual: definition.isEqual });
2095
+ break;
2096
+ }
2097
+ case "incremental-value": {
2098
+ const sources = Object.fromEntries(Object.entries(definition.dependencies).map(([key, dependency]) => [key, sourceOf(materialize(dependency))]));
2099
+ instance = runtime.value({
2100
+ sources,
2101
+ build: definition.build
2102
+ }, { isEqual: definition.isEqual });
2103
+ break;
2104
+ }
2105
+ case "incremental-collection": {
2106
+ const sources = Object.fromEntries(Object.entries(definition.dependencies).map(([key, dependency]) => [key, sourceOf(materialize(dependency))]));
2107
+ instance = runtime.collection()({
2108
+ sources,
2109
+ build: definition.build,
2110
+ isEqual: definition.isEqual,
2111
+ name: definition.name
2112
+ });
2113
+ break;
2114
+ }
2500
2115
  }
2501
2116
  instances.set(projection, instance);
2502
2117
  return instance;
2503
2118
  };
2504
- const current = (event) => {
2505
- if (event && typeof event === "object") {
2506
- if ("value" in event) return event.value;
2507
- if ("read" in event) return event.read;
2119
+ const get = (projection) => {
2120
+ const instance = materialize(projection);
2121
+ const current = instance.current();
2122
+ if (current && typeof current === "object" && "ids" in current && typeof current.ids === "function") {
2123
+ const existing = collections.get(instance);
2124
+ const revision = instance.revision();
2125
+ if (existing?.revision === revision) return existing.value;
2126
+ const value = mapView(current);
2127
+ collections.set(instance, {
2128
+ revision,
2129
+ value
2130
+ });
2131
+ return value;
2508
2132
  }
2509
- return event;
2133
+ if (isMapLike(current)) {
2134
+ const existing = collections.get(instance);
2135
+ const revision = instance.revision();
2136
+ if (existing?.revision === revision) return existing.value;
2137
+ const value = mapView(mapRead(current));
2138
+ collections.set(instance, {
2139
+ revision,
2140
+ value
2141
+ });
2142
+ return value;
2143
+ }
2144
+ return current;
2510
2145
  };
2511
2146
  const store = {
2512
- get: ((projection) => {
2513
- const instance = materialize(projection);
2514
- if ("current" in instance && typeof instance.current === "function") return instance.current();
2515
- throw new TypeError("Projection is a source and has no published value.");
2147
+ get,
2148
+ subscribe: ((projection, listener) => {
2149
+ return materialize(projection).subscribe(listener);
2516
2150
  }),
2517
- set: (projection, value) => {
2518
- materialize(projection);
2519
- const target = inputs.get(projection);
2151
+ set: ((input, value) => {
2152
+ materialize(input);
2153
+ const target = inputs.get(input);
2520
2154
  if (!target) throw new TypeError("Projection is not an input.");
2521
2155
  target.set(value);
2522
- },
2523
- subscribe: ((projection, listener) => {
2524
- const instance = materialize(projection);
2525
- if (!("subscribe" in instance)) throw new TypeError("Projection is a source and cannot be subscribed.");
2526
- return instance.subscribe(listener);
2527
2156
  }),
2528
- revision: (projection) => {
2529
- const instance = materialize(projection);
2530
- if (!("revision" in instance) || typeof instance.revision !== "function") throw new TypeError("Projection has no published revision.");
2531
- return instance.revision();
2532
- },
2533
- rebuild: (projection) => {
2534
- const instance = materialize(projection);
2535
- if (!("rebuild" in instance) || typeof instance.rebuild !== "function") throw new TypeError("Projection cannot be rebuilt.");
2536
- instance.rebuild();
2537
- },
2538
- release: (projection) => {
2539
- const instance = instances.get(projection);
2540
- if (!instance) return;
2541
- if ("dispose" in instance && typeof instance.dispose === "function") instance.dispose();
2542
- instances.delete(projection);
2543
- inputs.delete(projection);
2544
- },
2545
2157
  batch: runtime.batch,
2546
2158
  dispose: runtime.dispose
2547
2159
  };
2548
- stores.set(store, runtime);
2160
+ runtimeStates.set(store, {
2161
+ materialize,
2162
+ get
2163
+ });
2549
2164
  return Object.freeze(store);
2550
2165
  };
2166
+ const trackProjection = (owner, projection, selector) => {
2167
+ const state = runtimeStates.get(owner);
2168
+ if (!state) throw new TypeError("Unknown projection runtime.");
2169
+ const tracker = {
2170
+ keys: /* @__PURE__ */ new Set(),
2171
+ all: false,
2172
+ structure: false
2173
+ };
2174
+ const previous = activeTracker;
2175
+ activeTracker = tracker;
2176
+ try {
2177
+ return {
2178
+ value: selector(state.get(projection)),
2179
+ selection: Object.freeze({
2180
+ keys: new Set(tracker.keys),
2181
+ all: tracker.all,
2182
+ structure: tracker.structure
2183
+ })
2184
+ };
2185
+ } finally {
2186
+ activeTracker = previous;
2187
+ }
2188
+ };
2189
+ const subscribeProjection = (owner, projection, selection, listener) => {
2190
+ const state = runtimeStates.get(owner);
2191
+ if (!state) throw new TypeError("Unknown projection runtime.");
2192
+ const instance = state.materialize(projection);
2193
+ let previousCollection;
2194
+ const initialValue = state.get(projection);
2195
+ if (isMapLike(initialValue)) previousCollection = initialValue;
2196
+ return instance.subscribe(((change) => {
2197
+ if (!change && previousCollection) {
2198
+ const current = state.get(projection);
2199
+ if (isMapLike(current)) {
2200
+ change = mapChange(previousCollection, current);
2201
+ previousCollection = current;
2202
+ if (!change) return;
2203
+ } else previousCollection = void 0;
2204
+ }
2205
+ if (!change || change.kind === "reset") {
2206
+ listener();
2207
+ return;
2208
+ }
2209
+ if (selection.all && (selection.structure || change.added.size || change.removed.size || change.updated.size)) {
2210
+ listener();
2211
+ return;
2212
+ }
2213
+ for (const key of selection.keys) if (change.added.has(key) || change.removed.has(key) || change.updated.has(key)) {
2214
+ listener();
2215
+ return;
2216
+ }
2217
+ }));
2218
+ };
2551
2219
  //#endregion
2552
- export { object as A, parse as B, createAccess 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, replace as w, createImpact as x, DocumentReentrancyError as y, equalValue as z };
2220
+ export { variant as A, field as C, optional as D, object as E, table as O, compilePath as S, map as T, DocumentDisposedError as _, ProjectionError as a, createImpact as b, disposeNotification as c, subscribeDependencies as d, subscribeRoot as f, readWith as g, bindRuntimeAccess as h, ProjectionDisposedError as i, tree as k, notify as l, accessOf as m, subscribeProjection as n, bindDocumentReadable as o, subscribeTargets as p, trackProjection as r, createNotification as s, createProjectionRuntime as t, shareNotification as u, DocumentReentrancyError as v, list as w, same as x, TransactionRejected as y };
2553
2221
 
2554
- //# sourceMappingURL=store-DHFXfWSz.js.map
2222
+ //# sourceMappingURL=store-CtgvCVFg.js.map