doxum 0.1.13 → 0.1.15

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 (57) hide show
  1. package/README.md +10 -2
  2. package/dist/advanced.cjs +3 -2
  3. package/dist/advanced.cjs.map +1 -1
  4. package/dist/advanced.d.cts +1 -1
  5. package/dist/advanced.d.ts +1 -1
  6. package/dist/advanced.js +2 -1
  7. package/dist/advanced.js.map +1 -1
  8. package/dist/{contract-BT53Gg94.d.ts → contract-CATCCVu5.d.ts} +1 -1
  9. package/dist/{definition-BlMeHwdz.d.ts → definition-B6J0SSNa.d.ts} +38 -37
  10. package/dist/{definition-kxnWUvX9.d.cts → definition-DN6yW2vi.d.cts} +37 -36
  11. package/dist/definition-Z6TagMup.cjs +107 -0
  12. package/dist/definition-Z6TagMup.cjs.map +1 -0
  13. package/dist/definition-c6XQXzvK.js +72 -0
  14. package/dist/definition-c6XQXzvK.js.map +1 -0
  15. package/dist/index.cjs +2054 -55
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +16 -3
  18. package/dist/index.d.ts +17 -4
  19. package/dist/index.js +2001 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/integration-B7VDgAOJ.cjs +30 -0
  22. package/dist/integration-B7VDgAOJ.cjs.map +1 -0
  23. package/dist/{integration-Bco8ZB16.js → integration-CxVhq-KD.js} +3 -7
  24. package/dist/integration-CxVhq-KD.js.map +1 -0
  25. package/dist/integration.cjs +4 -9
  26. package/dist/integration.d.cts +2 -7
  27. package/dist/integration.d.ts +2 -7
  28. package/dist/integration.js +3 -3
  29. package/dist/local-sync.d.ts +1 -1
  30. package/dist/notification-CDSUfiip.cjs +398 -0
  31. package/dist/notification-CDSUfiip.cjs.map +1 -0
  32. package/dist/notification-IQIJyH3Q.js +285 -0
  33. package/dist/notification-IQIJyH3Q.js.map +1 -0
  34. package/dist/react.cjs +6 -52
  35. package/dist/react.cjs.map +1 -1
  36. package/dist/react.js +4 -50
  37. package/dist/react.js.map +1 -1
  38. package/dist/{definition-177L4Amk.cjs → scope-Ezs-Y3TY.cjs} +1 -118
  39. package/dist/scope-Ezs-Y3TY.cjs.map +1 -0
  40. package/dist/{definition-DXrKMVs7.js → scope-d-L87NeC.js} +2 -65
  41. package/dist/scope-d-L87NeC.js.map +1 -0
  42. package/package.json +1 -1
  43. package/skills/doxum-runtime/references/guide.en.md +2 -1
  44. package/skills/doxum-runtime/references/guide.zh-CN.md +2 -1
  45. package/skills/doxum-runtime/references/projections.en.md +15 -6
  46. package/skills/doxum-runtime/references/projections.zh-CN.md +13 -5
  47. package/dist/definition-177L4Amk.cjs.map +0 -1
  48. package/dist/definition-DXrKMVs7.js.map +0 -1
  49. package/dist/integration-Bco8ZB16.js.map +0 -1
  50. package/dist/integration-CEUn96tx.cjs +0 -52
  51. package/dist/integration-CEUn96tx.cjs.map +0 -1
  52. package/dist/store-CDeqZEE3.d.cts +0 -28
  53. package/dist/store-CtgvCVFg.js +0 -2222
  54. package/dist/store-CtgvCVFg.js.map +0 -1
  55. package/dist/store-DcDpDgjk.cjs +0 -2401
  56. package/dist/store-DcDpDgjk.cjs.map +0 -1
  57. package/dist/store-P5dJ7TVe.d.ts +0 -28
package/dist/index.cjs CHANGED
@@ -1,8 +1,195 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_issue = require("./issue-DhrNdQNg.cjs");
3
- const require_definition = require("./definition-177L4Amk.cjs");
4
- const require_store = require("./store-DcDpDgjk.cjs");
3
+ const require_scope = require("./scope-Ezs-Y3TY.cjs");
4
+ const require_notification = require("./notification-CDSUfiip.cjs");
5
5
  const require_driver = require("./driver-BCyfqxsl.cjs");
6
+ const require_definition = require("./definition-Z6TagMup.cjs");
7
+ //#region core/src/schema.ts
8
+ const node = (value) => Object.freeze(value);
9
+ const field = (validator) => node({
10
+ kind: "field",
11
+ ...validator ? { validator } : {}
12
+ });
13
+ const optional = (value) => {
14
+ if (![
15
+ "field",
16
+ "variant",
17
+ "map",
18
+ "list",
19
+ "tree"
20
+ ].includes(value.kind)) throw new TypeError("Only field, variant, map, list and tree nodes support optional presence.");
21
+ return node({
22
+ ...value,
23
+ optional: true
24
+ });
25
+ };
26
+ const object = (shape) => node({
27
+ kind: "object",
28
+ shape: Object.freeze({ ...shape })
29
+ });
30
+ const variant = (tag, variants) => node({
31
+ kind: "variant",
32
+ tag,
33
+ variants: Object.freeze({ ...variants })
34
+ });
35
+ const table = (value, options) => node({
36
+ kind: "table",
37
+ value,
38
+ ...options
39
+ });
40
+ const map = (value, options) => node({
41
+ kind: "map",
42
+ value,
43
+ ...options
44
+ });
45
+ const list = (value, config) => node({
46
+ kind: "list",
47
+ keyOf: config.keyOf,
48
+ value
49
+ });
50
+ const tree = (value) => node({
51
+ kind: "tree",
52
+ value
53
+ });
54
+ const paths = /* @__PURE__ */ new WeakMap();
55
+ const pathProxy = (nodes, address, owner) => {
56
+ const proxy = new Proxy({}, { get: (_target, property) => {
57
+ if (typeof property !== "string") return void 0;
58
+ if (nodes.every((node) => node.kind === "table" || node.kind === "map") && property === "item") return (id) => {
59
+ if (typeof id !== "string") throw new TypeError("Collection keys must be strings.");
60
+ for (const node of nodes) if (node.kind === "table" || node.kind === "map") {
61
+ const invalid = require_scope.checkKey(node.key, id, [...address, id]);
62
+ if (invalid) throw new TypeError(invalid.message);
63
+ }
64
+ return pathProxy(nodes.map((node) => node.value), [...address, id], owner);
65
+ };
66
+ const children = nodes.flatMap((node) => {
67
+ if (node.kind === "object") return Object.hasOwn(node.shape, property) ? [node.shape[property]] : [];
68
+ if (node.kind !== "variant") return [];
69
+ if (property === node.tag) return [field()];
70
+ return Object.values(node.variants).flatMap((branch) => Object.hasOwn(branch.shape, property) ? [branch.shape[property]] : []);
71
+ });
72
+ if (!children.length) throw new TypeError(`Invalid schema path segment: ${property}`);
73
+ return pathProxy(children, [...address, property], owner);
74
+ } });
75
+ paths.set(proxy, {
76
+ nodes,
77
+ address,
78
+ owner
79
+ });
80
+ return proxy;
81
+ };
82
+ const compilePath = (owner, kind, pick) => {
83
+ const scope = {};
84
+ const value = pick(pathProxy([owner], [], scope));
85
+ const selected = typeof value === "object" && value !== null ? paths.get(value) : void 0;
86
+ if (!selected || selected.owner !== scope) throw new TypeError("Selector must return a path from its callback.");
87
+ if (kind === "collection" && !selected.nodes.every((node) => node.kind === "table" || node.kind === "map")) throw new TypeError("Collection selectors require a table or map path.");
88
+ return Object.freeze({
89
+ kind,
90
+ schema: owner,
91
+ address: Object.freeze([...selected.address])
92
+ });
93
+ };
94
+ //#endregion
95
+ //#region core/src/impact.ts
96
+ const queries = /* @__PURE__ */ new WeakMap();
97
+ const affectsTarget = (impact, value) => queries.get(impact).affects(value);
98
+ const collectionImpact = (impact, selector) => queries.get(impact).collection(selector);
99
+ const createImpact = (schema, changes) => {
100
+ const reset = changes.changes.some((change) => change.kind === "reset");
101
+ let values;
102
+ let orders;
103
+ const index = () => {
104
+ if (!values) {
105
+ require_issue.profile.impact.index();
106
+ values = new require_issue.AddressIndex();
107
+ orders = new require_issue.AddressIndex();
108
+ for (const change of changes.changes) if (change.kind === "members") {
109
+ for (const member of change.members) values.add(change.at, true, member.key);
110
+ if (change.order) orders.add(change.at, true);
111
+ } else if (change.kind === "tree") values.add(change.at, true);
112
+ }
113
+ return values;
114
+ };
115
+ const cache = /* @__PURE__ */ new Map();
116
+ const implementation = {
117
+ affects(value) {
118
+ require_issue.profile.impact.affects();
119
+ if (!require_notification.belongs(value, schema)) return false;
120
+ if (reset) return true;
121
+ return require_notification.affected(value, index(), orders);
122
+ },
123
+ collection(selector) {
124
+ if (selector.schema !== schema) throw new TypeError("Collection belongs to another root model.");
125
+ const key = JSON.stringify(selector.address), cached = cache.get(key);
126
+ if (cached) return cached;
127
+ if (reset) {
128
+ const result = { kind: "reset" };
129
+ cache.set(key, result);
130
+ return result;
131
+ }
132
+ const at = selector.address;
133
+ const added = /* @__PURE__ */ new Set(), removed = /* @__PURE__ */ new Set(), updated = /* @__PURE__ */ new Set();
134
+ let orderChanged = false;
135
+ for (const change of changes.changes) {
136
+ if (change.kind === "reset") continue;
137
+ if (change.kind === "members") {
138
+ if (change.at.length < at.length && require_issue.contains(change.at, at)) {
139
+ if (change.members.some((member) => member.key === at[change.at.length])) {
140
+ const result = { kind: "reset" };
141
+ cache.set(key, result);
142
+ return result;
143
+ }
144
+ } else if (require_issue.contains(at, change.at)) if (change.at.length === at.length) {
145
+ if (change.order) {
146
+ const positions = new Map(change.order.after.map((id, i) => [id, i]));
147
+ let previous = -1;
148
+ for (const id of change.order.before) {
149
+ const position = positions.get(id);
150
+ if (position === void 0) continue;
151
+ if (position < previous) orderChanged = true;
152
+ previous = position;
153
+ }
154
+ }
155
+ for (const member of change.members) if (member.kind === "added") added.add(member.key);
156
+ else if (member.kind === "removed") removed.add(member.key);
157
+ else updated.add(member.key);
158
+ } else updated.add(change.at[at.length]);
159
+ continue;
160
+ }
161
+ if (change.kind === "tree" && require_issue.contains(change.at, at)) {
162
+ const result = { kind: "reset" };
163
+ cache.set(key, result);
164
+ return result;
165
+ }
166
+ if (!require_issue.contains(at, change.at)) continue;
167
+ if (change.at.length === at.length) continue;
168
+ const id = change.at[at.length];
169
+ updated.add(id);
170
+ }
171
+ added.forEach((id) => updated.delete(id));
172
+ removed.forEach((id) => updated.delete(id));
173
+ const result = {
174
+ kind: "incremental",
175
+ added,
176
+ removed,
177
+ updated,
178
+ orderChanged
179
+ };
180
+ cache.set(key, result);
181
+ return result;
182
+ }
183
+ };
184
+ const impact = Object.freeze({
185
+ kind: reset ? "reset" : "incremental",
186
+ affects: (pick) => implementation.affects(compilePath(schema, "value", pick)),
187
+ collection: (pick) => implementation.collection(compilePath(schema, "collection", pick))
188
+ });
189
+ queries.set(impact, implementation);
190
+ return impact;
191
+ };
192
+ //#endregion
6
193
  //#region core/src/history.ts
