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