doxum 0.1.32 → 0.1.33

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.
@@ -119,22 +119,40 @@ const mapRead = (value) => {
119
119
  /** Immutable map-like view over a CollectionRead. */
120
120
  const collectionView = (read, knownIds) => {
121
121
  const ids = knownIds ?? read.ids();
122
- const entries = function* () {
123
- for (const key of ids) yield [key, read.get(key)];
124
- };
125
- const values = function* () {
126
- for (const key of ids) yield read.get(key);
122
+ const iterate = (select) => {
123
+ read.ids();
124
+ let index = 0;
125
+ return {
126
+ next() {
127
+ read.ids();
128
+ if (index === ids.length) return {
129
+ done: true,
130
+ value: void 0
131
+ };
132
+ return {
133
+ done: false,
134
+ value: select(ids[index++])
135
+ };
136
+ },
137
+ [Symbol.iterator]() {
138
+ return this;
139
+ }
140
+ };
127
141
  };
142
+ const entries = () => iterate((key) => [key, read.get(key)]);
143
+ const values = () => iterate((key) => read.get(key));
128
144
  const view = {
129
145
  get: (key) => read.get(key),
130
146
  has: (key) => read.has(key),
131
147
  get size() {
148
+ read.ids();
132
149
  return ids.length;
133
150
  },
134
- keys: () => ids[Symbol.iterator](),
151
+ keys: () => iterate((key) => key),
135
152
  values,
136
153
  entries,
137
154
  forEach: (callback, thisArg) => {
155
+ read.ids();
138
156
  for (const key of ids) callback.call(thisArg, read.get(key), key, view);
139
157
  },
140
158
  [Symbol.iterator]: entries
@@ -183,141 +201,36 @@ const compileProjectionDependencies = (dependencies, label) => {
183
201
  });
184
202
  };
185
203
  //#endregion
186
- //#region core/src/value/array.ts
187
- /** Allocation-free strict equality for ordered readonly arrays. */
188
- const sameArray = (left, right) => {
189
- if (left === right) return true;
190
- if (left.length !== right.length) return false;
191
- for (let index = 0; index < left.length; index++) if (left[index] !== right[index]) return false;
192
- return true;
193
- };
194
- /** Owns an immutable shallow snapshot while reusing arrays that are already frozen. */
195
- const snapshotArray = (values) => Object.isFrozen(values) ? values : Object.freeze([...values]);
196
- /** Compares an iterable with an ordered array without materializing the iterable. */
197
- const iterableMatchesArray = (values, expected) => {
198
- let index = 0;
199
- for (const value of values) {
200
- if (index >= expected.length || value !== expected[index]) return false;
201
- index++;
202
- }
203
- return index === expected.length;
204
- };
205
- //#endregion
206
- //#region core/src/projection/collection/change.ts
207
- const transitionContains = (entries, key) => {
208
- for (const entry of entries) if (entry.key === key) return true;
209
- return false;
210
- };
211
- const transitionSet = (entries) => {
212
- if (entries.length <= 4) return void 0;
213
- const keys = /* @__PURE__ */ new Set();
214
- for (const entry of entries) keys.add(entry.key);
215
- return keys;
216
- };
217
- const nextExcluding = (iterator, excluded, index) => {
218
- while (true) {
219
- const next = iterator.next();
220
- if (next.done || !(index ? index.has(next.value) : transitionContains(excluded, next.value))) return next;
221
- }
222
- };
223
- /** Compares common-member order from known transitions without materializing common arrays. */
224
- const transitionOrderChanged = (before, after, removed, added) => {
225
- const removedKeys = transitionSet(removed);
226
- const addedKeys = transitionSet(added);
227
- const beforeIterator = before[Symbol.iterator]();
228
- const afterIterator = after[Symbol.iterator]();
229
- while (true) {
230
- const left = nextExcluding(beforeIterator, removed, removedKeys);
231
- const right = nextExcluding(afterIterator, added, addedKeys);
232
- if (left.done || right.done) return left.done !== right.done;
233
- if (left.value !== right.value) return true;
234
- }
235
- };
236
- const nextPresentIn = (iterator, membership) => {
237
- while (true) {
238
- const next = iterator.next();
239
- if (next.done || membership.has(next.value)) return next;
240
- }
241
- };
242
- /** Compares common-member order between two map snapshots without key snapshots or closures. */
243
- const mapOrderChanged = (before, after) => {
244
- const beforeIterator = before.keys();
245
- const afterIterator = after.keys();
246
- while (true) {
247
- const left = nextPresentIn(beforeIterator, after);
248
- const right = nextPresentIn(afterIterator, before);
249
- if (left.done || right.done) return left.done !== right.done;
250
- if (left.value !== right.value) return true;
204
+ //#region core/src/projection/keyed/invalidation.ts
205
+ /** Shared driver/dependency invalidation policy; owns no revisions or member cache. */
206
+ const collectKeyedInvalidation = (driver, sources, dependencies, reset) => {
207
+ const changed = dependencies.driverChanged(driver);
208
+ const change = changed ? driver.change : void 0;
209
+ if (reset || changed && (!change || change.kind === "reset")) return {
210
+ rebuild: true,
211
+ keys: driver.read.ids(),
212
+ removed: [],
213
+ structural: true
214
+ };
215
+ const dirty = /* @__PURE__ */ new Set();
216
+ let removed = [];
217
+ let structural = false;
218
+ if (change?.kind === "incremental") {
219
+ removed = change.removed;
220
+ for (const entry of change.added) dirty.add(entry.key);
221
+ for (const entry of change.updated) dirty.add(entry.key);
222
+ structural = Boolean(change.added.length || change.removed.length || change.order);
251
223
  }
252
- };
253
- const sealIncrementalChange = (added, updated, removed, order) => {
254
- if (!added.length && !updated.length && !removed.length && !order) return void 0;
255
- return Object.freeze({
256
- kind: "incremental",
257
- added: Object.freeze(added),
258
- updated: Object.freeze(updated),
259
- removed: Object.freeze(removed),
260
- ...order ? { order: Object.freeze({
261
- before: snapshotArray(order.before),
262
- after: snapshotArray(order.after)
263
- }) } : {}
264
- });
265
- };
266
- /** Builds the canonical incremental CollectionChange from already-known net transitions. */
267
- const createCollectionChange = (input) => {
268
- let orderChanged = false;
269
- if (input.beforeOrder !== input.afterOrder) if (!input.added.length && !input.removed.length) orderChanged = !sameArray(input.beforeOrder, input.afterOrder);
270
- else orderChanged = transitionOrderChanged(input.beforeOrder, input.afterOrder, input.removed, input.added);
271
- return sealIncrementalChange(input.added, input.updated, input.removed, orderChanged ? {
272
- before: input.beforeOrder,
273
- after: input.afterOrder
274
- } : void 0);
275
- };
276
- /** Exact fallback diff between two published map-like snapshots. */
277
- const diffCollection = (previous, current, isEqual = Object.is) => {
278
- const added = [];
279
- const updated = [];
280
- const removed = [];
281
- for (const [key, before] of previous) if (!current.has(key)) removed.push({
282
- key,
283
- before
284
- });
285
- else {
286
- const after = current.get(key);
287
- if (!isEqual(before, after)) updated.push({
288
- key,
289
- before,
290
- after
291
- });
224
+ const all = dependencies.collectInvalidated(sources, dirty);
225
+ if (!all) {
226
+ for (const key of dirty) if (!driver.read.has(key)) dirty.delete(key);
292
227
  }
293
- for (const [key, after] of current) if (!previous.has(key)) added.push({
294
- key,
295
- after
296
- });
297
- return sealIncrementalChange(added, updated, removed, mapOrderChanged(previous, current) ? {
298
- before: Object.freeze([...previous.keys()]),
299
- after: Object.freeze([...current.keys()])
300
- } : void 0);
301
- };
302
- const changedKeys = function* (change) {
303
- if (change.kind !== "incremental") throw new TypeError("collectionChange.keys requires an incremental CollectionChange.");
304
- for (const entry of change.added) yield entry.key;
305
- for (const entry of change.updated) yield entry.key;
306
- for (const entry of change.removed) yield entry.key;
307
- };
308
- const collectionChange = Object.freeze({ keys: changedKeys });
309
- const collectionHasStructuralChange = (change) => change.kind === "reset" || Boolean(change.added.length || change.removed.length || change.order);
310
- const collectionChangeTouchesKey = (change, key) => {
311
- for (const entry of change.added) if (entry.key === key) return true;
312
- for (const entry of change.updated) if (entry.key === key) return true;
313
- for (const entry of change.removed) if (entry.key === key) return true;
314
- return false;
315
- };
316
- const collectionChangeIntersects = (change, keys) => {
317
- for (const entry of change.added) if (keys.has(entry.key)) return true;
318
- for (const entry of change.updated) if (keys.has(entry.key)) return true;
319
- for (const entry of change.removed) if (keys.has(entry.key)) return true;
320
- return false;
228
+ return {
229
+ rebuild: false,
230
+ keys: all ? driver.read.ids() : dirty,
231
+ removed,
232
+ structural
233
+ };
321
234
  };
322
235
  //#endregion
323
236
  //#region core/src/projection/contract.ts
@@ -360,7 +273,7 @@ const createScheduler = (onError) => {
360
273
  };
361
274
  const assertIdle = () => {
362
275
  assertActive();
363
- if (phase !== "idle") throw new Error("Projection cannot be re-entered during processing or notification.");
276
+ if (phase !== "idle") throw new Error("Projection cannot be re-entered during input callbacks, processing or notification.");
364
277
  };
365
278
  const lock = (value) => guards.forEach((guard) => guard(value));
366
279
  const enqueue = (processor) => {
@@ -623,6 +536,17 @@ const createScheduler = (onError) => {
623
536
  },
624
537
  releaseProducer,
625
538
  order: () => sequence++,
539
+ acceptInput(callback) {
540
+ assertIdle();
541
+ phase = "input";
542
+ lock(true);
543
+ try {
544
+ return callback();
545
+ } finally {
546
+ lock(false);
547
+ phase = "idle";
548
+ }
549
+ },
626
550
  initialize(callback) {
627
551
  assertIdle();
628
552
  phase = "compute";
@@ -678,6 +602,26 @@ const assertScope = (active) => {
678
602
  if (!active()) throw new Error("Projection reader or writer is no longer active.");
679
603
  };
680
604
  //#endregion
605
+ //#region core/src/value/array.ts
606
+ /** Allocation-free strict equality for ordered readonly arrays. */
607
+ const sameArray = (left, right) => {
608
+ if (left === right) return true;
609
+ if (left.length !== right.length) return false;
610
+ for (let index = 0; index < left.length; index++) if (left[index] !== right[index]) return false;
611
+ return true;
612
+ };
613
+ /** Owns an immutable shallow snapshot while reusing arrays that are already frozen. */
614
+ const snapshotArray = (values) => Object.isFrozen(values) ? values : Object.freeze([...values]);
615
+ /** Compares an iterable with an ordered array without materializing the iterable. */
616
+ const iterableMatchesArray = (values, expected) => {
617
+ let index = 0;
618
+ for (const value of values) {
619
+ if (index >= expected.length || value !== expected[index]) return false;
620
+ index++;
621
+ }
622
+ return index === expected.length;
623
+ };
624
+ //#endregion
681
625
  //#region core/src/projection/keyed/relation.ts
682
626
  /** Runtime-local domain-neutral many-to-many keyed relation. */
683
627
  const createKeyRelation = () => {
@@ -906,7 +850,6 @@ const createKeyedDependencyRuntime = (compiled) => {
906
850
  for (const state of states) {
907
851
  const source = sources[state.sourceIndex];
908
852
  if (state.revision === source.revision) continue;
909
- state.entry;
910
853
  if (state.kind === "global") {
911
854
  all = true;
912
855
  continue;
@@ -948,6 +891,123 @@ const createKeyedDependencyRuntime = (compiled) => {
948
891
  };
949
892
  };
950
893
  //#endregion
894
+ //#region core/src/projection/collection/change.ts
895
+ const transitionContains = (entries, key) => {
896
+ for (const entry of entries) if (entry.key === key) return true;
897
+ return false;
898
+ };
899
+ const transitionSet = (entries) => {
900
+ if (entries.length <= 4) return void 0;
901
+ const keys = /* @__PURE__ */ new Set();
902
+ for (const entry of entries) keys.add(entry.key);
903
+ return keys;
904
+ };
905
+ const nextExcluding = (iterator, excluded, index) => {
906
+ while (true) {
907
+ const next = iterator.next();
908
+ if (next.done || !(index ? index.has(next.value) : transitionContains(excluded, next.value))) return next;
909
+ }
910
+ };
911
+ /** Compares common-member order from known transitions without materializing common arrays. */
912
+ const transitionOrderChanged = (before, after, removed, added) => {
913
+ const removedKeys = transitionSet(removed);
914
+ const addedKeys = transitionSet(added);
915
+ const beforeIterator = before[Symbol.iterator]();
916
+ const afterIterator = after[Symbol.iterator]();
917
+ while (true) {
918
+ const left = nextExcluding(beforeIterator, removed, removedKeys);
919
+ const right = nextExcluding(afterIterator, added, addedKeys);
920
+ if (left.done || right.done) return left.done !== right.done;
921
+ if (left.value !== right.value) return true;
922
+ }
923
+ };
924
+ const nextPresentIn = (iterator, membership) => {
925
+ while (true) {
926
+ const next = iterator.next();
927
+ if (next.done || membership.has(next.value)) return next;
928
+ }
929
+ };
930
+ /** Compares common-member order between two map snapshots without key snapshots or closures. */
931
+ const mapOrderChanged = (before, after) => {
932
+ const beforeIterator = before.keys();
933
+ const afterIterator = after.keys();
934
+ while (true) {
935
+ const left = nextPresentIn(beforeIterator, after);
936
+ const right = nextPresentIn(afterIterator, before);
937
+ if (left.done || right.done) return left.done !== right.done;
938
+ if (left.value !== right.value) return true;
939
+ }
940
+ };
941
+ const sealIncrementalChange = (added, updated, removed, order) => {
942
+ if (!added.length && !updated.length && !removed.length && !order) return void 0;
943
+ return Object.freeze({
944
+ kind: "incremental",
945
+ added: Object.freeze(added),
946
+ updated: Object.freeze(updated),
947
+ removed: Object.freeze(removed),
948
+ ...order ? { order: Object.freeze({
949
+ before: snapshotArray(order.before),
950
+ after: snapshotArray(order.after)
951
+ }) } : {}
952
+ });
953
+ };
954
+ /** Builds the canonical incremental CollectionChange from already-known net transitions. */
955
+ const createCollectionChange = (input) => {
956
+ let orderChanged = false;
957
+ if (input.beforeOrder !== input.afterOrder) if (!input.added.length && !input.removed.length) orderChanged = !sameArray(input.beforeOrder, input.afterOrder);
958
+ else orderChanged = transitionOrderChanged(input.beforeOrder, input.afterOrder, input.removed, input.added);
959
+ return sealIncrementalChange(input.added, input.updated, input.removed, orderChanged ? {
960
+ before: input.beforeOrder,
961
+ after: input.afterOrder
962
+ } : void 0);
963
+ };
964
+ /** Exact fallback diff between two published map-like snapshots. */
965
+ const diffCollection = (previous, current, isEqual = Object.is) => {
966
+ const added = [];
967
+ const updated = [];
968
+ const removed = [];
969
+ for (const [key, before] of previous) if (!current.has(key)) removed.push({
970
+ key,
971
+ before
972
+ });
973
+ else {
974
+ const after = current.get(key);
975
+ if (!isEqual(before, after)) updated.push({
976
+ key,
977
+ before,
978
+ after
979
+ });
980
+ }
981
+ for (const [key, after] of current) if (!previous.has(key)) added.push({
982
+ key,
983
+ after
984
+ });
985
+ return sealIncrementalChange(added, updated, removed, mapOrderChanged(previous, current) ? {
986
+ before: Object.freeze([...previous.keys()]),
987
+ after: Object.freeze([...current.keys()])
988
+ } : void 0);
989
+ };
990
+ const changedKeys = function* (change) {
991
+ if (change.kind !== "incremental") throw new TypeError("collectionChange.keys requires an incremental CollectionChange.");
992
+ for (const entry of change.added) yield entry.key;
993
+ for (const entry of change.updated) yield entry.key;
994
+ for (const entry of change.removed) yield entry.key;
995
+ };
996
+ const collectionChange = Object.freeze({ keys: changedKeys });
997
+ const collectionHasStructuralChange = (change) => change.kind === "reset" || Boolean(change.added.length || change.removed.length || change.order);
998
+ const collectionChangeTouchesKey = (change, key) => {
999
+ for (const entry of change.added) if (entry.key === key) return true;
1000
+ for (const entry of change.updated) if (entry.key === key) return true;
1001
+ for (const entry of change.removed) if (entry.key === key) return true;
1002
+ return false;
1003
+ };
1004
+ const collectionChangeIntersects = (change, keys) => {
1005
+ for (const entry of change.added) if (keys.has(entry.key)) return true;
1006
+ for (const entry of change.updated) if (keys.has(entry.key)) return true;
1007
+ for (const entry of change.removed) if (keys.has(entry.key)) return true;
1008
+ return false;
1009
+ };
1010
+ //#endregion
951
1011
  //#region core/src/projection/keyed/transform.ts
952
1012
  const keyedAbsent = Symbol("keyed-transform-absent");
953
1013
  /** Shared driver-keyed lifecycle for pure and retained keyed transforms. */
@@ -972,7 +1032,8 @@ const createKeyedTransform = (definition) => {
972
1032
  const driver = evaluation.sources[0];
973
1033
  const output = evaluation.outputs[0];
974
1034
  if (driver.kind !== "collection" || output.kind !== "collection") throw new Error(`${definition.name} requires collection input and output.`);
975
- dependencies.prepare(evaluation.sources, evaluation.reset);
1035
+ const invalidation = collectKeyedInvalidation(driver, evaluation.sources, dependencies, evaluation.reset);
1036
+ dependencies.prepare(evaluation.sources, invalidation.rebuild);
976
1037
  const stateFor = (key, value) => {
977
1038
  if (states.has(key)) return states.get(key);
978
1039
  const state = definition.state ? definition.state(value, key) : void 0;
@@ -997,49 +1058,29 @@ const createKeyedTransform = (definition) => {
997
1058
  else output.output.set(key, next);
998
1059
  return beforePresent !== output.next.has(key);
999
1060
  };
1000
- if (evaluation.reset) {
1061
+ if (invalidation.rebuild) {
1001
1062
  dependencies.clearBindings();
1002
1063
  const ids = driver.read.ids();
1003
1064
  for (const key of states.keys()) if (!driver.read.has(key)) states.delete(key);
1065
+ if (!evaluation.reset) {
1066
+ for (const key of output.previous.ids()) if (!driver.read.has(key)) output.output.remove(key);
1067
+ }
1004
1068
  for (const key of ids) project(key);
1005
1069
  output.output.order(ids.filter((key) => output.next.has(key)));
1006
1070
  dependencies.remember(evaluation.sources);
1007
1071
  return;
1008
1072
  }
1009
- const dirty = /* @__PURE__ */ new Set();
1010
- let all = false;
1011
- let updateOrder = false;
1012
1073
  let membershipChanged = false;
1013
- if (dependencies.driverChanged(driver)) {
1014
- const change = driver.change;
1015
- if (!change || change.kind === "reset") {
1016
- all = true;
1017
- updateOrder = true;
1018
- for (const key of output.previous.ids()) {
1019
- if (driver.read.has(key)) continue;
1020
- output.output.remove(key);
1021
- dropState(key);
1022
- membershipChanged = true;
1023
- }
1024
- } else {
1025
- for (const entry of change.removed) {
1026
- membershipChanged ||= output.previous.has(entry.key);
1027
- output.output.remove(entry.key);
1028
- dropState(entry.key);
1029
- dirty.delete(entry.key);
1030
- }
1031
- for (const entry of change.added) dirty.add(entry.key);
1032
- for (const entry of change.updated) dirty.add(entry.key);
1033
- updateOrder = Boolean(change.order);
1034
- }
1074
+ for (const entry of invalidation.removed) {
1075
+ membershipChanged ||= output.previous.has(entry.key);
1076
+ output.output.remove(entry.key);
1077
+ dropState(entry.key);
1035
1078
  }
1036
- all ||= dependencies.collectInvalidated(evaluation.sources, dirty);
1037
- if (all) for (const key of driver.read.ids()) dirty.add(key);
1038
- for (const key of dirty) {
1079
+ for (const key of invalidation.keys) {
1039
1080
  const changedMembership = project(key);
1040
1081
  membershipChanged ||= changedMembership;
1041
1082
  }
1042
- if (updateOrder || membershipChanged) output.output.order(driver.read.ids().filter((key) => output.next.has(key)));
1083
+ if (invalidation.structural || membershipChanged) output.output.order(driver.read.ids().filter((key) => output.next.has(key)));
1043
1084
  dependencies.remember(evaluation.sources);
1044
1085
  },
1045
1086
  release: () => {
@@ -1083,6 +1124,12 @@ Object.defineProperty(exports, "assertSynchronous", {
1083
1124
  return assertSynchronous;
1084
1125
  }
1085
1126
  });
1127
+ Object.defineProperty(exports, "collectKeyedInvalidation", {
1128
+ enumerable: true,
1129
+ get: function() {
1130
+ return collectKeyedInvalidation;
1131
+ }
1132
+ });
1086
1133
  Object.defineProperty(exports, "collectionChange", {
1087
1134
  enumerable: true,
1088
1135
  get: function() {
@@ -1252,4 +1299,4 @@ Object.defineProperty(exports, "snapshotDependencyValue", {
1252
1299
  }
1253
1300
  });
1254
1301
 
1255
- //# sourceMappingURL=transform-CENhTaVN.cjs.map
1302
+ //# sourceMappingURL=transform-CSPGdBe5.cjs.map