doxum 0.1.33 → 0.1.34

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.
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_transform = require("./transform-CSPGdBe5.cjs");
2
+ const require_transform = require("./transform-Bt1Mwgrj.cjs");
3
3
  const require_record = require("./record-ChYUi8PP.cjs");
4
4
  const require_driver = require("./driver-CN3dGxQI.cjs");
5
5
  //#region core/src/schema/model.ts
@@ -3618,6 +3618,104 @@ function createValueDerive(dependencies, compute, equality = Object.is) {
3618
3618
  }
3619
3619
  const derive = Object.assign(createValueDerive, { keyed: keyedDerive });
3620
3620
  //#endregion
3621
+ //#region core/src/projection/collection/changes.ts
3622
+ /** Net transitions for one consumer's unconsumed interval; bounded by touched keys. */
3623
+ const createCollectionChanges = () => {
3624
+ let first;
3625
+ let beforeOrder = [];
3626
+ let afterOrder = [];
3627
+ let cached;
3628
+ let valid = false;
3629
+ let transitions;
3630
+ const merge = (change) => {
3631
+ const write = (key, before, after) => {
3632
+ const previous = transitions.get(key);
3633
+ const initial = previous?.before ?? before;
3634
+ if (initial.present === after.present && (!initial.present || after.present && Object.is(initial.value, after.value))) transitions.delete(key);
3635
+ else if (previous) previous.after = after;
3636
+ else transitions.set(key, {
3637
+ before,
3638
+ after
3639
+ });
3640
+ };
3641
+ for (const entry of change.added) write(entry.key, { present: false }, {
3642
+ present: true,
3643
+ value: entry.after
3644
+ });
3645
+ for (const entry of change.updated) write(entry.key, {
3646
+ present: true,
3647
+ value: entry.before
3648
+ }, {
3649
+ present: true,
3650
+ value: entry.after
3651
+ });
3652
+ for (const entry of change.removed) write(entry.key, {
3653
+ present: true,
3654
+ value: entry.before
3655
+ }, { present: false });
3656
+ };
3657
+ return {
3658
+ add(change, before, after) {
3659
+ valid = false;
3660
+ afterOrder = after;
3661
+ if (!first) {
3662
+ first = change;
3663
+ beforeOrder = before;
3664
+ return;
3665
+ }
3666
+ if (first.kind === "reset") return;
3667
+ if (change.kind === "reset") {
3668
+ first = change;
3669
+ transitions = void 0;
3670
+ return;
3671
+ }
3672
+ if (!transitions) {
3673
+ transitions = /* @__PURE__ */ new Map();
3674
+ merge(first);
3675
+ }
3676
+ merge(change);
3677
+ },
3678
+ current() {
3679
+ if (!transitions) return first;
3680
+ if (valid) return cached;
3681
+ const added = [];
3682
+ const updated = [];
3683
+ const removed = [];
3684
+ for (const [key, { before, after }] of transitions) if (!before.present) {
3685
+ if (after.present) added.push({
3686
+ key,
3687
+ after: after.value
3688
+ });
3689
+ } else if (!after.present) removed.push({
3690
+ key,
3691
+ before: before.value
3692
+ });
3693
+ else if (!Object.is(before.value, after.value)) updated.push({
3694
+ key,
3695
+ before: before.value,
3696
+ after: after.value
3697
+ });
3698
+ cached = require_transform.createCollectionChange({
3699
+ added,
3700
+ updated,
3701
+ removed,
3702
+ beforeOrder,
3703
+ afterOrder
3704
+ });
3705
+ valid = true;
3706
+ return cached;
3707
+ },
3708
+ reset: () => first?.kind === "reset",
3709
+ clear() {
3710
+ first = void 0;
3711
+ cached = void 0;
3712
+ valid = false;
3713
+ transitions = void 0;
3714
+ beforeOrder = afterOrder = [];
3715
+ }
3716
+ };
3717
+ };
3718
+ //#endregion
3621
3719
  //#region core/src/projection/collection/index.ts
3622
3720
  const height = (node) => node?.height ?? 0;
