doxum 0.1.30 → 0.1.32
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 +29 -3
- package/dist/advanced.cjs +1 -1
- package/dist/advanced.d.cts +1 -1
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.js +1 -1
- package/dist/{dependency-B3E65M1E.d.cts → dependency-B1qIJJBL.d.cts} +15 -3
- package/dist/{dependency-BYbdDHNa.d.ts → dependency-BRgPvyN6.d.ts} +15 -3
- package/dist/index.cjs +276 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +276 -30
- package/dist/index.js.map +1 -1
- package/dist/{transform-B6YxfR0R.cjs → transform-CENhTaVN.cjs} +42 -18
- package/dist/{transform-B6YxfR0R.cjs.map → transform-CENhTaVN.cjs.map} +1 -1
- package/dist/{transform-BQY9I6il.js → transform-gOtqcdHe.js} +42 -18
- package/dist/{transform-BQY9I6il.js.map → transform-gOtqcdHe.js.map} +1 -1
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +6 -5
- package/skills/doxum-runtime/references/projections.md +44 -3
- package/skills/doxum-runtime/references/public-api.md +34 -1
- package/skills/doxum-runtime/references/recipes.md +35 -2
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
|
|
@@ -336,14 +354,22 @@ projection dependencies by name:
|
|
|
336
354
|
const cardContent = derive.keyed(
|
|
337
355
|
items,
|
|
338
356
|
{
|
|
357
|
+
metadata: { source: metadataByItemId },
|
|
339
358
|
record: { source: records, key: item => item.recordId },
|
|
340
359
|
view: activeView,
|
|
341
360
|
fields: visibleFields,
|
|
342
361
|
},
|
|
343
|
-
(item, itemId, { record, view, fields }) =>
|
|
362
|
+
(item, itemId, { metadata, record, view, fields }) =>
|
|
363
|
+
renderCard(itemId, item, metadata, record, view, fields)
|
|
344
364
|
);
|
|
345
365
|
```
|
|
346
366
|
|
|
367
|
+
Use `{ source }` when driver and dependency use the same key. It directly binds driver
|
|
368
|
+
key `K` to source key `K`, requires the driver key type to be assignable to the source
|
|
369
|
+
key type, and avoids a redundant `key: (_value, id) => id` selector. Source value or
|
|
370
|
+
membership changes invalidate only the driver entry with that key; source order-only
|
|
371
|
+
changes do not invalidate it.
|
|
372
|
+
|
|
347
373
|
For `{ source, key }` dependencies, the Runtime owns output-key → source-key bindings
|
|
348
374
|
and reverse invalidation. A missing source entry resolves to `undefined` but remains
|
|
349
375
|
bound, so adding it later invalidates the dependent output keys. An ordinary projection
|
package/dist/advanced.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-CENhTaVN.cjs");
|
|
3
3
|
const require_record = require("./record-ChYUi8PP.cjs");
|
|
4
4
|
//#region core/src/projection/advanced.ts
|
|
5
5
|
const resetCollectionChange = Object.freeze({ kind: "reset" });
|
package/dist/advanced.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as ProjectionChange, d as CollectionRead, l as CollectionChange, n as KeyedDependencyValues, o as KeyedProjection, s as Projection, t as KeyedDependencies, u as CollectionDraft } from "./dependency-
|
|
1
|
+
import { c as ProjectionChange, d as CollectionRead, l as CollectionChange, n as KeyedDependencyValues, o as KeyedProjection, s as Projection, t as KeyedDependencies, u as CollectionDraft } from "./dependency-B1qIJJBL.cjs";
|
|
2
2
|
|
|
3
3
|
//#region core/src/projection/collection/change.d.ts
|
|
4
4
|
declare const collectionChange: {
|
package/dist/advanced.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as ProjectionChange, d as CollectionRead, l as CollectionChange, n as KeyedDependencyValues, o as KeyedProjection, s as Projection, t as KeyedDependencies, u as CollectionDraft } from "./dependency-
|
|
1
|
+
import { c as ProjectionChange, d as CollectionRead, l as CollectionChange, n as KeyedDependencyValues, o as KeyedProjection, s as Projection, t as KeyedDependencies, u as CollectionDraft } from "./dependency-BRgPvyN6.js";
|
|
2
2
|
|
|
3
3
|
//#region core/src/projection/collection/change.d.ts
|
|
4
4
|
declare const collectionChange: {
|
package/dist/advanced.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as defineProcessor, S as compileProjectionDependencies, l as assertSynchronous, p as collectionChange, t as createKeyedTransform, w as collectionView } from "./transform-
|
|
1
|
+
import { D as defineProcessor, S as compileProjectionDependencies, l as assertSynchronous, p as collectionChange, t as createKeyedTransform, w as collectionView } from "./transform-gOtqcdHe.js";
|
|
2
2
|
import { n as isPlainObject } from "./record-B2yRhYsc.js";
|
|
3
3
|
//#region core/src/projection/advanced.ts
|
|
4
4
|
const resetCollectionChange = Object.freeze({ kind: "reset" });
|
|
@@ -113,6 +113,11 @@ type SingularKeyedDependency<DriverKey extends string, DriverValue, SourceKey ex
|
|
|
113
113
|
readonly key: (value: DriverValue, key: DriverKey) => Synchronous<SourceKey | undefined>;
|
|
114
114
|
readonly keys?: never;
|
|
115
115
|
};
|
|
116
|
+
type SameKeyedDependency<SourceKey extends string, SourceValue> = {
|
|
117
|
+
readonly source: KeyedProjection<SourceKey, SourceValue>;
|
|
118
|
+
readonly key?: never;
|
|
119
|
+
readonly keys?: never;
|
|
120
|
+
};
|
|
116
121
|
type PluralKeyedDependency<DriverKey extends string, DriverValue, SourceKey extends string, SourceValue> = {
|
|
117
122
|
readonly source: KeyedProjection<SourceKey, SourceValue>;
|
|
118
123
|
readonly keys: (value: DriverValue, key: DriverKey) => Synchronous<readonly SourceKey[]>;
|
|
@@ -122,8 +127,15 @@ type KeyedDependency<DriverKey extends string, DriverValue> = (Projection<unknow
|
|
|
122
127
|
readonly source?: never;
|
|
123
128
|
readonly key?: never;
|
|
124
129
|
readonly keys?: never;
|
|
125
|
-
}) | SingularKeyedDependency<DriverKey, DriverValue, string, unknown> | PluralKeyedDependency<DriverKey, DriverValue, string, unknown>;
|
|
126
|
-
type
|
|
130
|
+
}) | SameKeyedDependency<string, unknown> | SingularKeyedDependency<DriverKey, DriverValue, string, unknown> | PluralKeyedDependency<DriverKey, DriverValue, string, unknown>;
|
|
131
|
+
type SameKeyConstraint<DriverKey extends string, Dependency> = Dependency extends {
|
|
132
|
+
readonly source: KeyedProjection<infer SourceKey extends string, unknown>;
|
|
133
|
+
} ? Dependency extends {
|
|
134
|
+
readonly key: unknown;
|
|
135
|
+
} | {
|
|
136
|
+
readonly keys: unknown;
|
|
137
|
+
} ? unknown : DriverKey extends SourceKey ? unknown : never : unknown;
|
|
138
|
+
type KeyedDependencies<DriverKey extends string, DriverValue, D extends object> = { readonly [P in keyof D]: P extends string ? KeyedDependency<DriverKey, DriverValue> & SameKeyConstraint<DriverKey, D[P]> : never };
|
|
127
139
|
type KeyedDependencyValues<D extends object> = { readonly [P in keyof D]: D[P] extends {
|
|
128
140
|
readonly source: KeyedProjection<infer K extends string, infer V>;
|
|
129
141
|
readonly keys: (...args: never[]) => unknown;
|
|
@@ -132,4 +144,4 @@ type KeyedDependencyValues<D extends object> = { readonly [P in keyof D]: D[P] e
|
|
|
132
144
|
} ? V | undefined : D[P] extends Projection<infer T> ? T : never };
|
|
133
145
|
//#endregion
|
|
134
146
|
export { ProjectionDisposedError as _, Input as a, ProjectionChange as c, CollectionRead as d, ExternalCollectionEvent as f, ExternalValueSource as g, ExternalValueEvent as h, CollectionInputDraft as i, CollectionChange as l, ExternalCollectionSource as m, KeyedDependencyValues as n, KeyedProjection as o, ExternalCollectionRead as p, CollectionInput as r, Projection as s, KeyedDependencies as t, CollectionDraft as u, ProjectionError as v };
|
|
135
|
-
//# sourceMappingURL=dependency-
|
|
147
|
+
//# sourceMappingURL=dependency-B1qIJJBL.d.cts.map
|
|
@@ -113,6 +113,11 @@ type SingularKeyedDependency<DriverKey extends string, DriverValue, SourceKey ex
|
|
|
113
113
|
readonly key: (value: DriverValue, key: DriverKey) => Synchronous<SourceKey | undefined>;
|
|
114
114
|
readonly keys?: never;
|
|
115
115
|
};
|
|
116
|
+
type SameKeyedDependency<SourceKey extends string, SourceValue> = {
|
|
117
|
+
readonly source: KeyedProjection<SourceKey, SourceValue>;
|
|
118
|
+
readonly key?: never;
|
|
119
|
+
readonly keys?: never;
|
|
120
|
+
};
|
|
116
121
|
type PluralKeyedDependency<DriverKey extends string, DriverValue, SourceKey extends string, SourceValue> = {
|
|
117
122
|
readonly source: KeyedProjection<SourceKey, SourceValue>;
|
|
118
123
|
readonly keys: (value: DriverValue, key: DriverKey) => Synchronous<readonly SourceKey[]>;
|
|
@@ -122,8 +127,15 @@ type KeyedDependency<DriverKey extends string, DriverValue> = (Projection<unknow
|
|
|
122
127
|
readonly source?: never;
|
|
123
128
|
readonly key?: never;
|
|
124
129
|
readonly keys?: never;
|
|
125
|
-
}) | SingularKeyedDependency<DriverKey, DriverValue, string, unknown> | PluralKeyedDependency<DriverKey, DriverValue, string, unknown>;
|
|
126
|
-
type
|
|
130
|
+
}) | SameKeyedDependency<string, unknown> | SingularKeyedDependency<DriverKey, DriverValue, string, unknown> | PluralKeyedDependency<DriverKey, DriverValue, string, unknown>;
|
|
131
|
+
type SameKeyConstraint<DriverKey extends string, Dependency> = Dependency extends {
|
|
132
|
+
readonly source: KeyedProjection<infer SourceKey extends string, unknown>;
|
|
133
|
+
} ? Dependency extends {
|
|
134
|
+
readonly key: unknown;
|
|
135
|
+
} | {
|
|
136
|
+
readonly keys: unknown;
|
|
137
|
+
} ? unknown : DriverKey extends SourceKey ? unknown : never : unknown;
|
|
138
|
+
type KeyedDependencies<DriverKey extends string, DriverValue, D extends object> = { readonly [P in keyof D]: P extends string ? KeyedDependency<DriverKey, DriverValue> & SameKeyConstraint<DriverKey, D[P]> : never };
|
|
127
139
|
type KeyedDependencyValues<D extends object> = { readonly [P in keyof D]: D[P] extends {
|
|
128
140
|
readonly source: KeyedProjection<infer K extends string, infer V>;
|
|
129
141
|
readonly keys: (...args: never[]) => unknown;
|
|
@@ -132,4 +144,4 @@ type KeyedDependencyValues<D extends object> = { readonly [P in keyof D]: D[P] e
|
|
|
132
144
|
} ? V | undefined : D[P] extends Projection<infer T> ? T : never };
|
|
133
145
|
//#endregion
|
|
134
146
|
export { ProjectionDisposedError as _, Input as a, ProjectionChange as c, CollectionRead as d, ExternalCollectionEvent as f, ExternalValueSource as g, ExternalValueEvent as h, CollectionInputDraft as i, CollectionChange as l, ExternalCollectionSource as m, KeyedDependencyValues as n, KeyedProjection as o, ExternalCollectionRead as p, CollectionInput as r, Projection as s, KeyedDependencies as t, CollectionDraft as u, ProjectionError as v };
|
|
135
|
-
//# sourceMappingURL=dependency-
|
|
147
|
+
//# sourceMappingURL=dependency-BRgPvyN6.d.ts.map
|
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-CENhTaVN.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,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.`);
|
|
@@ -3221,40 +3494,13 @@ function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
|
|
|
3221
3494
|
});
|
|
3222
3495
|
return projection;
|
|
3223
3496
|
}
|
|
3224
|
-
const createKeyedSingleton = (source, keyOf, equality = Object.is) => {
|
|
3225
|
-
if (require_transform.outputKind(source) !== "value") throw new TypeError("derive.keyed.singleton source must be a scalar projection.");
|
|
3226
|
-
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.singleton requires a key selector.");
|
|
3227
|
-
const [projection] = require_transform.defineProcessor({
|
|
3228
|
-
dependencies: [source],
|
|
3229
|
-
outputs: [{
|
|
3230
|
-
kind: "collection",
|
|
3231
|
-
equality
|
|
3232
|
-
}],
|
|
3233
|
-
create: () => ({ evaluate: (evaluation) => {
|
|
3234
|
-
const scalar = evaluation.sources[0];
|
|
3235
|
-
const output = evaluation.outputs[0];
|
|
3236
|
-
if (scalar.kind !== "value" || output.kind !== "collection") throw new Error("derive.keyed.singleton resolved invalid input/output kinds.");
|
|
3237
|
-
const previous = output.previous.ids();
|
|
3238
|
-
if (scalar.value === void 0) {
|
|
3239
|
-
for (const key of previous) output.output.remove(key);
|
|
3240
|
-
return;
|
|
3241
|
-
}
|
|
3242
|
-
const key = keyOf(scalar.value);
|
|
3243
|
-
require_transform.assertSynchronous(key);
|
|
3244
|
-
if (typeof key !== "string") throw new TypeError("derive.keyed.singleton key selector must return a string.");
|
|
3245
|
-
for (const previousKey of previous) if (previousKey !== key) output.output.remove(previousKey);
|
|
3246
|
-
output.output.set(key, scalar.value);
|
|
3247
|
-
if (previous.length !== 1 || previous[0] !== key) output.output.order([key]);
|
|
3248
|
-
} }),
|
|
3249
|
-
name: "derive.keyed.singleton"
|
|
3250
|
-
});
|
|
3251
|
-
return projection;
|
|
3252
|
-
};
|
|
3253
3497
|
const keyedDerive = Object.assign(createKeyedDerive, {
|
|
3254
3498
|
keys: createKeyedKeys,
|
|
3255
3499
|
values: createKeyedValues,
|
|
3256
3500
|
entries: createKeyedEntries,
|
|
3257
3501
|
get: createKeyedGet,
|
|
3502
|
+
from: createKeyedFrom,
|
|
3503
|
+
merge: createKeyedMerge,
|
|
3258
3504
|
groupBy: createKeyedGroupBy,
|
|
3259
3505
|
singleton: createKeyedSingleton,
|
|
3260
3506
|
subset: createKeyedSubset,
|