doxum 0.1.33 → 0.1.35
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 +18 -7
- package/dist/advanced.cjs +1 -1
- package/dist/advanced.js +1 -1
- package/dist/index.cjs +395 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -11
- package/dist/index.d.ts +13 -11
- package/dist/index.js +395 -142
- package/dist/index.js.map +1 -1
- package/dist/{transform-A9Lx5R2l.js → transform-3mj0eStL.js} +147 -152
- package/dist/transform-3mj0eStL.js.map +1 -0
- package/dist/{transform-CSPGdBe5.cjs → transform-D45KWMzj.cjs} +148 -159
- package/dist/transform-D45KWMzj.cjs.map +1 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +3 -3
- 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 +48 -20
- package/skills/doxum-runtime/references/public-api.md +22 -21
- package/skills/doxum-runtime/references/recipes.md +25 -2
- package/dist/transform-A9Lx5R2l.js.map +0 -1
- package/dist/transform-CSPGdBe5.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_transform = require("./transform-
|
|
2
|
+
const require_transform = require("./transform-D45KWMzj.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
|
|
@@ -2775,6 +2775,12 @@ const isExternalSource = (source) => {
|
|
|
2775
2775
|
return (candidate.kind === "value" || candidate.kind === "collection") && typeof candidate.current === "function" && typeof candidate.revision === "function" && typeof candidate.subscribe === "function";
|
|
2776
2776
|
};
|
|
2777
2777
|
//#endregion
|
|
2778
|
+
//#region core/src/projection/keyed/entries.ts
|
|
2779
|
+
/** Shared entry boundary; membership and duplicate policy belong to each operation. */
|
|
2780
|
+
function assertKeyedEntry(entry, name) {
|
|
2781
|
+
if (!Array.isArray(entry) || entry.length !== 2 || typeof entry[0] !== "string") throw new TypeError(`${name} entries must be [string key, value] tuples.`);
|
|
2782
|
+
}
|
|
2783
|
+
//#endregion
|
|
2778
2784
|
//#region core/src/projection/keyed/flat-map.ts
|
|
2779
2785
|
function createKeyedFlatMap(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
2780
2786
|
const name = "derive.keyed.flatMap";
|
|
@@ -2821,7 +2827,7 @@ function createKeyedFlatMap(source, dependenciesOrSelect, selectOrEquality, mayb
|
|
|
2821
2827
|
if (!Array.isArray(entries)) throw new TypeError(`${name} selector must return an array of entries.`);
|
|
2822
2828
|
const keys = [];
|
|
2823
2829
|
for (const entry of entries) {
|
|
2824
|
-
|
|
2830
|
+
assertKeyedEntry(entry, name);
|
|
2825
2831
|
const [key, child] = entry;
|
|
2826
2832
|
const owner = ownerByOutput.get(key);
|
|
2827
2833
|
if (proposals.has(key) || owner !== void 0 && !replaced.has(owner)) throw new TypeError(`${name} duplicate key ${JSON.stringify(key)}.`);
|
|
@@ -2908,6 +2914,25 @@ const assertCollectionSources = (sources, expected) => {
|
|
|
2908
2914
|
if (sources.length !== expected) throw new Error("derive.keyed.merge resolved an invalid dependency count.");
|
|
2909
2915
|
for (const source of sources) if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2910
2916
|
};
|
|
2917
|
+
/** Complete ordered result reconciliation, without retaining a second member/value cache. */
|
|
2918
|
+
const stageKeyedSnapshot = (values, output, keyOf, valueOf, name) => {
|
|
2919
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2920
|
+
const order = new Array(values.length);
|
|
2921
|
+
const previous = output.previous.ids();
|
|
2922
|
+
require_transform.profile.collectionView.idsScanned(values.length + previous.length);
|
|
2923
|
+
for (let index = 0; index < values.length; index++) {
|
|
2924
|
+
const value = values[index];
|
|
2925
|
+
const key = keyOf(value);
|
|
2926
|
+
require_transform.assertSynchronous(key);
|
|
2927
|
+
if (typeof key !== "string") throw new TypeError(`${name} key selector must return a string.`);
|
|
2928
|
+
if (seen.has(key)) throw new TypeError(`${name} duplicate key ${JSON.stringify(key)}.`);
|
|
2929
|
+
seen.add(key);
|
|
2930
|
+
order[index] = key;
|
|
2931
|
+
output.output.set(key, valueOf(value));
|
|
2932
|
+
}
|
|
2933
|
+
for (const key of previous) if (!seen.has(key)) output.output.remove(key);
|
|
2934
|
+
output.output.order(order);
|
|
2935
|
+
};
|
|
2911
2936
|
function createKeyedFrom(source, keyOf, equality) {
|
|
2912
2937
|
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.from requires a key selector.");
|
|
2913
2938
|
const outputEquality = assertEquality(equality, "derive.keyed.from");
|
|
@@ -2937,25 +2962,38 @@ function createKeyedFrom(source, keyOf, equality) {
|
|
|
2937
2962
|
values = scalar.value;
|
|
2938
2963
|
}
|
|
2939
2964
|
if (!values) throw new Error("derive.keyed.from has no source values.");
|
|
2940
|
-
|
|
2941
|
-
const order = new Array(values.length);
|
|
2942
|
-
for (let index = 0; index < values.length; index++) {
|
|
2943
|
-
const value = values[index];
|
|
2944
|
-
const key = keyOf(value);
|
|
2945
|
-
require_transform.assertSynchronous(key);
|
|
2946
|
-
if (typeof key !== "string") throw new TypeError("derive.keyed.from key selector must return a string.");
|
|
2947
|
-
if (seen.has(key)) throw new TypeError(`derive.keyed.from duplicate key ${JSON.stringify(key)}.`);
|
|
2948
|
-
seen.add(key);
|
|
2949
|
-
order[index] = key;
|
|
2950
|
-
output.output.set(key, value);
|
|
2951
|
-
}
|
|
2952
|
-
for (const previousKey of output.previous.ids()) if (!seen.has(previousKey)) output.output.remove(previousKey);
|
|
2953
|
-
output.output.order(order);
|
|
2965
|
+
stageKeyedSnapshot(values, output, keyOf, (value) => value, "derive.keyed.from");
|
|
2954
2966
|
} }),
|
|
2955
2967
|
name: "derive.keyed.from"
|
|
2956
2968
|
});
|
|
2957
2969
|
return projection;
|
|
2958
2970
|
}
|
|
2971
|
+
function createKeyedFromEntries(dependencies, compute, equality) {
|
|
2972
|
+
const name = "derive.keyed.fromEntries";
|
|
2973
|
+
const compiled = require_transform.compileProjectionDependencies(dependencies, name);
|
|
2974
|
+
if (typeof compute !== "function") throw new TypeError(`${name} requires a compute callback.`);
|
|
2975
|
+
const outputEquality = assertEquality(equality, name);
|
|
2976
|
+
const [projection] = require_transform.defineProcessor({
|
|
2977
|
+
dependencies: compiled.projections,
|
|
2978
|
+
outputs: [{
|
|
2979
|
+
kind: "collection",
|
|
2980
|
+
equality: outputEquality
|
|
2981
|
+
}],
|
|
2982
|
+
name,
|
|
2983
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
2984
|
+
const output = evaluation.outputs[0];
|
|
2985
|
+
if (output.kind !== "collection") throw new Error(`${name} requires a collection output.`);
|
|
2986
|
+
const entries = compute(require_transform.snapshotProjectionValues(compiled.names, evaluation.sources));
|
|
2987
|
+
require_transform.assertSynchronous(entries);
|
|
2988
|
+
if (!Array.isArray(entries)) throw new TypeError(`${name} compute must return an array of entries.`);
|
|
2989
|
+
stageKeyedSnapshot(entries, output, (entry) => {
|
|
2990
|
+
assertKeyedEntry(entry, name);
|
|
2991
|
+
return entry[0];
|
|
2992
|
+
}, (entry) => entry[1], name);
|
|
2993
|
+
} })
|
|
2994
|
+
});
|
|
2995
|
+
return projection;
|
|
2996
|
+
}
|
|
2959
2997
|
function createKeyedMerge(sources, options) {
|
|
2960
2998
|
if (!Array.isArray(sources)) throw new TypeError("derive.keyed.merge sources must be an array of keyed projections.");
|
|
2961
2999
|
const capturedSources = require_transform.snapshotArray(sources);
|
|
@@ -3584,6 +3622,7 @@ const keyedDerive = Object.assign(createKeyedDerive, {
|
|
|
3584
3622
|
entries: createKeyedEntries,
|
|
3585
3623
|
get: createKeyedGet,
|
|
3586
3624
|
from: createKeyedFrom,
|
|
3625
|
+
fromEntries: createKeyedFromEntries,
|
|
3587
3626
|
merge: createKeyedMerge,
|
|
3588
3627
|
groupBy: createKeyedGroupBy,
|
|
3589
3628
|
flatMap: createKeyedFlatMap,
|
|
@@ -3607,9 +3646,7 @@ function createValueDerive(dependencies, compute, equality = Object.is) {
|
|
|
3607
3646
|
create: () => ({ evaluate: (evaluation) => {
|
|
3608
3647
|
const output = evaluation.outputs[0];
|
|
3609
3648
|
if (output.kind !== "value") throw new Error("derive requires a value output.");
|
|
3610
|
-
const
|
|
3611
|
-
for (let index = 0; index < names.length; index++) values[names[index]] = require_transform.snapshotDependencyValue(evaluation.sources[index]);
|
|
3612
|
-
const next = compute(Object.freeze(values));
|
|
3649
|
+
const next = compute(require_transform.snapshotProjectionValues(names, evaluation.sources));
|
|
3613
3650
|
require_transform.assertSynchronous(next);
|
|
3614
3651
|
output.output.set(next);
|
|
3615
3652
|
} })
|
|
@@ -3618,6 +3655,104 @@ function createValueDerive(dependencies, compute, equality = Object.is) {
|
|
|
3618
3655
|
}
|
|
3619
3656
|
const derive = Object.assign(createValueDerive, { keyed: keyedDerive });
|
|
3620
3657
|
//#endregion
|
|
3658
|
+
//#region core/src/projection/collection/changes.ts
|
|
3659
|
+
/** Net transitions for one consumer's unconsumed interval; bounded by touched keys. */
|
|
3660
|
+
const createCollectionChanges = () => {
|
|
3661
|
+
let first;
|
|
3662
|
+
let beforeOrder = [];
|
|
3663
|
+
let afterOrder = [];
|
|
3664
|
+
let cached;
|
|
3665
|
+
let valid = false;
|
|
3666
|
+
let transitions;
|
|
3667
|
+
const merge = (change) => {
|
|
3668
|
+
const write = (key, before, after) => {
|
|
3669
|
+
const previous = transitions.get(key);
|
|
3670
|
+
const initial = previous?.before ?? before;
|
|
3671
|
+
if (initial.present === after.present && (!initial.present || after.present && Object.is(initial.value, after.value))) transitions.delete(key);
|
|
3672
|
+
else if (previous) previous.after = after;
|
|
3673
|
+
else transitions.set(key, {
|
|
3674
|
+
before,
|
|
3675
|
+
after
|
|
3676
|
+
});
|
|
3677
|
+
};
|
|
3678
|
+
for (const entry of change.added) write(entry.key, { present: false }, {
|
|
3679
|
+
present: true,
|
|
3680
|
+
value: entry.after
|
|
3681
|
+
});
|
|
3682
|
+
for (const entry of change.updated) write(entry.key, {
|
|
3683
|
+
present: true,
|
|
3684
|
+
value: entry.before
|
|
3685
|
+
}, {
|
|
3686
|
+
present: true,
|
|
3687
|
+
value: entry.after
|
|
3688
|
+
});
|
|
3689
|
+
for (const entry of change.removed) write(entry.key, {
|
|
3690
|
+
present: true,
|
|
3691
|
+
value: entry.before
|
|
3692
|
+
}, { present: false });
|
|
3693
|
+
};
|
|
3694
|
+
return {
|
|
3695
|
+
add(change, before, after) {
|
|
3696
|
+
valid = false;
|
|
3697
|
+
afterOrder = after;
|
|
3698
|
+
if (!first) {
|
|
3699
|
+
first = change;
|
|
3700
|
+
beforeOrder = before;
|
|
3701
|
+
return;
|
|
3702
|
+
}
|
|
3703
|
+
if (first.kind === "reset") return;
|
|
3704
|
+
if (change.kind === "reset") {
|
|
3705
|
+
first = change;
|
|
3706
|
+
transitions = void 0;
|
|
3707
|
+
return;
|
|
3708
|
+
}
|
|
3709
|
+
if (!transitions) {
|
|
3710
|
+
transitions = /* @__PURE__ */ new Map();
|
|
3711
|
+
merge(first);
|
|
3712
|
+
}
|
|
3713
|
+
merge(change);
|
|
3714
|
+
},
|
|
3715
|
+
current() {
|
|
3716
|
+
if (!transitions) return first;
|
|
3717
|
+
if (valid) return cached;
|
|
3718
|
+
const added = [];
|
|
3719
|
+
const updated = [];
|
|
3720
|
+
const removed = [];
|
|
3721
|
+
for (const [key, { before, after }] of transitions) if (!before.present) {
|
|
3722
|
+
if (after.present) added.push({
|
|
3723
|
+
key,
|
|
3724
|
+
after: after.value
|
|
3725
|
+
});
|
|
3726
|
+
} else if (!after.present) removed.push({
|
|
3727
|
+
key,
|
|
3728
|
+
before: before.value
|
|
3729
|
+
});
|
|
3730
|
+
else if (!Object.is(before.value, after.value)) updated.push({
|
|
3731
|
+
key,
|
|
3732
|
+
before: before.value,
|
|
3733
|
+
after: after.value
|
|
3734
|
+
});
|
|
3735
|
+
cached = require_transform.createCollectionChange({
|
|
3736
|
+
added,
|
|
3737
|
+
updated,
|
|
3738
|
+
removed,
|
|
3739
|
+
beforeOrder,
|
|
3740
|
+
afterOrder
|
|
3741
|
+
});
|
|
3742
|
+
valid = true;
|
|
3743
|
+
return cached;
|
|
3744
|
+
},
|
|
3745
|
+
reset: () => first?.kind === "reset",
|
|
3746
|
+
clear() {
|
|
3747
|
+
first = void 0;
|
|
3748
|
+
cached = void 0;
|
|
3749
|
+
valid = false;
|
|
3750
|
+
transitions = void 0;
|
|
3751
|
+
beforeOrder = afterOrder = [];
|
|
3752
|
+
}
|
|
3753
|
+
};
|
|
3754
|
+
};
|
|
3755
|
+
//#endregion
|
|
3621
3756
|
//#region core/src/projection/collection/index.ts
|
|
3622
3757
|
const height = (node) => node?.height ?? 0;
|
|
3623
3758
|
const node = (key, value, left, right) => {
|
|
@@ -3712,7 +3847,7 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
|
3712
3847
|
const createCollectionState = (initial) => {
|
|
3713
3848
|
const values = new Map(initial);
|
|
3714
3849
|
let ids = Object.freeze([...values.keys()]);
|
|
3715
|
-
let index
|
|
3850
|
+
let index;
|
|
3716
3851
|
return {
|
|
3717
3852
|
get: (key) => values.get(key),
|
|
3718
3853
|
has: (key) => values.has(key),
|
|
@@ -3720,7 +3855,7 @@ const createCollectionState = (initial) => {
|
|
|
3720
3855
|
size: () => values.size,
|
|
3721
3856
|
ids: () => ids,
|
|
3722
3857
|
read: (check) => {
|
|
3723
|
-
const snapshotIndex = index;
|
|
3858
|
+
const snapshotIndex = index ??= PersistentKeyedIndex.from(values);
|
|
3724
3859
|
const snapshotIds = ids;
|
|
3725
3860
|
return Object.freeze({
|
|
3726
3861
|
get: (key) => {
|
|
@@ -3741,9 +3876,9 @@ const createCollectionState = (initial) => {
|
|
|
3741
3876
|
if (reset) {
|
|
3742
3877
|
values.clear();
|
|
3743
3878
|
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
3744
|
-
index = PersistentKeyedIndex.from(values);
|
|
3879
|
+
if (index) index = PersistentKeyedIndex.from(values);
|
|
3745
3880
|
} else for (const [key, entry] of staged) {
|
|
3746
|
-
index = entry.present ? index.set(key, entry.value) : index.remove(key);
|
|
3881
|
+
if (index) index = entry.present ? index.set(key, entry.value) : index.remove(key);
|
|
3747
3882
|
if (entry.present) values.set(key, entry.value);
|
|
3748
3883
|
else values.delete(key);
|
|
3749
3884
|
}
|
|
@@ -3752,17 +3887,17 @@ const createCollectionState = (initial) => {
|
|
|
3752
3887
|
release: () => {
|
|
3753
3888
|
values.clear();
|
|
3754
3889
|
ids = Object.freeze([]);
|
|
3755
|
-
index =
|
|
3890
|
+
index = void 0;
|
|
3756
3891
|
}
|
|
3757
3892
|
};
|
|
3758
3893
|
};
|
|
3759
3894
|
//#endregion
|
|
3760
3895
|
//#region core/src/projection/output/collection.ts
|
|
3761
|
-
/** Owns staged keyed intent, exact transitions and
|
|
3896
|
+
/** Owns staged keyed intent, exact transitions and current collection state. */
|
|
3762
3897
|
const createCollectionOutput = (binding) => {
|
|
3763
|
-
const
|
|
3898
|
+
const currentState = createCollectionState();
|
|
3764
3899
|
const staged = /* @__PURE__ */ new Map();
|
|
3765
|
-
let nextIds =
|
|
3900
|
+
let nextIds = currentState.ids();
|
|
3766
3901
|
let change;
|
|
3767
3902
|
let initialized = false;
|
|
3768
3903
|
let reset = false;
|
|
@@ -3770,23 +3905,26 @@ const createCollectionOutput = (binding) => {
|
|
|
3770
3905
|
let explicitOrder;
|
|
3771
3906
|
let cleared = false;
|
|
3772
3907
|
const listeners = /* @__PURE__ */ new Set();
|
|
3773
|
-
const consumers = /* @__PURE__ */ new
|
|
3774
|
-
const
|
|
3908
|
+
const consumers = /* @__PURE__ */ new Map();
|
|
3909
|
+
const notifications = createCollectionChanges();
|
|
3910
|
+
const observers = /* @__PURE__ */ new Set();
|
|
3911
|
+
let baseline;
|
|
3912
|
+
const hasNext = (key) => staged.has(key) ? staged.get(key).present : !cleared && currentState.has(key);
|
|
3775
3913
|
const getNext = (key) => {
|
|
3776
3914
|
const entry = staged.get(key);
|
|
3777
|
-
return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 :
|
|
3915
|
+
return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : currentState.get(key);
|
|
3778
3916
|
};
|
|
3779
3917
|
const deriveIds = () => {
|
|
3780
3918
|
const result = [];
|
|
3781
3919
|
if (!cleared) {
|
|
3782
|
-
for (const key of
|
|
3920
|
+
for (const key of currentState.ids()) if (hasNext(key)) result.push(key);
|
|
3783
3921
|
}
|
|
3784
|
-
for (const [key, entry] of staged) if (entry.present && (cleared || !
|
|
3922
|
+
for (const [key, entry] of staged) if (entry.present && (cleared || !currentState.has(key))) result.push(key);
|
|
3785
3923
|
return result;
|
|
3786
3924
|
};
|
|
3787
3925
|
const begin = (active, initialize) => {
|
|
3788
3926
|
staged.clear();
|
|
3789
|
-
nextIds =
|
|
3927
|
+
nextIds = currentState.ids();
|
|
3790
3928
|
change = void 0;
|
|
3791
3929
|
explicitOrder = void 0;
|
|
3792
3930
|
cleared = initialize;
|
|
@@ -3794,15 +3932,15 @@ const createCollectionOutput = (binding) => {
|
|
|
3794
3932
|
const read = (next) => ({
|
|
3795
3933
|
get: (key) => {
|
|
3796
3934
|
require_transform.assertScope(active);
|
|
3797
|
-
return next ? getNext(key) :
|
|
3935
|
+
return next ? getNext(key) : currentState.get(key);
|
|
3798
3936
|
},
|
|
3799
3937
|
has: (key) => {
|
|
3800
3938
|
require_transform.assertScope(active);
|
|
3801
|
-
return next ? hasNext(key) :
|
|
3939
|
+
return next ? hasNext(key) : currentState.has(key);
|
|
3802
3940
|
},
|
|
3803
3941
|
ids: () => {
|
|
3804
3942
|
require_transform.assertScope(active);
|
|
3805
|
-
if (!next) return
|
|
3943
|
+
if (!next) return currentState.ids();
|
|
3806
3944
|
if (explicitOrder) return explicitOrder;
|
|
3807
3945
|
return Object.freeze(deriveIds());
|
|
3808
3946
|
}
|
|
@@ -3832,15 +3970,28 @@ const createCollectionOutput = (binding) => {
|
|
|
3832
3970
|
};
|
|
3833
3971
|
const seal = (nextReset, isEqual) => {
|
|
3834
3972
|
if (cleared) {
|
|
3835
|
-
for (const key of
|
|
3973
|
+
for (const key of currentState.keys()) if (!staged.has(key)) staged.set(key, { present: false });
|
|
3836
3974
|
}
|
|
3837
3975
|
require_transform.profile.projection("touchedKeys", staged.size);
|
|
3838
3976
|
const addedEntries = [];
|
|
3839
3977
|
const updatedEntries = [];
|
|
3840
3978
|
const removedEntries = [];
|
|
3841
|
-
for (const [key,
|
|
3842
|
-
|
|
3979
|
+
for (const [key, original] of staged) {
|
|
3980
|
+
let entry = original;
|
|
3981
|
+
const existed = currentState.has(key);
|
|
3843
3982
|
if (entry.present) {
|
|
3983
|
+
if (baseline?.has(key) && (!existed || !Object.is(currentState.get(key), baseline.get(key)))) {
|
|
3984
|
+
const before = baseline.get(key);
|
|
3985
|
+
const equivalent = isEqual(before, entry.value);
|
|
3986
|
+
require_transform.assertSynchronous(equivalent);
|
|
3987
|
+
if (equivalent) {
|
|
3988
|
+
entry = {
|
|
3989
|
+
present: true,
|
|
3990
|
+
value: before
|
|
3991
|
+
};
|
|
3992
|
+
staged.set(key, entry);
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3844
3995
|
if (!existed) {
|
|
3845
3996
|
addedEntries.push({
|
|
3846
3997
|
key,
|
|
@@ -3848,7 +3999,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3848
3999
|
});
|
|
3849
4000
|
continue;
|
|
3850
4001
|
}
|
|
3851
|
-
const before =
|
|
4002
|
+
const before = currentState.get(key);
|
|
3852
4003
|
const equal = isEqual(before, entry.value);
|
|
3853
4004
|
require_transform.assertSynchronous(equal);
|
|
3854
4005
|
if (!equal) updatedEntries.push({
|
|
@@ -3865,13 +4016,13 @@ const createCollectionOutput = (binding) => {
|
|
|
3865
4016
|
}
|
|
3866
4017
|
if (existed) removedEntries.push({
|
|
3867
4018
|
key,
|
|
3868
|
-
before:
|
|
4019
|
+
before: currentState.get(key)
|
|
3869
4020
|
});
|
|
3870
4021
|
else staged.delete(key);
|
|
3871
4022
|
}
|
|
3872
|
-
const ids =
|
|
4023
|
+
const ids = currentState.ids();
|
|
3873
4024
|
if (explicitOrder) {
|
|
3874
|
-
const expectedLength =
|
|
4025
|
+
const expectedLength = currentState.size() + addedEntries.length - removedEntries.length;
|
|
3875
4026
|
const seen = /* @__PURE__ */ new Set();
|
|
3876
4027
|
for (const key of explicitOrder) {
|
|
3877
4028
|
if (seen.has(key) || !hasNext(key)) throw new TypeError("Projection order must contain every key exactly once.");
|
|
@@ -3880,7 +4031,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3880
4031
|
if (seen.size !== expectedLength) throw new TypeError("Projection order must contain every key exactly once.");
|
|
3881
4032
|
nextIds = require_transform.sameArray(ids, explicitOrder) ? ids : explicitOrder;
|
|
3882
4033
|
} else if (addedEntries.length || removedEntries.length) {
|
|
3883
|
-
const order = new Array(
|
|
4034
|
+
const order = new Array(currentState.size() + addedEntries.length - removedEntries.length);
|
|
3884
4035
|
let index = 0;
|
|
3885
4036
|
for (const key of ids) if (hasNext(key)) order[index++] = key;
|
|
3886
4037
|
for (const entry of addedEntries) order[index++] = entry.key;
|
|
@@ -3897,7 +4048,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3897
4048
|
reset = nextReset;
|
|
3898
4049
|
return change !== void 0;
|
|
3899
4050
|
};
|
|
3900
|
-
const context = (active, cause) => Object.freeze({
|
|
4051
|
+
const context = (active, cause, consumer) => Object.freeze({
|
|
3901
4052
|
kind: "collection",
|
|
3902
4053
|
read: {
|
|
3903
4054
|
get: (key) => {
|
|
@@ -3913,9 +4064,9 @@ const createCollectionOutput = (binding) => {
|
|
|
3913
4064
|
return nextIds;
|
|
3914
4065
|
}
|
|
3915
4066
|
},
|
|
3916
|
-
change,
|
|
4067
|
+
change: consumer ? consumers.get(consumer)?.current() : change,
|
|
3917
4068
|
revision,
|
|
3918
|
-
reset,
|
|
4069
|
+
reset: consumer ? consumers.get(consumer)?.reset() ?? false : reset,
|
|
3919
4070
|
cause
|
|
3920
4071
|
});
|
|
3921
4072
|
return {
|
|
@@ -3925,35 +4076,59 @@ const createCollectionOutput = (binding) => {
|
|
|
3925
4076
|
},
|
|
3926
4077
|
begin,
|
|
3927
4078
|
seal,
|
|
3928
|
-
context: (active) => context(active, binding.cause()),
|
|
3929
|
-
|
|
4079
|
+
context: (active, consumer) => context(active, binding.cause(), consumer),
|
|
4080
|
+
acknowledge: (consumer) => consumers.get(consumer)?.clear(),
|
|
4081
|
+
pending: (consumer) => consumers.get(consumer)?.current() !== void 0,
|
|
4082
|
+
baseline: () => baseline ?? currentState.read(binding.check),
|
|
4083
|
+
current: () => currentState.read(binding.check),
|
|
3930
4084
|
revision: () => revision,
|
|
3931
|
-
reset: () => reset,
|
|
4085
|
+
reset: (consumer) => consumer ? consumers.get(consumer)?.reset() ?? false : reset,
|
|
3932
4086
|
publish: () => {
|
|
3933
|
-
if (change)
|
|
3934
|
-
|
|
4087
|
+
if (change) {
|
|
4088
|
+
if (initialized) {
|
|
4089
|
+
baseline ??= currentState.read(() => void 0);
|
|
4090
|
+
notifications.add(change, currentState.ids(), nextIds);
|
|
4091
|
+
for (const pending of consumers.values()) pending.add(change, currentState.ids(), nextIds);
|
|
4092
|
+
revision++;
|
|
4093
|
+
}
|
|
4094
|
+
currentState.install(staged, nextIds, change.kind === "reset");
|
|
4095
|
+
}
|
|
3935
4096
|
initialized = true;
|
|
3936
4097
|
},
|
|
3937
|
-
emit: (call) => {
|
|
3938
|
-
|
|
4098
|
+
emit: (call, force) => {
|
|
4099
|
+
const net = notifications.current();
|
|
4100
|
+
for (const listener of [...observers]) call(listener, net);
|
|
4101
|
+
if (net || force) for (const listener of [...listeners]) call(listener, net);
|
|
4102
|
+
},
|
|
4103
|
+
finish: () => {
|
|
4104
|
+
baseline = void 0;
|
|
4105
|
+
notifications.clear();
|
|
4106
|
+
},
|
|
4107
|
+
observe: (listener) => {
|
|
4108
|
+
binding.check();
|
|
4109
|
+
observers.add(listener);
|
|
4110
|
+
return () => observers.delete(listener);
|
|
3939
4111
|
},
|
|
3940
4112
|
hasConsumers: () => consumers.size > 0,
|
|
3941
|
-
forEachConsumer: (run) => consumers.forEach(run),
|
|
3942
|
-
attachConsumer: (consumer) => consumers.
|
|
4113
|
+
forEachConsumer: (run) => consumers.forEach((_pending, consumer) => run(consumer)),
|
|
4114
|
+
attachConsumer: (consumer) => consumers.set(consumer, createCollectionChanges()),
|
|
3943
4115
|
detachConsumer: (consumer) => consumers.delete(consumer),
|
|
3944
4116
|
clear: () => {
|
|
3945
4117
|
staged.clear();
|
|
3946
|
-
nextIds =
|
|
4118
|
+
nextIds = currentState.ids();
|
|
3947
4119
|
change = void 0;
|
|
3948
4120
|
reset = false;
|
|
3949
4121
|
explicitOrder = void 0;
|
|
3950
4122
|
cleared = false;
|
|
3951
4123
|
},
|
|
3952
4124
|
release: () => {
|
|
3953
|
-
|
|
4125
|
+
currentState.release();
|
|
3954
4126
|
staged.clear();
|
|
3955
4127
|
listeners.clear();
|
|
3956
4128
|
consumers.clear();
|
|
4129
|
+
observers.clear();
|
|
4130
|
+
baseline = void 0;
|
|
4131
|
+
notifications.clear();
|
|
3957
4132
|
nextIds = Object.freeze([]);
|
|
3958
4133
|
change = void 0;
|
|
3959
4134
|
initialized = false;
|
|
@@ -3972,7 +4147,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3972
4147
|
//#endregion
|
|
3973
4148
|
//#region core/src/projection/output/value.ts
|
|
3974
4149
|
const unset = Symbol("projection-value-unset");
|
|
3975
|
-
/** Owns staged and
|
|
4150
|
+
/** Owns staged and current state for one scalar Projection output. */
|
|
3976
4151
|
const createValueOutput = (binding) => {
|
|
3977
4152
|
let value;
|
|
3978
4153
|
let staged = unset;
|
|
@@ -3982,7 +4157,10 @@ const createValueOutput = (binding) => {
|
|
|
3982
4157
|
let changed = false;
|
|
3983
4158
|
let reset = false;
|
|
3984
4159
|
const listeners = /* @__PURE__ */ new Set();
|
|
3985
|
-
const consumers = /* @__PURE__ */ new
|
|
4160
|
+
const consumers = /* @__PURE__ */ new Map();
|
|
4161
|
+
const observers = /* @__PURE__ */ new Set();
|
|
4162
|
+
let baseline = unset;
|
|
4163
|
+
let resetRevision = -1;
|
|
3986
4164
|
const begin = (active, initialize) => {
|
|
3987
4165
|
staged = unset;
|
|
3988
4166
|
next = value;
|
|
@@ -4009,7 +4187,12 @@ const createValueOutput = (binding) => {
|
|
|
4009
4187
|
next = value;
|
|
4010
4188
|
return false;
|
|
4011
4189
|
}
|
|
4012
|
-
|
|
4190
|
+
let candidate = staged;
|
|
4191
|
+
if (baseline !== unset && !Object.is(value, baseline)) {
|
|
4192
|
+
const equivalent = isEqual(baseline, candidate);
|
|
4193
|
+
require_transform.assertSynchronous(equivalent);
|
|
4194
|
+
if (equivalent) candidate = baseline;
|
|
4195
|
+
}
|
|
4013
4196
|
const equal = initialized ? isEqual(value, candidate) : false;
|
|
4014
4197
|
require_transform.assertSynchronous(equal);
|
|
4015
4198
|
changed = !equal;
|
|
@@ -4024,13 +4207,13 @@ const createValueOutput = (binding) => {
|
|
|
4024
4207
|
},
|
|
4025
4208
|
begin,
|
|
4026
4209
|
seal,
|
|
4027
|
-
context: (active) => {
|
|
4210
|
+
context: (active, consumer) => {
|
|
4028
4211
|
require_transform.assertScope(active);
|
|
4029
4212
|
return Object.freeze({
|
|
4030
4213
|
kind: "value",
|
|
4031
4214
|
value: next,
|
|
4032
4215
|
revision,
|
|
4033
|
-
reset,
|
|
4216
|
+
reset: consumer ? (consumers.get(consumer)?.revision ?? -1) < resetRevision : reset,
|
|
4034
4217
|
cause: binding.cause()
|
|
4035
4218
|
});
|
|
4036
4219
|
},
|
|
@@ -4039,19 +4222,48 @@ const createValueOutput = (binding) => {
|
|
|
4039
4222
|
return value;
|
|
4040
4223
|
},
|
|
4041
4224
|
revision: () => revision,
|
|
4042
|
-
reset: () => reset,
|
|
4043
|
-
|
|
4044
|
-
if (
|
|
4225
|
+
reset: (consumer) => consumer ? (consumers.get(consumer)?.revision ?? -1) < resetRevision : reset,
|
|
4226
|
+
acknowledge: (consumer) => {
|
|
4227
|
+
if (consumers.has(consumer)) consumers.set(consumer, {
|
|
4228
|
+
revision,
|
|
4229
|
+
value
|
|
4230
|
+
});
|
|
4231
|
+
},
|
|
4232
|
+
pending: (consumer) => {
|
|
4233
|
+
const previous = consumers.get(consumer);
|
|
4234
|
+
return !previous || previous.revision < resetRevision || !Object.is(previous.value, value);
|
|
4235
|
+
},
|
|
4236
|
+
baseline: () => baseline === unset ? value : baseline,
|
|
4237
|
+
publish: (forceReset) => {
|
|
4238
|
+
if (initialized && (changed || forceReset)) {
|
|
4239
|
+
if (baseline === unset) baseline = value;
|
|
4240
|
+
revision++;
|
|
4241
|
+
if (reset) resetRevision = revision;
|
|
4242
|
+
}
|
|
4045
4243
|
if (changed) value = next;
|
|
4046
4244
|
initialized = true;
|
|
4047
4245
|
},
|
|
4048
|
-
emit: (call) => {
|
|
4049
|
-
|
|
4050
|
-
|
|
4246
|
+
emit: (call, force) => {
|
|
4247
|
+
for (const listener of [...observers]) call(listener, void 0);
|
|
4248
|
+
if (force || baseline !== unset && !Object.is(baseline, value)) {
|
|
4249
|
+
require_transform.profile.materialized.notification();
|
|
4250
|
+
for (const listener of [...listeners]) call(listener, void 0);
|
|
4251
|
+
}
|
|
4252
|
+
},
|
|
4253
|
+
finish: () => {
|
|
4254
|
+
baseline = unset;
|
|
4255
|
+
},
|
|
4256
|
+
observe: (listener) => {
|
|
4257
|
+
binding.check();
|
|
4258
|
+
observers.add(listener);
|
|
4259
|
+
return () => observers.delete(listener);
|
|
4051
4260
|
},
|
|
4052
4261
|
hasConsumers: () => consumers.size > 0,
|
|
4053
|
-
forEachConsumer: (run) => consumers.forEach(run),
|
|
4054
|
-
attachConsumer: (consumer) => consumers.
|
|
4262
|
+
forEachConsumer: (run) => consumers.forEach((_revision, consumer) => run(consumer)),
|
|
4263
|
+
attachConsumer: (consumer) => consumers.set(consumer, {
|
|
4264
|
+
revision,
|
|
4265
|
+
value
|
|
4266
|
+
}),
|
|
4055
4267
|
detachConsumer: (consumer) => consumers.delete(consumer),
|
|
4056
4268
|
clear: () => {
|
|
4057
4269
|
staged = unset;
|
|
@@ -4062,6 +4274,8 @@ const createValueOutput = (binding) => {
|
|
|
4062
4274
|
release: () => {
|
|
4063
4275
|
listeners.clear();
|
|
4064
4276
|
consumers.clear();
|
|
4277
|
+
observers.clear();
|
|
4278
|
+
baseline = unset;
|
|
4065
4279
|
staged = unset;
|
|
4066
4280
|
value = next = void 0;
|
|
4067
4281
|
initialized = false;
|
|
@@ -4132,7 +4346,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
4132
4346
|
let active = true;
|
|
4133
4347
|
try {
|
|
4134
4348
|
const scopeActive = () => active;
|
|
4135
|
-
const sources = Object.freeze(dependencies.map((output) => output.context(scopeActive)));
|
|
4349
|
+
const sources = Object.freeze(dependencies.map((output) => output.context(scopeActive, processor)));
|
|
4136
4350
|
const outputs = begin(scopeActive, reset);
|
|
4137
4351
|
if (recreate && instance) {
|
|
4138
4352
|
instance.release?.();
|
|
@@ -4146,16 +4360,17 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
4146
4360
|
sources,
|
|
4147
4361
|
outputs
|
|
4148
4362
|
}));
|
|
4149
|
-
|
|
4363
|
+
const changed = seal(reset);
|
|
4364
|
+
dependencies.forEach((output) => output.acknowledge(processor));
|
|
4365
|
+
return changed;
|
|
4150
4366
|
} finally {
|
|
4151
4367
|
active = false;
|
|
4152
4368
|
}
|
|
4153
4369
|
};
|
|
4154
|
-
const
|
|
4370
|
+
const id = scheduler.nextId();
|
|
4155
4371
|
processor = {
|
|
4156
4372
|
kind: "processor",
|
|
4157
|
-
|
|
4158
|
-
name: definition.name ? `${definition.name} (${order})` : `projection-${order}`,
|
|
4373
|
+
name: definition.name ? `${definition.name} (${id})` : `projection-${id}`,
|
|
4159
4374
|
dependencies: Object.freeze([...dependencies]),
|
|
4160
4375
|
outputs: Object.freeze(bound.map((entry) => entry.state)),
|
|
4161
4376
|
fault: void 0,
|
|
@@ -4282,7 +4497,9 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
4282
4497
|
let initialized = false;
|
|
4283
4498
|
let value;
|
|
4284
4499
|
let selectedRevision = 0;
|
|
4500
|
+
let notified;
|
|
4285
4501
|
let sourceRevision = -1;
|
|
4502
|
+
let notificationRevision = -1;
|
|
4286
4503
|
let selection = Object.freeze({
|
|
4287
4504
|
keys: /* @__PURE__ */ new Set(),
|
|
4288
4505
|
all: true,
|
|
@@ -4318,8 +4535,9 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
4318
4535
|
});
|
|
4319
4536
|
const changed = !initialized || !equality(value, next);
|
|
4320
4537
|
if (changed) {
|
|
4538
|
+
const candidate = unsubscribeSource && equality(notified, next) ? notified : next;
|
|
4321
4539
|
if (initialized) selectedRevision++;
|
|
4322
|
-
value =
|
|
4540
|
+
value = candidate;
|
|
4323
4541
|
}
|
|
4324
4542
|
selection = nextSelection;
|
|
4325
4543
|
initialized = true;
|
|
@@ -4329,31 +4547,44 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
4329
4547
|
};
|
|
4330
4548
|
const ensureCurrent = () => {
|
|
4331
4549
|
const revision = source.revision();
|
|
4332
|
-
if (!initialized
|
|
4333
|
-
|
|
4334
|
-
};
|
|
4335
|
-
const onProjectionChange = (incoming) => {
|
|
4336
|
-
let change = incoming;
|
|
4337
|
-
if (!change && previousCollection) {
|
|
4550
|
+
if (!initialized) evaluate();
|
|
4551
|
+
else if (sourceRevision !== revision) {
|
|
4338
4552
|
const current = source.current();
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
if (!
|
|
4343
|
-
|
|
4344
|
-
|
|
4553
|
+
let affected = true;
|
|
4554
|
+
if (previousCollection && isMapLike(current) && !selection.all) {
|
|
4555
|
+
affected = selection.structure && !require_transform.iterableMatchesArray(current.keys(), [...previousCollection.keys()]);
|
|
4556
|
+
if (!affected) {
|
|
4557
|
+
for (const key of selection.keys) if (previousCollection.has(key) !== current.has(key) || !Object.is(previousCollection.get(key), current.get(key))) {
|
|
4558
|
+
affected = true;
|
|
4559
|
+
break;
|
|
4560
|
+
}
|
|
4345
4561
|
}
|
|
4346
|
-
}
|
|
4562
|
+
}
|
|
4563
|
+
if (affected) evaluate();
|
|
4564
|
+
else {
|
|
4565
|
+
sourceRevision = revision;
|
|
4566
|
+
updateCollectionSnapshot(current);
|
|
4567
|
+
}
|
|
4347
4568
|
}
|
|
4348
|
-
|
|
4569
|
+
return value;
|
|
4570
|
+
};
|
|
4571
|
+
const onProjectionChange = (incoming) => {
|
|
4572
|
+
if (sourceRevision === notificationRevision && sourceRevision !== source.revision() && incoming && !selectionAffects(selection, incoming)) {
|
|
4349
4573
|
sourceRevision = source.revision();
|
|
4350
4574
|
updateCollectionSnapshot();
|
|
4351
|
-
|
|
4575
|
+
} else ensureCurrent();
|
|
4576
|
+
notificationRevision = sourceRevision;
|
|
4577
|
+
if (!equality(notified, value)) {
|
|
4578
|
+
notified = value;
|
|
4579
|
+
notifyProjectionListeners(listeners, "Projection selector listeners failed.");
|
|
4352
4580
|
}
|
|
4353
|
-
if (evaluate()) notifyProjectionListeners(listeners, "Projection selector listeners failed.");
|
|
4354
4581
|
};
|
|
4355
4582
|
const installSource = () => {
|
|
4356
|
-
if (!unsubscribeSource)
|
|
4583
|
+
if (!unsubscribeSource) {
|
|
4584
|
+
notified = value;
|
|
4585
|
+
notificationRevision = sourceRevision;
|
|
4586
|
+
unsubscribeSource = source.observe(onProjectionChange);
|
|
4587
|
+
}
|
|
4357
4588
|
};
|
|
4358
4589
|
return Object.freeze({
|
|
4359
4590
|
current: ensureCurrent,
|
|
@@ -4735,13 +4966,12 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
|
|
|
4735
4966
|
active = false;
|
|
4736
4967
|
}
|
|
4737
4968
|
},
|
|
4738
|
-
publish: state.publish,
|
|
4969
|
+
publish: () => state.publish(pendingReset),
|
|
4739
4970
|
clear: () => {
|
|
4740
4971
|
const failed = boundary.fault !== void 0;
|
|
4741
4972
|
state.clear();
|
|
4742
4973
|
pendingReset = false;
|
|
4743
4974
|
pendingCause = void 0;
|
|
4744
|
-
contextCause = void 0;
|
|
4745
4975
|
recovering = false;
|
|
4746
4976
|
clearSource(failed);
|
|
4747
4977
|
},
|
|
@@ -4797,6 +5027,7 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
|
|
|
4797
5027
|
return {
|
|
4798
5028
|
producer: boundary,
|
|
4799
5029
|
output,
|
|
5030
|
+
baseline: state.baseline,
|
|
4800
5031
|
mark,
|
|
4801
5032
|
fail,
|
|
4802
5033
|
detach: (next) => {
|
|
@@ -4816,7 +5047,7 @@ const stageCollectionRead = (state, read, active, options) => {
|
|
|
4816
5047
|
}
|
|
4817
5048
|
evaluation.output.order(ids);
|
|
4818
5049
|
} else {
|
|
4819
|
-
const previous =
|
|
5050
|
+
const previous = evaluation.previous;
|
|
4820
5051
|
let currentIds;
|
|
4821
5052
|
const ids = () => currentIds ??= read.ids();
|
|
4822
5053
|
if (options.candidates) for (const key of options.candidates) if (read.has(key)) {
|
|
@@ -4897,7 +5128,6 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
4897
5128
|
state.clear();
|
|
4898
5129
|
pendingReset = false;
|
|
4899
5130
|
pendingCause = void 0;
|
|
4900
|
-
contextCause = void 0;
|
|
4901
5131
|
recovering = false;
|
|
4902
5132
|
fullScan = false;
|
|
4903
5133
|
orderMayChange = false;
|
|
@@ -4950,7 +5180,11 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
4950
5180
|
fullScan ||= metadata.fullScan ?? false;
|
|
4951
5181
|
orderMayChange ||= metadata.orderMayChange ?? false;
|
|
4952
5182
|
if (metadata.candidates) for (const key of metadata.candidates) candidates.add(key);
|
|
4953
|
-
if (boundary.fault)
|
|
5183
|
+
if (boundary.fault) {
|
|
5184
|
+
recovering = true;
|
|
5185
|
+
fullScan = true;
|
|
5186
|
+
orderMayChange = true;
|
|
5187
|
+
}
|
|
4954
5188
|
scheduler.capture(boundary);
|
|
4955
5189
|
};
|
|
4956
5190
|
const fail = (cause) => {
|
|
@@ -4961,6 +5195,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
4961
5195
|
return {
|
|
4962
5196
|
producer: boundary,
|
|
4963
5197
|
output,
|
|
5198
|
+
baseline: state.baseline,
|
|
4964
5199
|
mark,
|
|
4965
5200
|
fail,
|
|
4966
5201
|
detach: (next) => {
|
|
@@ -5510,13 +5745,12 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5510
5745
|
...boundary,
|
|
5511
5746
|
input: {
|
|
5512
5747
|
kind: "value",
|
|
5513
|
-
read: () => latest,
|
|
5514
5748
|
set(value) {
|
|
5515
5749
|
const next = scheduler.acceptInput(() => {
|
|
5516
5750
|
if (equal(latest, value)) return latest;
|
|
5517
5751
|
if (source.equality === Object.is) return value;
|
|
5518
|
-
const
|
|
5519
|
-
return !Object.is(latest,
|
|
5752
|
+
const baseline = boundary.baseline();
|
|
5753
|
+
return !Object.is(latest, baseline) && equal(baseline, value) ? baseline : value;
|
|
5520
5754
|
});
|
|
5521
5755
|
if (Object.is(latest, next)) return;
|
|
5522
5756
|
latest = next;
|
|
@@ -5527,7 +5761,6 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5527
5761
|
};
|
|
5528
5762
|
}
|
|
5529
5763
|
const values = createCollectionState(source.initial);
|
|
5530
|
-
let view;
|
|
5531
5764
|
const latest = {
|
|
5532
5765
|
get: values.get,
|
|
5533
5766
|
has: values.has,
|
|
@@ -5536,17 +5769,11 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5536
5769
|
const boundary = createCollectionBoundary(scheduler, "projection collection input", () => latest, Object.is, () => void 0);
|
|
5537
5770
|
boundary.detach(() => {
|
|
5538
5771
|
values.release();
|
|
5539
|
-
view = void 0;
|
|
5540
5772
|
});
|
|
5541
|
-
const check = () => {
|
|
5542
|
-
scheduler.assertActive();
|
|
5543
|
-
if (boundary.producer.disposed) throw new require_transform.ProjectionDisposedError();
|
|
5544
|
-
};
|
|
5545
5773
|
return {
|
|
5546
5774
|
...boundary,
|
|
5547
5775
|
input: {
|
|
5548
5776
|
kind: "collection",
|
|
5549
|
-
read: () => view ??= require_transform.collectionView(values.read(check)),
|
|
5550
5777
|
update(run) {
|
|
5551
5778
|
const accepted = scheduler.acceptInput(() => {
|
|
5552
5779
|
const staged = /* @__PURE__ */ new Map();
|
|
@@ -5593,7 +5820,7 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5593
5820
|
} finally {
|
|
5594
5821
|
active = false;
|
|
5595
5822
|
}
|
|
5596
|
-
const
|
|
5823
|
+
const baseline = source.equality === Object.is ? void 0 : boundary.baseline();
|
|
5597
5824
|
for (const [key, entry] of staged) {
|
|
5598
5825
|
const existed = values.has(key);
|
|
5599
5826
|
if (!entry.present) {
|
|
@@ -5603,8 +5830,8 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5603
5830
|
const previous = values.get(key);
|
|
5604
5831
|
let next = entry.value;
|
|
5605
5832
|
if (existed && equal(previous, next)) next = previous;
|
|
5606
|
-
else if (
|
|
5607
|
-
const before =
|
|
5833
|
+
else if (baseline?.has(key)) {
|
|
5834
|
+
const before = baseline.get(key);
|
|
5608
5835
|
if ((!existed || !Object.is(previous, before)) && equal(before, next)) next = before;
|
|
5609
5836
|
}
|
|
5610
5837
|
if (existed && Object.is(previous, next)) staged.delete(key);
|
|
@@ -5629,7 +5856,6 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5629
5856
|
});
|
|
5630
5857
|
if (!accepted.staged.size && !accepted.structural) return;
|
|
5631
5858
|
values.install(accepted.staged, accepted.order, false);
|
|
5632
|
-
view = void 0;
|
|
5633
5859
|
boundary.mark({
|
|
5634
5860
|
candidates: accepted.staged.keys(),
|
|
5635
5861
|
orderMayChange: accepted.structural
|
|
@@ -5682,6 +5908,11 @@ const createProjectionItems = (source, checkOwner) => {
|
|
|
5682
5908
|
const waiting = /* @__PURE__ */ new Map();
|
|
5683
5909
|
let keysValue = Object.freeze([...source.current().keys()]);
|
|
5684
5910
|
let keysRevision = 0;
|
|
5911
|
+
let notifiedKeys = keysValue;
|
|
5912
|
+
let settledRevision = source.revision();
|
|
5913
|
+
let keysSourceRevision = settledRevision;
|
|
5914
|
+
const pendingKeys = /* @__PURE__ */ new Set();
|
|
5915
|
+
const changedStates = /* @__PURE__ */ new Set();
|
|
5685
5916
|
const keyListeners = /* @__PURE__ */ new Set();
|
|
5686
5917
|
const check = () => {
|
|
5687
5918
|
if (disposed) throw new require_transform.ProjectionDisposedError();
|
|
@@ -5717,7 +5948,7 @@ const createProjectionItems = (source, checkOwner) => {
|
|
|
5717
5948
|
}
|
|
5718
5949
|
state.value = value;
|
|
5719
5950
|
state.revision++;
|
|
5720
|
-
|
|
5951
|
+
changedStates.add(state);
|
|
5721
5952
|
};
|
|
5722
5953
|
const attachActive = (state, value) => {
|
|
5723
5954
|
if (state.ended) return;
|
|
@@ -5760,10 +5991,12 @@ const createProjectionItems = (source, checkOwner) => {
|
|
|
5760
5991
|
if (extra) for (const state of extra) publishState(state, value);
|
|
5761
5992
|
};
|
|
5762
5993
|
const updateKeys = (current) => {
|
|
5994
|
+
const revision = source.revision();
|
|
5995
|
+
if (keysSourceRevision === revision) return;
|
|
5996
|
+
keysSourceRevision = revision;
|
|
5763
5997
|
if (require_transform.iterableMatchesArray(current.keys(), keysValue)) return;
|
|
5764
|
-
keysValue = Object.freeze([...current.keys()]);
|
|
5998
|
+
keysValue = require_transform.iterableMatchesArray(current.keys(), notifiedKeys) ? notifiedKeys : Object.freeze([...current.keys()]);
|
|
5765
5999
|
keysRevision++;
|
|
5766
|
-
notify(keyListeners);
|
|
5767
6000
|
};
|
|
5768
6001
|
const createItem = (key, initial) => {
|
|
5769
6002
|
const state = {
|
|
@@ -5772,12 +6005,15 @@ const createProjectionItems = (source, checkOwner) => {
|
|
|
5772
6005
|
tracked: initial !== void 0,
|
|
5773
6006
|
ended: false,
|
|
5774
6007
|
value: initial?.value,
|
|
6008
|
+
notified: initial?.value,
|
|
5775
6009
|
revision: 0
|
|
5776
6010
|
};
|
|
5777
6011
|
const ensureObserved = () => {
|
|
5778
|
-
if (state.ended
|
|
6012
|
+
if (state.ended) return;
|
|
5779
6013
|
const current = source.current();
|
|
5780
|
-
if (
|
|
6014
|
+
if (source.revision() !== settledRevision) pendingKeys.add(key);
|
|
6015
|
+
if (!state.tracked && current.has(key)) attachActive(state, current.get(key));
|
|
6016
|
+
else if (state.tracked) publishState(state, current.get(key));
|
|
5781
6017
|
};
|
|
5782
6018
|
state.readable = Object.freeze({
|
|
5783
6019
|
current: () => {
|
|
@@ -5812,25 +6048,45 @@ const createProjectionItems = (source, checkOwner) => {
|
|
|
5812
6048
|
const onChange = (change) => {
|
|
5813
6049
|
check();
|
|
5814
6050
|
const current = source.current();
|
|
5815
|
-
if (
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
6051
|
+
if (change?.kind === "reset") reconcile(current);
|
|
6052
|
+
else {
|
|
6053
|
+
if (change) {
|
|
6054
|
+
for (const entry of change.removed) terminateKey(entry.key);
|
|
6055
|
+
for (const entry of change.added) activateWaiting(entry.key, entry.after);
|
|
6056
|
+
for (const entry of change.updated) updateKey(entry.key, entry.after);
|
|
6057
|
+
if (change.added.length || change.removed.length || change.order) updateKeys(current);
|
|
6058
|
+
}
|
|
6059
|
+
for (const key of pendingKeys) if (!current.has(key)) terminateKey(key);
|
|
6060
|
+
else {
|
|
6061
|
+
updateKey(key, current.get(key));
|
|
6062
|
+
activateWaiting(key, current.get(key));
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
pendingKeys.clear();
|
|
6066
|
+
settledRevision = source.revision();
|
|
6067
|
+
for (const state of changedStates) if (!Object.is(state.notified, state.value)) {
|
|
6068
|
+
state.notified = state.value;
|
|
6069
|
+
notify(state.listeners);
|
|
6070
|
+
}
|
|
6071
|
+
changedStates.clear();
|
|
6072
|
+
if (keysValue !== notifiedKeys) updateKeys(current);
|
|
6073
|
+
else keysSourceRevision = settledRevision;
|
|
6074
|
+
if (keysValue !== notifiedKeys) {
|
|
6075
|
+
notifiedKeys = keysValue;
|
|
6076
|
+
notify(keyListeners);
|
|
5819
6077
|
}
|
|
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
6078
|
flushNotifications();
|
|
5825
6079
|
};
|
|
5826
|
-
const stop = source.
|
|
6080
|
+
const stop = source.observe(onChange);
|
|
5827
6081
|
const keys = Object.freeze({
|
|
5828
6082
|
current: () => {
|
|
5829
6083
|
check();
|
|
6084
|
+
updateKeys(source.current());
|
|
5830
6085
|
return keysValue;
|
|
5831
6086
|
},
|
|
5832
6087
|
revision: () => {
|
|
5833
6088
|
check();
|
|
6089
|
+
updateKeys(source.current());
|
|
5834
6090
|
return keysRevision;
|
|
5835
6091
|
},
|
|
5836
6092
|
subscribe: (listener) => {
|
|
@@ -5844,11 +6100,12 @@ const createProjectionItems = (source, checkOwner) => {
|
|
|
5844
6100
|
keys,
|
|
5845
6101
|
get: (key) => {
|
|
5846
6102
|
check();
|
|
6103
|
+
const current = source.current();
|
|
6104
|
+
if (source.revision() !== settledRevision) pendingKeys.add(key);
|
|
5847
6105
|
const canonical = active.get(key);
|
|
5848
6106
|
if (canonical) return canonical.readable;
|
|
5849
6107
|
const pending = waiting.get(key)?.values().next().value;
|
|
5850
6108
|
if (pending) return pending.readable;
|
|
5851
|
-
const current = source.current();
|
|
5852
6109
|
if (current.has(key)) {
|
|
5853
6110
|
const state = createItem(key, {
|
|
5854
6111
|
present: true,
|
|
@@ -5874,6 +6131,8 @@ const createProjectionItems = (source, checkOwner) => {
|
|
|
5874
6131
|
followers.clear();
|
|
5875
6132
|
waiting.clear();
|
|
5876
6133
|
notifications.length = 0;
|
|
6134
|
+
pendingKeys.clear();
|
|
6135
|
+
changedStates.clear();
|
|
5877
6136
|
}
|
|
5878
6137
|
};
|
|
5879
6138
|
};
|
|
@@ -5934,6 +6193,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5934
6193
|
} else {
|
|
5935
6194
|
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
6195
|
const dependencies = definition.dependencies.map((dependency) => resolveOutput(dependency, requester));
|
|
6196
|
+
dependencies.forEach((output) => scheduler.ensureCurrent(output));
|
|
5937
6197
|
const processor = createProcessor(scheduler, definition, Object.freeze(dependencies));
|
|
5938
6198
|
result = Object.freeze({
|
|
5939
6199
|
record: processor,
|
|
@@ -5957,6 +6217,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5957
6217
|
if (output.owner.fault) throw output.owner.fault;
|
|
5958
6218
|
};
|
|
5959
6219
|
const publicCurrent = (output) => {
|
|
6220
|
+
scheduler.ensureCurrent(output);
|
|
5960
6221
|
assertOutput(output);
|
|
5961
6222
|
const current = output.current();
|
|
5962
6223
|
const revision = output.revision();
|
|
@@ -5988,10 +6249,12 @@ const createProjectionRuntime = (options) => {
|
|
|
5988
6249
|
kind: output.kind,
|
|
5989
6250
|
current: () => readProjection(projection, requester),
|
|
5990
6251
|
revision: () => {
|
|
6252
|
+
scheduler.ensureCurrent(output);
|
|
5991
6253
|
assertOutput(output);
|
|
5992
6254
|
return output.revision();
|
|
5993
6255
|
},
|
|
5994
|
-
subscribe: output.subscribe
|
|
6256
|
+
subscribe: output.subscribe,
|
|
6257
|
+
observe: output.observe
|
|
5995
6258
|
});
|
|
5996
6259
|
};
|
|
5997
6260
|
const projectionItems = (projection, requester) => {
|
|
@@ -6002,6 +6265,7 @@ const createProjectionRuntime = (options) => {
|
|
|
6002
6265
|
if (existing) return existing.items;
|
|
6003
6266
|
const controller = createProjectionItems(readableSource(projection, requester), () => {
|
|
6004
6267
|
if (requester && !requester.active) throw new require_transform.ProjectionDisposedError();
|
|
6268
|
+
scheduler.ensureCurrent(output);
|
|
6005
6269
|
assertOutput(output);
|
|
6006
6270
|
});
|
|
6007
6271
|
registry.set(output, controller);
|
|
@@ -6036,18 +6300,7 @@ const createProjectionRuntime = (options) => {
|
|
|
6036
6300
|
function update(target, valueOrRun) {
|
|
6037
6301
|
updateInput(target, valueOrRun);
|
|
6038
6302
|
}
|
|
6039
|
-
const batch = (run, options
|
|
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);
|
|
6303
|
+
const batch = (run, options) => scheduler.batch(run, options);
|
|
6051
6304
|
const disposeScope = (state) => {
|
|
6052
6305
|
if (!state.active) return;
|
|
6053
6306
|
scheduler.assertIdle();
|
|
@@ -6139,7 +6392,7 @@ const createProjectionRuntime = (options) => {
|
|
|
6139
6392
|
}
|
|
6140
6393
|
function scopeBatch(run, options) {
|
|
6141
6394
|
assertActive();
|
|
6142
|
-
return batch(run, options
|
|
6395
|
+
return batch(run, options);
|
|
6143
6396
|
}
|
|
6144
6397
|
return Object.freeze({
|
|
6145
6398
|
own,
|