3623
3721
  const node = (key, value, left, right) => {
@@ -3712,7 +3810,7 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
3712
3810
  const createCollectionState = (initial) => {
3713
3811
  const values = new Map(initial);
3714
3812
  let ids = Object.freeze([...values.keys()]);
3715
- let index = values.size ? PersistentKeyedIndex.from(values) : PersistentKeyedIndex.empty();
3813
+ let index;
3716
3814
  return {
3717
3815
  get: (key) => values.get(key),
3718
3816
  has: (key) => values.has(key),
@@ -3720,7 +3818,7 @@ const createCollectionState = (initial) => {
3720
3818
  size: () => values.size,
3721
3819
  ids: () => ids,
3722
3820
  read: (check) => {
3723
- const snapshotIndex = index;
3821
+ const snapshotIndex = index ??= PersistentKeyedIndex.from(values);
3724
3822
  const snapshotIds = ids;
3725
3823
  return Object.freeze({
3726
3824
  get: (key) => {
@@ -3741,9 +3839,9 @@ const createCollectionState = (initial) => {
3741
3839
  if (reset) {
3742
3840
  values.clear();
3743
3841
  for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
3744
- index = PersistentKeyedIndex.from(values);
3842
+ if (index) index = PersistentKeyedIndex.from(values);
3745
3843
  } else for (const [key, entry] of staged) {
3746
- index = entry.present ? index.set(key, entry.value) : index.remove(key);
3844
+ if (index) index = entry.present ? index.set(key, entry.value) : index.remove(key);
3747
3845
  if (entry.present) values.set(key, entry.value);
3748
3846
  else values.delete(key);
3749
3847
  }
@@ -3752,17 +3850,17 @@ const createCollectionState = (initial) => {
3752
3850
  release: () => {
3753
3851
  values.clear();
3754
3852
  ids = Object.freeze([]);
3755
- index = PersistentKeyedIndex.empty();
3853
+ index = void 0;
3756
3854
  }
3757
3855
  };
3758
3856
  };
3759
3857
  //#endregion
3760
3858
  //#region core/src/projection/output/collection.ts
3761
- /** Owns staged keyed intent, exact transitions and published collection state. */
3859
+ /** Owns staged keyed intent, exact transitions and current collection state. */
3762
3860
  const createCollectionOutput = (binding) => {
3763
- const published = createCollectionState();
3861
+ const currentState = createCollectionState();
3764
3862
  const staged = /* @__PURE__ */ new Map();
3765
- let nextIds = published.ids();
3863
+ let nextIds = currentState.ids();
3766
3864
  let change;
3767
3865
  let initialized = false;
3768
3866
  let reset = false;
@@ -3770,23 +3868,26 @@ const createCollectionOutput = (binding) => {
3770
3868
  let explicitOrder;
3771
3869
  let cleared = false;
3772
3870
  const listeners = /* @__PURE__ */ new Set();
3773
- const consumers = /* @__PURE__ */ new Set();
3774
- const hasNext = (key) => staged.has(key) ? staged.get(key).present : !cleared && published.has(key);
3871
+ const consumers = /* @__PURE__ */ new Map();
3872
+ const notifications = createCollectionChanges();
3873
+ const observers = /* @__PURE__ */ new Set();
3874
+ let baseline;
3875
+ const hasNext = (key) => staged.has(key) ? staged.get(key).present : !cleared && currentState.has(key);
3775
3876
  const getNext = (key) => {
3776
3877
  const entry = staged.get(key);
3777
- return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : published.get(key);
3878
+ return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : currentState.get(key);
3778
3879
  };
3779
3880
  const deriveIds = () => {
3780
3881
  const result = [];
3781
3882
  if (!cleared) {
3782
- for (const key of published.ids()) if (hasNext(key)) result.push(key);
3883
+ for (const key of currentState.ids()) if (hasNext(key)) result.push(key);
3783
3884
  }
3784
- for (const [key, entry] of staged) if (entry.present && (cleared || !published.has(key))) result.push(key);
3885
+ for (const [key, entry] of staged) if (entry.present && (cleared || !currentState.has(key))) result.push(key);
3785
3886
  return result;
3786
3887
  };
3787
3888
  const begin = (active, initialize) => {
3788
3889
  staged.clear();
3789
- nextIds = published.ids();
3890
+ nextIds = currentState.ids();
3790
3891
  change = void 0;
3791
3892
  explicitOrder = void 0;
3792
3893
  cleared = initialize;
@@ -3794,15 +3895,15 @@ const createCollectionOutput = (binding) => {
3794
3895
  const read = (next) => ({
3795
3896
  get: (key) => {
3796
3897
  require_transform.assertScope(active);
3797
- return next ? getNext(key) : published.get(key);
3898
+ return next ? getNext(key) : currentState.get(key);
3798
3899
  },
3799
3900
  has: (key) => {
3800
3901
  require_transform.assertScope(active);
3801
- return next ? hasNext(key) : published.has(key);
3902
+ return next ? hasNext(key) : currentState.has(key);
3802
3903
  },
3803
3904
  ids: () => {
3804
3905
  require_transform.assertScope(active);
3805
- if (!next) return published.ids();
3906
+ if (!next) return currentState.ids();
3806
3907
  if (explicitOrder) return explicitOrder;
3807
3908
  return Object.freeze(deriveIds());
3808
3909
  }
@@ -3832,15 +3933,28 @@ const createCollectionOutput = (binding) => {
3832
3933
  };
3833
3934
  const seal = (nextReset, isEqual) => {
3834
3935
  if (cleared) {
3835
- for (const key of published.keys()) if (!staged.has(key)) staged.set(key, { present: false });
3936
+ for (const key of currentState.keys()) if (!staged.has(key)) staged.set(key, { present: false });
3836
3937
  }
3837
3938
  require_transform.profile.projection("touchedKeys", staged.size);
3838
3939
  const addedEntries = [];
3839
3940
  const updatedEntries = [];
3840
3941
  const removedEntries = [];
3841
- for (const [key, entry] of staged) {
3842
- const existed = published.has(key);
3942
+ for (const [key, original] of staged) {
3943
+ let entry = original;
3944
+ const existed = currentState.has(key);
3843
3945
  if (entry.present) {
3946
+ if (baseline?.has(key) && (!existed || !Object.is(currentState.get(key), baseline.get(key)))) {
3947
+ const before = baseline.get(key);
3948
+ const equivalent = isEqual(before, entry.value);
3949
+ require_transform.assertSynchronous(equivalent);
3950
+ if (equivalent) {
3951
+ entry = {
3952
+ present: true,
3953
+ value: before
3954
+ };
3955
+ staged.set(key, entry);
3956
+ }
3957
+ }
3844
3958
  if (!existed) {
3845
3959
  addedEntries.push({
3846
3960
  key,
@@ -3848,7 +3962,7 @@ const createCollectionOutput = (binding) => {
3848
3962
  });
3849
3963
  continue;
3850
3964
  }
3851
- const before = published.get(key);
3965
+ const before = currentState.get(key);
3852
3966
  const equal = isEqual(before, entry.value);
3853
3967
  require_transform.assertSynchronous(equal);
3854
3968
  if (!equal) updatedEntries.push({
@@ -3865,13 +3979,13 @@ const createCollectionOutput = (binding) => {
3865
3979
  }
3866
3980
  if (existed) removedEntries.push({
3867
3981
  key,
3868
- before: published.get(key)
3982
+ before: currentState.get(key)
3869
3983
  });
3870
3984
  else staged.delete(key);
3871
3985
  }
3872
- const ids = published.ids();
3986
+ const ids = currentState.ids();
3873
3987
  if (explicitOrder) {
3874
- const expectedLength = published.size() + addedEntries.length - removedEntries.length;
3988
+ const expectedLength = currentState.size() + addedEntries.length - removedEntries.length;
3875
3989
  const seen = /* @__PURE__ */ new Set();
3876
3990
  for (const key of explicitOrder) {
3877
3991
  if (seen.has(key) || !hasNext(key)) throw new TypeError("Projection order must contain every key exactly once.");
@@ -3880,7 +3994,7 @@ const createCollectionOutput = (binding) => {
3880
3994
  if (seen.size !== expectedLength) throw new TypeError("Projection order must contain every key exactly once.");
3881
3995
  nextIds = require_transform.sameArray(ids, explicitOrder) ? ids : explicitOrder;
3882
3996
  } else if (addedEntries.length || removedEntries.length) {
3883
- const order = new Array(published.size() + addedEntries.length - removedEntries.length);
3997
+ const order = new Array(currentState.size() + addedEntries.length - removedEntries.length);
3884
3998
  let index = 0;
3885
3999
  for (const key of ids) if (hasNext(key)) order[index++] = key;
3886
4000
  for (const entry of addedEntries) order[index++] = entry.key;
@@ -3897,7 +4011,7 @@ const createCollectionOutput = (binding) => {
3897
4011
  reset = nextReset;
3898
4012
  return change !== void 0;
3899
4013
  };
3900
- const context = (active, cause) => Object.freeze({
4014
+ const context = (active, cause, consumer) => Object.freeze({
3901
4015
  kind: "collection",
3902
4016
  read: {
3903
4017
  get: (key) => {
@@ -3913,9 +4027,9 @@ const createCollectionOutput = (binding) => {
3913
4027
  return nextIds;
3914
4028
  }
3915
4029
  },
3916
- change,
4030
+ change: consumer ? consumers.get(consumer)?.current() : change,
3917
4031
  revision,
3918
- reset,
4032
+ reset: consumer ? consumers.get(consumer)?.reset() ?? false : reset,
3919
4033
  cause
3920
4034
  });
3921
4035
  return {
@@ -3925,35 +4039,59 @@ const createCollectionOutput = (binding) => {
3925
4039
  },
3926
4040
  begin,
3927
4041
  seal,
3928
- context: (active) => context(active, binding.cause()),
3929
- current: () => published.read(binding.check),
4042
+ context: (active, consumer) => context(active, binding.cause(), consumer),
4043
+ acknowledge: (consumer) => consumers.get(consumer)?.clear(),
4044
+ pending: (consumer) => consumers.get(consumer)?.current() !== void 0,
4045
+ baseline: () => baseline ?? currentState.read(binding.check),
4046
+ current: () => currentState.read(binding.check),
3930
4047
  revision: () => revision,
3931
- reset: () => reset,
4048
+ reset: (consumer) => consumer ? consumers.get(consumer)?.reset() ?? false : reset,
3932
4049
  publish: () => {
3933
- if (change) published.install(staged, nextIds, change.kind === "reset");
3934
- if (change && initialized) revision++;
4050
+ if (change) {
4051
+ if (initialized) {
4052
+ baseline ??= currentState.read(() => void 0);
4053
+ notifications.add(change, currentState.ids(), nextIds);
4054
+ for (const pending of consumers.values()) pending.add(change, currentState.ids(), nextIds);
4055
+ revision++;
4056
+ }
4057
+ currentState.install(staged, nextIds, change.kind === "reset");
4058
+ }
3935
4059
  initialized = true;
3936
4060
  },
3937
- emit: (call) => {
3938
- Array.from(listeners).forEach((listener) => call(listener, change));
4061
+ emit: (call, force) => {
4062
+ const net = notifications.current();
4063
+ for (const listener of [...observers]) call(listener, net);
4064
+ if (net || force) for (const listener of [...listeners]) call(listener, net);
4065
+ },
4066
+ finish: () => {
4067
+ baseline = void 0;
4068
+ notifications.clear();
4069
+ },
4070
+ observe: (listener) => {
4071
+ binding.check();
4072
+ observers.add(listener);
4073
+ return () => observers.delete(listener);
3939
4074
  },
3940
4075
  hasConsumers: () => consumers.size > 0,
3941
- forEachConsumer: (run) => consumers.forEach(run),
3942
- attachConsumer: (consumer) => consumers.add(consumer),
4076
+ forEachConsumer: (run) => consumers.forEach((_pending, consumer) => run(consumer)),
4077
+ attachConsumer: (consumer) => consumers.set(consumer, createCollectionChanges()),
3943
4078
  detachConsumer: (consumer) => consumers.delete(consumer),
3944
4079
  clear: () => {
3945
4080
  staged.clear();
3946
- nextIds = published.ids();
4081
+ nextIds = currentState.ids();
3947
4082
  change = void 0;
3948
4083
  reset = false;
3949
4084
  explicitOrder = void 0;
3950
4085
  cleared = false;
3951
4086
  },
3952
4087
  release: () => {
3953
- published.release();
4088
+ currentState.release();
3954
4089
  staged.clear();
3955
4090
  listeners.clear();
3956
4091
  consumers.clear();
4092
+ observers.clear();
4093
+ baseline = void 0;
4094
+ notifications.clear();
3957
4095
  nextIds = Object.freeze([]);
3958
4096
  change = void 0;
3959
4097
  initialized = false;
@@ -3972,7 +4110,7 @@ const createCollectionOutput = (binding) => {
3972
4110
  //#endregion
3973
4111
  //#region core/src/projection/output/value.ts
3974
4112
  const unset = Symbol("projection-value-unset");
3975
- /** Owns staged and published state for one scalar Projection output. */
4113
+ /** Owns staged and current state for one scalar Projection output. */
3976
4114
  const createValueOutput = (binding) => {
3977
4115
  let value;
3978
4116
  let staged = unset;
@@ -3982,7 +4120,10 @@ const createValueOutput = (binding) => {
3982
4120
  let changed = false;
3983
4121
  let reset = false;
3984
4122
  const listeners = /* @__PURE__ */ new Set();
3985
- const consumers = /* @__PURE__ */ new Set();
4123
+ const consumers = /* @__PURE__ */ new Map();
4124
+ const observers = /* @__PURE__ */ new Set();
4125
+ let baseline = unset;
4126
+ let resetRevision = -1;
3986
4127
  const begin = (active, initialize) => {
3987
4128
  staged = unset;
3988
4129
  next = value;
@@ -4009,7 +4150,12 @@ const createValueOutput = (binding) => {
4009
4150
  next = value;
4010
4151
  return false;
4011
4152
  }
4012
- const candidate = staged;
4153
+ let candidate = staged;
4154
+ if (baseline !== unset && !Object.is(value, baseline)) {
4155
+ const equivalent = isEqual(baseline, candidate);
4156
+ require_transform.assertSynchronous(equivalent);
4157
+ if (equivalent) candidate = baseline;
4158
+ }
4013
4159
  const equal = initialized ? isEqual(value, candidate) : false;
4014
4160
  require_transform.assertSynchronous(equal);
4015
4161
  changed = !equal;
@@ -4024,13 +4170,13 @@ const createValueOutput = (binding) => {
4024
4170
  },
4025
4171
  begin,
4026
4172
  seal,
4027
- context: (active) => {
4173
+ context: (active, consumer) => {
4028
4174
  require_transform.assertScope(active);
4029
4175
  return Object.freeze({
4030
4176
  kind: "value",
4031
4177
  value: next,
4032
4178
  revision,
4033
- reset,
4179
+ reset: consumer ? (consumers.get(consumer)?.revision ?? -1) < resetRevision : reset,
4034
4180
  cause: binding.cause()
4035
4181
  });
4036
4182
  },
@@ -4039,19 +4185,48 @@ const createValueOutput = (binding) => {
4039
4185
  return value;
4040
4186
  },
4041
4187
  revision: () => revision,
4042
- reset: () => reset,
4043
- publish: () => {
4044
- if (changed && initialized) revision++;
4188
+ reset: (consumer) => consumer ? (consumers.get(consumer)?.revision ?? -1) < resetRevision : reset,
4189
+ acknowledge: (consumer) => {
4190
+ if (consumers.has(consumer)) consumers.set(consumer, {
4191
+ revision,
4192
+ value
4193
+ });
4194
+ },
4195
+ pending: (consumer) => {
4196
+ const previous = consumers.get(consumer);
4197
+ return !previous || previous.revision < resetRevision || !Object.is(previous.value, value);
4198
+ },
4199
+ baseline: () => baseline === unset ? value : baseline,
4200
+ publish: (forceReset) => {
4201
+ if (initialized && (changed || forceReset)) {
4202
+ if (baseline === unset) baseline = value;
4203
+ revision++;
4204
+ if (reset) resetRevision = revision;
4205
+ }
4045
4206
  if (changed) value = next;
4046
4207
  initialized = true;
4047
4208
  },
4048
- emit: (call) => {
4049
- require_transform.profile.materialized.notification();
4050
- Array.from(listeners).forEach((listener) => call(listener, void 0));
4209
+ emit: (call, force) => {
4210
+ for (const listener of [...observers]) call(listener, void 0);
4211
+ if (force || baseline !== unset && !Object.is(baseline, value)) {
4212
+ require_transform.profile.materialized.notification();
4213
+ for (const listener of [...listeners]) call(listener, void 0);
4214
+ }
4215
+ },
4216
+ finish: () => {
4217
+ baseline = unset;
4218
+ },
4219
+ observe: (listener) => {
4220
+ binding.check();
4221
+ observers.add(listener);
4222
+ return () => observers.delete(listener);
4051
4223
  },
4052
4224
  hasConsumers: () => consumers.size > 0,
4053
- forEachConsumer: (run) => consumers.forEach(run),
4054
- attachConsumer: (consumer) => consumers.add(consumer),
4225
+ forEachConsumer: (run) => consumers.forEach((_revision, consumer) => run(consumer)),
4226
+ attachConsumer: (consumer) => consumers.set(consumer, {
4227
+ revision,
4228
+ value
4229
+ }),
4055
4230
  detachConsumer: (consumer) => consumers.delete(consumer),
4056
4231
  clear: () => {
4057
4232
  staged = unset;
@@ -4062,6 +4237,8 @@ const createValueOutput = (binding) => {
4062
4237
  release: () => {
4063
4238
  listeners.clear();
4064
4239
  consumers.clear();
4240
+ observers.clear();
4241
+ baseline = unset;
4065
4242
  staged = unset;
4066
4243
  value = next = void 0;
4067
4244
  initialized = false;
@@ -4132,7 +4309,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
4132
4309
  let active = true;
4133
4310
  try {
4134
4311
  const scopeActive = () => active;
4135
- const sources = Object.freeze(dependencies.map((output) => output.context(scopeActive)));
4312
+ const sources = Object.freeze(dependencies.map((output) => output.context(scopeActive, processor)));
4136
4313
  const outputs = begin(scopeActive, reset);
4137
4314
  if (recreate && instance) {
4138
4315
  instance.release?.();
@@ -4146,16 +4323,17 @@ const createProcessor = (scheduler, definition, dependencies) => {
4146
4323
  sources,
4147
4324
  outputs
4148
4325
  }));
4149
- return seal(reset);
4326
+ const changed = seal(reset);
4327
+ dependencies.forEach((output) => output.acknowledge(processor));
4328
+ return changed;
4150
4329
  } finally {
4151
4330
  active = false;
4152
4331
  }
4153
4332
  };
4154
- const order = scheduler.order();
4333
+ const id = scheduler.nextId();
4155
4334
  processor = {
4156
4335
  kind: "processor",
4157
- order,
4158
- name: definition.name ? `${definition.name} (${order})` : `projection-${order}`,
4336
+ name: definition.name ? `${definition.name} (${id})` : `projection-${id}`,
4159
4337
  dependencies: Object.freeze([...dependencies]),
4160
4338
  outputs: Object.freeze(bound.map((entry) => entry.state)),
4161
4339
  fault: void 0,
@@ -4282,7 +4460,9 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
4282
4460
  let initialized = false;
4283
4461
  let value;
4284
4462
  let selectedRevision = 0;
4463
+ let notified;
4285
4464
  let sourceRevision = -1;
4465
+ let notificationRevision = -1;
4286
4466
  let selection = Object.freeze({
4287
4467
  keys: /* @__PURE__ */ new Set(),
4288
4468
  all: true,
@@ -4318,8 +4498,9 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
4318
4498
  });
4319
4499
  const changed = !initialized || !equality(value, next);
4320
4500
  if (changed) {
4501
+ const candidate = unsubscribeSource && equality(notified, next) ? notified : next;
4321
4502
  if (initialized) selectedRevision++;
4322
- value = next;
4503
+ value = candidate;
4323
4504
  }
4324
4505
  selection = nextSelection;
4325
4506
  initialized = true;
@@ -4329,31 +4510,44 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
4329
4510
  };
4330
4511
  const ensureCurrent = () => {
4331
4512
  const revision = source.revision();
4332
- if (!initialized || !unsubscribeSource && sourceRevision !== revision) evaluate();
4333
- return value;
4334
- };
4335
- const onProjectionChange = (incoming) => {
4336
- let change = incoming;
4337
- if (!change && previousCollection) {
4513
+ if (!initialized) evaluate();
4514
+ else if (sourceRevision !== revision) {
4338
4515
  const current = source.current();
4339
- if (isMapLike(current)) {
4340
- change = require_transform.diffCollection(previousCollection, current);
4341
- previousCollection = current;
4342
- if (!change) {
4343
- sourceRevision = source.revision();
4344
- return;
4516
+ let affected = true;
4517
+ if (previousCollection && isMapLike(current) && !selection.all) {
4518
+ affected = selection.structure && !require_transform.iterableMatchesArray(current.keys(), [...previousCollection.keys()]);
4519
+ if (!affected) {
4520
+ for (const key of selection.keys) if (previousCollection.has(key) !== current.has(key) || !Object.is(previousCollection.get(key), current.get(key))) {
4521
+ affected = true;
4522
+ break;
4523
+ }
4345
4524
  }
4346
- } else previousCollection = void 0;
4525
+ }
4526
+ if (affected) evaluate();
4527
+ else {
4528
+ sourceRevision = revision;
4529
+ updateCollectionSnapshot(current);
4530
+ }
4347
4531
  }
4348
- if (!selectionAffects(selection, change)) {
4532
+ return value;
4533
+ };
4534
+ const onProjectionChange = (incoming) => {
4535
+ if (sourceRevision === notificationRevision && sourceRevision !== source.revision() && incoming && !selectionAffects(selection, incoming)) {
4349
4536
  sourceRevision = source.revision();
4350
4537
  updateCollectionSnapshot();
4351
- return;
4538
+ } else ensureCurrent();
4539
+ notificationRevision = sourceRevision;
4540
+ if (!equality(notified, value)) {
4541
+ notified = value;
4542
+ notifyProjectionListeners(listeners, "Projection selector listeners failed.");
4352
4543
  }
4353
- if (evaluate()) notifyProjectionListeners(listeners, "Projection selector listeners failed.");
4354
4544
  };
4355
4545
  const installSource = () => {
4356
- if (!unsubscribeSource) unsubscribeSource = source.subscribe(onProjectionChange);
4546
+ if (!unsubscribeSource) {
4547
+ notified = value;
4548
+ notificationRevision = sourceRevision;
4549
+ unsubscribeSource = source.observe(onProjectionChange);
4550
+ }
4357
4551
  };
4358
4552
  return Object.freeze({
4359
4553
  current: ensureCurrent,
@@ -4735,13 +4929,12 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
4735
4929
  active = false;
4736
4930
  }
4737
4931
  },
4738
- publish: state.publish,
4932
+ publish: () => state.publish(pendingReset),
4739
4933
  clear: () => {
4740
4934
  const failed = boundary.fault !== void 0;
4741
4935
  state.clear();
4742
4936
  pendingReset = false;
4743
4937
  pendingCause = void 0;
4744
- contextCause = void 0;
4745
4938
  recovering = false;
4746
4939
  clearSource(failed);
4747
4940
  },
@@ -4797,6 +4990,7 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
4797
4990
  return {
4798
4991
  producer: boundary,
4799
4992
  output,
4993
+ baseline: state.baseline,
4800
4994
  mark,
4801
4995
  fail,
4802
4996
  detach: (next) => {
@@ -4816,7 +5010,7 @@ const stageCollectionRead = (state, read, active, options) => {
4816
5010
  }
4817
5011
  evaluation.output.order(ids);
4818
5012
  } else {
4819
- const previous = state.current();
5013
+ const previous = evaluation.previous;
4820
5014
  let currentIds;
4821
5015
  const ids = () => currentIds ??= read.ids();
4822
5016
  if (options.candidates) for (const key of options.candidates) if (read.has(key)) {
@@ -4897,7 +5091,6 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
4897
5091
  state.clear();
4898
5092
  pendingReset = false;
4899
5093
  pendingCause = void 0;
4900
- contextCause = void 0;
4901
5094
  recovering = false;
4902
5095
  fullScan = false;
4903
5096
  orderMayChange = false;
@@ -4950,7 +5143,11 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
4950
5143
  fullScan ||= metadata.fullScan ?? false;
4951
5144
  orderMayChange ||= metadata.orderMayChange ?? false;
4952
5145
  if (metadata.candidates) for (const key of metadata.candidates) candidates.add(key);
4953
- if (boundary.fault) recovering = true;
5146
+ if (boundary.fault) {
5147
+ recovering = true;
5148
+ fullScan = true;
5149
+ orderMayChange = true;
5150
+ }
4954
5151
  scheduler.capture(boundary);
4955
5152
  };
4956
5153
  const fail = (cause) => {
@@ -4961,6 +5158,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
4961
5158
  return {
4962
5159
  producer: boundary,
4963
5160
  output,
5161
+ baseline: state.baseline,
4964
5162
  mark,
4965
5163
  fail,
4966
5164
  detach: (next) => {
@@ -5510,13 +5708,12 @@ const materializeInputSource = (scheduler, definition) => {
5510
5708
  ...boundary,
5511
5709
  input: {
5512
5710
  kind: "value",
5513
- read: () => latest,
5514
5711
  set(value) {
5515
5712
  const next = scheduler.acceptInput(() => {
5516
5713
  if (equal(latest, value)) return latest;
5517
5714
  if (source.equality === Object.is) return value;
5518
- const published = boundary.output.current();
5519
- return !Object.is(latest, published) && equal(published, value) ? published : value;
5715
+ const baseline = boundary.baseline();
5716
+ return !Object.is(latest, baseline) && equal(baseline, value) ? baseline : value;
5520
5717
  });
5521
5718
  if (Object.is(latest, next)) return;
5522
5719
  latest = next;
@@ -5527,7 +5724,6 @@ const materializeInputSource = (scheduler, definition) => {
5527
5724
  };
5528
5725
  }
5529
5726
  const values = createCollectionState(source.initial);
5530
- let view;
5531
5727
  const latest = {
5532
5728
  get: values.get,
5533
5729
  has: values.has,
@@ -5536,17 +5732,11 @@ const materializeInputSource = (scheduler, definition) => {
5536
5732
  const boundary = createCollectionBoundary(scheduler, "projection collection input", () => latest, Object.is, () => void 0);
5537
5733
  boundary.detach(() => {
5538
5734
  values.release();
5539
- view = void 0;
5540
5735
  });
5541
- const check = () => {
5542
- scheduler.assertActive();
5543
- if (boundary.producer.disposed) throw new require_transform.ProjectionDisposedError();
5544
- };
5545
5736
  return {
5546
5737
  ...boundary,
5547
5738
  input: {
5548
5739
  kind: "collection",
5549
- read: () => view ??= require_transform.collectionView(values.read(check)),
5550
5740
  update(run) {
5551
5741
  const accepted = scheduler.acceptInput(() => {
5552
5742
  const staged = /* @__PURE__ */ new Map();
@@ -5593,7 +5783,7 @@ const materializeInputSource = (scheduler, definition) => {
5593
5783
  } finally {
5594
5784
  active = false;
5595
5785
  }
5596
- const published = source.equality === Object.is ? void 0 : boundary.output.current();
5786
+ const baseline = source.equality === Object.is ? void 0 : boundary.baseline();
5597
5787
  for (const [key, entry] of staged) {
5598
5788
  const existed = values.has(key);
5599
5789
  if (!entry.present) {
@@ -5603,8 +5793,8 @@ const materializeInputSource = (scheduler, definition) => {
5603
5793
  const previous = values.get(key);
5604
5794
  let next = entry.value;
5605
5795
  if (existed && equal(previous, next)) next = previous;
5606
- else if (published?.has(key)) {
5607
- const before = published.get(key);
5796
+ else if (baseline?.has(key)) {
5797
+ const before = baseline.get(key);
5608
5798
  if ((!existed || !Object.is(previous, before)) && equal(before, next)) next = before;
5609
5799
  }
5610
5800
  if (existed && Object.is(previous, next)) staged.delete(key);
@@ -5629,7 +5819,6 @@ const materializeInputSource = (scheduler, definition) => {
5629
5819
  });
5630
5820
  if (!accepted.staged.size && !accepted.structural) return;
5631
5821
  values.install(accepted.staged, accepted.order, false);
5632
- view = void 0;
5633
5822
  boundary.mark({
5634
5823
  candidates: accepted.staged.keys(),
5635
5824
  orderMayChange: accepted.structural
@@ -5682,6 +5871,11 @@ const createProjectionItems = (source, checkOwner) => {
5682
5871
  const waiting = /* @__PURE__ */ new Map();
5683
5872
  let keysValue = Object.freeze([...source.current().keys()]);
5684
5873
  let keysRevision = 0;
5874
+ let notifiedKeys = keysValue;
5875
+ let settledRevision = source.revision();
5876
+ let keysSourceRevision = settledRevision;
5877
+ const pendingKeys = /* @__PURE__ */ new Set();
5878
+ const changedStates = /* @__PURE__ */ new Set();
5685
5879
  const keyListeners = /* @__PURE__ */ new Set();
5686
5880
  const check = () => {
5687
5881
  if (disposed) throw new require_transform.ProjectionDisposedError();
@@ -5717,7 +5911,7 @@ const createProjectionItems = (source, checkOwner) => {
5717
5911
  }
5718
5912
  state.value = value;
5719
5913
  state.revision++;
5720
- notify(state.listeners);
5914
+ changedStates.add(state);
5721
5915
  };
5722
5916
  const attachActive = (state, value) => {
5723
5917
  if (state.ended) return;
@@ -5760,10 +5954,12 @@ const createProjectionItems = (source, checkOwner) => {
5760
5954
  if (extra) for (const state of extra) publishState(state, value);
5761
5955
  };
5762
5956
  const updateKeys = (current) => {
5957
+ const revision = source.revision();
5958
+ if (keysSourceRevision === revision) return;
5959
+ keysSourceRevision = revision;
5763
5960
  if (require_transform.iterableMatchesArray(current.keys(), keysValue)) return;
5764
- keysValue = Object.freeze([...current.keys()]);
5961
+ keysValue = require_transform.iterableMatchesArray(current.keys(), notifiedKeys) ? notifiedKeys : Object.freeze([...current.keys()]);
5765
5962
  keysRevision++;
5766
- notify(keyListeners);
5767
5963
  };
5768
5964
  const createItem = (key, initial) => {
5769
5965
  const state = {
@@ -5772,12 +5968,15 @@ const createProjectionItems = (source, checkOwner) => {
5772
5968
  tracked: initial !== void 0,
5773
5969
  ended: false,
5774
5970
  value: initial?.value,
5971
+ notified: initial?.value,
5775
5972
  revision: 0
5776
5973
  };
5777
5974
  const ensureObserved = () => {
5778
- if (state.ended || state.tracked) return;
5975
+ if (state.ended) return;
5779
5976
  const current = source.current();
5780
- if (current.has(key)) attachActive(state, current.get(key));
5977
+ if (source.revision() !== settledRevision) pendingKeys.add(key);
5978
+ if (!state.tracked && current.has(key)) attachActive(state, current.get(key));
5979
+ else if (state.tracked) publishState(state, current.get(key));
5781
5980
  };
5782
5981
  state.readable = Object.freeze({
5783
5982
  current: () => {
@@ -5812,25 +6011,45 @@ const createProjectionItems = (source, checkOwner) => {
5812
6011
  const onChange = (change) => {
5813
6012
  check();
5814
6013
  const current = source.current();
5815
- if (!change || change.kind === "reset") {
5816
- reconcile(current);
5817
- flushNotifications();
5818
- return;
6014
+ if (change?.kind === "reset") reconcile(current);
6015
+ else {
6016
+ if (change) {
6017
+ for (const entry of change.removed) terminateKey(entry.key);
6018
+ for (const entry of change.added) activateWaiting(entry.key, entry.after);
6019
+ for (const entry of change.updated) updateKey(entry.key, entry.after);
6020
+ if (change.added.length || change.removed.length || change.order) updateKeys(current);
6021
+ }
6022
+ for (const key of pendingKeys) if (!current.has(key)) terminateKey(key);
6023
+ else {
6024
+ updateKey(key, current.get(key));
6025
+ activateWaiting(key, current.get(key));
6026
+ }
6027
+ }
6028
+ pendingKeys.clear();
6029
+ settledRevision = source.revision();
6030
+ for (const state of changedStates) if (!Object.is(state.notified, state.value)) {
6031
+ state.notified = state.value;
6032
+ notify(state.listeners);
6033
+ }
6034
+ changedStates.clear();
6035
+ if (keysValue !== notifiedKeys) updateKeys(current);
6036
+ else keysSourceRevision = settledRevision;
6037
+ if (keysValue !== notifiedKeys) {
6038
+ notifiedKeys = keysValue;
6039
+ notify(keyListeners);
5819
6040
  }
5820
- for (const entry of change.removed) terminateKey(entry.key);
5821
- for (const entry of change.added) activateWaiting(entry.key, entry.after);
5822
- for (const entry of change.updated) updateKey(entry.key, entry.after);
5823
- if (change.added.length || change.removed.length || change.order) updateKeys(current);
5824
6041
  flushNotifications();
5825
6042
  };
5826
- const stop = source.subscribe(onChange);
6043
+ const stop = source.observe(onChange);
5827
6044
  const keys = Object.freeze({
5828
6045
  current: () => {
5829
6046
  check();
6047
+ updateKeys(source.current());
5830
6048
  return keysValue;
5831
6049
  },
5832
6050
  revision: () => {
5833
6051
  check();
6052
+ updateKeys(source.current());
5834
6053
  return keysRevision;
5835
6054
  },
5836
6055
  subscribe: (listener) => {
@@ -5844,11 +6063,12 @@ const createProjectionItems = (source, checkOwner) => {
5844
6063
  keys,
5845
6064
  get: (key) => {
5846
6065
  check();
6066
+ const current = source.current();
6067
+ if (source.revision() !== settledRevision) pendingKeys.add(key);
5847
6068
  const canonical = active.get(key);
5848
6069
  if (canonical) return canonical.readable;
5849
6070
  const pending = waiting.get(key)?.values().next().value;
5850
6071
  if (pending) return pending.readable;
5851
- const current = source.current();
5852
6072
  if (current.has(key)) {
5853
6073
  const state = createItem(key, {
5854
6074
  present: true,
@@ -5874,6 +6094,8 @@ const createProjectionItems = (source, checkOwner) => {
5874
6094
  followers.clear();
5875
6095
  waiting.clear();
5876
6096
  notifications.length = 0;
6097
+ pendingKeys.clear();
6098
+ changedStates.clear();
5877
6099
  }
5878
6100
  };
5879
6101
  };
@@ -5934,6 +6156,7 @@ const createProjectionRuntime = (options) => {
5934
6156
  } else {
5935
6157
  if (definition.owner === void 0 && definition.dependencies.some((dependency) => require_transform.producerOf(dependency).owner !== void 0)) throw new TypeError("A root projection cannot depend on a scoped projection.");
5936
6158
  const dependencies = definition.dependencies.map((dependency) => resolveOutput(dependency, requester));
6159
+ dependencies.forEach((output) => scheduler.ensureCurrent(output));
5937
6160
  const processor = createProcessor(scheduler, definition, Object.freeze(dependencies));
5938
6161
  result = Object.freeze({
5939
6162
  record: processor,
@@ -5957,6 +6180,7 @@ const createProjectionRuntime = (options) => {
5957
6180
  if (output.owner.fault) throw output.owner.fault;
5958
6181
  };
5959
6182
  const publicCurrent = (output) => {
6183
+ scheduler.ensureCurrent(output);
5960
6184
  assertOutput(output);
5961
6185
  const current = output.current();
5962
6186
  const revision = output.revision();
@@ -5988,10 +6212,12 @@ const createProjectionRuntime = (options) => {
5988
6212
  kind: output.kind,
5989
6213
  current: () => readProjection(projection, requester),
5990
6214
  revision: () => {
6215
+ scheduler.ensureCurrent(output);
5991
6216
  assertOutput(output);
5992
6217
  return output.revision();
5993
6218
  },
5994
- subscribe: output.subscribe
6219
+ subscribe: output.subscribe,
6220
+ observe: output.observe
5995
6221
  });
5996
6222
  };
5997
6223
  const projectionItems = (projection, requester) => {
@@ -6002,6 +6228,7 @@ const createProjectionRuntime = (options) => {
6002
6228
  if (existing) return existing.items;
6003
6229
  const controller = createProjectionItems(readableSource(projection, requester), () => {
6004
6230
  if (requester && !requester.active) throw new require_transform.ProjectionDisposedError();
6231
+ scheduler.ensureCurrent(output);
6005
6232
  assertOutput(output);
6006
6233
  });
6007
6234
  registry.set(output, controller);
@@ -6036,18 +6263,7 @@ const createProjectionRuntime = (options) => {
6036
6263
  function update(target, valueOrRun) {
6037
6264
  updateInput(target, valueOrRun);
6038
6265
  }
6039
- const batch = (run, options, requester) => scheduler.batch(() => {
6040
- let active = true;
6041
- const read = (target) => {
6042
- require_transform.assertScope(() => active);
6043
- return inputAccess(target, requester).read();
6044
- };
6045
- try {
6046
- return run(read);
6047
- } finally {
6048
- active = false;
6049
- }
6050
- }, options);
6266
+ const batch = (run, options) => scheduler.batch(run, options);
6051
6267
  const disposeScope = (state) => {
6052
6268
  if (!state.active) return;
6053
6269
  scheduler.assertIdle();
@@ -6139,7 +6355,7 @@ const createProjectionRuntime = (options) => {
6139
6355
  }
6140
6356
  function scopeBatch(run, options) {
6141
6357
  assertActive();
6142
- return batch(run, options, state);
6358
+ return batch(run, options);
6143
6359
  }
6144
6360
  return Object.freeze({
6145
6361
  own,