doxum 0.1.25 → 0.1.27
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 +32 -2
- package/dist/advanced.cjs +5 -21
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +24 -25
- package/dist/advanced.d.ts +24 -25
- package/dist/advanced.js +5 -22
- package/dist/advanced.js.map +1 -1
- package/dist/{contract-Dawq6LKS.d.ts → contract-CNR6BS75.d.cts} +174 -47
- package/dist/{contract-DB8XvNt0.d.cts → contract-COgxO5sv.d.ts} +174 -47
- package/dist/{definition-DT1NpMul.d.ts → definition-CDOuvb5C.d.cts} +13 -12
- package/dist/{definition-CHVS-RDY.d.cts → definition-xgk3YNBp.d.ts} +13 -12
- package/dist/index.cjs +366 -194
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -22
- package/dist/index.d.ts +45 -22
- package/dist/index.js +363 -191
- package/dist/index.js.map +1 -1
- package/dist/local-sync.d.cts +1 -1
- package/dist/local-sync.d.ts +1 -1
- package/dist/{scheduler-D4VthaaK.cjs → scheduler-k1ZGR1PG.cjs} +136 -1
- package/dist/scheduler-k1ZGR1PG.cjs.map +1 -0
- package/dist/{scheduler--137L3j-.js → scheduler-vzf3OcjT.js} +95 -2
- package/dist/scheduler-vzf3OcjT.js.map +1 -0
- package/package.json +3 -2
- package/skills/doxum-runtime/SKILL.md +15 -5
- package/skills/doxum-runtime/references/api.en.md +40 -19
- package/skills/doxum-runtime/references/api.zh-CN.md +38 -17
- package/skills/doxum-runtime/references/projections.en.md +35 -3
- package/skills/doxum-runtime/references/projections.zh-CN.md +34 -2
- package/dist/scheduler--137L3j-.js.map +0 -1
- package/dist/scheduler-D4VthaaK.cjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ProjectionError, c as
|
|
1
|
+
import { S as profile, _ as isProjection, a as ProjectionError, b as producerOf, c as createCollectionChange, d as snapshotDependencyValue, f as collectionView, g as defineSource, h as defineProcessor, i as ProjectionDisposedError, l as diffCollection, m as snapshotCollectionView, n as assertSynchronous, o as collectionChange, p as mapRead, r as createScheduler, s as collectionHasStructuralChange, t as assertScope, u as compileProjectionDependencies, v as outputDefinitionOf, x as projectionRef, y as ownProjections } from "./scheduler-vzf3OcjT.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-B5vdPohL.js";
|
|
4
4
|
//#region core/src/schema/model.ts
|
|
@@ -9,7 +9,7 @@ const field = (validator) => node$2({
|
|
|
9
9
|
kind: "field",
|
|
10
10
|
...validator ? { validator } : {}
|
|
11
11
|
});
|
|
12
|
-
|
|
12
|
+
function optional(value) {
|
|
13
13
|
const concrete = schemaNodeOf(value);
|
|
14
14
|
if (![
|
|
15
15
|
"field",
|
|
@@ -22,7 +22,7 @@ const optional = (value) => {
|
|
|
22
22
|
...concrete,
|
|
23
23
|
optional: true
|
|
24
24
|
});
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
const object = (shape) => node$2({
|
|
27
27
|
kind: "object",
|
|
28
28
|
shape: Object.freeze(Object.fromEntries(Object.entries(shape).map(([key, value]) => [key, schemaNodeOf(value)])))
|
|
@@ -2734,14 +2734,9 @@ const isExternalSource = (source) => {
|
|
|
2734
2734
|
return (candidate.kind === "value" || candidate.kind === "collection") && typeof candidate.current === "function" && typeof candidate.revision === "function" && typeof candidate.subscribe === "function";
|
|
2735
2735
|
};
|
|
2736
2736
|
//#endregion
|
|
2737
|
-
//#region core/src/projection/derive.ts
|
|
2738
|
-
const sourceValue = (source) => source.kind === "value" ? source.value : snapshotCollectionView(source.read);
|
|
2737
|
+
//#region core/src/projection/derive/keyed.ts
|
|
2739
2738
|
const outputKind = (projection) => {
|
|
2740
|
-
|
|
2741
|
-
const definition = ref.producer;
|
|
2742
|
-
const output = definition.kind === "source" ? definition.output : definition.outputs[ref.output];
|
|
2743
|
-
if (!output) throw new TypeError("Projection output does not exist.");
|
|
2744
|
-
return output.kind;
|
|
2739
|
+
return outputDefinitionOf(projection).kind;
|
|
2745
2740
|
};
|
|
2746
2741
|
const assertKeyedProjection = (projection, role) => {
|
|
2747
2742
|
if (outputKind(projection) !== "collection") throw new TypeError(`${role} must be a keyed collection projection.`);
|
|
@@ -2767,41 +2762,29 @@ const bindKey = (index, outputKey, sourceKey) => {
|
|
|
2767
2762
|
else index.reverse.set(sourceKey, new Set([outputKey]));
|
|
2768
2763
|
};
|
|
2769
2764
|
const unbindKey = (indexes, outputKey) => {
|
|
2770
|
-
for (const index of indexes) bindKey(index, outputKey, void 0);
|
|
2765
|
+
for (const index of indexes) if (index) bindKey(index, outputKey, void 0);
|
|
2771
2766
|
};
|
|
2772
2767
|
const createBindingIndex = () => ({
|
|
2773
2768
|
forward: /* @__PURE__ */ new Map(),
|
|
2774
2769
|
reverse: /* @__PURE__ */ new Map()
|
|
2775
2770
|
});
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
const
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
const next = compute(Object.freeze(values));
|
|
2793
|
-
assertSynchronous(next);
|
|
2794
|
-
output.output.set(next);
|
|
2795
|
-
} })
|
|
2796
|
-
});
|
|
2797
|
-
return projection;
|
|
2798
|
-
}
|
|
2799
|
-
function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
2771
|
+
const emptyKeyedDependencies = Object.freeze(Object.create(null));
|
|
2772
|
+
const absent = Symbol("keyed-transform-absent");
|
|
2773
|
+
const compileOrderedKeys = (value, role) => {
|
|
2774
|
+
if (!Array.isArray(value)) throw new TypeError(`${role} must be an array of string keys.`);
|
|
2775
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2776
|
+
for (const key of value) {
|
|
2777
|
+
if (typeof key !== "string") throw new TypeError(`${role} must contain only string keys.`);
|
|
2778
|
+
if (seen.has(key)) throw new TypeError(`${role} must not contain duplicate keys.`);
|
|
2779
|
+
seen.add(key);
|
|
2780
|
+
}
|
|
2781
|
+
return {
|
|
2782
|
+
keys: value,
|
|
2783
|
+
requested: seen
|
|
2784
|
+
};
|
|
2785
|
+
};
|
|
2786
|
+
const createKeyedTransform = (source, dependencySpecs, select, equality, name) => {
|
|
2800
2787
|
assertKeyedProjection(source, "Keyed derive source");
|
|
2801
|
-
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
2802
|
-
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
2803
|
-
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
2804
|
-
if (typeof select !== "function") throw new TypeError("Keyed derive requires a selector.");
|
|
2805
2788
|
const dependencies = [source];
|
|
2806
2789
|
const dependencyNames = [];
|
|
2807
2790
|
const keyed = [];
|
|
@@ -2832,81 +2815,91 @@ function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybe
|
|
|
2832
2815
|
}],
|
|
2833
2816
|
create: () => {
|
|
2834
2817
|
const bindings = keyed.map((dependency) => dependency ? createBindingIndex() : void 0);
|
|
2835
|
-
const bindingIndexes = bindings.filter((index) => index !== void 0);
|
|
2836
2818
|
const globalValues = keyed.map(() => void 0);
|
|
2837
|
-
|
|
2819
|
+
const revisions = [];
|
|
2838
2820
|
const clearBindings = () => {
|
|
2839
2821
|
for (const index of bindings) {
|
|
2840
2822
|
index?.forward.clear();
|
|
2841
2823
|
index?.reverse.clear();
|
|
2842
2824
|
}
|
|
2843
2825
|
};
|
|
2826
|
+
const rememberRevisions = (sources) => {
|
|
2827
|
+
revisions.length = sources.length;
|
|
2828
|
+
for (let index = 0; index < sources.length; index++) revisions[index] = sources[index].revision;
|
|
2829
|
+
};
|
|
2844
2830
|
return {
|
|
2845
2831
|
evaluate: (evaluation) => {
|
|
2846
2832
|
const driver = evaluation.sources[0];
|
|
2847
2833
|
const output = evaluation.outputs[0];
|
|
2848
2834
|
if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("Keyed derive requires collection input and output.");
|
|
2849
|
-
const currentRevisions = evaluation.sources.map((current) => current.revision);
|
|
2850
2835
|
for (let index = 0; index < keyed.length; index++) {
|
|
2851
2836
|
if (keyed[index]) continue;
|
|
2852
|
-
|
|
2837
|
+
const dependencySource = evaluation.sources[index + 1];
|
|
2838
|
+
if (evaluation.reset || revisions[index + 1] !== dependencySource.revision) globalValues[index] = snapshotDependencyValue(dependencySource);
|
|
2853
2839
|
}
|
|
2854
2840
|
const project = (key) => {
|
|
2855
2841
|
if (!driver.read.has(key)) return;
|
|
2856
2842
|
const value = driver.read.get(key);
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
const
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2843
|
+
let dependencyValues = emptyKeyedDependencies;
|
|
2844
|
+
if (keyed.length) {
|
|
2845
|
+
const values = Object.create(null);
|
|
2846
|
+
for (let index = 0; index < keyed.length; index++) {
|
|
2847
|
+
const dependency = keyed[index];
|
|
2848
|
+
if (!dependency) {
|
|
2849
|
+
values[dependencyNames[index]] = globalValues[index];
|
|
2850
|
+
continue;
|
|
2851
|
+
}
|
|
2852
|
+
const sourceContext = evaluation.sources[index + 1];
|
|
2853
|
+
if (sourceContext.kind !== "collection") throw new TypeError("Dynamic keyed dependency resolved to a non-collection source.");
|
|
2854
|
+
const selectedKey = dependency.key(value, key);
|
|
2855
|
+
assertSynchronous(selectedKey);
|
|
2856
|
+
if (selectedKey !== void 0 && typeof selectedKey !== "string") throw new TypeError("Dynamic keyed dependency keys must be strings or undefined.");
|
|
2857
|
+
bindKey(bindings[index], key, selectedKey);
|
|
2858
|
+
values[dependencyNames[index]] = selectedKey === void 0 ? void 0 : sourceContext.read.get(selectedKey);
|
|
2863
2859
|
}
|
|
2864
|
-
|
|
2865
|
-
if (sourceContext.kind !== "collection") throw new TypeError("Dynamic keyed dependency resolved to a non-collection source.");
|
|
2866
|
-
const selectedKey = dependency.key(value, key);
|
|
2867
|
-
assertSynchronous(selectedKey);
|
|
2868
|
-
if (selectedKey !== void 0 && typeof selectedKey !== "string") throw new TypeError("Dynamic keyed dependency keys must be strings or undefined.");
|
|
2869
|
-
bindKey(bindings[index], key, selectedKey);
|
|
2870
|
-
dependencyValues[dependencyNames[index]] = selectedKey === void 0 ? void 0 : sourceContext.read.get(selectedKey);
|
|
2860
|
+
dependencyValues = Object.freeze(values);
|
|
2871
2861
|
}
|
|
2872
|
-
const next =
|
|
2862
|
+
const next = select(value, key, dependencyValues);
|
|
2873
2863
|
assertSynchronous(next);
|
|
2874
|
-
output.output.
|
|
2864
|
+
if (next === absent) output.output.remove(key);
|
|
2865
|
+
else output.output.set(key, next);
|
|
2875
2866
|
};
|
|
2876
2867
|
if (evaluation.reset) {
|
|
2877
2868
|
clearBindings();
|
|
2878
2869
|
const ids = driver.read.ids();
|
|
2879
2870
|
for (const key of ids) project(key);
|
|
2880
|
-
output.output.order(ids);
|
|
2881
|
-
|
|
2871
|
+
output.output.order(ids.filter((key) => output.next.has(key)));
|
|
2872
|
+
rememberRevisions(evaluation.sources);
|
|
2882
2873
|
return;
|
|
2883
2874
|
}
|
|
2884
2875
|
const dirty = /* @__PURE__ */ new Set();
|
|
2885
2876
|
let all = false;
|
|
2886
2877
|
let updateOrder = false;
|
|
2887
|
-
|
|
2878
|
+
let membershipChanged = false;
|
|
2879
|
+
if (revisions[0] !== driver.revision) {
|
|
2888
2880
|
const change = driver.change;
|
|
2889
2881
|
if (!change || change.kind === "reset") {
|
|
2890
2882
|
all = true;
|
|
2891
2883
|
updateOrder = true;
|
|
2892
2884
|
} else {
|
|
2893
2885
|
for (const entry of change.removed) {
|
|
2886
|
+
membershipChanged ||= output.previous.has(entry.key);
|
|
2894
2887
|
output.output.remove(entry.key);
|
|
2895
|
-
unbindKey(
|
|
2888
|
+
unbindKey(bindings, entry.key);
|
|
2896
2889
|
dirty.delete(entry.key);
|
|
2897
2890
|
}
|
|
2898
2891
|
for (const entry of change.added) dirty.add(entry.key);
|
|
2899
2892
|
for (const entry of change.updated) dirty.add(entry.key);
|
|
2900
|
-
updateOrder = Boolean(change.
|
|
2893
|
+
updateOrder = Boolean(change.order);
|
|
2901
2894
|
}
|
|
2902
2895
|
}
|
|
2903
2896
|
for (let index = 0; index < keyed.length; index++) {
|
|
2904
|
-
|
|
2897
|
+
const sourceContext = evaluation.sources[index + 1];
|
|
2898
|
+
if (revisions[index + 1] === sourceContext.revision) continue;
|
|
2905
2899
|
if (!keyed[index]) {
|
|
2906
2900
|
all = true;
|
|
2907
2901
|
continue;
|
|
2908
2902
|
}
|
|
2909
|
-
const sourceContext = evaluation.sources[index + 1];
|
|
2910
2903
|
if (sourceContext.kind !== "collection" || !sourceContext.change) {
|
|
2911
2904
|
all = true;
|
|
2912
2905
|
continue;
|
|
@@ -2916,86 +2909,274 @@ function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybe
|
|
|
2916
2909
|
continue;
|
|
2917
2910
|
}
|
|
2918
2911
|
const reverse = bindings[index].reverse;
|
|
2919
|
-
for (const
|
|
2920
|
-
for (const transition of sourceContext.change.updated) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
|
|
2921
|
-
for (const transition of sourceContext.change.removed) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
|
|
2912
|
+
for (const sourceKey of collectionChange.keys(sourceContext.change)) for (const outputKey of reverse.get(sourceKey) ?? []) dirty.add(outputKey);
|
|
2922
2913
|
}
|
|
2923
2914
|
if (all) for (const key of driver.read.ids()) dirty.add(key);
|
|
2924
|
-
for (const key of dirty)
|
|
2925
|
-
|
|
2926
|
-
|
|
2915
|
+
for (const key of dirty) {
|
|
2916
|
+
if (!driver.read.has(key)) continue;
|
|
2917
|
+
const beforePresent = output.previous.has(key);
|
|
2918
|
+
project(key);
|
|
2919
|
+
membershipChanged ||= beforePresent !== output.next.has(key);
|
|
2920
|
+
}
|
|
2921
|
+
if (updateOrder || membershipChanged) output.output.order(driver.read.ids().filter((key) => output.next.has(key)));
|
|
2922
|
+
rememberRevisions(evaluation.sources);
|
|
2927
2923
|
},
|
|
2928
2924
|
release: () => {
|
|
2929
2925
|
clearBindings();
|
|
2930
|
-
revisions =
|
|
2926
|
+
revisions.length = 0;
|
|
2931
2927
|
}
|
|
2932
2928
|
};
|
|
2933
2929
|
},
|
|
2934
|
-
name
|
|
2930
|
+
name
|
|
2935
2931
|
});
|
|
2936
2932
|
return projection;
|
|
2933
|
+
};
|
|
2934
|
+
function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
2935
|
+
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
2936
|
+
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
2937
|
+
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
2938
|
+
if (typeof select !== "function") throw new TypeError("Keyed derive requires a selector.");
|
|
2939
|
+
return createKeyedTransform(source, dependencySpecs, (value, key, dependencies) => dependencySpecs === void 0 ? select(value, key) : select(value, key, dependencies), equality, "derive.keyed");
|
|
2937
2940
|
}
|
|
2938
|
-
const
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
}
|
|
2946
|
-
|
|
2947
|
-
const
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
kind: "incremental",
|
|
2954
|
-
added: Object.freeze([...input.added]),
|
|
2955
|
-
updated: Object.freeze([...input.updated]),
|
|
2956
|
-
removed: Object.freeze([...input.removed]),
|
|
2957
|
-
...orderChanged ? { order: Object.freeze({
|
|
2958
|
-
before: Object.freeze([...input.beforeOrder]),
|
|
2959
|
-
after: Object.freeze([...input.afterOrder])
|
|
2960
|
-
}) } : {}
|
|
2941
|
+
const createKeyedKeys = (source) => {
|
|
2942
|
+
assertKeyedProjection(source, "Keyed keys source");
|
|
2943
|
+
const [projection] = defineProcessor({
|
|
2944
|
+
dependencies: [source],
|
|
2945
|
+
outputs: [{
|
|
2946
|
+
kind: "value",
|
|
2947
|
+
equality: Object.is
|
|
2948
|
+
}],
|
|
2949
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
2950
|
+
const driver = evaluation.sources[0];
|
|
2951
|
+
const output = evaluation.outputs[0];
|
|
2952
|
+
if (driver.kind !== "collection" || output.kind !== "value") throw new Error("derive.keyed.keys requires collection input and value output.");
|
|
2953
|
+
if (evaluation.reset || !driver.change || collectionHasStructuralChange(driver.change)) output.output.set(driver.read.ids());
|
|
2954
|
+
} }),
|
|
2955
|
+
name: "derive.keyed.keys"
|
|
2961
2956
|
});
|
|
2957
|
+
return projection;
|
|
2962
2958
|
};
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
const
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2959
|
+
const createKeyedValues = (source) => {
|
|
2960
|
+
assertKeyedProjection(source, "Keyed values source");
|
|
2961
|
+
const [projection] = defineProcessor({
|
|
2962
|
+
dependencies: [source],
|
|
2963
|
+
outputs: [{
|
|
2964
|
+
kind: "value",
|
|
2965
|
+
equality: Object.is
|
|
2966
|
+
}],
|
|
2967
|
+
create: () => {
|
|
2968
|
+
let indexes = /* @__PURE__ */ new Map();
|
|
2969
|
+
const rebuild = (driver) => {
|
|
2970
|
+
const nextIndexes = /* @__PURE__ */ new Map();
|
|
2971
|
+
const ids = driver.read.ids();
|
|
2972
|
+
const values = Object.freeze(ids.map((key, index) => {
|
|
2973
|
+
nextIndexes.set(key, index);
|
|
2974
|
+
return driver.read.get(key);
|
|
2975
|
+
}));
|
|
2976
|
+
indexes = nextIndexes;
|
|
2977
|
+
return values;
|
|
2978
|
+
};
|
|
2979
|
+
const rebuildIncremental = (driver, change, previous) => {
|
|
2980
|
+
const changedValues = /* @__PURE__ */ new Map();
|
|
2981
|
+
for (const entry of change.added) changedValues.set(entry.key, entry.after);
|
|
2982
|
+
for (const entry of change.updated) changedValues.set(entry.key, entry.after);
|
|
2983
|
+
const nextIndexes = /* @__PURE__ */ new Map();
|
|
2984
|
+
const values = Object.freeze(driver.read.ids().map((key, index) => {
|
|
2985
|
+
nextIndexes.set(key, index);
|
|
2986
|
+
if (changedValues.has(key)) return changedValues.get(key);
|
|
2987
|
+
const previousIndex = indexes.get(key);
|
|
2988
|
+
return previousIndex === void 0 ? driver.read.get(key) : previous[previousIndex];
|
|
2989
|
+
}));
|
|
2990
|
+
indexes = nextIndexes;
|
|
2991
|
+
return values;
|
|
2992
|
+
};
|
|
2993
|
+
return {
|
|
2994
|
+
evaluate: (evaluation) => {
|
|
2995
|
+
const driver = evaluation.sources[0];
|
|
2996
|
+
const output = evaluation.outputs[0];
|
|
2997
|
+
if (driver.kind !== "collection" || output.kind !== "value") throw new Error("derive.keyed.values requires collection input and value output.");
|
|
2998
|
+
const change = driver.change;
|
|
2999
|
+
if (evaluation.reset || !change || change.kind === "reset") {
|
|
3000
|
+
output.output.set(rebuild(driver));
|
|
3001
|
+
return;
|
|
3002
|
+
}
|
|
3003
|
+
const previous = output.previous;
|
|
3004
|
+
if (!previous) {
|
|
3005
|
+
output.output.set(rebuild(driver));
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
if (collectionHasStructuralChange(change)) {
|
|
3009
|
+
output.output.set(rebuildIncremental(driver, change, previous));
|
|
3010
|
+
return;
|
|
3011
|
+
}
|
|
3012
|
+
if (!change.updated.length) return;
|
|
3013
|
+
const next = [...previous];
|
|
3014
|
+
for (const entry of change.updated) {
|
|
3015
|
+
const index = indexes.get(entry.key);
|
|
3016
|
+
if (index === void 0) {
|
|
3017
|
+
output.output.set(rebuild(driver));
|
|
3018
|
+
return;
|
|
3019
|
+
}
|
|
3020
|
+
next[index] = entry.after;
|
|
3021
|
+
}
|
|
3022
|
+
output.output.set(Object.freeze(next));
|
|
3023
|
+
},
|
|
3024
|
+
release: () => indexes.clear()
|
|
3025
|
+
};
|
|
3026
|
+
},
|
|
3027
|
+
name: "derive.keyed.values"
|
|
2983
3028
|
});
|
|
2984
|
-
return
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3029
|
+
return projection;
|
|
3030
|
+
};
|
|
3031
|
+
function createKeyedFilter(source, dependenciesOrPredicate, maybePredicate) {
|
|
3032
|
+
const dependencySpecs = typeof dependenciesOrPredicate === "function" ? void 0 : dependenciesOrPredicate;
|
|
3033
|
+
const predicate = dependencySpecs === void 0 ? dependenciesOrPredicate : maybePredicate;
|
|
3034
|
+
if (typeof predicate !== "function") throw new TypeError("Keyed filter requires a predicate.");
|
|
3035
|
+
return createKeyedTransform(source, dependencySpecs, (value, key, dependencies) => {
|
|
3036
|
+
const included = dependencySpecs === void 0 ? predicate(value, key) : predicate(value, key, dependencies);
|
|
3037
|
+
assertSynchronous(included);
|
|
3038
|
+
if (typeof included !== "boolean") throw new TypeError("Keyed filter predicate must return boolean.");
|
|
3039
|
+
return included ? value : absent;
|
|
3040
|
+
}, Object.is, "derive.keyed.filter");
|
|
3041
|
+
}
|
|
3042
|
+
function createKeyedCompact(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
3043
|
+
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
3044
|
+
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
3045
|
+
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
3046
|
+
if (typeof select !== "function") throw new TypeError("Keyed compact requires a selector.");
|
|
3047
|
+
return createKeyedTransform(source, dependencySpecs, (value, key, dependencies) => {
|
|
3048
|
+
const next = dependencySpecs === void 0 ? select(value, key) : select(value, key, dependencies);
|
|
3049
|
+
assertSynchronous(next);
|
|
3050
|
+
return next === void 0 ? absent : next;
|
|
3051
|
+
}, equality, "derive.keyed.compact");
|
|
3052
|
+
}
|
|
3053
|
+
const createKeyedSubset = (source, orderedKeys) => {
|
|
3054
|
+
assertKeyedProjection(source, "Keyed subset source");
|
|
3055
|
+
const keyProjection = isProjection(orderedKeys) ? orderedKeys : void 0;
|
|
3056
|
+
if (keyProjection && outputKind(keyProjection) !== "value") throw new TypeError("Keyed subset ordered keys must be a value projection.");
|
|
3057
|
+
const staticSelection = keyProjection ? void 0 : (() => {
|
|
3058
|
+
const compiled = compileOrderedKeys(orderedKeys, "Keyed subset ordered keys");
|
|
3059
|
+
return {
|
|
3060
|
+
keys: Object.freeze([...compiled.keys]),
|
|
3061
|
+
requested: compiled.requested
|
|
3062
|
+
};
|
|
3063
|
+
})();
|
|
3064
|
+
const [projection] = defineProcessor({
|
|
3065
|
+
dependencies: keyProjection ? [source, keyProjection] : [source],
|
|
3066
|
+
outputs: [{
|
|
3067
|
+
kind: "collection",
|
|
3068
|
+
equality: Object.is
|
|
3069
|
+
}],
|
|
3070
|
+
create: () => {
|
|
3071
|
+
let keys = staticSelection?.keys;
|
|
3072
|
+
let requested = staticSelection?.requested;
|
|
3073
|
+
let keyRevision = -1;
|
|
3074
|
+
return {
|
|
3075
|
+
evaluate: (evaluation) => {
|
|
3076
|
+
const driver = evaluation.sources[0];
|
|
3077
|
+
const output = evaluation.outputs[0];
|
|
3078
|
+
if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("derive.keyed.subset requires collection input and output.");
|
|
3079
|
+
let keysChanged = false;
|
|
3080
|
+
if (keyProjection) {
|
|
3081
|
+
const sourceContext = evaluation.sources[1];
|
|
3082
|
+
if (sourceContext?.kind !== "value") throw new TypeError("Keyed subset ordered keys resolved to a collection.");
|
|
3083
|
+
if (keys === void 0 || sourceContext.reset || keyRevision !== sourceContext.revision) {
|
|
3084
|
+
const compiled = compileOrderedKeys(sourceContext.value, "Keyed subset ordered keys");
|
|
3085
|
+
keys = compiled.keys;
|
|
3086
|
+
requested = compiled.requested;
|
|
3087
|
+
keyRevision = sourceContext.revision;
|
|
3088
|
+
keysChanged = true;
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
if (!keys || !requested) throw new Error("Keyed subset keys were not initialized.");
|
|
3092
|
+
const activeKeys = keys;
|
|
3093
|
+
const activeRequested = requested;
|
|
3094
|
+
const orderedPresentKeys = () => activeKeys.filter((key) => driver.read.has(key));
|
|
3095
|
+
const rebuild = () => {
|
|
3096
|
+
const nextKeys = orderedPresentKeys();
|
|
3097
|
+
for (const key of nextKeys) output.output.set(key, driver.read.get(key));
|
|
3098
|
+
output.output.order(nextKeys);
|
|
3099
|
+
};
|
|
3100
|
+
const reconcileKeys = () => {
|
|
3101
|
+
const nextKeys = orderedPresentKeys();
|
|
3102
|
+
for (const key of output.previous.ids()) if (!activeRequested.has(key) || !driver.read.has(key)) output.output.remove(key);
|
|
3103
|
+
for (const key of nextKeys) if (!output.previous.has(key)) output.output.set(key, driver.read.get(key));
|
|
3104
|
+
output.output.order(nextKeys);
|
|
3105
|
+
};
|
|
3106
|
+
const change = driver.change;
|
|
3107
|
+
if (evaluation.reset || change?.kind === "reset") rebuild();
|
|
3108
|
+
else if (keysChanged) {
|
|
3109
|
+
reconcileKeys();
|
|
3110
|
+
if (change) for (const entry of change.updated) {
|
|
3111
|
+
const key = entry.key;
|
|
3112
|
+
if (activeRequested.has(key) && output.previous.has(key)) output.output.set(key, entry.after);
|
|
3113
|
+
}
|
|
3114
|
+
} else if (change) {
|
|
3115
|
+
let updateOrder = false;
|
|
3116
|
+
for (const entry of change.removed) {
|
|
3117
|
+
const key = entry.key;
|
|
3118
|
+
if (!activeRequested.has(key)) continue;
|
|
3119
|
+
output.output.remove(key);
|
|
3120
|
+
updateOrder = true;
|
|
3121
|
+
}
|
|
3122
|
+
for (const entry of change.added) {
|
|
3123
|
+
const key = entry.key;
|
|
3124
|
+
if (!activeRequested.has(key)) continue;
|
|
3125
|
+
output.output.set(key, entry.after);
|
|
3126
|
+
updateOrder = true;
|
|
3127
|
+
}
|
|
3128
|
+
for (const entry of change.updated) {
|
|
3129
|
+
const key = entry.key;
|
|
3130
|
+
if (activeRequested.has(key)) output.output.set(key, entry.after);
|
|
3131
|
+
}
|
|
3132
|
+
if (updateOrder) output.output.order(orderedPresentKeys());
|
|
3133
|
+
}
|
|
3134
|
+
},
|
|
3135
|
+
release: () => {
|
|
3136
|
+
if (keyProjection) {
|
|
3137
|
+
keys = void 0;
|
|
3138
|
+
requested = void 0;
|
|
3139
|
+
keyRevision = -1;
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
};
|
|
3143
|
+
},
|
|
3144
|
+
name: "derive.keyed.subset"
|
|
2990
3145
|
});
|
|
3146
|
+
return projection;
|
|
2991
3147
|
};
|
|
2992
|
-
const
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
3148
|
+
const keyedDerive = Object.assign(createKeyedDerive, {
|
|
3149
|
+
keys: createKeyedKeys,
|
|
3150
|
+
values: createKeyedValues,
|
|
3151
|
+
subset: createKeyedSubset,
|
|
3152
|
+
filter: createKeyedFilter,
|
|
3153
|
+
compact: createKeyedCompact
|
|
3154
|
+
});
|
|
3155
|
+
//#endregion
|
|
3156
|
+
//#region core/src/projection/derive.ts
|
|
3157
|
+
function createValueDerive(dependencies, compute, equality = Object.is) {
|
|
3158
|
+
const compiled = compileProjectionDependencies(dependencies, "Derive");
|
|
3159
|
+
const names = compiled.names;
|
|
3160
|
+
const ordered = compiled.projections;
|
|
3161
|
+
const [projection] = defineProcessor({
|
|
3162
|
+
dependencies: ordered,
|
|
3163
|
+
outputs: [{
|
|
3164
|
+
kind: "value",
|
|
3165
|
+
equality
|
|
3166
|
+
}],
|
|
3167
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
3168
|
+
const output = evaluation.outputs[0];
|
|
3169
|
+
if (output.kind !== "value") throw new Error("derive requires a value output.");
|
|
3170
|
+
const values = Object.create(null);
|
|
3171
|
+
for (let index = 0; index < names.length; index++) values[names[index]] = snapshotDependencyValue(evaluation.sources[index]);
|
|
3172
|
+
const next = compute(Object.freeze(values));
|
|
3173
|
+
assertSynchronous(next);
|
|
3174
|
+
output.output.set(next);
|
|
3175
|
+
} })
|
|
3176
|
+
});
|
|
3177
|
+
return projection;
|
|
3178
|
+
}
|
|
3179
|
+
const derive = Object.assign(createValueDerive, { keyed: keyedDerive });
|
|
2999
3180
|
//#endregion
|
|
3000
3181
|
//#region core/src/projection/collection/index.ts
|
|
3001
3182
|
const height = (node) => node?.height ?? 0;
|
|
@@ -3208,15 +3389,36 @@ const createCollectionOutput = (binding) => {
|
|
|
3208
3389
|
profile.projection("touchedKeys", staged.size);
|
|
3209
3390
|
const added = /* @__PURE__ */ new Set();
|
|
3210
3391
|
const removed = /* @__PURE__ */ new Set();
|
|
3211
|
-
const
|
|
3392
|
+
const addedEntries = [];
|
|
3393
|
+
const updatedEntries = [];
|
|
3394
|
+
const removedEntries = [];
|
|
3212
3395
|
for (const [key, entry] of staged) {
|
|
3213
3396
|
const existed = published.has(key);
|
|
3214
3397
|
if (entry.present) {
|
|
3215
|
-
if (!existed)
|
|
3216
|
-
|
|
3398
|
+
if (!existed) {
|
|
3399
|
+
added.add(key);
|
|
3400
|
+
addedEntries.push({
|
|
3401
|
+
key,
|
|
3402
|
+
after: entry.value
|
|
3403
|
+
});
|
|
3404
|
+
continue;
|
|
3405
|
+
}
|
|
3406
|
+
const before = published.get(key);
|
|
3407
|
+
if (!isEqual(before, entry.value)) updatedEntries.push({
|
|
3408
|
+
key,
|
|
3409
|
+
before,
|
|
3410
|
+
after: entry.value
|
|
3411
|
+
});
|
|
3217
3412
|
else if (!nextReset) staged.delete(key);
|
|
3218
|
-
|
|
3219
|
-
|
|
3413
|
+
continue;
|
|
3414
|
+
}
|
|
3415
|
+
if (existed) {
|
|
3416
|
+
removed.add(key);
|
|
3417
|
+
removedEntries.push({
|
|
3418
|
+
key,
|
|
3419
|
+
before: published.get(key)
|
|
3420
|
+
});
|
|
3421
|
+
} else staged.delete(key);
|
|
3220
3422
|
}
|
|
3221
3423
|
const ids = published.ids();
|
|
3222
3424
|
if (explicitOrder || added.size || removed.size) {
|
|
@@ -3224,35 +3426,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3224
3426
|
if (new Set(order).size !== order.length || order.length !== published.size() + added.size - removed.size || order.some((key) => !hasNext(key))) throw new TypeError("Projection order must contain every key exactly once.");
|
|
3225
3427
|
nextIds = ids.length === order.length && ids.every((key, index) => key === order[index]) ? ids : Object.freeze([...order]);
|
|
3226
3428
|
} else nextIds = ids;
|
|
3227
|
-
|
|
3228
|
-
added.forEach((key) => changedKeys.add(key));
|
|
3229
|
-
removed.forEach((key) => changedKeys.add(key));
|
|
3230
|
-
updated.forEach((key) => changedKeys.add(key));
|
|
3231
|
-
profile.projection("changedKeys", changedKeys.size);
|
|
3232
|
-
const addedEntries = [];
|
|
3233
|
-
const updatedEntries = [];
|
|
3234
|
-
const removedEntries = [];
|
|
3235
|
-
for (const key of changedKeys) {
|
|
3236
|
-
const beforePresent = published.has(key);
|
|
3237
|
-
const afterPresent = hasNext(key);
|
|
3238
|
-
if (beforePresent === afterPresent) {
|
|
3239
|
-
if (!beforePresent) continue;
|
|
3240
|
-
if (isEqual !== Object.is && isEqual(published.get(key), getNext(key))) continue;
|
|
3241
|
-
}
|
|
3242
|
-
if (!beforePresent) addedEntries.push({
|
|
3243
|
-
key,
|
|
3244
|
-
after: getNext(key)
|
|
3245
|
-
});
|
|
3246
|
-
else if (!afterPresent) removedEntries.push({
|
|
3247
|
-
key,
|
|
3248
|
-
before: published.get(key)
|
|
3249
|
-
});
|
|
3250
|
-
else updatedEntries.push({
|
|
3251
|
-
key,
|
|
3252
|
-
before: published.get(key),
|
|
3253
|
-
after: getNext(key)
|
|
3254
|
-
});
|
|
3255
|
-
}
|
|
3429
|
+
profile.projection("changedKeys", addedEntries.length + updatedEntries.length + removedEntries.length);
|
|
3256
3430
|
change = nextReset ? Object.freeze({ kind: "reset" }) : createCollectionChange({
|
|
3257
3431
|
added: addedEntries,
|
|
3258
3432
|
updated: updatedEntries,
|
|
@@ -3617,10 +3791,9 @@ const trackedMap = (source) => {
|
|
|
3617
3791
|
};
|
|
3618
3792
|
const selectionAffects = (selection, change) => {
|
|
3619
3793
|
if (!change || change.kind === "reset") return true;
|
|
3620
|
-
if (selection.all
|
|
3794
|
+
if (selection.all) return true;
|
|
3621
3795
|
if (selection.structure && collectionHasStructuralChange(change)) return true;
|
|
3622
|
-
const
|
|
3623
|
-
for (const key of selection.keys) if (changedKeys.has(key)) return true;
|
|
3796
|
+
for (const key of collectionChange.keys(change)) if (selection.keys.has(key)) return true;
|
|
3624
3797
|
return false;
|
|
3625
3798
|
};
|
|
3626
3799
|
const createDirectReadable = (source) => Object.freeze({
|
|
@@ -4162,7 +4335,7 @@ const stageCollectionRead = (state, read, active, options) => {
|
|
|
4162
4335
|
evaluation.output.order(ids);
|
|
4163
4336
|
} else {
|
|
4164
4337
|
const previous = state.current();
|
|
4165
|
-
const keys = options.candidates ? [...
|
|
4338
|
+
const keys = options.candidates ? [...options.candidates] : [...new Set([...previous.ids(), ...read.ids()])];
|
|
4166
4339
|
if (!options.candidates) profile.collectionView.idsScanned(keys.length);
|
|
4167
4340
|
for (const key of keys) if (read.has(key)) {
|
|
4168
4341
|
profile.collectionView.mapped();
|
|
@@ -4851,6 +5024,7 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
4851
5024
|
scheduler.assertIdle();
|
|
4852
5025
|
const staged = /* @__PURE__ */ new Map();
|
|
4853
5026
|
const operations = [];
|
|
5027
|
+
let structural = false;
|
|
4854
5028
|
let active = true;
|
|
4855
5029
|
const draft = Object.freeze({
|
|
4856
5030
|
get: (key) => {
|
|
@@ -4866,6 +5040,8 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
4866
5040
|
set: (key, value) => {
|
|
4867
5041
|
assertScope(() => active);
|
|
4868
5042
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
5043
|
+
const previous = staged.get(key);
|
|
5044
|
+
structural ||= previous ? !previous.present : !values.has(key);
|
|
4869
5045
|
staged.set(key, {
|
|
4870
5046
|
present: true,
|
|
4871
5047
|
value
|
|
@@ -4879,8 +5055,11 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
4879
5055
|
remove: (key) => {
|
|
4880
5056
|
assertScope(() => active);
|
|
4881
5057
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
5058
|
+
const previous = staged.get(key);
|
|
5059
|
+
const present = previous ? previous.present : values.has(key);
|
|
5060
|
+
structural ||= present;
|
|
4882
5061
|
staged.set(key, { present: false });
|
|
4883
|
-
operations.push({
|
|
5062
|
+
if (present) operations.push({
|
|
4884
5063
|
kind: "remove",
|
|
4885
5064
|
key
|
|
4886
5065
|
});
|
|
@@ -4891,32 +5070,25 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
4891
5070
|
} finally {
|
|
4892
5071
|
active = false;
|
|
4893
5072
|
}
|
|
5073
|
+
if (!operations.length) return;
|
|
4894
5074
|
const candidates = /* @__PURE__ */ new Set();
|
|
4895
|
-
|
|
4896
|
-
const
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
value: operation.value
|
|
4908
|
-
});
|
|
4909
|
-
} else {
|
|
4910
|
-
if (!present) continue;
|
|
4911
|
-
structural = true;
|
|
4912
|
-
overlay.set(operation.key, { present: false });
|
|
4913
|
-
}
|
|
4914
|
-
accepted.push(operation);
|
|
4915
|
-
candidates.add(operation.key);
|
|
5075
|
+
const retained = /* @__PURE__ */ new Map();
|
|
5076
|
+
for (const [key, entry] of staged) {
|
|
5077
|
+
const existed = values.has(key);
|
|
5078
|
+
if (entry.present) {
|
|
5079
|
+
if (!existed) {
|
|
5080
|
+
candidates.add(key);
|
|
5081
|
+
continue;
|
|
5082
|
+
}
|
|
5083
|
+
const previous = values.get(key);
|
|
5084
|
+
if (source.equality(previous, entry.value)) retained.set(key, previous);
|
|
5085
|
+
else candidates.add(key);
|
|
5086
|
+
} else if (existed) candidates.add(key);
|
|
4916
5087
|
}
|
|
4917
|
-
if (!
|
|
4918
|
-
for (const operation of
|
|
5088
|
+
if (!candidates.size && !structural) return;
|
|
5089
|
+
for (const operation of operations) if (operation.kind === "set") values.set(operation.key, operation.value);
|
|
4919
5090
|
else values.delete(operation.key);
|
|
5091
|
+
for (const [key, value] of retained) if (values.has(key)) values.set(key, value);
|
|
4920
5092
|
boundary.mark({
|
|
4921
5093
|
candidates,
|
|
4922
5094
|
orderMayChange: structural
|