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 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 order follows first appearance in that same order. `singleton` converts an
324
- optional scalar projection into a zero-or-one keyed projection.
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 }) => renderCard(itemId, item, 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-B6YxfR0R.cjs");
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" });
@@ -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-B3E65M1E.cjs";
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: {
@@ -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-BYbdDHNa.js";
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-BQY9I6il.js";
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 KeyedDependencies<DriverKey extends string, DriverValue, D extends object> = { readonly [P in keyof D]: P extends string ? KeyedDependency<DriverKey, DriverValue> : never };
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-B3E65M1E.d.cts.map
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 KeyedDependencies<DriverKey extends string, DriverValue, D extends object> = { readonly [P in keyof D]: P extends string ? KeyedDependency<DriverKey, DriverValue> : never };
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-BYbdDHNa.d.ts.map
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-B6YxfR0R.cjs");
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,