7
194
  const createHistory = (input) => {
8
195
  let undos = [];
@@ -91,7 +278,7 @@ const createHistory = (input) => {
91
278
  current: () => state,
92
279
  revision: () => revision,
93
280
  subscribe: (listener) => {
94
- if (disposed) throw new require_store.DocumentDisposedError();
281
+ if (disposed) throw new require_notification.DocumentDisposedError();
95
282
  listeners.add(listener);
96
283
  return () => {
97
284
  listeners.delete(listener);
@@ -216,8 +403,8 @@ const copyNode = (node) => ({
216
403
  });
217
404
  const transition = (node, key, beforePresent, before, afterPresent, after) => {
218
405
  if (beforePresent === afterPresent && (!beforePresent || Object.is(before, after))) return;
219
- if (node.kind !== "field" && beforePresent === afterPresent && require_definition.equalValue(node, before, after)) return;
220
- const value = afterPresent ? node.kind === "field" ? after : require_definition.copyValue(node, after) : void 0;
406
+ if (node.kind !== "field" && beforePresent === afterPresent && require_scope.equalValue(node, before, after)) return;
407
+ const value = afterPresent ? node.kind === "field" ? after : require_scope.copyValue(node, after) : void 0;
221
408
  require_issue.profile.recorder("transitions");
222
409
  return !beforePresent ? {
223
410
  key,
@@ -290,7 +477,7 @@ const restore = (fact, schema, root, skip = 0) => {
290
477
  const at = skip ? fact.at.slice(skip) : fact.at;
291
478
  if (fact.kind === "member") {
292
479
  const member = fact.member;
293
- const value = member.present ? require_definition.copyValue(member.node, member.value) : void 0;
480
+ const value = member.present ? require_scope.copyValue(member.node, member.value) : void 0;
294
481
  if (!at.length) return value;
295
482
  const location = require_issue.resolveLocated(schema, root, at);
296
483
  if (!location) throw new Error("Cannot restore an unresolved member.");
@@ -309,7 +496,7 @@ const restore = (fact, schema, root, skip = 0) => {
309
496
  return root;
310
497
  };
311
498
  const reconstructBefore = (node, current, records, offset) => {
312
- let before = require_definition.copyValue(node, current);
499
+ let before = require_scope.copyValue(node, current);
313
500
  for (let i = records.length - 1; i >= 0; i--) if (records[i].kind !== "order") before = restore(records[i], node, before, offset);
314
501
  for (const record of records) if (record.kind === "order") before = restore(record, node, before, offset);
315
502
  return before;
@@ -349,7 +536,7 @@ const sealTree = (fact, state, changes) => {
349
536
  for (const [id, before] of fact.nodes) {
350
537
  const after = Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : void 0;
351
538
  if (!before && !after) continue;
352
- if (before && after && before.parentId === after.parentId && require_issue.equal(before.children, after.children) && Object.hasOwn(before, "value") === Object.hasOwn(after, "value") && require_definition.equalValue(node.value, before.value, after.value)) continue;
539
+ if (before && after && before.parentId === after.parentId && require_issue.equal(before.children, after.children) && Object.hasOwn(before, "value") === Object.hasOwn(after, "value") && require_scope.equalValue(node.value, before.value, after.value)) continue;
353
540
  nodes.push(!before ? {
354
541
  id,
355
542
  kind: "added",
@@ -557,7 +744,7 @@ var ChangeRecorder = class {
557
744
  const fact = facts[i];
558
745
  if (fact.kind === "members") for (const member of fact.members?.values() ?? []) {
559
746
  if (!member) continue;
560
- installMember(fact.parent, require_issue.memberKey(fact, member.key), member.present, member.present ? require_definition.copyValue(member.node, member.value) : void 0);
747
+ installMember(fact.parent, require_issue.memberKey(fact, member.key), member.present, member.present ? require_scope.copyValue(member.node, member.value) : void 0);
561
748
  }
562
749
  else if (fact.kind === "tree") restore(fact, this.state.schema, this.state.document);
563
750
  }
@@ -565,12 +752,12 @@ var ChangeRecorder = class {
565
752
  }
566
753
  seal() {
567
754
  if (this.resetBefore) {
568
- if (require_definition.equalValue(this.state.schema, this.resetBefore.value, this.state.document)) return require_driver.sealChanges([]);
755
+ if (require_scope.equalValue(this.state.schema, this.resetBefore.value, this.state.document)) return require_driver.sealChanges([]);
569
756
  require_issue.profile.recorder("sealed");
570
757
  return require_driver.sealChanges([{
571
758
  kind: "reset",
572
759
  before: this.resetBefore.value,
573
- after: require_definition.copyValue(this.state.schema, this.state.document)
760
+ after: require_scope.copyValue(this.state.schema, this.state.document)
574
761
  }]);
575
762
  }
576
763
  const changes = [];
@@ -597,7 +784,7 @@ var MutationSession = class {
597
784
  this.resolvedRoot = state.document;
598
785
  }
599
786
  validate(node, value, at) {
600
- const issue = require_definition.checkValue(node, value, at);
787
+ const issue = require_scope.checkValue(node, value, at);
601
788
  if (issue) require_issue.invalidValue(issue);
602
789
  }
603
790
  resolveContainer(at) {
@@ -619,7 +806,7 @@ var MutationSession = class {
619
806
  this.validate(this.state.schema, value, at);
620
807
  if (Object.is(this.state.document, value)) return;
621
808
  this.recorder.reset();
622
- this.state.document = require_definition.copyValue(this.state.schema, value);
809
+ this.state.document = require_scope.copyValue(this.state.schema, value);
623
810
  this.invalidate();
624
811
  return;
625
812
  }
@@ -661,12 +848,12 @@ var MutationSession = class {
661
848
  const previous = container.parent[physicalKey];
662
849
  if (existed === present && (!present || Object.is(previous, value))) return false;
663
850
  if (parentNode.kind === "map" || parentNode.kind === "table") {
664
- const issue = require_definition.checkKey(parentNode.key, key, []);
851
+ const issue = require_scope.checkKey(parentNode.key, key, []);
665
852
  if (issue) return require_issue.fail(container.at.concat(key, ...issue.address), "invalid-key", issue.message);
666
853
  }
667
854
  if (present) {
668
855
  if (node.kind === "field") {
669
- const issue = require_definition.checkValue(node, value, []);
856
+ const issue = require_scope.checkValue(node, value, []);
670
857
  if (issue) return require_issue.invalidValue(issue, container.at.concat(key, ...issue.address));
671
858
  } else this.validate(node, value, container.at.concat(key));
672
859
  if (parentNode.kind === "list" && parentNode.keyOf(value) !== key) return require_issue.fail(container.at.concat(key), "invalid-list-key", "Replacing an item must retain its addressed key.");
@@ -674,7 +861,7 @@ var MutationSession = class {
674
861
  const membershipChanged = entry && existed !== present;
675
862
  if (membershipChanged && (parentNode.kind === "list" || parentNode.kind === "table")) this.recorder.order(container);
676
863
  this.recorder.member(container, key, member, physicalKey);
677
- const next = present ? node.kind === "field" ? value : require_definition.copyValue(node, value) : void 0;
864
+ const next = present ? node.kind === "field" ? value : require_scope.copyValue(node, value) : void 0;
678
865
  installMember(container.parent, physicalKey, present, next);
679
866
  if (node.kind !== "field" || membershipChanged) this.invalidate();
680
867
  return membershipChanged;
@@ -744,18 +931,18 @@ function apply(session, changes, direction) {
744
931
  //#region core/src/runtime.ts
745
932
  const createDocument = (input) => {
746
933
  if (input.schema.kind !== "object") throw new TypeError("Document schema must be a root object node.");
747
- const invalid = require_definition.checkValue(input.schema, input.initial);
748
- if (invalid) throw new require_definition.ParseError(invalid);
934
+ const invalid = require_scope.checkValue(input.schema, input.initial);
935
+ if (invalid) throw new require_scope.ParseError(invalid);
749
936
  const state = {
750
937
  schema: input.schema,
751
- document: require_definition.copyValue(input.schema, input.initial),
938
+ document: require_scope.copyValue(input.schema, input.initial),
752
939
  disposed: false
753
940
  };
754
941
  let revision = 0, busy = false;
755
942
  let runtime, notification;
756
943
  const idle = () => {
757
- if (state.disposed) throw new require_store.DocumentDisposedError();
758
- if (busy || require_store.accessOf(runtime).projectionLocks) throw new require_store.DocumentReentrancyError();
944
+ if (state.disposed) throw new require_notification.DocumentDisposedError();
945
+ if (busy || require_notification.accessOf(runtime).projectionLocks) throw new require_notification.DocumentReentrancyError();
759
946
  };
760
947
  const writable = (intent) => {
761
948
  idle();
@@ -785,7 +972,7 @@ const createDocument = (input) => {
785
972
  revision: ++revision,
786
973
  source,
787
974
  changes,
788
- impact: require_store.createImpact(input.schema, changes)
975
+ impact: createImpact(input.schema, changes)
789
976
  };
790
977
  if (source === "remote") history.invalidate();
791
978
  else if (recordHistory && (source === "local" || source === "system")) history.record(changes);
@@ -794,7 +981,7 @@ const createDocument = (input) => {
794
981
  return {
795
982
  status: "committed",
796
983
  commit,
797
- observerErrors: require_store.notify(notification, commit, history.flush)
984
+ observerErrors: require_notification.notify(notification, commit, history.flush)
798
985
  };
799
986
  };
800
987
  function mutate(intent, recordHistory, execute) {
@@ -813,7 +1000,7 @@ const createDocument = (input) => {
813
1000
  issues: [error.issue],
814
1001
  revision
815
1002
  };
816
- if (intent.kind === "update" && error instanceof require_store.TransactionRejected) return {
1003
+ if (intent.kind === "update" && error instanceof require_notification.TransactionRejected) return {
817
1004
  status: "rejected",
818
1005
  issues: error.issues,
819
1006
  revision
@@ -846,7 +1033,7 @@ const createDocument = (input) => {
846
1033
  kind: "update",
847
1034
  source
848
1035
  }, options?.history ?? true, (session) => {
849
- value = run(require_definition.createAccess({
1036
+ value = run(require_scope.createAccess({
850
1037
  state,
851
1038
  session
852
1039
  }));
@@ -872,15 +1059,15 @@ const createDocument = (input) => {
872
1059
  }, source !== "remote", (session) => session.replace([], value));
873
1060
  },
874
1061
  snapshot: () => {
875
- if (state.disposed) throw new require_store.DocumentDisposedError();
876
- return require_definition.copyValue(input.schema, state.document);
1062
+ if (state.disposed) throw new require_notification.DocumentDisposedError();
1063
+ return require_scope.copyValue(input.schema, state.document);
877
1064
  },
878
1065
  subscribe: ((pick, listener) => {
879
- if (state.disposed) throw new require_store.DocumentDisposedError();
880
- if (!listener) return require_store.subscribeRoot(notification, pick);
1066
+ if (state.disposed) throw new require_notification.DocumentDisposedError();
1067
+ if (!listener) return require_notification.subscribeRoot(notification, pick);
881
1068
  const picks = Array.isArray(pick) ? pick : [pick];
882
1069
  if (!picks.length) throw new TypeError("Expected at least one subscription path.");
883
- return require_store.subscribeTargets(notification, picks.map((p) => require_store.compilePath(input.schema, "value", p)), listener);
1070
+ return require_notification.subscribeTargets(notification, picks.map((p) => compilePath(input.schema, "value", p)), listener);
884
1071
  }),
885
1072
  history: history.api,
886
1073
  dispose: () => {
@@ -888,17 +1075,17 @@ const createDocument = (input) => {
888
1075
  idle();
889
1076
  state.disposed = true;
890
1077
  try {
891
- require_store.disposeNotification(notification);
1078
+ require_notification.disposeNotification(notification);
892
1079
  } finally {
893
1080
  history.dispose();
894
1081
  require_driver.disposeRuntimeDriver(runtime);
895
1082
  }
896
1083
  }
897
1084
  };
898
- require_store.bindRuntimeAccess(runtime, state);
1085
+ require_notification.bindRuntimeAccess(runtime, state);
899
1086
  require_driver.bindRuntimeDriver(runtime);
900
- notification = require_store.createNotification(runtime);
901
- require_store.bindDocumentReadable(history.api, runtime);
1087
+ notification = require_notification.createNotification(runtime);
1088
+ require_notification.bindDocumentReadable(history.api, runtime);
902
1089
  return runtime;
903
1090
  };
904
1091
  //#endregion
@@ -909,37 +1096,1849 @@ const asReadable = (runtime) => {
909
1096
  revision: runtime.revision,
910
1097
  subscribe: runtime.subscribe
911
1098
  };
912
- require_store.bindRuntimeAccess(readable, require_store.accessOf(runtime));
913
- require_store.shareNotification(runtime, readable);
1099
+ require_notification.bindRuntimeAccess(readable, require_notification.accessOf(runtime));
1100
+ require_notification.shareNotification(runtime, readable);
914
1101
  return Object.freeze(readable);
915
1102
  };
916
1103
  //#endregion
917
1104
  //#region core/src/projection/select.ts
918
- const read = (runtime, selector) => require_store.readWith(runtime, selector);
1105
+ const read = (runtime, selector) => require_notification.readWith(runtime, selector);
1106
+ //#endregion
1107
+ //#region core/src/projection/contract.ts
1108
+ var ProjectionError = class extends Error {
1109
+ constructor(phase, identity, revisions, cause) {
1110
+ super(`Projection ${phase} failed: ${identity}`, { cause });
1111
+ this.phase = phase;
1112
+ this.identity = identity;
1113
+ this.revisions = revisions;
1114
+ this.name = "ProjectionError";
1115
+ }
1116
+ };
1117
+ var ProjectionDisposedError = class extends Error {
1118
+ constructor() {
1119
+ super("Projection has been disposed.");
1120
+ this.name = "ProjectionDisposedError";
1121
+ }
1122
+ };
1123
+ //#endregion
1124
+ //#region core/src/projection/scheduler.ts
1125
+ const projectionHandles = /* @__PURE__ */ new WeakSet();
1126
+ const createScheduler = (onError) => {
1127
+ let disposed = false;
1128
+ let depth = 0;
1129
+ let batchSequence = 0;
1130
+ let activeBatch;
1131
+ let phase = "idle";
1132
+ let sequence = 0;
1133
+ const records = /* @__PURE__ */ new Map();
1134
+ const nodes = /* @__PURE__ */ new Set();
1135
+ const pending = /* @__PURE__ */ new Set();
1136
+ const heap = [];
1137
+ const queued = /* @__PURE__ */ new Set();
1138
+ const completed = [];
1139
+ const emissions = /* @__PURE__ */ new Set();
1140
+ const errors = [];
1141
+ let reportingFailures = [];
1142
+ const guards = /* @__PURE__ */ new Set();
1143
+ const cleanups = /* @__PURE__ */ new Set();
1144
+ const assertActive = () => {
1145
+ if (disposed) throw new ProjectionDisposedError();
1146
+ };
1147
+ const assertIdle = () => {
1148
+ assertActive();
1149
+ if (phase !== "idle") throw new Error("Projection cannot be re-entered during processing or notification.");
1150
+ };
1151
+ const lock = (value) => guards.forEach((guard) => guard(value));
1152
+ const enqueue = (node) => {
1153
+ if (node.disposed || queued.has(node)) return;
1154
+ queued.add(node);
1155
+ require_issue.profile.projection("scheduledNodes");
1156
+ let i = heap.length;
1157
+ heap.push(node);
1158
+ while (i > 0) {
1159
+ const p = i - 1 >> 1;
1160
+ if (heap[p].order < node.order) break;
1161
+ heap[i] = heap[p];
1162
+ i = p;
1163
+ }
1164
+ heap[i] = node;
1165
+ };
1166
+ const pop = () => {
1167
+ const first = heap[0];
1168
+ const last = heap.pop();
1169
+ if (heap.length) {
1170
+ let i = 0;
1171
+ while (i * 2 + 1 < heap.length) {
1172
+ let c = i * 2 + 1;
1173
+ if (c + 1 < heap.length && heap[c + 1].order < heap[c].order) c++;
1174
+ if (last.order < heap[c].order) break;
1175
+ heap[i] = heap[c];
1176
+ i = c;
1177
+ }
1178
+ heap[i] = last;
1179
+ }
1180
+ return first;
1181
+ };
1182
+ const errorFor = (node, cause, kind = "processor") => new ProjectionError(kind, node.name, node.sources.map((source) => source.revision()), cause);
1183
+ const capture = (source) => {
1184
+ if (disposed) return;
1185
+ if (phase !== "idle") {
1186
+ source.fault = new ProjectionError("source", "reentrant source", [source.revision()], /* @__PURE__ */ new Error("Source changed while projection was running."));
1187
+ errors.push(source.fault);
1188
+ throw source.fault;
1189
+ }
1190
+ pending.add(source);
1191
+ require_issue.profile.projection("sourceEvents");
1192
+ if (source.fault) errors.push(source.fault);
1193
+ };
1194
+ const settle = () => {
1195
+ if (disposed || depth || phase !== "idle") return;
1196
+ phase = "compute";
1197
+ lock(true);
1198
+ try {
1199
+ pending.forEach((source) => source.consumers.forEach(enqueue));
1200
+ while (heap.length) {
1201
+ const node = pop();
1202
+ if (node.disposed) continue;
1203
+ const wasFaulted = node.fault !== void 0;
1204
+ require_issue.profile.projection("processedNodes");
1205
+ let changed = false;
1206
+ const blocked = node.sources.find((source) => source.fault || source.disposed);
1207
+ if (blocked) node.fault = errorFor(node, blocked.fault ?? new ProjectionDisposedError(), "blocked");
1208
+ else {
1209
+ const build = node.forced || wasFaulted || node.sources.some((source) => source.reset());
1210
+ try {
1211
+ changed = node.evaluate(build);
1212
+ const failure = node.sources.find((source) => source.fault)?.fault;
1213
+ if (failure) throw failure;
1214
+ node.fault = void 0;
1215
+ } catch (cause) {
1216
+ const error = errorFor(node, cause);
1217
+ errors.push(error);
1218
+ node.fault = error;
1219
+ if (!build) try {
1220
+ changed = node.evaluate(true);
1221
+ const failure = node.sources.find((source) => source.fault)?.fault;
1222
+ if (failure) throw failure;
1223
+ node.fault = void 0;
1224
+ } catch (cause) {
1225
+ node.fault = errorFor(node, cause);
1226
+ errors.push(node.fault);
1227
+ }
1228
+ }
1229
+ }
1230
+ node.forced = false;
1231
+ node.statusChanged = wasFaulted !== (node.fault !== void 0);
1232
+ completed.push(node);
1233
+ if (changed || wasFaulted || node.fault) {
1234
+ require_issue.profile.projection("publishedNodes");
1235
+ emissions.add(node);
1236
+ node.consumers.forEach(enqueue);
1237
+ }
1238
+ }
1239
+ completed.forEach((node) => node.publish());
1240
+ } finally {
1241
+ lock(false);
1242
+ phase = "idle";
1243
+ }
1244
+ };
1245
+ const flush = () => {
1246
+ if (disposed || depth) return [];
1247
+ if (pending.size || completed.length) require_issue.profile.projection("flushes");
1248
+ phase = "notify";
1249
+ lock(true);
1250
+ const reportFailures = [];
1251
+ try {
1252
+ emissions.forEach((node) => node.emit((listener) => {
1253
+ try {
1254
+ listener();
1255
+ } catch (cause) {
1256
+ errors.push(errorFor(node, cause, "listener"));
1257
+ }
1258
+ }));
1259
+ for (const error of errors.slice()) try {
1260
+ onError(error);
1261
+ } catch (cause) {
1262
+ reportFailures.push(cause);
1263
+ }
1264
+ return Object.freeze([...errors.map((error) => Object.freeze({
1265
+ phase: error.phase === "listener" ? "listener" : "processor",
1266
+ error
1267
+ })), ...reportFailures.map((error) => Object.freeze({
1268
+ phase: "listener",
1269
+ error
1270
+ }))]);
1271
+ } finally {
1272
+ pending.forEach((source) => source.clear());
1273
+ completed.forEach((node) => node.clear());
1274
+ pending.clear();
1275
+ queued.clear();
1276
+ completed.length = 0;
1277
+ emissions.clear();
1278
+ errors.length = 0;
1279
+ reportingFailures = reportFailures;
1280
+ lock(false);
1281
+ phase = "idle";
1282
+ }
1283
+ };
1284
+ const run = () => {
1285
+ settle();
1286
+ const result = flush();
1287
+ const failures = reportingFailures;
1288
+ reportingFailures = [];
1289
+ if (failures.length) throw new AggregateError(failures, "Projection error reporter failed.");
1290
+ return result;
1291
+ };
1292
+ const batch = (optionsOrCallback, maybeCallback) => {
1293
+ assertIdle();
1294
+ const options = typeof optionsOrCallback === "function" ? void 0 : optionsOrCallback;
1295
+ const callback = typeof optionsOrCallback === "function" ? optionsOrCallback : maybeCallback;
1296
+ if (!callback) throw new TypeError("Projection batch requires a callback.");
1297
+ if (depth === 0) activeBatch = Object.freeze({
1298
+ id: ++batchSequence,
1299
+ ...options?.cause === void 0 ? {} : { cause: options.cause }
1300
+ });
1301
+ depth++;
1302
+ let failed = false;
1303
+ try {
1304
+ const value = callback();
1305
+ assertSynchronous(value);
1306
+ return value;
1307
+ } catch (error) {
1308
+ failed = true;
1309
+ throw error;
1310
+ } finally {
1311
+ depth--;
1312
+ if (!depth) try {
1313
+ if (failed) try {
1314
+ run();
1315
+ } catch {}
1316
+ else run();
1317
+ } finally {
1318
+ activeBatch = void 0;
1319
+ }
1320
+ }
1321
+ };
1322
+ return {
1323
+ assertActive,
1324
+ assertIdle,
1325
+ capture,
1326
+ settle,
1327
+ flush,
1328
+ run,
1329
+ records,
1330
+ guards,
1331
+ cleanups,
1332
+ get active() {
1333
+ return !disposed;
1334
+ },
1335
+ register(handle, record) {
1336
+ assertIdle();
1337
+ records.set(handle, record);
1338
+ projectionHandles.add(handle);
1339
+ },
1340
+ unregisterSource(handle) {
1341
+ const record = records.get(handle);
1342
+ if (record) {
1343
+ record.disposed = true;
1344
+ pending.delete(record);
1345
+ record.clear();
1346
+ records.delete(handle);
1347
+ }
1348
+ },
1349
+ source(handle) {
1350
+ assertActive();
1351
+ const source = records.get(handle);
1352
+ if (!source || source.disposed) throw new Error("Unknown, disposed, or foreign projection source.");
1353
+ return source;
1354
+ },
1355
+ addNode(node) {
1356
+ nodes.add(node);
1357
+ node.sources.forEach((source) => source.consumers.add(node));
1358
+ },
1359
+ order: () => sequence++,
1360
+ initialize(run) {
1361
+ assertIdle();
1362
+ phase = "compute";
1363
+ lock(true);
1364
+ try {
1365
+ return run();
1366
+ } finally {
1367
+ lock(false);
1368
+ phase = "idle";
1369
+ }
1370
+ },
1371
+ rebuild(node) {
1372
+ assertIdle();
1373
+ if (node.disposed) throw new ProjectionDisposedError();
1374
+ node.forced = true;
1375
+ enqueue(node);
1376
+ run();
1377
+ },
1378
+ disposeNode(node) {
1379
+ if (node.disposed) return;
1380
+ assertIdle();
1381
+ if (node.consumers.size) throw new Error("Cannot dispose a projection node with downstream consumers.");
1382
+ node.disposed = true;
1383
+ node.sources.forEach((source) => source.consumers.delete(node));
1384
+ nodes.delete(node);
1385
+ node.release();
1386
+ for (const [handle, record] of records) if (record === node) records.delete(handle);
1387
+ },
1388
+ batch,
1389
+ batchContext: () => activeBatch,
1390
+ dispose() {
1391
+ if (disposed) return;
1392
+ assertIdle();
1393
+ disposed = true;
1394
+ const failures = [];
1395
+ cleanups.forEach((cleanup) => {
1396
+ try {
1397
+ cleanup();
1398
+ } catch (error) {
1399
+ failures.push(error);
1400
+ }
1401
+ });
1402
+ cleanups.clear();
1403
+ nodes.forEach((node) => {
1404
+ node.disposed = true;
1405
+ node.release();
1406
+ node.consumers.clear();
1407
+ });
1408
+ records.forEach((source) => {
1409
+ source.disposed = true;
1410
+ source.consumers.clear();
1411
+ source.clear();
1412
+ });
1413
+ nodes.clear();
1414
+ records.clear();
1415
+ pending.clear();
1416
+ heap.length = 0;
1417
+ completed.length = 0;
1418
+ queued.clear();
1419
+ emissions.clear();
1420
+ guards.clear();
1421
+ errors.length = 0;
1422
+ reportingFailures = [];
1423
+ if (failures.length) throw new AggregateError(failures, "Projection cleanup failed.");
1424
+ },
1425
+ debug: () => ({
1426
+ nodes: nodes.size,
1427
+ sources: records.size - nodes.size,
1428
+ subscriptions: cleanups.size,
1429
+ pending: pending.size + heap.length
1430
+ })
1431
+ };
1432
+ };
1433
+ const assertSynchronous = (value) => {
1434
+ if (value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function") throw new TypeError("Projection callbacks must be synchronous.");
1435
+ };
1436
+ const assertScope = (active) => {
1437
+ if (!active()) throw new Error("Projection reader or writer is no longer active.");
1438
+ };
1439
+ //#endregion
1440
+ //#region core/src/projection/node.ts
1441
+ const createNode = (scheduler, spec, behavior) => {
1442
+ scheduler.assertIdle();
1443
+ const entries = Object.entries(spec.sources).map(([key, handle]) => [key, scheduler.source(handle)]);
1444
+ const order = scheduler.order();
1445
+ const node = {
1446
+ ...behavior,
1447
+ publish: () => {
1448
+ if (node.fault) behavior.clear();
1449
+ else behavior.publish();
1450
+ },
1451
+ order,
1452
+ name: spec.name ? `${spec.name} (${order})` : `projection-${order}`,
1453
+ sources: entries.map(([, source]) => source),
1454
+ consumers: /* @__PURE__ */ new Set(),
1455
+ disposed: false,
1456
+ fault: void 0,
1457
+ forced: false,
1458
+ statusChanged: false,
1459
+ evaluate: (build) => {
1460
+ let active = true;
1461
+ try {
1462
+ const scopeActive = () => active;
1463
+ const inputs = Object.fromEntries(entries.map(([key, source]) => [key, source.context(scopeActive)]));
1464
+ return behavior.evaluate(inputs, build, scopeActive);
1465
+ } finally {
1466
+ active = false;
1467
+ }
1468
+ }
1469
+ };
1470
+ scheduler.initialize(() => {
1471
+ const invalid = node.sources.find((source) => source.fault || source.disposed);
1472
+ if (invalid) throw invalid.fault ?? new ProjectionDisposedError();
1473
+ node.evaluate(true);
1474
+ node.publish();
1475
+ node.clear();
1476
+ });
1477
+ const check = () => {
1478
+ if (node.disposed || !scheduler.active) throw new ProjectionDisposedError();
1479
+ if (node.fault) throw node.fault;
1480
+ };
1481
+ return {
1482
+ node,
1483
+ check,
1484
+ install(handle) {
1485
+ scheduler.register(handle, node);
1486
+ scheduler.addNode(node);
1487
+ },
1488
+ rebuild: () => scheduler.rebuild(node),
1489
+ dispose: () => scheduler.disposeNode(node)
1490
+ };
1491
+ };
1492
+ //#endregion
1493
+ //#region core/src/projection/collection.ts
1494
+ const collectionHandles = /* @__PURE__ */ new WeakSet();
1495
+ const readonlySet = (values) => {
1496
+ const set = new Set(values);
1497
+ const view = Object.freeze({
1498
+ get size() {
1499
+ return set.size;
1500
+ },
1501
+ has: (value) => set.has(value),
1502
+ entries: () => set.entries(),
1503
+ keys: () => set.keys(),
1504
+ values: () => set.values(),
1505
+ [Symbol.iterator]: () => set.values(),
1506
+ forEach: (callback, thisArg) => set.forEach((value) => callback.call(thisArg, value, value, view))
1507
+ });
1508
+ return view;
1509
+ };
1510
+ const createCollection = (scheduler, spec) => {
1511
+ const values = /* @__PURE__ */ new Map();
1512
+ let ids = Object.freeze([]);
1513
+ let staged = /* @__PURE__ */ new Map();
1514
+ let nextIds = ids;
1515
+ let change;
1516
+ let instance;
1517
+ let initialized = false;
1518
+ let reset = false;
1519
+ let revision = 0;
1520
+ let idsRevision = 0;
1521
+ let batch;
1522
+ let cause;
1523
+ let publishedTransitions = Object.freeze([]);
1524
+ let all;
1525
+ const items = /* @__PURE__ */ new Map();
1526
+ const subscribedItems = /* @__PURE__ */ new Set();
1527
+ const idsListeners = /* @__PURE__ */ new Set();
1528
+ const allListeners = /* @__PURE__ */ new Set();
1529
+ const listeners = /* @__PURE__ */ new Set();
1530
+ const changedKeys = /* @__PURE__ */ new Set();
1531
+ let orderChanged = false;
1532
+ const current = Object.freeze({
1533
+ get: (key) => {
1534
+ owner.check();
1535
+ return values.get(key);
1536
+ },
1537
+ has: (key) => {
1538
+ owner.check();
1539
+ return values.has(key);
1540
+ },
1541
+ ids: () => {
1542
+ owner.check();
1543
+ return ids;
1544
+ }
1545
+ });
1546
+ const hasNext = (key) => staged.has(key) ? staged.get(key).present : values.has(key);
1547
+ const getNext = (key) => {
1548
+ const entry = staged.get(key);
1549
+ return entry ? entry.present ? entry.value : void 0 : values.get(key);
1550
+ };
1551
+ const owner = createNode(scheduler, spec, {
1552
+ evaluate: (sources, build, active) => {
1553
+ 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));
1554
+ batch = scheduler.batchContext() ?? metadata?.batch;
1555
+ cause = metadata?.cause ?? batch?.cause;
1556
+ staged = /* @__PURE__ */ new Map();
1557
+ nextIds = ids;
1558
+ change = void 0;
1559
+ changedKeys.clear();
1560
+ let explicitOrder;
1561
+ let cleared = false;
1562
+ const nextHas = (key) => staged.has(key) ? staged.get(key).present : !cleared && values.has(key);
1563
+ const nextGet = (key) => {
1564
+ const entry = staged.get(key);
1565
+ return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : values.get(key);
1566
+ };
1567
+ const deriveIds = () => {
1568
+ const result = cleared ? [] : ids.filter(nextHas);
1569
+ for (const [key, entry] of staged) if (entry.present && (cleared || !values.has(key))) result.push(key);
1570
+ return result;
1571
+ };
1572
+ const read = (next) => ({
1573
+ get: (key) => {
1574
+ assertScope(active);
1575
+ return next ? nextGet(key) : values.get(key);
1576
+ },
1577
+ has: (key) => {
1578
+ assertScope(active);
1579
+ return next ? nextHas(key) : values.has(key);
1580
+ },
1581
+ ids: () => {
1582
+ assertScope(active);
1583
+ return next ? Object.freeze(explicitOrder ? explicitOrder.slice() : deriveIds()) : ids;
1584
+ }
1585
+ });
1586
+ const writer = {
1587
+ set: (key, value) => {
1588
+ assertScope(active);
1589
+ if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
1590
+ staged.set(key, {
1591
+ present: true,
1592
+ value
1593
+ });
1594
+ },
1595
+ remove: (key) => {
1596
+ assertScope(active);
1597
+ staged.set(key, { present: false });
1598
+ },
1599
+ order: (order) => {
1600
+ assertScope(active);
1601
+ explicitOrder = order.slice();
1602
+ },
1603
+ replace: (entries) => {
1604
+ assertScope(active);
1605
+ staged.clear();
1606
+ cleared = true;
1607
+ const order = [];
1608
+ for (const [key, value] of entries) {
1609
+ if (staged.has(key)) throw new TypeError("Duplicate projection key.");
1610
+ writer.set(key, value);
1611
+ order.push(key);
1612
+ }
1613
+ explicitOrder = order;
1614
+ }
1615
+ };
1616
+ const input = {
1617
+ sources,
1618
+ previous: read(false),
1619
+ next: read(true),
1620
+ writer
1621
+ };
1622
+ if (build || !instance) {
1623
+ require_issue.profile.materialized.rebuilt();
1624
+ instance = void 0;
1625
+ cleared = true;
1626
+ const built = spec.build(input);
1627
+ assertSynchronous(built);
1628
+ instance = built;
1629
+ } else {
1630
+ require_issue.profile.materialized.updated();
1631
+ const result = instance.update(input);
1632
+ assertSynchronous(result);
1633
+ if (result?.kind === "rebuild") {
1634
+ staged.clear();
1635
+ cleared = true;
1636
+ explicitOrder = void 0;
1637
+ instance = void 0;
1638
+ const built = spec.build(input);
1639
+ assertSynchronous(built);
1640
+ instance = built;
1641
+ build = true;
1642
+ }
1643
+ }
1644
+ if (cleared) {
1645
+ for (const key of values.keys()) if (!staged.has(key)) staged.set(key, { present: false });
1646
+ }
1647
+ const added = /* @__PURE__ */ new Set();
1648
+ const removed = /* @__PURE__ */ new Set();
1649
+ const updated = /* @__PURE__ */ new Set();
1650
+ require_issue.profile.projection("touchedKeys", staged.size);
1651
+ for (const [key, entry] of staged) {
1652
+ const existed = values.has(key);
1653
+ if (entry.present) if (!existed) added.add(key);
1654
+ else if (!(spec.isEqual ?? Object.is)(values.get(key), entry.value)) updated.add(key);
1655
+ else staged.delete(key);
1656
+ else if (existed) removed.add(key);
1657
+ else staged.delete(key);
1658
+ }
1659
+ if (explicitOrder || added.size || removed.size) {
1660
+ const order = explicitOrder ?? [...ids.filter((key) => !removed.has(key)), ...added];
1661
+ 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.");
1662
+ nextIds = ids.length === order.length && ids.every((key, i) => key === order[i]) ? ids : Object.freeze(order.slice());
1663
+ }
1664
+ orderChanged = nextIds !== ids;
1665
+ if (added.size || removed.size || updated.size || orderChanged) {
1666
+ let commonOrderChanged = false;
1667
+ if (orderChanged) {
1668
+ const before = ids.filter((key) => !removed.has(key));
1669
+ const after = nextIds.filter((key) => !added.has(key));
1670
+ commonOrderChanged = before.some((key, i) => after[i] !== key);
1671
+ }
1672
+ change = Object.freeze({
1673
+ kind: "incremental",
1674
+ added: readonlySet(added),
1675
+ removed: readonlySet(removed),
1676
+ updated: readonlySet(updated),
1677
+ orderChanged: commonOrderChanged
1678
+ });
1679
+ [
1680
+ ...added,
1681
+ ...removed,
1682
+ ...updated
1683
+ ].forEach((key) => changedKeys.add(key));
1684
+ require_issue.profile.projection("changedKeys", changedKeys.size);
1685
+ }
1686
+ publishedTransitions = Object.freeze([...changedKeys].flatMap((key) => {
1687
+ const beforePresent = values.has(key);
1688
+ const afterPresent = hasNext(key);
1689
+ if (beforePresent === afterPresent) {
1690
+ if (!beforePresent) return [];
1691
+ if ((spec.isEqual ?? Object.is)(values.get(key), getNext(key))) return [];
1692
+ }
1693
+ return [{
1694
+ key,
1695
+ kind: !beforePresent ? "added" : !afterPresent ? "removed" : "updated",
1696
+ before: beforePresent ? values.get(key) : void 0,
1697
+ after: afterPresent ? getNext(key) : void 0
1698
+ }];
1699
+ }));
1700
+ reset = build;
1701
+ return change !== void 0;
1702
+ },
1703
+ context: (active) => {
1704
+ const transitions = (keys) => {
1705
+ assertScope(active);
1706
+ const selected = keys ? new Set(keys) : void 0;
1707
+ return Object.freeze(publishedTransitions.filter((transition) => !selected || selected.has(transition.key)));
1708
+ };
1709
+ const input = {
1710
+ get: (key) => {
1711
+ assertScope(active);
1712
+ return getNext(key);
1713
+ },
1714
+ has: (key) => {
1715
+ assertScope(active);
1716
+ return hasNext(key);
1717
+ },
1718
+ ids: () => {
1719
+ assertScope(active);
1720
+ return nextIds;
1721
+ },
1722
+ previous: Object.freeze({
1723
+ get: (key) => {
1724
+ assertScope(active);
1725
+ return values.get(key);
1726
+ },
1727
+ has: (key) => {
1728
+ assertScope(active);
1729
+ return values.has(key);
1730
+ },
1731
+ ids: () => {
1732
+ assertScope(active);
1733
+ return ids;
1734
+ }
1735
+ }),
1736
+ change,
1737
+ revision: revision + (change ? 1 : 0),
1738
+ reset,
1739
+ transitions,
1740
+ batch,
1741
+ cause
1742
+ };
1743
+ return Object.freeze(input);
1744
+ },
1745
+ revision: () => revision,
1746
+ reset: () => reset,
1747
+ publish: () => {
1748
+ for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
1749
+ else values.delete(key);
1750
+ if (change) {
1751
+ if (initialized) revision++;
1752
+ if (orderChanged && initialized) idsRevision++;
1753
+ all = void 0;
1754
+ changedKeys.forEach((key) => {
1755
+ const item = items.get(key);
1756
+ if (item && initialized) item.revision++;
1757
+ });
1758
+ }
1759
+ ids = nextIds;
1760
+ initialized = true;
1761
+ },
1762
+ emit: (call) => {
1763
+ require_issue.profile.materialized.notification();
1764
+ const fault = owner.node.fault !== void 0;
1765
+ const resetEvent = Object.freeze({ kind: "reset" });
1766
+ Array.from(listeners).forEach((listener) => call(() => listener(change ?? resetEvent)));
1767
+ if (orderChanged || !change || fault || owner.node.statusChanged) Array.from(idsListeners).forEach((listener) => call(listener));
1768
+ Array.from(allListeners).forEach((listener) => call(listener));
1769
+ if (!change || fault || owner.node.statusChanged) for (const item of subscribedItems) Array.from(item.listeners ?? []).forEach((listener) => call(listener));
1770
+ else for (const key of changedKeys) Array.from(items.get(key)?.listeners ?? []).forEach((listener) => call(listener));
1771
+ },
1772
+ clear: () => {
1773
+ staged.clear();
1774
+ nextIds = ids;
1775
+ change = void 0;
1776
+ reset = false;
1777
+ changedKeys.clear();
1778
+ publishedTransitions = Object.freeze([]);
1779
+ batch = void 0;
1780
+ cause = void 0;
1781
+ orderChanged = false;
1782
+ },
1783
+ release: () => {
1784
+ values.clear();
1785
+ staged.clear();
1786
+ items.clear();
1787
+ subscribedItems.clear();
1788
+ idsListeners.clear();
1789
+ allListeners.clear();
1790
+ listeners.clear();
1791
+ ids = nextIds = Object.freeze([]);
1792
+ all = void 0;
1793
+ instance = void 0;
1794
+ }
1795
+ });
1796
+ const handle = {
1797
+ current: () => {
1798
+ owner.check();
1799
+ return current;
1800
+ },
1801
+ ids: {
1802
+ current: () => {
1803
+ owner.check();
1804
+ return ids;
1805
+ },
1806
+ revision: () => {
1807
+ owner.check();
1808
+ return idsRevision;
1809
+ },
1810
+ subscribe: (listener) => {
1811
+ owner.check();
1812
+ idsListeners.add(listener);
1813
+ return () => {
1814
+ idsListeners.delete(listener);
1815
+ };
1816
+ }
1817
+ },
1818
+ all: {
1819
+ current: () => {
1820
+ owner.check();
1821
+ if (!all) {
1822
+ require_issue.profile.collectionView.arrayCopied();
1823
+ all = Object.freeze(ids.map((key) => values.get(key)));
1824
+ }
1825
+ return all;
1826
+ },
1827
+ revision: () => {
1828
+ owner.check();
1829
+ return revision;
1830
+ },
1831
+ subscribe: (listener) => {
1832
+ owner.check();
1833
+ allListeners.add(listener);
1834
+ return () => {
1835
+ allListeners.delete(listener);
1836
+ };
1837
+ }
1838
+ },
1839
+ item: (key) => {
1840
+ owner.check();
1841
+ const old = items.get(key);
1842
+ if (old) return old.readable;
1843
+ const readable = Object.freeze({
1844
+ current: () => {
1845
+ owner.check();
1846
+ return values.get(key);
1847
+ },
1848
+ revision: () => {
1849
+ owner.check();
1850
+ return item.revision;
1851
+ },
1852
+ subscribe: (listener) => {
1853
+ owner.check();
1854
+ const set = item.listeners ??= /* @__PURE__ */ new Set();
1855
+ set.add(listener);
1856
+ subscribedItems.add(item);
1857
+ return () => {
1858
+ set.delete(listener);
1859
+ if (!set.size) subscribedItems.delete(item);
1860
+ };
1861
+ }
1862
+ });
1863
+ const item = {
1864
+ readable,
1865
+ revision: 0
1866
+ };
1867
+ projectionHandles.add(readable);
1868
+ items.set(key, item);
1869
+ return readable;
1870
+ },
1871
+ revision: () => {
1872
+ owner.check();
1873
+ return revision;
1874
+ },
1875
+ subscribe: (listener) => {
1876
+ owner.check();
1877
+ listeners.add(listener);
1878
+ return () => {
1879
+ listeners.delete(listener);
1880
+ };
1881
+ },
1882
+ rebuild: owner.rebuild,
1883
+ dispose: owner.dispose
1884
+ };
1885
+ projectionHandles.add(handle.ids);
1886
+ projectionHandles.add(handle.all);
1887
+ collectionHandles.add(handle);
1888
+ owner.install(handle);
1889
+ return Object.freeze(handle);
1890
+ };
1891
+ //#endregion
1892
+ //#region core/src/projection/source.ts
1893
+ const createSources = (scheduler) => {
1894
+ const documents = /* @__PURE__ */ new Map();
1895
+ const readables = /* @__PURE__ */ new Map();
1896
+ const collections = /* @__PURE__ */ new WeakSet();
1897
+ const externalSources = /* @__PURE__ */ new WeakMap();
1898
+ const document = (runtime) => {
1899
+ scheduler.assertIdle();
1900
+ const state = require_notification.accessOf(runtime);
1901
+ if (state.disposed) throw new Error("Document has been disposed.");
1902
+ const existing = documents.get(state);
1903
+ if (existing) return existing;
1904
+ const bindings = [];
1905
+ let batch;
1906
+ const make = (input) => {
1907
+ const collection = "collection" in input ? input.collection : void 0;
1908
+ const targets = "targets" in input ? input.targets : [input.collection];
1909
+ if (state.disposed) throw new Error("Document has been disposed.");
1910
+ targets.forEach((value) => {
1911
+ if (!require_notification.belongs(value, state.schema)) throw new Error("Projection target belongs to another schema.");
1912
+ });
1913
+ const old = bindings.find((entry) => Boolean(collection) === collections.has(entry.handle) && entry.targets.length === targets.length && entry.targets.every((value, i) => require_notification.same(value, targets[i])));
1914
+ if (old) return old.handle;
1915
+ let commits = [];
1916
+ let reset = false;
1917
+ let orderDirty = false;
1918
+ const keys = /* @__PURE__ */ new Set();
1919
+ let candidates;
1920
+ const record = {
1921
+ consumers: /* @__PURE__ */ new Set(),
1922
+ disposed: false,
1923
+ fault: void 0,
1924
+ revision: runtime.revision,
1925
+ reset: () => reset,
1926
+ clear: () => {
1927
+ commits = [];
1928
+ reset = orderDirty = false;
1929
+ batch = void 0;
1930
+ keys.clear();
1931
+ candidates = void 0;
1932
+ },
1933
+ context: (active) => {
1934
+ assertScope(active);
1935
+ if (state.disposed) throw new Error("Document has been disposed.");
1936
+ const context = {
1937
+ state,
1938
+ active
1939
+ };
1940
+ const read = collection ? require_scope.collectionAccess(context, collection.address) : require_scope.createAccess(context);
1941
+ return Object.freeze({
1942
+ read,
1943
+ revision: runtime.revision(),
1944
+ commits: Object.freeze(commits.slice()),
1945
+ reset: record.reset(),
1946
+ batch,
1947
+ cause: batch?.cause,
1948
+ ...collection ? { candidates: candidates ??= Object.freeze({
1949
+ keys: Object.freeze([...keys]),
1950
+ orderDirty
1951
+ }) } : {}
1952
+ });
1953
+ }
1954
+ };
1955
+ const handle = collection ? {} : {
1956
+ collection: (pick) => {
1957
+ scheduler.assertIdle();
1958
+ return make({ collection: compilePath(state.schema, "collection", pick) });
1959
+ },
1960
+ targets: (...selected) => {
1961
+ scheduler.assertIdle();
1962
+ if (!selected.length) throw new TypeError("Expected at least one target.");
1963
+ return make({ targets: selected.map((pick) => compilePath(state.schema, "value", pick)) });
1964
+ }
1965
+ };
1966
+ Object.freeze(handle);
1967
+ if (collection) collections.add(handle);
1968
+ scheduler.register(handle, record);
1969
+ const receive = (commit) => {
1970
+ commits.push(commit);
1971
+ reset ||= commit.impact.kind === "reset";
1972
+ if (collection) {
1973
+ const change = collectionImpact(commit.impact, collection);
1974
+ if (change.kind === "reset") reset = true;
1975
+ else {
1976
+ change.added.forEach((key) => keys.add(key));
1977
+ change.removed.forEach((key) => keys.add(key));
1978
+ change.updated.forEach((key) => keys.add(key));
1979
+ orderDirty ||= Boolean(change.added.size || change.removed.size || change.orderChanged);
1980
+ }
1981
+ candidates = void 0;
1982
+ }
1983
+ };
1984
+ bindings.push({
1985
+ handle,
1986
+ targets,
1987
+ record,
1988
+ receive
1989
+ });
1990
+ return handle;
1991
+ };
1992
+ const handle = make({ targets: [] });
1993
+ const guard = (locked) => {
1994
+ state.projectionLocks = (state.projectionLocks ?? 0) + (locked ? 1 : -1);
1995
+ };
1996
+ scheduler.guards.add(guard);
1997
+ const unsubscribe = require_notification.attachProjection(runtime, {
1998
+ capture: (commit) => {
1999
+ for (const binding of bindings) if (!binding.targets.length || binding.targets.some((value) => affectsTarget(commit.impact, value))) {
2000
+ binding.receive(commit);
2001
+ batch = scheduler.batchContext();
2002
+ scheduler.capture(binding.record);
2003
+ }
2004
+ },
2005
+ settle: scheduler.settle,
2006
+ flush: scheduler.flush,
2007
+ dispose: () => {
2008
+ bindings.forEach(({ record }) => {
2009
+ record.fault = new ProjectionError("source", "document disposed", [runtime.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
2010
+ scheduler.capture(record);
2011
+ });
2012
+ scheduler.run();
2013
+ }
2014
+ });
2015
+ scheduler.cleanups.add(() => {
2016
+ unsubscribe();
2017
+ scheduler.guards.delete(guard);
2018
+ });
2019
+ documents.set(state, handle);
2020
+ return handle;
2021
+ };
2022
+ const valueSource = (initial, equal) => {
2023
+ let value = initial;
2024
+ let previous = initial;
2025
+ let revision = 0;
2026
+ let detail;
2027
+ let cause;
2028
+ let batch;
2029
+ let reset = false;
2030
+ let pending = false;
2031
+ const handle = Object.freeze({});
2032
+ const record = {
2033
+ consumers: /* @__PURE__ */ new Set(),
2034
+ disposed: false,
2035
+ fault: void 0,
2036
+ revision: () => revision,
2037
+ reset: () => reset,
2038
+ clear: () => {
2039
+ previous = value;
2040
+ pending = false;
2041
+ detail = void 0;
2042
+ cause = void 0;
2043
+ batch = void 0;
2044
+ reset = false;
2045
+ },
2046
+ context: (active) => {
2047
+ assertScope(active);
2048
+ return Object.freeze({
2049
+ value,
2050
+ previous,
2051
+ revision,
2052
+ changed: !equal(previous, value),
2053
+ reset,
2054
+ detail,
2055
+ cause,
2056
+ batch
2057
+ });
2058
+ }
2059
+ };
2060
+ scheduler.register(handle, record);
2061
+ const accept = (next, metadata) => {
2062
+ scheduler.assertIdle();
2063
+ const recovering = record.fault !== void 0;
2064
+ 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);
2065
+ if (equal(value, next) && !recovering && !eventful) return;
2066
+ record.fault = void 0;
2067
+ if (!pending) previous = value;
2068
+ value = next;
2069
+ revision = metadata?.revision ?? revision + 1;
2070
+ detail = metadata?.detail;
2071
+ cause = metadata?.cause ?? cause;
2072
+ batch = scheduler.batchContext() ?? metadata?.batch ?? batch;
2073
+ reset ||= metadata?.reset ?? false;
2074
+ scheduler.capture(record);
2075
+ pending = true;
2076
+ };
2077
+ return {
2078
+ source: handle,
2079
+ accept,
2080
+ set(next) {
2081
+ accept(next);
2082
+ scheduler.run();
2083
+ }
2084
+ };
2085
+ };
2086
+ const externalValueSource = (port, equal) => {
2087
+ scheduler.assertIdle();
2088
+ const old = externalSources.get(port);
2089
+ if (old) return old;
2090
+ const input = valueSource(port.current(), equal);
2091
+ const fail = (cause) => {
2092
+ const record = scheduler.source(input.source);
2093
+ record.fault = new ProjectionError("source", "external source", [record.revision()], cause);
2094
+ scheduler.capture(record);
2095
+ };
2096
+ let unsubscribe;
2097
+ try {
2098
+ unsubscribe = port.subscribe((event) => {
2099
+ try {
2100
+ input.accept(event.value, {
2101
+ revision: event.revision,
2102
+ reset: event.reset,
2103
+ detail: event.detail,
2104
+ cause: event.cause,
2105
+ batch: event.batch
2106
+ });
2107
+ } catch (cause) {
2108
+ fail(cause);
2109
+ }
2110
+ scheduler.run();
2111
+ });
2112
+ } catch (cause) {
2113
+ unsubscribe?.();
2114
+ scheduler.unregisterSource(input.source);
2115
+ throw cause;
2116
+ }
2117
+ scheduler.cleanups.add(() => unsubscribe?.());
2118
+ externalSources.set(port, input.source);
2119
+ return input.source;
2120
+ };
2121
+ const externalCollectionSource = (port) => {
2122
+ scheduler.assertIdle();
2123
+ const old = externalSources.get(port);
2124
+ if (old) return old;
2125
+ let read = port.current();
2126
+ let previous = read;
2127
+ let revision = port.revision();
2128
+ let change;
2129
+ let reset = false;
2130
+ let detail;
2131
+ let cause;
2132
+ let batch;
2133
+ let publishedTransitions = Object.freeze([]);
2134
+ let transitionKeys = /* @__PURE__ */ new Set();
2135
+ let pending = false;
2136
+ const handle = Object.freeze({});
2137
+ const record = {
2138
+ consumers: /* @__PURE__ */ new Set(),
2139
+ disposed: false,
2140
+ fault: void 0,
2141
+ revision: () => revision,
2142
+ reset: () => reset,
2143
+ clear: () => {
2144
+ previous = read;
2145
+ pending = false;
2146
+ change = void 0;
2147
+ reset = false;
2148
+ detail = void 0;
2149
+ cause = void 0;
2150
+ batch = void 0;
2151
+ publishedTransitions = Object.freeze([]);
2152
+ transitionKeys.clear();
2153
+ },
2154
+ context: (active) => {
2155
+ assertScope(active);
2156
+ const currentRead = Object.freeze({
2157
+ get: (key) => {
2158
+ assertScope(active);
2159
+ return read.get(key);
2160
+ },
2161
+ has: (key) => {
2162
+ assertScope(active);
2163
+ return read.has(key);
2164
+ },
2165
+ ids: () => {
2166
+ assertScope(active);
2167
+ return read.ids();
2168
+ }
2169
+ });
2170
+ const previousRead = Object.freeze({
2171
+ get: (key) => {
2172
+ assertScope(active);
2173
+ return previous.get(key);
2174
+ },
2175
+ has: (key) => {
2176
+ assertScope(active);
2177
+ return previous.has(key);
2178
+ },
2179
+ ids: () => {
2180
+ assertScope(active);
2181
+ return previous.ids();
2182
+ }
2183
+ });
2184
+ return Object.freeze({
2185
+ ...currentRead,
2186
+ previous: previousRead,
2187
+ change,
2188
+ revision,
2189
+ reset,
2190
+ transitions: (keys) => {
2191
+ assertScope(active);
2192
+ const selected = keys ? new Set(keys) : void 0;
2193
+ return Object.freeze(publishedTransitions.filter((transition) => !selected || selected.has(transition.key)));
2194
+ },
2195
+ detail,
2196
+ cause,
2197
+ batch
2198
+ });
2199
+ }
2200
+ };
2201
+ scheduler.register(handle, record);
2202
+ collections.add(handle);
2203
+ const fail = (error) => {
2204
+ record.fault = new ProjectionError("source", "external collection source", [revision], error);
2205
+ scheduler.capture(record);
2206
+ };
2207
+ const addTransitionKeys = (impact) => {
2208
+ if (impact?.kind === "incremental") {
2209
+ impact.added.forEach((key) => transitionKeys.add(key));
2210
+ impact.removed.forEach((key) => transitionKeys.add(key));
2211
+ impact.updated.forEach((key) => transitionKeys.add(key));
2212
+ }
2213
+ };
2214
+ const recomputeTransitions = () => {
2215
+ const transitions = [];
2216
+ for (const key of transitionKeys) {
2217
+ const beforePresent = previous.has(key);
2218
+ const afterPresent = read.has(key);
2219
+ const before = beforePresent ? previous.get(key) : void 0;
2220
+ const after = afterPresent ? read.get(key) : void 0;
2221
+ if (beforePresent === afterPresent && Object.is(before, after)) continue;
2222
+ transitions.push({
2223
+ key,
2224
+ kind: !beforePresent ? "added" : !afterPresent ? "removed" : "updated",
2225
+ before,
2226
+ after
2227
+ });
2228
+ }
2229
+ publishedTransitions = Object.freeze(transitions);
2230
+ };
2231
+ const deriveChange = () => {
2232
+ if (reset) return Object.freeze({ kind: "reset" });
2233
+ const added = /* @__PURE__ */ new Set();
2234
+ const removed = /* @__PURE__ */ new Set();
2235
+ const updated = /* @__PURE__ */ new Set();
2236
+ publishedTransitions.forEach((transition) => {
2237
+ if (transition.kind === "added") added.add(transition.key);
2238
+ else if (transition.kind === "removed") removed.add(transition.key);
2239
+ else updated.add(transition.key);
2240
+ });
2241
+ const beforeIds = previous.ids();
2242
+ const afterIds = read.ids();
2243
+ const beforeCommon = beforeIds.filter((key) => read.has(key));
2244
+ const afterCommon = afterIds.filter((key) => previous.has(key));
2245
+ const orderChanged = beforeCommon.length !== afterCommon.length || beforeCommon.some((key, index) => afterCommon[index] !== key);
2246
+ if (!added.size && !removed.size && !updated.size && !orderChanged) return void 0;
2247
+ return Object.freeze({
2248
+ kind: "incremental",
2249
+ added,
2250
+ removed,
2251
+ updated,
2252
+ orderChanged
2253
+ });
2254
+ };
2255
+ let unsubscribe;
2256
+ try {
2257
+ unsubscribe = port.subscribe((event) => {
2258
+ try {
2259
+ scheduler.assertIdle();
2260
+ if (!pending) previous = event.previous;
2261
+ read = port.current();
2262
+ revision = event.revision;
2263
+ addTransitionKeys(event.change);
2264
+ reset ||= event.reset || event.change?.kind === "reset";
2265
+ detail = event.detail;
2266
+ cause = event.cause ?? cause;
2267
+ batch = scheduler.batchContext() ?? event.batch;
2268
+ recomputeTransitions();
2269
+ change = deriveChange();
2270
+ record.fault = void 0;
2271
+ scheduler.capture(record);
2272
+ pending = true;
2273
+ } catch (error) {
2274
+ fail(error);
2275
+ }
2276
+ scheduler.run();
2277
+ });
2278
+ } catch (error) {
2279
+ unsubscribe?.();
2280
+ scheduler.unregisterSource(handle);
2281
+ throw error;
2282
+ }
2283
+ scheduler.cleanups.add(() => unsubscribe?.());
2284
+ externalSources.set(port, handle);
2285
+ return handle;
2286
+ };
2287
+ return {
2288
+ dispose: () => {
2289
+ documents.clear();
2290
+ readables.clear();
2291
+ },
2292
+ document,
2293
+ fromSource: (source, options) => externalValueSource(source, options?.isEqual ?? Object.is),
2294
+ fromCollectionSource: (source) => externalCollectionSource(source),
2295
+ input: (initial, options) => {
2296
+ const { source, set } = valueSource(initial, options?.isEqual ?? Object.is);
2297
+ return Object.freeze({
2298
+ source,
2299
+ set
2300
+ });
2301
+ },
2302
+ fromReadable: (readable, options) => {
2303
+ scheduler.assertIdle();
2304
+ if (projectionHandles.has(readable)) throw new Error("Projection nodes must be declared directly as sources.");
2305
+ const equal = options?.isEqual ?? Object.is;
2306
+ let bindings = readables.get(readable);
2307
+ const old = bindings?.get(equal);
2308
+ if (old) return old.source;
2309
+ const input = valueSource(readable.current(), equal);
2310
+ if (bindings) {
2311
+ bindings.set(equal, {
2312
+ source: input.source,
2313
+ accept: (value) => input.accept(value)
2314
+ });
2315
+ return input.source;
2316
+ }
2317
+ bindings = new Map([[equal, {
2318
+ source: input.source,
2319
+ accept: (value) => input.accept(value)
2320
+ }]]);
2321
+ const members = bindings;
2322
+ const receive = (settle = true) => {
2323
+ if (!scheduler.active) return;
2324
+ let value;
2325
+ try {
2326
+ value = readable.current();
2327
+ } catch (cause) {
2328
+ for (const member of members.values()) {
2329
+ const record = scheduler.source(member.source);
2330
+ record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
2331
+ scheduler.capture(record);
2332
+ }
2333
+ if (settle) scheduler.run();
2334
+ return;
2335
+ }
2336
+ for (const member of members.values()) try {
2337
+ member.accept(value);
2338
+ } catch (cause) {
2339
+ const record = scheduler.source(member.source);
2340
+ record.fault = new ProjectionError("source", "external readable", [record.revision()], cause);
2341
+ scheduler.capture(record);
2342
+ }
2343
+ if (settle) scheduler.run();
2344
+ };
2345
+ let unsubscribe;
2346
+ let detach;
2347
+ try {
2348
+ const owner = require_notification.documentReadableOwner(readable);
2349
+ if (owner) {
2350
+ document(owner);
2351
+ detach = require_notification.attachProjection(owner, {
2352
+ capture: () => receive(false),
2353
+ settle: scheduler.settle,
2354
+ flush: scheduler.flush,
2355
+ dispose: () => {
2356
+ for (const member of members.values()) {
2357
+ const record = scheduler.source(member.source);
2358
+ record.fault = new ProjectionError("source", "document readable disposed", [record.revision()], /* @__PURE__ */ new Error("Document has been disposed."));
2359
+ scheduler.capture(record);
2360
+ }
2361
+ scheduler.run();
2362
+ }
2363
+ });
2364
+ }
2365
+ unsubscribe = readable.subscribe(() => receive());
2366
+ input.accept(readable.current());
2367
+ } catch (error) {
2368
+ try {
2369
+ unsubscribe?.();
2370
+ detach?.();
2371
+ } finally {
2372
+ scheduler.unregisterSource(input.source);
2373
+ }
2374
+ throw error;
2375
+ }
2376
+ scheduler.cleanups.add(unsubscribe);
2377
+ if (detach) scheduler.cleanups.add(detach);
2378
+ readables.set(readable, bindings);
2379
+ scheduler.run();
2380
+ return input.source;
2381
+ },
2382
+ assertCollection: (handle) => {
2383
+ scheduler.source(handle);
2384
+ if (!collections.has(handle) && !collectionHandles.has(handle)) throw new Error("map requires a collection source.");
2385
+ }
2386
+ };
2387
+ };
2388
+ //#endregion
2389
+ //#region core/src/projection/value.ts
2390
+ const createValue = (scheduler, spec, options) => {
2391
+ let instance;
2392
+ let value;
2393
+ let next;
2394
+ let previous;
2395
+ let initialized = false;
2396
+ let revision = 0;
2397
+ let changed = false;
2398
+ let reset = false;
2399
+ let batch;
2400
+ let cause;
2401
+ const listeners = /* @__PURE__ */ new Set();
2402
+ const owner = createNode(scheduler, spec, {
2403
+ evaluate: (sources, build) => {
2404
+ 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));
2405
+ batch = scheduler.batchContext() ?? metadata?.batch;
2406
+ cause = metadata?.cause ?? batch?.cause;
2407
+ let candidate;
2408
+ if (build || !instance) {
2409
+ require_issue.profile.materialized.rebuilt();
2410
+ instance = void 0;
2411
+ const built = spec.build(sources);
2412
+ assertSynchronous(built);
2413
+ instance = built;
2414
+ candidate = built.value;
2415
+ } else {
2416
+ require_issue.profile.materialized.updated();
2417
+ const result = instance.update(sources);
2418
+ assertSynchronous(result);
2419
+ if (result.kind === "rebuild") {
2420
+ instance = void 0;
2421
+ const built = spec.build(sources);
2422
+ assertSynchronous(built);
2423
+ instance = built;
2424
+ candidate = built.value;
2425
+ build = true;
2426
+ } else candidate = result.kind === "changed" ? result.value : value;
2427
+ }
2428
+ previous = value;
2429
+ changed = !initialized || !(options?.isEqual ?? Object.is)(value, candidate);
2430
+ next = changed ? candidate : value;
2431
+ reset = build;
2432
+ return changed;
2433
+ },
2434
+ context: (active) => {
2435
+ assertScope(active);
2436
+ return Object.freeze({
2437
+ value: next,
2438
+ previous,
2439
+ changed,
2440
+ revision: revision + (changed ? 1 : 0),
2441
+ reset,
2442
+ cause,
2443
+ batch
2444
+ });
2445
+ },
2446
+ revision: () => revision,
2447
+ reset: () => reset,
2448
+ publish: () => {
2449
+ if (changed && initialized) revision++;
2450
+ value = next;
2451
+ initialized = true;
2452
+ },
2453
+ emit: (call) => {
2454
+ require_issue.profile.materialized.notification();
2455
+ Array.from(listeners).forEach((listener) => call(listener));
2456
+ },
2457
+ clear: () => {
2458
+ next = value;
2459
+ previous = value;
2460
+ changed = false;
2461
+ reset = false;
2462
+ batch = void 0;
2463
+ cause = void 0;
2464
+ },
2465
+ release: () => {
2466
+ listeners.clear();
2467
+ instance = void 0;
2468
+ value = next = previous = void 0;
2469
+ }
2470
+ });
2471
+ const handle = Object.freeze({
2472
+ current: () => {
2473
+ owner.check();
2474
+ return value;
2475
+ },
2476
+ revision: () => {
2477
+ owner.check();
2478
+ return revision;
2479
+ },
2480
+ subscribe: (listener) => {
2481
+ owner.check();
2482
+ listeners.add(listener);
2483
+ return () => {
2484
+ listeners.delete(listener);
2485
+ };
2486
+ },
2487
+ rebuild: owner.rebuild,
2488
+ dispose: owner.dispose
2489
+ });
2490
+ owner.install(handle);
2491
+ return handle;
2492
+ };
2493
+ //#endregion
2494
+ //#region core/src/projection/runtime.ts
2495
+ const createRuntimeExecutor = (options) => {
2496
+ const scheduler = createScheduler(options.onError);
2497
+ const sources = createSources(scheduler);
2498
+ function value(input, computeOrOptions, options) {
2499
+ if (typeof computeOrOptions !== "function") return createValue(scheduler, input, computeOrOptions);
2500
+ const compute = (input) => {
2501
+ const result = computeOrOptions(input);
2502
+ assertSynchronous(result);
2503
+ return result;
2504
+ };
2505
+ return createValue(scheduler, {
2506
+ sources: input,
2507
+ build: (input) => ({
2508
+ value: compute(input),
2509
+ update: (input) => ({
2510
+ kind: "changed",
2511
+ value: compute(input)
2512
+ })
2513
+ })
2514
+ }, options);
2515
+ }
2516
+ const map = (source, mapper, options) => {
2517
+ sources.assertCollection(source);
2518
+ const calculate = (id, entry) => {
2519
+ const value = mapper(id, entry);
2520
+ assertSynchronous(value);
2521
+ return value;
2522
+ };
2523
+ return createCollection(scheduler, {
2524
+ sources: { source },
2525
+ isEqual: options?.isEqual,
2526
+ build: ({ sources, writer }) => {
2527
+ const input = sources.source;
2528
+ const read = "read" in input ? input.read : input;
2529
+ const ids = read.ids();
2530
+ require_issue.profile.collectionView.idsScanned(ids.length);
2531
+ for (const id of ids) {
2532
+ require_issue.profile.collectionView.mapped();
2533
+ writer.set(id, calculate(id, read.get(id)));
2534
+ }
2535
+ writer.order(ids);
2536
+ return { update: ({ sources, writer }) => {
2537
+ const input = sources.source;
2538
+ if (input.reset) return { kind: "rebuild" };
2539
+ const read = "read" in input ? input.read : input;
2540
+ let keys;
2541
+ let structural;
2542
+ if ("candidates" in input) {
2543
+ keys = input.candidates.keys;
2544
+ structural = input.candidates.orderDirty;
2545
+ } else {
2546
+ const change = input.change;
2547
+ if (!change) return;
2548
+ if (change.kind === "reset") return { kind: "rebuild" };
2549
+ keys = new Set([
2550
+ ...change.added,
2551
+ ...change.removed,
2552
+ ...change.updated
2553
+ ]);
2554
+ structural = Boolean(change.added.size || change.removed.size || change.orderChanged);
2555
+ }
2556
+ for (const key of keys) if (!read.has(key)) writer.remove(key);
2557
+ else {
2558
+ require_issue.profile.collectionView.mapped();
2559
+ writer.set(key, calculate(key, read.get(key)));
2560
+ }
2561
+ if (structural) {
2562
+ const ids = read.ids();
2563
+ require_issue.profile.collectionView.idsScanned(ids.length);
2564
+ writer.order(ids);
2565
+ }
2566
+ } };
2567
+ }
2568
+ });
2569
+ };
2570
+ const runtime = {
2571
+ document: sources.document,
2572
+ fromSource: (source, options) => sources.fromSource(source, options),
2573
+ fromCollectionSource: (source) => sources.fromCollectionSource(source),
2574
+ input: sources.input,
2575
+ fromReadable: sources.fromReadable,
2576
+ value,
2577
+ collection: () => (spec) => createCollection(scheduler, spec),
2578
+ map,
2579
+ batch: scheduler.batch,
2580
+ dispose: () => {
2581
+ try {
2582
+ scheduler.dispose();
2583
+ } finally {
2584
+ if (!scheduler.active) sources.dispose();
2585
+ }
2586
+ }
2587
+ };
2588
+ return Object.freeze(runtime);
2589
+ };
2590
+ //#endregion
2591
+ //#region core/src/projection/store.ts
2592
+ let activeTracker;
2593
+ const trackKey = (key) => {
2594
+ activeTracker?.keys.add(key);
2595
+ };
2596
+ const trackStructure = () => {
2597
+ if (!activeTracker) return;
2598
+ activeTracker.structure = true;
2599
+ };
2600
+ const trackAll = () => {
2601
+ if (!activeTracker) return;
2602
+ activeTracker.all = true;
2603
+ };
2604
+ const mapView = (read) => {
2605
+ const keys = read.ids();
2606
+ const source = /* @__PURE__ */ new Map();
2607
+ for (const key of keys) source.set(key, read.get(key));
2608
+ const view = {
2609
+ get: (key) => {
2610
+ trackKey(key);
2611
+ return source.get(key);
2612
+ },
2613
+ has: (key) => {
2614
+ trackKey(key);
2615
+ return source.has(key);
2616
+ },
2617
+ get size() {
2618
+ trackStructure();
2619
+ return source.size;
2620
+ },
2621
+ keys: () => {
2622
+ trackStructure();
2623
+ return source.keys();
2624
+ },
2625
+ values: () => {
2626
+ trackAll();
2627
+ return source.values();
2628
+ },
2629
+ entries: () => {
2630
+ trackAll();
2631
+ return source.entries();
2632
+ },
2633
+ forEach: (callback, thisArg) => {
2634
+ trackAll();
2635
+ source.forEach((value, key) => callback.call(thisArg, value, key, view));
2636
+ },
2637
+ [Symbol.iterator]: () => {
2638
+ trackAll();
2639
+ return source[Symbol.iterator]();
2640
+ }
2641
+ };
2642
+ return Object.freeze(view);
2643
+ };
2644
+ const isMapLike = (value) => value instanceof Map || value !== null && typeof value === "object" && typeof value.get === "function" && typeof value.keys === "function" && typeof value.entries === "function";
2645
+ const mapRead = (value) => ({
2646
+ get: (key) => value.get(key),
2647
+ has: (key) => value.has(key),
2648
+ ids: () => Object.freeze([...value.keys()])
2649
+ });
2650
+ const mapChange = (previous, current) => {
2651
+ const added = /* @__PURE__ */ new Set();
2652
+ const removed = /* @__PURE__ */ new Set();
2653
+ const updated = /* @__PURE__ */ new Set();
2654
+ for (const key of previous.keys()) if (!current.has(key)) removed.add(key);
2655
+ else if (!Object.is(previous.get(key), current.get(key))) updated.add(key);
2656
+ for (const key of current.keys()) if (!previous.has(key)) added.add(key);
2657
+ const before = [...previous.keys()].filter((key) => current.has(key));
2658
+ const after = [...current.keys()].filter((key) => previous.has(key));
2659
+ const orderChanged = before.length !== after.length || before.some((key, index) => key !== after[index]);
2660
+ if (!added.size && !removed.size && !updated.size && !orderChanged) return void 0;
2661
+ return {
2662
+ kind: "incremental",
2663
+ added,
2664
+ removed,
2665
+ updated,
2666
+ orderChanged
2667
+ };
2668
+ };
2669
+ const sameSelection = (left, right) => {
2670
+ if (left.all !== right.all || left.structure !== right.structure || left.keys.size !== right.keys.size) return false;
2671
+ for (const key of left.keys) if (!right.keys.has(key)) return false;
2672
+ return true;
2673
+ };
2674
+ const selectionAffects = (selection, change) => {
2675
+ if (!change || change.kind === "reset") return true;
2676
+ if (selection.all && (change.added.size || change.removed.size || change.updated.size || change.orderChanged)) return true;
2677
+ if (selection.structure && (change.added.size || change.removed.size || change.orderChanged)) return true;
2678
+ for (const key of selection.keys) if (change.added.has(key) || change.removed.has(key) || change.updated.has(key)) return true;
2679
+ return false;
2680
+ };
2681
+ const currentValue = (event) => {
2682
+ if (!event || typeof event !== "object") return event;
2683
+ if ("value" in event) return event.value;
2684
+ if ("read" in event) return require_scope.snapshot(event.read);
2685
+ if ("ids" in event && typeof event.ids === "function") return mapView(event);
2686
+ return event;
2687
+ };
2688
+ const sourceOf = (instance) => instance;
2689
+ const createProjectionRuntime = (options) => {
2690
+ const runtime = createRuntimeExecutor({ onError: options?.onError ?? (() => void 0) });
2691
+ const instances = /* @__PURE__ */ new WeakMap();
2692
+ const inputs = /* @__PURE__ */ new WeakMap();
2693
+ const collections = /* @__PURE__ */ new WeakMap();
2694
+ const materialize = (projection) => {
2695
+ const old = instances.get(projection);
2696
+ if (old) return old;
2697
+ const definition = require_definition.definitionOf(projection);
2698
+ let instance;
2699
+ switch (definition.kind) {
2700
+ case "input": {
2701
+ const input = runtime.input(definition.initial, { isEqual: definition.isEqual });
2702
+ inputs.set(projection, input);
2703
+ instance = runtime.value({ input: input.source }, ({ input }) => input.value);
2704
+ break;
2705
+ }
2706
+ case "readable": {
2707
+ const source = runtime.fromReadable(definition.readable, { isEqual: definition.isEqual });
2708
+ instance = runtime.value({ source }, ({ source }) => source.value);
2709
+ break;
2710
+ }
2711
+ case "source-value":
2712
+ {
2713
+ const source = runtime.fromSource(definition.source);
2714
+ instance = runtime.value({ source }, ({ source }) => source.value);
2715
+ }
2716
+ break;
2717
+ case "source-collection": {
2718
+ const source = runtime.fromCollectionSource(definition.source);
2719
+ instance = runtime.map(source, (_key, value) => value);
2720
+ break;
2721
+ }
2722
+ case "document": {
2723
+ const document = runtime.document(definition.document);
2724
+ if (!definition.selector) {
2725
+ instance = runtime.value({ document }, ({ document }) => require_scope.snapshot(document.read));
2726
+ break;
2727
+ }
2728
+ const state = require_notification.accessOf(definition.document);
2729
+ try {
2730
+ const collection = document.collection(definition.selector);
2731
+ instance = runtime.map(collection, (_id, entry) => require_scope.snapshot(entry));
2732
+ break;
2733
+ } catch {
2734
+ const selected = document.targets(definition.selector);
2735
+ const address = compilePath(state.schema, "value", definition.selector).address;
2736
+ instance = runtime.value({ selected }, () => {
2737
+ return require_issue.read(require_scope.snapshot(state.document), address, state.schema);
2738
+ });
2739
+ }
2740
+ break;
2741
+ }
2742
+ case "derive": {
2743
+ const sources = Object.fromEntries(definition.dependencies.map((dependency, index) => [`d${index}`, sourceOf(materialize(dependency))]));
2744
+ instance = runtime.value(sources, (values) => definition.compute(...Object.values(values).map(currentValue)), { isEqual: definition.isEqual });
2745
+ break;
2746
+ }
2747
+ case "incremental-value": {
2748
+ const sources = Object.fromEntries(Object.entries(definition.dependencies).map(([key, dependency]) => [key, sourceOf(materialize(dependency))]));
2749
+ instance = runtime.value({
2750
+ sources,
2751
+ build: definition.build
2752
+ }, { isEqual: definition.isEqual });
2753
+ break;
2754
+ }
2755
+ case "incremental-collection": {
2756
+ const sources = Object.fromEntries(Object.entries(definition.dependencies).map(([key, dependency]) => [key, sourceOf(materialize(dependency))]));
2757
+ instance = runtime.collection()({
2758
+ sources,
2759
+ build: definition.build,
2760
+ isEqual: definition.isEqual,
2761
+ name: definition.name
2762
+ });
2763
+ break;
2764
+ }
2765
+ }
2766
+ instances.set(projection, instance);
2767
+ return instance;
2768
+ };
2769
+ const get = (projection) => {
2770
+ const instance = materialize(projection);
2771
+ const current = instance.current();
2772
+ if (current && typeof current === "object" && "ids" in current && typeof current.ids === "function") {
2773
+ const existing = collections.get(instance);
2774
+ const revision = instance.revision();
2775
+ if (existing?.revision === revision) return existing.value;
2776
+ const value = mapView(current);
2777
+ collections.set(instance, {
2778
+ revision,
2779
+ value
2780
+ });
2781
+ return value;
2782
+ }
2783
+ if (isMapLike(current)) {
2784
+ const existing = collections.get(instance);
2785
+ const revision = instance.revision();
2786
+ if (existing?.revision === revision) return existing.value;
2787
+ const value = mapView(mapRead(current));
2788
+ collections.set(instance, {
2789
+ revision,
2790
+ value
2791
+ });
2792
+ return value;
2793
+ }
2794
+ return current;
2795
+ };
2796
+ function readable(projection, selector, equality = Object.is) {
2797
+ if (!selector) return Object.freeze({
2798
+ current: () => get(projection),
2799
+ revision: () => materialize(projection).revision(),
2800
+ subscribe: (listener) => materialize(projection).subscribe(listener)
2801
+ });
2802
+ let initialized = false;
2803
+ let value;
2804
+ let selectedRevision = 0;
2805
+ let sourceRevision = -1;
2806
+ let selection = Object.freeze({
2807
+ keys: /* @__PURE__ */ new Set(),
2808
+ all: true,
2809
+ structure: false
2810
+ });
2811
+ let previousCollection;
2812
+ let unsubscribeSource;
2813
+ const listeners = /* @__PURE__ */ new Set();
2814
+ const updateCollectionSnapshot = () => {
2815
+ const current = get(projection);
2816
+ previousCollection = isMapLike(current) ? current : void 0;
2817
+ };
2818
+ const evaluate = () => {
2819
+ const tracker = {
2820
+ keys: /* @__PURE__ */ new Set(),
2821
+ all: false,
2822
+ structure: false
2823
+ };
2824
+ const previousTracker = activeTracker;
2825
+ activeTracker = tracker;
2826
+ let source;
2827
+ let next;
2828
+ try {
2829
+ source = get(projection);
2830
+ next = selector(source);
2831
+ } finally {
2832
+ activeTracker = previousTracker;
2833
+ }
2834
+ const nextSelection = Object.freeze({
2835
+ keys: new Set(tracker.keys),
2836
+ all: tracker.all || isMapLike(source) && tracker.keys.size === 0,
2837
+ structure: tracker.structure
2838
+ });
2839
+ const changed = !initialized || !equality(value, next);
2840
+ if (changed) {
2841
+ if (initialized) selectedRevision++;
2842
+ value = next;
2843
+ }
2844
+ selection = nextSelection;
2845
+ initialized = true;
2846
+ sourceRevision = materialize(projection).revision();
2847
+ updateCollectionSnapshot();
2848
+ return changed;
2849
+ };
2850
+ const ensureCurrent = () => {
2851
+ const revision = materialize(projection).revision();
2852
+ if (!initialized || !unsubscribeSource && sourceRevision !== revision) evaluate();
2853
+ return value;
2854
+ };
2855
+ const onProjectionChange = (incoming) => {
2856
+ let change = incoming;
2857
+ if (!change && previousCollection) {
2858
+ const current = get(projection);
2859
+ if (isMapLike(current)) {
2860
+ change = mapChange(previousCollection, current);
2861
+ previousCollection = current;
2862
+ if (!change) {
2863
+ sourceRevision = materialize(projection).revision();
2864
+ return;
2865
+ }
2866
+ } else previousCollection = void 0;
2867
+ }
2868
+ if (!selectionAffects(selection, change)) {
2869
+ sourceRevision = materialize(projection).revision();
2870
+ updateCollectionSnapshot();
2871
+ return;
2872
+ }
2873
+ const previousSelection = selection;
2874
+ const changed = evaluate();
2875
+ if (unsubscribeSource && !sameSelection(previousSelection, selection)) {
2876
+ unsubscribeSource();
2877
+ unsubscribeSource = materialize(projection).subscribe(onProjectionChange);
2878
+ }
2879
+ if (changed) Array.from(listeners).forEach((listener) => listener());
2880
+ };
2881
+ const installSource = () => {
2882
+ if (!unsubscribeSource) unsubscribeSource = materialize(projection).subscribe(onProjectionChange);
2883
+ };
2884
+ return Object.freeze({
2885
+ current: ensureCurrent,
2886
+ revision: () => {
2887
+ ensureCurrent();
2888
+ return selectedRevision;
2889
+ },
2890
+ subscribe: (listener) => {
2891
+ ensureCurrent();
2892
+ listeners.add(listener);
2893
+ installSource();
2894
+ return () => {
2895
+ listeners.delete(listener);
2896
+ if (!listeners.size && unsubscribeSource) {
2897
+ unsubscribeSource();
2898
+ unsubscribeSource = void 0;
2899
+ }
2900
+ };
2901
+ }
2902
+ });
2903
+ }
2904
+ const store = {
2905
+ get,
2906
+ readable,
2907
+ set: ((input, value) => {
2908
+ materialize(input);
2909
+ const target = inputs.get(input);
2910
+ if (!target) throw new TypeError("Projection is not an input.");
2911
+ target.set(value);
2912
+ }),
2913
+ batch: runtime.batch,
2914
+ dispose: runtime.dispose
2915
+ };
2916
+ return Object.freeze(store);
2917
+ };
919
2918
  //#endregion
920
- exports.DocumentDisposedError = require_store.DocumentDisposedError;
921
- exports.DocumentReentrancyError = require_store.DocumentReentrancyError;
922
- exports.ParseError = require_definition.ParseError;
923
- exports.ProjectionDisposedError = require_store.ProjectionDisposedError;
924
- exports.ProjectionError = require_store.ProjectionError;
925
- exports.TransactionRejected = require_store.TransactionRejected;
2919
+ exports.DocumentDisposedError = require_notification.DocumentDisposedError;
2920
+ exports.DocumentReentrancyError = require_notification.DocumentReentrancyError;
2921
+ exports.ParseError = require_scope.ParseError;
2922
+ exports.ProjectionDisposedError = ProjectionDisposedError;
2923
+ exports.ProjectionError = ProjectionError;
2924
+ exports.TransactionRejected = require_notification.TransactionRejected;
926
2925
  exports.asReadable = asReadable;
927
2926
  exports.createDocument = createDocument;
928
- exports.createProjectionRuntime = require_store.createProjectionRuntime;
2927
+ exports.createProjectionRuntime = createProjectionRuntime;
929
2928
  exports.derive = require_definition.derive;
930
- exports.field = require_store.field;
2929
+ exports.field = field;
931
2930
  exports.input = require_definition.input;
932
- exports.list = require_store.list;
933
- exports.map = require_store.map;
934
- exports.object = require_store.object;
2931
+ exports.list = list;
2932
+ exports.map = map;
2933
+ exports.object = object;
935
2934
  exports.observe = require_definition.observe;
936
- exports.optional = require_store.optional;
937
- exports.parse = require_definition.parse;
2935
+ exports.optional = optional;
2936
+ exports.parse = require_scope.parse;
938
2937
  exports.read = read;
939
- exports.replace = require_definition.replace;
940
- exports.snapshot = require_definition.snapshot;
941
- exports.table = require_store.table;
942
- exports.tree = require_store.tree;
943
- exports.variant = require_store.variant;
2938
+ exports.replace = require_scope.replace;
2939
+ exports.snapshot = require_scope.snapshot;
2940
+ exports.table = table;
2941
+ exports.tree = tree;
2942
+ exports.variant = variant;
944
2943
 
945
2944
  //# sourceMappingURL=index.cjs.map