doxum 0.1.29 → 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 +23 -4
- package/dist/index.cjs +281 -31
- 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 +281 -31
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/skills/doxum-runtime/SKILL.md +65 -82
- package/skills/doxum-runtime/agents/openai.yaml +2 -2
- package/skills/doxum-runtime/references/document-runtime.md +370 -0
- package/skills/doxum-runtime/references/integrations.md +215 -0
- package/skills/doxum-runtime/references/{invariants.en.md → invariants.md} +5 -2
- package/skills/doxum-runtime/references/projections.md +505 -0
- package/skills/doxum-runtime/references/public-api.md +655 -0
- package/skills/doxum-runtime/references/recipes.md +524 -0
- package/skills/doxum-runtime/references/api.en.md +0 -283
- package/skills/doxum-runtime/references/api.zh-CN.md +0 -276
- package/skills/doxum-runtime/references/guide.en.md +0 -138
- package/skills/doxum-runtime/references/guide.zh-CN.md +0 -116
- package/skills/doxum-runtime/references/invariants.zh-CN.md +0 -53
- package/skills/doxum-runtime/references/patterns.en.md +0 -162
- package/skills/doxum-runtime/references/patterns.zh-CN.md +0 -159
- package/skills/doxum-runtime/references/projections.en.md +0 -313
- package/skills/doxum-runtime/references/projections.zh-CN.md +0 -306
package/README.md
CHANGED
|
@@ -300,6 +300,8 @@ const ids = derive.keyed.keys(records);
|
|
|
300
300
|
const all = derive.keyed.values(records);
|
|
301
301
|
const entries = derive.keyed.entries(records);
|
|
302
302
|
const active = derive.keyed.get(records, activeRecordId);
|
|
303
|
+
const rowsFromArray = derive.keyed.from(rowArray, row => row.id);
|
|
304
|
+
const effectiveRows = derive.keyed.merge([baseRows, rowOverrides], { conflict: 'last' });
|
|
303
305
|
const visible = derive.keyed.filter(records, record => record.visible);
|
|
304
306
|
const orderedVisible = derive.keyed.subset(records, visibleIds);
|
|
305
307
|
const definedContent = derive.keyed.compact(records, record => record.content);
|
|
@@ -318,10 +320,26 @@ source-relative order. `compact` omits keys whose selected value is `undefined`
|
|
|
318
320
|
uses its optional equality for present values. `filter` and `compact` support the same
|
|
319
321
|
named global/dynamic keyed dependency object as ordinary `derive.keyed`.
|
|
320
322
|
|
|
323
|
+
`from` turns either a static readonly array or `Projection<readonly V[]>` into a keyed
|
|
324
|
+
projection using `keyOf(value)`. Input order becomes formal keyed order; duplicate keys
|
|
325
|
+
are errors rather than implicit overwrites. Static outer arrays are snapshotted at
|
|
326
|
+
definition creation, while key selection remains lazy. Same-key equality-equivalent
|
|
327
|
+
values retain the previous published identity.
|
|
328
|
+
|
|
329
|
+
`merge` combines a fixed list of keyed sources with union membership and an explicit
|
|
330
|
+
`error`, `first`, `last`, or `resolve` conflict policy. Its formal order is the stable
|
|
331
|
+
first occurrence from the sources' formal orders, independent of which source supplies
|
|
332
|
+
the effective value. `derive.keyed.merge([base, overrides], { conflict: 'last' })` is
|
|
333
|
+
therefore the standard sparse-override composition: shared override values win without
|
|
334
|
+
moving their base positions, override-only keys join the union, and removing an override
|
|
335
|
+
falls back to the base without breaking the key's merged membership lifecycle.
|
|
336
|
+
|
|
321
337
|
`groupBy` is the general reverse-index primitive. A source entry may return one group
|
|
322
338
|
key or several; each output bucket contains source keys in the source's formal order,
|
|
323
|
-
and group
|
|
324
|
-
|
|
339
|
+
and group keys are ordered by the earliest current source member that belongs to them.
|
|
340
|
+
If several groups first appear on the same source member, they follow that member's
|
|
341
|
+
selector-result order. Source membership/order changes may therefore reorder group keys.
|
|
342
|
+
`singleton` converts an optional scalar projection into a zero-or-one keyed projection.
|
|
325
343
|
|
|
326
344
|
All keyed projection producers use the public `KeyedProjection<K,V>` handle, including
|
|
327
345
|
collection `observe`, `derive.keyed`, `incremental.collection`, and collection leaves
|
|
@@ -599,8 +617,9 @@ Builds produce root `dist` ESM/CJS/declarations for `doxum`,
|
|
|
599
617
|
The runtime shares one transaction lifecycle; complete mutation operations are
|
|
600
618
|
organized by domain under `core/src/mutation/operations`, with access, first-touch
|
|
601
619
|
recording and publication retaining their own responsibilities.
|
|
602
|
-
The published [runtime skill](skills/doxum-runtime/SKILL.md)
|
|
603
|
-
|
|
620
|
+
The published [runtime skill](skills/doxum-runtime/SKILL.md) is a source-free consumer
|
|
621
|
+
contract with complete public API routing, lifecycle guidance, recipes, integration
|
|
622
|
+
semantics and separate maintainer invariants.
|
|
604
623
|
|
|
605
624
|
## Release
|
|
606
625
|
|
package/dist/index.cjs
CHANGED
|
@@ -2775,6 +2775,279 @@ 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/composition.ts
|
|
2779
|
+
const assertEquality = (equality, role) => {
|
|
2780
|
+
if (equality !== void 0 && typeof equality !== "function") throw new TypeError(`${role} equality must be a function.`);
|
|
2781
|
+
return equality ?? Object.is;
|
|
2782
|
+
};
|
|
2783
|
+
const compileMergeOptions = (options) => {
|
|
2784
|
+
if (options === null || typeof options !== "object") throw new TypeError("derive.keyed.merge requires conflict options.");
|
|
2785
|
+
const equality = assertEquality(options.equality, "derive.keyed.merge");
|
|
2786
|
+
switch (options.conflict) {
|
|
2787
|
+
case "error":
|
|
2788
|
+
case "first":
|
|
2789
|
+
case "last":
|
|
2790
|
+
if ("resolve" in options && options.resolve !== void 0) throw new TypeError("derive.keyed.merge resolve is only valid when conflict is \"resolve\".");
|
|
2791
|
+
return Object.freeze({
|
|
2792
|
+
conflict: options.conflict,
|
|
2793
|
+
equality
|
|
2794
|
+
});
|
|
2795
|
+
case "resolve":
|
|
2796
|
+
if (typeof options.resolve !== "function") throw new TypeError("derive.keyed.merge requires a resolver when conflict is \"resolve\".");
|
|
2797
|
+
return Object.freeze({
|
|
2798
|
+
conflict: options.conflict,
|
|
2799
|
+
equality,
|
|
2800
|
+
resolve: options.resolve
|
|
2801
|
+
});
|
|
2802
|
+
default: throw new TypeError("derive.keyed.merge conflict must be \"error\", \"first\", \"last\", or \"resolve\".");
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
const assertCollectionSources = (sources, expected) => {
|
|
2806
|
+
if (sources.length !== expected) throw new Error("derive.keyed.merge resolved an invalid dependency count.");
|
|
2807
|
+
for (const source of sources) if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2808
|
+
};
|
|
2809
|
+
function createKeyedFrom(source, keyOf, equality) {
|
|
2810
|
+
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.from requires a key selector.");
|
|
2811
|
+
const outputEquality = assertEquality(equality, "derive.keyed.from");
|
|
2812
|
+
let dependency;
|
|
2813
|
+
let staticValues;
|
|
2814
|
+
if (require_transform.isProjection(source)) {
|
|
2815
|
+
if (require_transform.outputKind(source) !== "value") throw new TypeError("derive.keyed.from source projection must be scalar.");
|
|
2816
|
+
dependency = source;
|
|
2817
|
+
} else {
|
|
2818
|
+
if (!Array.isArray(source)) throw new TypeError("derive.keyed.from source must be a scalar projection or readonly array.");
|
|
2819
|
+
staticValues = require_transform.snapshotArray(source);
|
|
2820
|
+
}
|
|
2821
|
+
const [projection] = require_transform.defineProcessor({
|
|
2822
|
+
dependencies: dependency ? [dependency] : [],
|
|
2823
|
+
outputs: [{
|
|
2824
|
+
kind: "collection",
|
|
2825
|
+
equality: outputEquality
|
|
2826
|
+
}],
|
|
2827
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
2828
|
+
const output = evaluation.outputs[0];
|
|
2829
|
+
if (output.kind !== "collection") throw new Error("derive.keyed.from requires a collection output.");
|
|
2830
|
+
let values = staticValues;
|
|
2831
|
+
if (dependency) {
|
|
2832
|
+
const scalar = evaluation.sources[0];
|
|
2833
|
+
if (scalar?.kind !== "value") throw new Error("derive.keyed.from resolved a non-scalar input.");
|
|
2834
|
+
if (!Array.isArray(scalar.value)) throw new TypeError("derive.keyed.from source value must be an array.");
|
|
2835
|
+
values = scalar.value;
|
|
2836
|
+
}
|
|
2837
|
+
if (!values) throw new Error("derive.keyed.from has no source values.");
|
|
2838
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2839
|
+
const order = new Array(values.length);
|
|
2840
|
+
for (let index = 0; index < values.length; index++) {
|
|
2841
|
+
const value = values[index];
|
|
2842
|
+
const key = keyOf(value);
|
|
2843
|
+
require_transform.assertSynchronous(key);
|
|
2844
|
+
if (typeof key !== "string") throw new TypeError("derive.keyed.from key selector must return a string.");
|
|
2845
|
+
if (seen.has(key)) throw new TypeError(`derive.keyed.from duplicate key ${JSON.stringify(key)}.`);
|
|
2846
|
+
seen.add(key);
|
|
2847
|
+
order[index] = key;
|
|
2848
|
+
output.output.set(key, value);
|
|
2849
|
+
}
|
|
2850
|
+
for (const previousKey of output.previous.ids()) if (!seen.has(previousKey)) output.output.remove(previousKey);
|
|
2851
|
+
output.output.order(order);
|
|
2852
|
+
} }),
|
|
2853
|
+
name: "derive.keyed.from"
|
|
2854
|
+
});
|
|
2855
|
+
return projection;
|
|
2856
|
+
}
|
|
2857
|
+
function createKeyedMerge(sources, options) {
|
|
2858
|
+
if (!Array.isArray(sources)) throw new TypeError("derive.keyed.merge sources must be an array of keyed projections.");
|
|
2859
|
+
const capturedSources = require_transform.snapshotArray(sources);
|
|
2860
|
+
for (let index = 0; index < capturedSources.length; index++) require_transform.assertKeyedProjection(capturedSources[index], `derive.keyed.merge source ${index}`);
|
|
2861
|
+
const compiled = compileMergeOptions(options);
|
|
2862
|
+
const [projection] = require_transform.defineProcessor({
|
|
2863
|
+
dependencies: capturedSources,
|
|
2864
|
+
outputs: [{
|
|
2865
|
+
kind: "collection",
|
|
2866
|
+
equality: compiled.equality
|
|
2867
|
+
}],
|
|
2868
|
+
create: () => {
|
|
2869
|
+
const revisions = new Array(capturedSources.length).fill(-1);
|
|
2870
|
+
const remember = (sources) => {
|
|
2871
|
+
for (let index = 0; index < revisions.length; index++) revisions[index] = sources[index].revision;
|
|
2872
|
+
};
|
|
2873
|
+
const writeEffective = (sources, output, key) => {
|
|
2874
|
+
if (compiled.conflict === "first") {
|
|
2875
|
+
for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
|
|
2876
|
+
const source = sources[sourceIndex];
|
|
2877
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2878
|
+
if (!source.read.has(key)) continue;
|
|
2879
|
+
output.set(key, source.read.get(key));
|
|
2880
|
+
return;
|
|
2881
|
+
}
|
|
2882
|
+
output.remove(key);
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
let present = false;
|
|
2886
|
+
let selected;
|
|
2887
|
+
let firstSourceIndex = -1;
|
|
2888
|
+
let contributions;
|
|
2889
|
+
for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
|
|
2890
|
+
const source = sources[sourceIndex];
|
|
2891
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2892
|
+
if (!source.read.has(key)) continue;
|
|
2893
|
+
const value = source.read.get(key);
|
|
2894
|
+
if (!present) {
|
|
2895
|
+
present = true;
|
|
2896
|
+
selected = value;
|
|
2897
|
+
firstSourceIndex = sourceIndex;
|
|
2898
|
+
continue;
|
|
2899
|
+
}
|
|
2900
|
+
if (compiled.conflict === "error") throw new TypeError(`derive.keyed.merge duplicate key ${JSON.stringify(key)}.`);
|
|
2901
|
+
if (compiled.conflict === "last") {
|
|
2902
|
+
selected = value;
|
|
2903
|
+
continue;
|
|
2904
|
+
}
|
|
2905
|
+
if (!contributions) contributions = [Object.freeze({
|
|
2906
|
+
sourceIndex: firstSourceIndex,
|
|
2907
|
+
value: selected
|
|
2908
|
+
}), Object.freeze({
|
|
2909
|
+
sourceIndex,
|
|
2910
|
+
value
|
|
2911
|
+
})];
|
|
2912
|
+
else contributions.push(Object.freeze({
|
|
2913
|
+
sourceIndex,
|
|
2914
|
+
value
|
|
2915
|
+
}));
|
|
2916
|
+
}
|
|
2917
|
+
if (!present) {
|
|
2918
|
+
output.remove(key);
|
|
2919
|
+
return;
|
|
2920
|
+
}
|
|
2921
|
+
if (compiled.conflict === "resolve" && contributions) {
|
|
2922
|
+
const resolved = compiled.resolve(Object.freeze(contributions), key);
|
|
2923
|
+
require_transform.assertSynchronous(resolved);
|
|
2924
|
+
output.set(key, resolved);
|
|
2925
|
+
return;
|
|
2926
|
+
}
|
|
2927
|
+
output.set(key, selected);
|
|
2928
|
+
};
|
|
2929
|
+
const rebuild = (sources, output, previousIds, reset) => {
|
|
2930
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2931
|
+
const order = [];
|
|
2932
|
+
const conflicts = compiled.conflict === "resolve" ? /* @__PURE__ */ new Set() : void 0;
|
|
2933
|
+
for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
|
|
2934
|
+
const source = sources[sourceIndex];
|
|
2935
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2936
|
+
for (const key of source.read.ids()) {
|
|
2937
|
+
const duplicate = seen.has(key);
|
|
2938
|
+
if (!duplicate) {
|
|
2939
|
+
seen.add(key);
|
|
2940
|
+
order.push(key);
|
|
2941
|
+
}
|
|
2942
|
+
if (compiled.conflict === "error") {
|
|
2943
|
+
if (duplicate) throw new TypeError(`derive.keyed.merge duplicate key ${JSON.stringify(key)}.`);
|
|
2944
|
+
output.set(key, source.read.get(key));
|
|
2945
|
+
continue;
|
|
2946
|
+
}
|
|
2947
|
+
if (compiled.conflict === "first") {
|
|
2948
|
+
if (!duplicate) output.set(key, source.read.get(key));
|
|
2949
|
+
continue;
|
|
2950
|
+
}
|
|
2951
|
+
if (compiled.conflict === "last") {
|
|
2952
|
+
output.set(key, source.read.get(key));
|
|
2953
|
+
continue;
|
|
2954
|
+
}
|
|
2955
|
+
if (!duplicate) output.set(key, source.read.get(key));
|
|
2956
|
+
else conflicts.add(key);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
if (conflicts) for (const key of conflicts) writeEffective(sources, output, key);
|
|
2960
|
+
if (!reset) {
|
|
2961
|
+
for (const previousKey of previousIds) if (!seen.has(previousKey)) output.remove(previousKey);
|
|
2962
|
+
}
|
|
2963
|
+
output.order(order);
|
|
2964
|
+
};
|
|
2965
|
+
const rebuildOrder = (sources, output) => {
|
|
2966
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2967
|
+
const order = [];
|
|
2968
|
+
for (const source of sources) {
|
|
2969
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2970
|
+
for (const key of source.read.ids()) {
|
|
2971
|
+
if (seen.has(key)) continue;
|
|
2972
|
+
seen.add(key);
|
|
2973
|
+
order.push(key);
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
output.order(order);
|
|
2977
|
+
};
|
|
2978
|
+
return {
|
|
2979
|
+
evaluate: (evaluation) => {
|
|
2980
|
+
const output = evaluation.outputs[0];
|
|
2981
|
+
if (output.kind !== "collection") throw new Error("derive.keyed.merge requires a collection output.");
|
|
2982
|
+
assertCollectionSources(evaluation.sources, capturedSources.length);
|
|
2983
|
+
if (evaluation.reset) {
|
|
2984
|
+
rebuild(evaluation.sources, output.output, output.previous.ids(), true);
|
|
2985
|
+
remember(evaluation.sources);
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2988
|
+
const affected = /* @__PURE__ */ new Set();
|
|
2989
|
+
let fullRebuild = false;
|
|
2990
|
+
let orderDirty = false;
|
|
2991
|
+
for (let sourceIndex = 0; sourceIndex < evaluation.sources.length; sourceIndex++) {
|
|
2992
|
+
const source = evaluation.sources[sourceIndex];
|
|
2993
|
+
if (revisions[sourceIndex] === source.revision) continue;
|
|
2994
|
+
if (source.kind !== "collection") throw new Error("derive.keyed.merge requires collection inputs.");
|
|
2995
|
+
const change = source.change;
|
|
2996
|
+
if (!change || change.kind === "reset") {
|
|
2997
|
+
fullRebuild = true;
|
|
2998
|
+
continue;
|
|
2999
|
+
}
|
|
3000
|
+
for (const entry of change.added) affected.add(entry.key);
|
|
3001
|
+
for (const entry of change.updated) affected.add(entry.key);
|
|
3002
|
+
for (const entry of change.removed) affected.add(entry.key);
|
|
3003
|
+
if (Boolean(change.added.length || change.removed.length || change.order)) orderDirty = true;
|
|
3004
|
+
}
|
|
3005
|
+
if (fullRebuild) {
|
|
3006
|
+
rebuild(evaluation.sources, output.output, output.previous.ids(), false);
|
|
3007
|
+
remember(evaluation.sources);
|
|
3008
|
+
return;
|
|
3009
|
+
}
|
|
3010
|
+
for (const key of affected) writeEffective(evaluation.sources, output.output, key);
|
|
3011
|
+
if (orderDirty) rebuildOrder(evaluation.sources, output.output);
|
|
3012
|
+
remember(evaluation.sources);
|
|
3013
|
+
},
|
|
3014
|
+
release: () => revisions.fill(-1)
|
|
3015
|
+
};
|
|
3016
|
+
},
|
|
3017
|
+
name: "derive.keyed.merge"
|
|
3018
|
+
});
|
|
3019
|
+
return projection;
|
|
3020
|
+
}
|
|
3021
|
+
const createKeyedSingleton = (source, keyOf, equality = Object.is) => {
|
|
3022
|
+
if (require_transform.outputKind(source) !== "value") throw new TypeError("derive.keyed.singleton source must be a scalar projection.");
|
|
3023
|
+
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.singleton requires a key selector.");
|
|
3024
|
+
const [projection] = require_transform.defineProcessor({
|
|
3025
|
+
dependencies: [source],
|
|
3026
|
+
outputs: [{
|
|
3027
|
+
kind: "collection",
|
|
3028
|
+
equality
|
|
3029
|
+
}],
|
|
3030
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
3031
|
+
const scalar = evaluation.sources[0];
|
|
3032
|
+
const output = evaluation.outputs[0];
|
|
3033
|
+
if (scalar.kind !== "value" || output.kind !== "collection") throw new Error("derive.keyed.singleton resolved invalid input/output kinds.");
|
|
3034
|
+
const previous = output.previous.ids();
|
|
3035
|
+
if (scalar.value === void 0) {
|
|
3036
|
+
for (const key of previous) output.output.remove(key);
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
3039
|
+
const key = keyOf(scalar.value);
|
|
3040
|
+
require_transform.assertSynchronous(key);
|
|
3041
|
+
if (typeof key !== "string") throw new TypeError("derive.keyed.singleton key selector must return a string.");
|
|
3042
|
+
for (const previousKey of previous) if (previousKey !== key) output.output.remove(previousKey);
|
|
3043
|
+
output.output.set(key, scalar.value);
|
|
3044
|
+
if (previous.length !== 1 || previous[0] !== key) output.output.order([key]);
|
|
3045
|
+
} }),
|
|
3046
|
+
name: "derive.keyed.singleton"
|
|
3047
|
+
});
|
|
3048
|
+
return projection;
|
|
3049
|
+
};
|
|
3050
|
+
//#endregion
|
|
2778
3051
|
//#region core/src/projection/derive/keyed.ts
|
|
2779
3052
|
const compileOrderedKeys = (value, role) => {
|
|
2780
3053
|
if (!Array.isArray(value)) throw new TypeError(`${role} must be an array of string keys.`);
|
|
@@ -3187,7 +3460,8 @@ function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
|
|
|
3187
3460
|
return;
|
|
3188
3461
|
} else {
|
|
3189
3462
|
for (const entry of change.removed) {
|
|
3190
|
-
|
|
3463
|
+
const changed = removeSourceKey(entry.key, affected);
|
|
3464
|
+
orderDirty ||= changed;
|
|
3191
3465
|
dependencies.remove(entry.key);
|
|
3192
3466
|
dirty.delete(entry.key);
|
|
3193
3467
|
}
|
|
@@ -3200,7 +3474,10 @@ function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
|
|
|
3200
3474
|
}
|
|
3201
3475
|
all ||= dependencies.collectInvalidated(evaluation.sources, dirty);
|
|
3202
3476
|
if (all) for (const key of driver.read.ids()) dirty.add(key);
|
|
3203
|
-
for (const key of dirty)
|
|
3477
|
+
for (const key of dirty) {
|
|
3478
|
+
const changed = selectGroups(driver, evaluation.sources, key, affected);
|
|
3479
|
+
orderDirty ||= changed;
|
|
3480
|
+
}
|
|
3204
3481
|
if (structural) markAllGroups(output, affected);
|
|
3205
3482
|
publishAffected(affected, output, orderDirty);
|
|
3206
3483
|
dependencies.remember(evaluation.sources);
|
|
@@ -3217,40 +3494,13 @@ function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
|
|
|
3217
3494
|
});
|
|
3218
3495
|
return projection;
|
|
3219
3496
|
}
|
|
3220
|
-
const createKeyedSingleton = (source, keyOf, equality = Object.is) => {
|
|
3221
|
-
if (require_transform.outputKind(source) !== "value") throw new TypeError("derive.keyed.singleton source must be a scalar projection.");
|
|
3222
|
-
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.singleton requires a key selector.");
|
|
3223
|
-
const [projection] = require_transform.defineProcessor({
|
|
3224
|
-
dependencies: [source],
|
|
3225
|
-
outputs: [{
|
|
3226
|
-
kind: "collection",
|
|
3227
|
-
equality
|
|
3228
|
-
}],
|
|
3229
|
-
create: () => ({ evaluate: (evaluation) => {
|
|
3230
|
-
const scalar = evaluation.sources[0];
|
|
3231
|
-
const output = evaluation.outputs[0];
|
|
3232
|
-
if (scalar.kind !== "value" || output.kind !== "collection") throw new Error("derive.keyed.singleton resolved invalid input/output kinds.");
|
|
3233
|
-
const previous = output.previous.ids();
|
|
3234
|
-
if (scalar.value === void 0) {
|
|
3235
|
-
for (const key of previous) output.output.remove(key);
|
|
3236
|
-
return;
|
|
3237
|
-
}
|
|
3238
|
-
const key = keyOf(scalar.value);
|
|
3239
|
-
require_transform.assertSynchronous(key);
|
|
3240
|
-
if (typeof key !== "string") throw new TypeError("derive.keyed.singleton key selector must return a string.");
|
|
3241
|
-
for (const previousKey of previous) if (previousKey !== key) output.output.remove(previousKey);
|
|
3242
|
-
output.output.set(key, scalar.value);
|
|
3243
|
-
if (previous.length !== 1 || previous[0] !== key) output.output.order([key]);
|
|
3244
|
-
} }),
|
|
3245
|
-
name: "derive.keyed.singleton"
|
|
3246
|
-
});
|
|
3247
|
-
return projection;
|
|
3248
|
-
};
|
|
3249
3497
|
const keyedDerive = Object.assign(createKeyedDerive, {
|
|
3250
3498
|
keys: createKeyedKeys,
|
|
3251
3499
|
values: createKeyedValues,
|
|
3252
3500
|
entries: createKeyedEntries,
|
|
3253
3501
|
get: createKeyedGet,
|
|
3502
|
+
from: createKeyedFrom,
|
|
3503
|
+
merge: createKeyedMerge,
|
|
3254
3504
|
groupBy: createKeyedGroupBy,
|
|
3255
3505
|
singleton: createKeyedSingleton,
|
|
3256
3506
|
subset: createKeyedSubset,
|