doxum 0.1.30 → 0.1.31
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 +20 -2
- package/dist/index.cjs +275 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +275 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +6 -5
- package/skills/doxum-runtime/references/projections.md +38 -0
- package/skills/doxum-runtime/references/public-api.md +32 -0
- package/skills/doxum-runtime/references/recipes.md +31 -0
package/dist/index.d.cts
CHANGED
|
@@ -29,6 +29,23 @@ declare function observe<T>(readable: Readable<T>): Projection<T>;
|
|
|
29
29
|
declare function observe<S extends ObjectSchema<object>, P extends CollectionPath>(document: ReadonlyDocument<S>, selector: (path: SchemaPath<S>) => P): KeyedProjection<CollectionId<P>, ReadonlyValue<CollectionEntry<P>>>;
|
|
30
30
|
declare function observe<S extends ObjectSchema<object>, P>(document: ReadonlyDocument<S>, selector: (path: SchemaPath<S>) => P): Projection<PathValueOf<P>>;
|
|
31
31
|
//#endregion
|
|
32
|
+
//#region core/src/projection/keyed/composition.d.ts
|
|
33
|
+
type Equality$1<T> = (previous: T, next: T) => boolean;
|
|
34
|
+
declare function createKeyedFrom<K extends string, V>(source: Projection<readonly V[]>, keyOf: (value: NoInfer<V>) => Synchronous<K>, equality?: Equality$1<V>): KeyedProjection<K, V>;
|
|
35
|
+
declare function createKeyedFrom<K extends string, V>(source: readonly V[], keyOf: (value: NoInfer<V>) => Synchronous<K>, equality?: Equality$1<V>): KeyedProjection<K, V>;
|
|
36
|
+
declare function createKeyedMerge<K extends string, V>(sources: readonly KeyedProjection<K, V>[], options: {
|
|
37
|
+
readonly conflict: 'error' | 'first' | 'last';
|
|
38
|
+
readonly equality?: Equality$1<V>;
|
|
39
|
+
readonly resolve?: never;
|
|
40
|
+
} | {
|
|
41
|
+
readonly conflict: 'resolve';
|
|
42
|
+
readonly resolve: (contributions: readonly {
|
|
43
|
+
readonly sourceIndex: number;
|
|
44
|
+
readonly value: V;
|
|
45
|
+
}[], key: NoInfer<K>) => Synchronous<V>;
|
|
46
|
+
readonly equality?: Equality$1<V>;
|
|
47
|
+
}): KeyedProjection<K, V>;
|
|
48
|
+
//#endregion
|
|
32
49
|
//#region core/src/projection/derive.d.ts
|
|
33
50
|
type ProjectionDependencies = Readonly<Record<string, Projection<unknown>>>;
|
|
34
51
|
type ProjectionValues<D extends ProjectionDependencies> = { readonly [K in keyof D]: D[K] extends Projection<infer T> ? T : never };
|
|
@@ -43,6 +60,8 @@ declare const derive: typeof createValueDerive & {
|
|
|
43
60
|
values: <K extends string, V>(source: KeyedProjection<K, V>) => Projection<readonly V[]>;
|
|
44
61
|
entries: <K extends string, V>(source: KeyedProjection<K, V>) => Projection<readonly (readonly [K, V])[]>;
|
|
45
62
|
get: <K extends string, V>(source: KeyedProjection<K, V>, key: Projection<NoInfer<K> | undefined>, equality?: (previous: V | undefined, next: V | undefined) => boolean) => Projection<V | undefined>;
|
|
63
|
+
from: typeof createKeyedFrom;
|
|
64
|
+
merge: typeof createKeyedMerge;
|
|
46
65
|
groupBy: {
|
|
47
66
|
<K extends string, V, G extends string>(source: KeyedProjection<K, V>, selector: (value: NoInfer<V>, key: NoInfer<K>) => Synchronous<G | readonly G[]>): KeyedProjection<G, readonly K[]>;
|
|
48
67
|
<K extends string, V, const D extends object, G extends string>(source: KeyedProjection<K, V>, dependencies: D & KeyedDependencies<NoInfer<K>, NoInfer<V>, D>, selector: (value: NoInfer<V>, key: NoInfer<K>, dependencies: KeyedDependencyValues<D>) => Synchronous<G | readonly G[]>): KeyedProjection<G, readonly K[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,23 @@ declare function observe<T>(readable: Readable<T>): Projection<T>;
|
|
|
29
29
|
declare function observe<S extends ObjectSchema<object>, P extends CollectionPath>(document: ReadonlyDocument<S>, selector: (path: SchemaPath<S>) => P): KeyedProjection<CollectionId<P>, ReadonlyValue<CollectionEntry<P>>>;
|
|
30
30
|
declare function observe<S extends ObjectSchema<object>, P>(document: ReadonlyDocument<S>, selector: (path: SchemaPath<S>) => P): Projection<PathValueOf<P>>;
|
|
31
31
|
//#endregion
|
|
32
|
+
//#region core/src/projection/keyed/composition.d.ts
|
|
33
|
+
type Equality$1<T> = (previous: T, next: T) => boolean;
|
|
34
|
+
declare function createKeyedFrom<K extends string, V>(source: Projection<readonly V[]>, keyOf: (value: NoInfer<V>) => Synchronous<K>, equality?: Equality$1<V>): KeyedProjection<K, V>;
|
|
35
|
+
declare function createKeyedFrom<K extends string, V>(source: readonly V[], keyOf: (value: NoInfer<V>) => Synchronous<K>, equality?: Equality$1<V>): KeyedProjection<K, V>;
|
|
36
|
+
declare function createKeyedMerge<K extends string, V>(sources: readonly KeyedProjection<K, V>[], options: {
|
|
37
|
+
readonly conflict: 'error' | 'first' | 'last';
|
|
38
|
+
readonly equality?: Equality$1<V>;
|
|
39
|
+
readonly resolve?: never;
|
|
40
|
+
} | {
|
|
41
|
+
readonly conflict: 'resolve';
|
|
42
|
+
readonly resolve: (contributions: readonly {
|
|
43
|
+
readonly sourceIndex: number;
|
|
44
|
+
readonly value: V;
|
|
45
|
+
}[], key: NoInfer<K>) => Synchronous<V>;
|
|
46
|
+
readonly equality?: Equality$1<V>;
|
|
47
|
+
}): KeyedProjection<K, V>;
|
|
48
|
+
//#endregion
|
|
32
49
|
//#region core/src/projection/derive.d.ts
|
|
33
50
|
type ProjectionDependencies = Readonly<Record<string, Projection<unknown>>>;
|
|
34
51
|
type ProjectionValues<D extends ProjectionDependencies> = { readonly [K in keyof D]: D[K] extends Projection<infer T> ? T : never };
|
|
@@ -43,6 +60,8 @@ declare const derive: typeof createValueDerive & {
|
|
|
43
60
|
values: <K extends string, V>(source: KeyedProjection<K, V>) => Projection<readonly V[]>;
|
|
44
61
|
entries: <K extends string, V>(source: KeyedProjection<K, V>) => Projection<readonly (readonly [K, V])[]>;
|
|
45
62
|
get: <K extends string, V>(source: KeyedProjection<K, V>, key: Projection<NoInfer<K> | undefined>, equality?: (previous: V | undefined, next: V | undefined) => boolean) => Projection<V | undefined>;
|
|
63
|
+
from: typeof createKeyedFrom;
|
|
64
|
+
merge: typeof createKeyedMerge;
|
|
46
65
|
groupBy: {
|
|
47
66
|
<K extends string, V, G extends string>(source: KeyedProjection<K, V>, selector: (value: NoInfer<V>, key: NoInfer<K>) => Synchronous<G | readonly G[]>): KeyedProjection<G, readonly K[]>;
|
|
48
67
|
<K extends string, V, const D extends object, G extends string>(source: KeyedProjection<K, V>, dependencies: D & KeyedDependencies<NoInfer<K>, NoInfer<V>, D>, selector: (value: NoInfer<V>, key: NoInfer<K>, dependencies: KeyedDependencyValues<D>) => Synchronous<G | readonly G[]>): KeyedProjection<G, readonly K[]>;
|
package/dist/index.js
CHANGED
|
@@ -2774,6 +2774,279 @@ const isExternalSource = (source) => {
|
|
|
2774
2774
|
return (candidate.kind === "value" || candidate.kind === "collection") && typeof candidate.current === "function" && typeof candidate.revision === "function" && typeof candidate.subscribe === "function";
|
|
2775
2775
|
};
|
|
2776
2776
|
//#endregion
|
|
2777
|
+
//#region core/src/projection/keyed/composition.ts
|
|
2778
|
+
const assertEquality = (equality, role) => {
|
|
2779
|
+
if (equality !== void 0 && typeof equality !== "function") throw new TypeError(`${role} equality must be a function.`);
|
|
2780
|
+
return equality ?? Object.is;
|
|
2781
|
+
};
|
|
2782
|
+
const compileMergeOptions = (options) => {
|
|
2783
|
+
if (options === null || typeof options !== "object") throw new TypeError("derive.keyed.merge requires conflict options.");
|
|
2784
|
+
const equality = assertEquality(options.equality, "derive.keyed.merge");
|
|
2785
|
+
switch (options.conflict) {
|
|
2786
|
+
case "error":
|
|
2787
|
+
case "first":
|
|
2788
|
+
case "last":
|
|
2789
|
+
if ("resolve" in options && options.resolve !== void 0) throw new TypeError("derive.keyed.merge resolve is only valid when conflict is \"resolve\".");
|
|
2790
|
+
return Object.freeze({
|
|
2791
|
+
conflict: options.conflict,
|
|
2792
|
+
equality
|
|
2793
|
+
});
|
|
2794
|
+
case "resolve":
|
|
2795
|
+
if (typeof options.resolve !== "function") throw new TypeError("derive.keyed.merge requires a resolver when conflict is \"resolve\".");
|
|
2796
|
+
return Object.freeze({
|
|
2797
|
+
conflict: options.conflict,
|
|
2798
|
+
equality,
|
|
2799
|
+
resolve: options.resolve
|
|
2800
|
+
});
|
|
2801
|
+
default: throw new TypeError("derive.keyed.merge conflict must be \"error\", \"first\", \"last\", or \"resolve\".");
|
|
2802
|
+
}
|
|
2803
|
+
};
|
|
2804
|
+
const assertCollectionSources = (sources, expected) => {
|
|
2805
|
+
if (sources.length !== expected) throw new Error("derive.keyed.merge resolved an invalid dependency count.");
|
|
2806
|
+
for (const source of sources) if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2807
|
+
};
|
|
2808
|
+
function createKeyedFrom(source, keyOf, equality) {
|
|
2809
|
+
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.from requires a key selector.");
|
|
2810
|
+
const outputEquality = assertEquality(equality, "derive.keyed.from");
|
|
2811
|
+
let dependency;
|
|
2812
|
+
let staticValues;
|
|
2813
|
+
if (isProjection(source)) {
|
|
2814
|
+
if (outputKind(source) !== "value") throw new TypeError("derive.keyed.from source projection must be scalar.");
|
|
2815
|
+
dependency = source;
|
|
2816
|
+
} else {
|
|
2817
|
+
if (!Array.isArray(source)) throw new TypeError("derive.keyed.from source must be a scalar projection or readonly array.");
|
|
2818
|
+
staticValues = snapshotArray(source);
|
|
2819
|
+
}
|
|
2820
|
+
const [projection] = defineProcessor({
|
|
2821
|
+
dependencies: dependency ? [dependency] : [],
|
|
2822
|
+
outputs: [{
|
|
2823
|
+
kind: "collection",
|
|
2824
|
+
equality: outputEquality
|
|
2825
|
+
}],
|
|
2826
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
2827
|
+
const output = evaluation.outputs[0];
|
|
2828
|
+
if (output.kind !== "collection") throw new Error("derive.keyed.from requires a collection output.");
|
|
2829
|
+
let values = staticValues;
|
|
2830
|
+
if (dependency) {
|
|
2831
|
+
const scalar = evaluation.sources[0];
|
|
2832
|
+
if (scalar?.kind !== "value") throw new Error("derive.keyed.from resolved a non-scalar input.");
|
|
2833
|
+
if (!Array.isArray(scalar.value)) throw new TypeError("derive.keyed.from source value must be an array.");
|
|
2834
|
+
values = scalar.value;
|
|
2835
|
+
}
|
|
2836
|
+
if (!values) throw new Error("derive.keyed.from has no source values.");
|
|
2837
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2838
|
+
const order = new Array(values.length);
|
|
2839
|
+
for (let index = 0; index < values.length; index++) {
|
|
2840
|
+
const value = values[index];
|
|
2841
|
+
const key = keyOf(value);
|
|
2842
|
+
assertSynchronous(key);
|
|
2843
|
+
if (typeof key !== "string") throw new TypeError("derive.keyed.from key selector must return a string.");
|
|
2844
|
+
if (seen.has(key)) throw new TypeError(`derive.keyed.from duplicate key ${JSON.stringify(key)}.`);
|
|
2845
|
+
seen.add(key);
|
|
2846
|
+
order[index] = key;
|
|
2847
|
+
output.output.set(key, value);
|
|
2848
|
+
}
|
|
2849
|
+
for (const previousKey of output.previous.ids()) if (!seen.has(previousKey)) output.output.remove(previousKey);
|
|
2850
|
+
output.output.order(order);
|
|
2851
|
+
} }),
|
|
2852
|
+
name: "derive.keyed.from"
|
|
2853
|
+
});
|
|
2854
|
+
return projection;
|
|
2855
|
+
}
|
|
2856
|
+
function createKeyedMerge(sources, options) {
|
|
2857
|
+
if (!Array.isArray(sources)) throw new TypeError("derive.keyed.merge sources must be an array of keyed projections.");
|
|
2858
|
+
const capturedSources = snapshotArray(sources);
|
|
2859
|
+
for (let index = 0; index < capturedSources.length; index++) assertKeyedProjection(capturedSources[index], `derive.keyed.merge source ${index}`);
|
|
2860
|
+
const compiled = compileMergeOptions(options);
|
|
2861
|
+
const [projection] = defineProcessor({
|
|
2862
|
+
dependencies: capturedSources,
|
|
2863
|
+
outputs: [{
|
|
2864
|
+
kind: "collection",
|
|
2865
|
+
equality: compiled.equality
|
|
2866
|
+
}],
|
|
2867
|
+
create: () => {
|
|
2868
|
+
const revisions = new Array(capturedSources.length).fill(-1);
|
|
2869
|
+
const remember = (sources) => {
|
|
2870
|
+
for (let index = 0; index < revisions.length; index++) revisions[index] = sources[index].revision;
|
|
2871
|
+
};
|
|
2872
|
+
const writeEffective = (sources, output, key) => {
|
|
2873
|
+
if (compiled.conflict === "first") {
|
|
2874
|
+
for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
|
|
2875
|
+
const source = sources[sourceIndex];
|
|
2876
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2877
|
+
if (!source.read.has(key)) continue;
|
|
2878
|
+
output.set(key, source.read.get(key));
|
|
2879
|
+
return;
|
|
2880
|
+
}
|
|
2881
|
+
output.remove(key);
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
let present = false;
|
|
2885
|
+
let selected;
|
|
2886
|
+
let firstSourceIndex = -1;
|
|
2887
|
+
let contributions;
|
|
2888
|
+
for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
|
|
2889
|
+
const source = sources[sourceIndex];
|
|
2890
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2891
|
+
if (!source.read.has(key)) continue;
|
|
2892
|
+
const value = source.read.get(key);
|
|
2893
|
+
if (!present) {
|
|
2894
|
+
present = true;
|
|
2895
|
+
selected = value;
|
|
2896
|
+
firstSourceIndex = sourceIndex;
|
|
2897
|
+
continue;
|
|
2898
|
+
}
|
|
2899
|
+
if (compiled.conflict === "error") throw new TypeError(`derive.keyed.merge duplicate key ${JSON.stringify(key)}.`);
|
|
2900
|
+
if (compiled.conflict === "last") {
|
|
2901
|
+
selected = value;
|
|
2902
|
+
continue;
|
|
2903
|
+
}
|
|
2904
|
+
if (!contributions) contributions = [Object.freeze({
|
|
2905
|
+
sourceIndex: firstSourceIndex,
|
|
2906
|
+
value: selected
|
|
2907
|
+
}), Object.freeze({
|
|
2908
|
+
sourceIndex,
|
|
2909
|
+
value
|
|
2910
|
+
})];
|
|
2911
|
+
else contributions.push(Object.freeze({
|
|
2912
|
+
sourceIndex,
|
|
2913
|
+
value
|
|
2914
|
+
}));
|
|
2915
|
+
}
|
|
2916
|
+
if (!present) {
|
|
2917
|
+
output.remove(key);
|
|
2918
|
+
return;
|
|
2919
|
+
}
|
|
2920
|
+
if (compiled.conflict === "resolve" && contributions) {
|
|
2921
|
+
const resolved = compiled.resolve(Object.freeze(contributions), key);
|
|
2922
|
+
assertSynchronous(resolved);
|
|
2923
|
+
output.set(key, resolved);
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
output.set(key, selected);
|
|
2927
|
+
};
|
|
2928
|
+
const rebuild = (sources, output, previousIds, reset) => {
|
|
2929
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2930
|
+
const order = [];
|
|
2931
|
+
const conflicts = compiled.conflict === "resolve" ? /* @__PURE__ */ new Set() : void 0;
|
|
2932
|
+
for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
|
|
2933
|
+
const source = sources[sourceIndex];
|
|
2934
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2935
|
+
for (const key of source.read.ids()) {
|
|
2936
|
+
const duplicate = seen.has(key);
|
|
2937
|
+
if (!duplicate) {
|
|
2938
|
+
seen.add(key);
|
|
2939
|
+
order.push(key);
|
|
2940
|
+
}
|
|
2941
|
+
if (compiled.conflict === "error") {
|
|
2942
|
+
if (duplicate) throw new TypeError(`derive.keyed.merge duplicate key ${JSON.stringify(key)}.`);
|
|
2943
|
+
output.set(key, source.read.get(key));
|
|
2944
|
+
continue;
|
|
2945
|
+
}
|
|
2946
|
+
if (compiled.conflict === "first") {
|
|
2947
|
+
if (!duplicate) output.set(key, source.read.get(key));
|
|
2948
|
+
continue;
|
|
2949
|
+
}
|
|
2950
|
+
if (compiled.conflict === "last") {
|
|
2951
|
+
output.set(key, source.read.get(key));
|
|
2952
|
+
continue;
|
|
2953
|
+
}
|
|
2954
|
+
if (!duplicate) output.set(key, source.read.get(key));
|
|
2955
|
+
else conflicts.add(key);
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
if (conflicts) for (const key of conflicts) writeEffective(sources, output, key);
|
|
2959
|
+
if (!reset) {
|
|
2960
|
+
for (const previousKey of previousIds) if (!seen.has(previousKey)) output.remove(previousKey);
|
|
2961
|
+
}
|
|
2962
|
+
output.order(order);
|
|
2963
|
+
};
|
|
2964
|
+
const rebuildOrder = (sources, output) => {
|
|
2965
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2966
|
+
const order = [];
|
|
2967
|
+
for (const source of sources) {
|
|
2968
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2969
|
+
for (const key of source.read.ids()) {
|
|
2970
|
+
if (seen.has(key)) continue;
|
|
2971
|
+
seen.add(key);
|
|
2972
|
+
order.push(key);
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
output.order(order);
|
|
2976
|
+
};
|
|
2977
|
+
return {
|
|
2978
|
+
evaluate: (evaluation) => {
|
|
2979
|
+
const output = evaluation.outputs[0];
|
|
2980
|
+
if (output.kind !== "collection") throw new Error("derive.keyed.merge requires a collection output.");
|
|
2981
|
+
assertCollectionSources(evaluation.sources, capturedSources.length);
|
|
2982
|
+
if (evaluation.reset) {
|
|
2983
|
+
rebuild(evaluation.sources, output.output, output.previous.ids(), true);
|
|
2984
|
+
remember(evaluation.sources);
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
const affected = /* @__PURE__ */ new Set();
|
|
2988
|
+
let fullRebuild = false;
|
|
2989
|
+
let orderDirty = false;
|
|
2990
|
+
for (let sourceIndex = 0; sourceIndex < evaluation.sources.length; sourceIndex++) {
|
|
2991
|
+
const source = evaluation.sources[sourceIndex];
|
|
2992
|
+
if (revisions[sourceIndex] === source.revision) continue;
|
|
2993
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2994
|
+
const change = source.change;
|
|
2995
|
+
if (!change || change.kind === "reset") {
|
|
2996
|
+
fullRebuild = true;
|
|
2997
|
+
continue;
|
|
2998
|
+
}
|
|
2999
|
+
for (const entry of change.added) affected.add(entry.key);
|
|
3000
|
+
for (const entry of change.updated) affected.add(entry.key);
|
|
3001
|
+
for (const entry of change.removed) affected.add(entry.key);
|
|
3002
|
+
if (Boolean(change.added.length || change.removed.length || change.order)) orderDirty = true;
|
|
3003
|
+
}
|
|
3004
|
+
if (fullRebuild) {
|
|
3005
|
+
rebuild(evaluation.sources, output.output, output.previous.ids(), false);
|
|
3006
|
+
remember(evaluation.sources);
|
|
3007
|
+
return;
|
|
3008
|
+
}
|
|
3009
|
+
for (const key of affected) writeEffective(evaluation.sources, output.output, key);
|
|
3010
|
+
if (orderDirty) rebuildOrder(evaluation.sources, output.output);
|
|
3011
|
+
remember(evaluation.sources);
|
|
3012
|
+
},
|
|
3013
|
+
release: () => revisions.fill(-1)
|
|
3014
|
+
};
|
|
3015
|
+
},
|
|
3016
|
+
name: "derive.keyed.merge"
|
|
3017
|
+
});
|
|
3018
|
+
return projection;
|
|
3019
|
+
}
|
|
3020
|
+
const createKeyedSingleton = (source, keyOf, equality = Object.is) => {
|
|
3021
|
+
if (outputKind(source) !== "value") throw new TypeError("derive.keyed.singleton source must be a scalar projection.");
|
|
3022
|
+
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.singleton requires a key selector.");
|
|
3023
|
+
const [projection] = defineProcessor({
|
|
3024
|
+
dependencies: [source],
|
|
3025
|
+
outputs: [{
|
|
3026
|
+
kind: "collection",
|
|
3027
|
+
equality
|
|
3028
|
+
}],
|
|
3029
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
3030
|
+
const scalar = evaluation.sources[0];
|
|
3031
|
+
const output = evaluation.outputs[0];
|
|
3032
|
+
if (scalar.kind !== "value" || output.kind !== "collection") throw new Error("derive.keyed.singleton resolved invalid input/output kinds.");
|
|
3033
|
+
const previous = output.previous.ids();
|
|
3034
|
+
if (scalar.value === void 0) {
|
|
3035
|
+
for (const key of previous) output.output.remove(key);
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
const key = keyOf(scalar.value);
|
|
3039
|
+
assertSynchronous(key);
|
|
3040
|
+
if (typeof key !== "string") throw new TypeError("derive.keyed.singleton key selector must return a string.");
|
|
3041
|
+
for (const previousKey of previous) if (previousKey !== key) output.output.remove(previousKey);
|
|
3042
|
+
output.output.set(key, scalar.value);
|
|
3043
|
+
if (previous.length !== 1 || previous[0] !== key) output.output.order([key]);
|
|
3044
|
+
} }),
|
|
3045
|
+
name: "derive.keyed.singleton"
|
|
3046
|
+
});
|
|
3047
|
+
return projection;
|
|
3048
|
+
};
|
|
3049
|
+
//#endregion
|
|
2777
3050
|
//#region core/src/projection/derive/keyed.ts
|
|
2778
3051
|
const compileOrderedKeys = (value, role) => {
|
|
2779
3052
|
if (!Array.isArray(value)) throw new TypeError(`${role} must be an array of string keys.`);
|
|
@@ -3220,40 +3493,13 @@ function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
|
|
|
3220
3493
|
});
|
|
3221
3494
|
return projection;
|
|
3222
3495
|
}
|
|
3223
|
-
const createKeyedSingleton = (source, keyOf, equality = Object.is) => {
|
|
3224
|
-
if (outputKind(source) !== "value") throw new TypeError("derive.keyed.singleton source must be a scalar projection.");
|
|
3225
|
-
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.singleton requires a key selector.");
|
|
3226
|
-
const [projection] = defineProcessor({
|
|
3227
|
-
dependencies: [source],
|
|
3228
|
-
outputs: [{
|
|
3229
|
-
kind: "collection",
|
|
3230
|
-
equality
|
|
3231
|
-
}],
|
|
3232
|
-
create: () => ({ evaluate: (evaluation) => {
|
|
3233
|
-
const scalar = evaluation.sources[0];
|
|
3234
|
-
const output = evaluation.outputs[0];
|
|
3235
|
-
if (scalar.kind !== "value" || output.kind !== "collection") throw new Error("derive.keyed.singleton resolved invalid input/output kinds.");
|
|
3236
|
-
const previous = output.previous.ids();
|
|
3237
|
-
if (scalar.value === void 0) {
|
|
3238
|
-
for (const key of previous) output.output.remove(key);
|
|
3239
|
-
return;
|
|
3240
|
-
}
|
|
3241
|
-
const key = keyOf(scalar.value);
|
|
3242
|
-
assertSynchronous(key);
|
|
3243
|
-
if (typeof key !== "string") throw new TypeError("derive.keyed.singleton key selector must return a string.");
|
|
3244
|
-
for (const previousKey of previous) if (previousKey !== key) output.output.remove(previousKey);
|
|
3245
|
-
output.output.set(key, scalar.value);
|
|
3246
|
-
if (previous.length !== 1 || previous[0] !== key) output.output.order([key]);
|
|
3247
|
-
} }),
|
|
3248
|
-
name: "derive.keyed.singleton"
|
|
3249
|
-
});
|
|
3250
|
-
return projection;
|
|
3251
|
-
};
|
|
3252
3496
|
const keyedDerive = Object.assign(createKeyedDerive, {
|
|
3253
3497
|
keys: createKeyedKeys,
|
|
3254
3498
|
values: createKeyedValues,
|
|
3255
3499
|
entries: createKeyedEntries,
|
|
3256
3500
|
get: createKeyedGet,
|
|
3501
|
+
from: createKeyedFrom,
|
|
3502
|
+
merge: createKeyedMerge,
|
|
3257
3503
|
groupBy: createKeyedGroupBy,
|
|
3258
3504
|
singleton: createKeyedSingleton,
|
|
3259
3505
|
subset: createKeyedSubset,
|