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/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.`);
@@ -3186,7 +3459,8 @@ function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
3186
3459
  return;
3187
3460
  } else {
3188
3461
  for (const entry of change.removed) {
3189
- orderDirty ||= removeSourceKey(entry.key, affected);
3462
+ const changed = removeSourceKey(entry.key, affected);
3463
+ orderDirty ||= changed;
3190
3464
  dependencies.remove(entry.key);
3191
3465
  dirty.delete(entry.key);
3192
3466
  }
@@ -3199,7 +3473,10 @@ function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
3199
3473
  }
3200
3474
  all ||= dependencies.collectInvalidated(evaluation.sources, dirty);
3201
3475
  if (all) for (const key of driver.read.ids()) dirty.add(key);
3202
- for (const key of dirty) orderDirty ||= selectGroups(driver, evaluation.sources, key, affected);
3476
+ for (const key of dirty) {
3477
+ const changed = selectGroups(driver, evaluation.sources, key, affected);
3478
+ orderDirty ||= changed;
3479
+ }
3203
3480
  if (structural) markAllGroups(output, affected);
3204
3481
  publishAffected(affected, output, orderDirty);
3205
3482
  dependencies.remember(evaluation.sources);
@@ -3216,40 +3493,13 @@ function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
3216
3493
  });
3217
3494
  return projection;
3218
3495
  }
3219
- const createKeyedSingleton = (source, keyOf, equality = Object.is) => {
3220
- if (outputKind(source) !== "value") throw new TypeError("derive.keyed.singleton source must be a scalar projection.");
3221
- if (typeof keyOf !== "function") throw new TypeError("derive.keyed.singleton requires a key selector.");
3222
- const [projection] = defineProcessor({
3223
- dependencies: [source],
3224
- outputs: [{
3225
- kind: "collection",
3226
- equality
3227
- }],
3228
- create: () => ({ evaluate: (evaluation) => {
3229
- const scalar = evaluation.sources[0];
3230
- const output = evaluation.outputs[0];
3231
- if (scalar.kind !== "value" || output.kind !== "collection") throw new Error("derive.keyed.singleton resolved invalid input/output kinds.");
3232
- const previous = output.previous.ids();
3233
- if (scalar.value === void 0) {
3234
- for (const key of previous) output.output.remove(key);
3235
- return;
3236
- }
3237
- const key = keyOf(scalar.value);
3238
- assertSynchronous(key);
3239
- if (typeof key !== "string") throw new TypeError("derive.keyed.singleton key selector must return a string.");
3240
- for (const previousKey of previous) if (previousKey !== key) output.output.remove(previousKey);
3241
- output.output.set(key, scalar.value);
3242
- if (previous.length !== 1 || previous[0] !== key) output.output.order([key]);
3243
- } }),
3244
- name: "derive.keyed.singleton"
3245
- });
3246
- return projection;
3247
- };
3248
3496
  const keyedDerive = Object.assign(createKeyedDerive, {
3249
3497
  keys: createKeyedKeys,
3250
3498
  values: createKeyedValues,
3251
3499
  entries: createKeyedEntries,
3252
3500
  get: createKeyedGet,
3501
+ from: createKeyedFrom,
3502
+ merge: createKeyedMerge,
3253
3503
  groupBy: createKeyedGroupBy,
3254
3504
  singleton: createKeyedSingleton,
3255
3505
  subset: createKeyedSubset,