doxum 0.1.25 → 0.1.27

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.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as ProjectionError, c as snapshotCollectionView, d as isProjection, f as ownProjections, h as profile, i as ProjectionDisposedError, l as defineProcessor, m as projectionRef, n as assertSynchronous, o as collectionView, p as producerOf, r as createScheduler, s as mapRead, t as assertScope, u as defineSource } from "./scheduler--137L3j-.js";
1
+ import { S as profile, _ as isProjection, a as ProjectionError, b as producerOf, c as createCollectionChange, d as snapshotDependencyValue, f as collectionView, g as defineSource, h as defineProcessor, i as ProjectionDisposedError, l as diffCollection, m as snapshotCollectionView, n as assertSynchronous, o as collectionChange, p as mapRead, r as createScheduler, s as collectionHasStructuralChange, t as assertScope, u as compileProjectionDependencies, v as outputDefinitionOf, x as projectionRef, y as ownProjections } from "./scheduler-vzf3OcjT.js";
2
2
  import { n as isPlainObject, r as isRecord, t as installOwn } from "./record-B2yRhYsc.js";
3
3
  import { c as AddressIndex, d as invalidValue, f as children, g as validate, h as parent, i as contextOf, l as MutationRejected, m as is, o as decodeChanges, p as contains$1, r as bindContext, s as sealChanges, t as assertRuntimeWritable, u as fail } from "./driver-B5vdPohL.js";
4
4
  //#region core/src/schema/model.ts
@@ -9,7 +9,7 @@ const field = (validator) => node$2({
9
9
  kind: "field",
10
10
  ...validator ? { validator } : {}
11
11
  });
12
- const optional = (value) => {
12
+ function optional(value) {
13
13
  const concrete = schemaNodeOf(value);
14
14
  if (![
15
15
  "field",
@@ -22,7 +22,7 @@ const optional = (value) => {
22
22
  ...concrete,
23
23
  optional: true
24
24
  });
25
- };
25
+ }
26
26
  const object = (shape) => node$2({
27
27
  kind: "object",
28
28
  shape: Object.freeze(Object.fromEntries(Object.entries(shape).map(([key, value]) => [key, schemaNodeOf(value)])))
@@ -2734,14 +2734,9 @@ const isExternalSource = (source) => {
2734
2734
  return (candidate.kind === "value" || candidate.kind === "collection") && typeof candidate.current === "function" && typeof candidate.revision === "function" && typeof candidate.subscribe === "function";
2735
2735
  };
2736
2736
  //#endregion
2737
- //#region core/src/projection/derive.ts
2738
- const sourceValue = (source) => source.kind === "value" ? source.value : snapshotCollectionView(source.read);
2737
+ //#region core/src/projection/derive/keyed.ts
2739
2738
  const outputKind = (projection) => {
2740
- const ref = projectionRef(projection);
2741
- const definition = ref.producer;
2742
- const output = definition.kind === "source" ? definition.output : definition.outputs[ref.output];
2743
- if (!output) throw new TypeError("Projection output does not exist.");
2744
- return output.kind;
2739
+ return outputDefinitionOf(projection).kind;
2745
2740
  };
2746
2741
  const assertKeyedProjection = (projection, role) => {
2747
2742
  if (outputKind(projection) !== "collection") throw new TypeError(`${role} must be a keyed collection projection.`);
@@ -2767,41 +2762,29 @@ const bindKey = (index, outputKey, sourceKey) => {
2767
2762
  else index.reverse.set(sourceKey, new Set([outputKey]));
2768
2763
  };
2769
2764
  const unbindKey = (indexes, outputKey) => {
2770
- for (const index of indexes) bindKey(index, outputKey, void 0);
2765
+ for (const index of indexes) if (index) bindKey(index, outputKey, void 0);
2771
2766
  };
2772
2767
  const createBindingIndex = () => ({
2773
2768
  forward: /* @__PURE__ */ new Map(),
2774
2769
  reverse: /* @__PURE__ */ new Map()
2775
2770
  });
2776
- function createValueDerive(dependencies, compute, equality = Object.is) {
2777
- if (!isPlainObject(dependencies) || isProjection(dependencies)) throw new TypeError("Derive dependencies must be a plain object.");
2778
- const names = Object.keys(dependencies);
2779
- const ordered = names.map((name) => dependencies[name]);
2780
- ordered.forEach(projectionRef);
2781
- const [projection] = defineProcessor({
2782
- dependencies: ordered,
2783
- outputs: [{
2784
- kind: "value",
2785
- equality
2786
- }],
2787
- create: () => ({ evaluate: (evaluation) => {
2788
- const output = evaluation.outputs[0];
2789
- if (output.kind !== "value") throw new Error("derive requires a value output.");
2790
- const values = Object.create(null);
2791
- for (let index = 0; index < names.length; index++) values[names[index]] = sourceValue(evaluation.sources[index]);
2792
- const next = compute(Object.freeze(values));
2793
- assertSynchronous(next);
2794
- output.output.set(next);
2795
- } })
2796
- });
2797
- return projection;
2798
- }
2799
- function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
2771
+ const emptyKeyedDependencies = Object.freeze(Object.create(null));
2772
+ const absent = Symbol("keyed-transform-absent");
2773
+ const compileOrderedKeys = (value, role) => {
2774
+ if (!Array.isArray(value)) throw new TypeError(`${role} must be an array of string keys.`);
2775
+ const seen = /* @__PURE__ */ new Set();
2776
+ for (const key of value) {
2777
+ if (typeof key !== "string") throw new TypeError(`${role} must contain only string keys.`);
2778
+ if (seen.has(key)) throw new TypeError(`${role} must not contain duplicate keys.`);
2779
+ seen.add(key);
2780
+ }
2781
+ return {
2782
+ keys: value,
2783
+ requested: seen
2784
+ };
2785
+ };
2786
+ const createKeyedTransform = (source, dependencySpecs, select, equality, name) => {
2800
2787
  assertKeyedProjection(source, "Keyed derive source");
2801
- const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
2802
- const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
2803
- const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
2804
- if (typeof select !== "function") throw new TypeError("Keyed derive requires a selector.");
2805
2788
  const dependencies = [source];
2806
2789
  const dependencyNames = [];
2807
2790
  const keyed = [];
@@ -2832,81 +2815,91 @@ function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybe
2832
2815
  }],
2833
2816
  create: () => {
2834
2817
  const bindings = keyed.map((dependency) => dependency ? createBindingIndex() : void 0);
2835
- const bindingIndexes = bindings.filter((index) => index !== void 0);
2836
2818
  const globalValues = keyed.map(() => void 0);
2837
- let revisions = Object.freeze([]);
2819
+ const revisions = [];
2838
2820
  const clearBindings = () => {
2839
2821
  for (const index of bindings) {
2840
2822
  index?.forward.clear();
2841
2823
  index?.reverse.clear();
2842
2824
  }
2843
2825
  };
2826
+ const rememberRevisions = (sources) => {
2827
+ revisions.length = sources.length;
2828
+ for (let index = 0; index < sources.length; index++) revisions[index] = sources[index].revision;
2829
+ };
2844
2830
  return {
2845
2831
  evaluate: (evaluation) => {
2846
2832
  const driver = evaluation.sources[0];
2847
2833
  const output = evaluation.outputs[0];
2848
2834
  if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("Keyed derive requires collection input and output.");
2849
- const currentRevisions = evaluation.sources.map((current) => current.revision);
2850
2835
  for (let index = 0; index < keyed.length; index++) {
2851
2836
  if (keyed[index]) continue;
2852
- if (evaluation.reset || revisions[index + 1] !== currentRevisions[index + 1]) globalValues[index] = sourceValue(evaluation.sources[index + 1]);
2837
+ const dependencySource = evaluation.sources[index + 1];
2838
+ if (evaluation.reset || revisions[index + 1] !== dependencySource.revision) globalValues[index] = snapshotDependencyValue(dependencySource);
2853
2839
  }
2854
2840
  const project = (key) => {
2855
2841
  if (!driver.read.has(key)) return;
2856
2842
  const value = driver.read.get(key);
2857
- const dependencyValues = Object.create(null);
2858
- for (let index = 0; index < keyed.length; index++) {
2859
- const dependency = keyed[index];
2860
- if (!dependency) {
2861
- dependencyValues[dependencyNames[index]] = globalValues[index];
2862
- continue;
2843
+ let dependencyValues = emptyKeyedDependencies;
2844
+ if (keyed.length) {
2845
+ const values = Object.create(null);
2846
+ for (let index = 0; index < keyed.length; index++) {
2847
+ const dependency = keyed[index];
2848
+ if (!dependency) {
2849
+ values[dependencyNames[index]] = globalValues[index];
2850
+ continue;
2851
+ }
2852
+ const sourceContext = evaluation.sources[index + 1];
2853
+ if (sourceContext.kind !== "collection") throw new TypeError("Dynamic keyed dependency resolved to a non-collection source.");
2854
+ const selectedKey = dependency.key(value, key);
2855
+ assertSynchronous(selectedKey);
2856
+ if (selectedKey !== void 0 && typeof selectedKey !== "string") throw new TypeError("Dynamic keyed dependency keys must be strings or undefined.");
2857
+ bindKey(bindings[index], key, selectedKey);
2858
+ values[dependencyNames[index]] = selectedKey === void 0 ? void 0 : sourceContext.read.get(selectedKey);
2863
2859
  }
2864
- const sourceContext = evaluation.sources[index + 1];
2865
- if (sourceContext.kind !== "collection") throw new TypeError("Dynamic keyed dependency resolved to a non-collection source.");
2866
- const selectedKey = dependency.key(value, key);
2867
- assertSynchronous(selectedKey);
2868
- if (selectedKey !== void 0 && typeof selectedKey !== "string") throw new TypeError("Dynamic keyed dependency keys must be strings or undefined.");
2869
- bindKey(bindings[index], key, selectedKey);
2870
- dependencyValues[dependencyNames[index]] = selectedKey === void 0 ? void 0 : sourceContext.read.get(selectedKey);
2860
+ dependencyValues = Object.freeze(values);
2871
2861
  }
2872
- const next = dependencySpecs === void 0 ? select(value, key) : select(value, key, Object.freeze(dependencyValues));
2862
+ const next = select(value, key, dependencyValues);
2873
2863
  assertSynchronous(next);
2874
- output.output.set(key, next);
2864
+ if (next === absent) output.output.remove(key);
2865
+ else output.output.set(key, next);
2875
2866
  };
2876
2867
  if (evaluation.reset) {
2877
2868
  clearBindings();
2878
2869
  const ids = driver.read.ids();
2879
2870
  for (const key of ids) project(key);
2880
- output.output.order(ids);
2881
- revisions = Object.freeze(currentRevisions);
2871
+ output.output.order(ids.filter((key) => output.next.has(key)));
2872
+ rememberRevisions(evaluation.sources);
2882
2873
  return;
2883
2874
  }
2884
2875
  const dirty = /* @__PURE__ */ new Set();
2885
2876
  let all = false;
2886
2877
  let updateOrder = false;
2887
- if (revisions[0] !== currentRevisions[0]) {
2878
+ let membershipChanged = false;
2879
+ if (revisions[0] !== driver.revision) {
2888
2880
  const change = driver.change;
2889
2881
  if (!change || change.kind === "reset") {
2890
2882
  all = true;
2891
2883
  updateOrder = true;
2892
2884
  } else {
2893
2885
  for (const entry of change.removed) {
2886
+ membershipChanged ||= output.previous.has(entry.key);
2894
2887
  output.output.remove(entry.key);
2895
- unbindKey(bindingIndexes, entry.key);
2888
+ unbindKey(bindings, entry.key);
2896
2889
  dirty.delete(entry.key);
2897
2890
  }
2898
2891
  for (const entry of change.added) dirty.add(entry.key);
2899
2892
  for (const entry of change.updated) dirty.add(entry.key);
2900
- updateOrder = Boolean(change.added.length || change.removed.length || change.order);
2893
+ updateOrder = Boolean(change.order);
2901
2894
  }
2902
2895
  }
2903
2896
  for (let index = 0; index < keyed.length; index++) {
2904
- if (revisions[index + 1] === currentRevisions[index + 1]) continue;
2897
+ const sourceContext = evaluation.sources[index + 1];
2898
+ if (revisions[index + 1] === sourceContext.revision) continue;
2905
2899
  if (!keyed[index]) {
2906
2900
  all = true;
2907
2901
  continue;
2908
2902
  }
2909
- const sourceContext = evaluation.sources[index + 1];
2910
2903
  if (sourceContext.kind !== "collection" || !sourceContext.change) {
2911
2904
  all = true;
2912
2905
  continue;
@@ -2916,86 +2909,274 @@ function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybe
2916
2909
  continue;
2917
2910
  }
2918
2911
  const reverse = bindings[index].reverse;
2919
- for (const transition of sourceContext.change.added) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
2920
- for (const transition of sourceContext.change.updated) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
2921
- for (const transition of sourceContext.change.removed) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
2912
+ for (const sourceKey of collectionChange.keys(sourceContext.change)) for (const outputKey of reverse.get(sourceKey) ?? []) dirty.add(outputKey);
2922
2913
  }
2923
2914
  if (all) for (const key of driver.read.ids()) dirty.add(key);
2924
- for (const key of dirty) if (driver.read.has(key)) project(key);
2925
- if (updateOrder) output.output.order(driver.read.ids());
2926
- revisions = Object.freeze(currentRevisions);
2915
+ for (const key of dirty) {
2916
+ if (!driver.read.has(key)) continue;
2917
+ const beforePresent = output.previous.has(key);
2918
+ project(key);
2919
+ membershipChanged ||= beforePresent !== output.next.has(key);
2920
+ }
2921
+ if (updateOrder || membershipChanged) output.output.order(driver.read.ids().filter((key) => output.next.has(key)));
2922
+ rememberRevisions(evaluation.sources);
2927
2923
  },
2928
2924
  release: () => {
2929
2925
  clearBindings();
2930
- revisions = Object.freeze([]);
2926
+ revisions.length = 0;
2931
2927
  }
2932
2928
  };
2933
2929
  },
2934
- name: "derive.keyed"
2930
+ name
2935
2931
  });
2936
2932
  return projection;
2933
+ };
2934
+ function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
2935
+ const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
2936
+ const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
2937
+ const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
2938
+ if (typeof select !== "function") throw new TypeError("Keyed derive requires a selector.");
2939
+ return createKeyedTransform(source, dependencySpecs, (value, key, dependencies) => dependencySpecs === void 0 ? select(value, key) : select(value, key, dependencies), equality, "derive.keyed");
2937
2940
  }
2938
- const derive = Object.assign(createValueDerive, { keyed: createKeyedDerive });
2939
- //#endregion
2940
- //#region core/src/projection/collection/change.ts
2941
- const commonOrderChanged = (before, after, added, removed) => {
2942
- const beforeCommon = before.filter((key) => !removed.has(key));
2943
- const afterCommon = after.filter((key) => !added.has(key));
2944
- return beforeCommon.some((key, index) => afterCommon[index] !== key);
2945
- };
2946
- /** Builds the canonical incremental CollectionChange from already-known net transitions. */
2947
- const createCollectionChange = (input) => {
2948
- const addedKeys = new Set(input.added.map((entry) => entry.key));
2949
- const removedKeys = new Set(input.removed.map((entry) => entry.key));
2950
- const orderChanged = input.beforeOrder !== input.afterOrder && commonOrderChanged(input.beforeOrder, input.afterOrder, addedKeys, removedKeys);
2951
- if (!input.added.length && !input.updated.length && !input.removed.length && !orderChanged) return void 0;
2952
- return Object.freeze({
2953
- kind: "incremental",
2954
- added: Object.freeze([...input.added]),
2955
- updated: Object.freeze([...input.updated]),
2956
- removed: Object.freeze([...input.removed]),
2957
- ...orderChanged ? { order: Object.freeze({
2958
- before: Object.freeze([...input.beforeOrder]),
2959
- after: Object.freeze([...input.afterOrder])
2960
- }) } : {}
2941
+ const createKeyedKeys = (source) => {
2942
+ assertKeyedProjection(source, "Keyed keys source");
2943
+ const [projection] = defineProcessor({
2944
+ dependencies: [source],
2945
+ outputs: [{
2946
+ kind: "value",
2947
+ equality: Object.is
2948
+ }],
2949
+ create: () => ({ evaluate: (evaluation) => {
2950
+ const driver = evaluation.sources[0];
2951
+ const output = evaluation.outputs[0];
2952
+ if (driver.kind !== "collection" || output.kind !== "value") throw new Error("derive.keyed.keys requires collection input and value output.");
2953
+ if (evaluation.reset || !driver.change || collectionHasStructuralChange(driver.change)) output.output.set(driver.read.ids());
2954
+ } }),
2955
+ name: "derive.keyed.keys"
2961
2956
  });
2957
+ return projection;
2962
2958
  };
2963
- /** Exact fallback diff between two published map-like snapshots. */
2964
- const diffCollection = (previous, current, isEqual = Object.is) => {
2965
- const added = [];
2966
- const updated = [];
2967
- const removed = [];
2968
- for (const [key, before] of previous) if (!current.has(key)) removed.push({
2969
- key,
2970
- before
2971
- });
2972
- else {
2973
- const after = current.get(key);
2974
- if (!isEqual(before, after)) updated.push({
2975
- key,
2976
- before,
2977
- after
2978
- });
2979
- }
2980
- for (const [key, after] of current) if (!previous.has(key)) added.push({
2981
- key,
2982
- after
2959
+ const createKeyedValues = (source) => {
2960
+ assertKeyedProjection(source, "Keyed values source");
2961
+ const [projection] = defineProcessor({
2962
+ dependencies: [source],
2963
+ outputs: [{
2964
+ kind: "value",
2965
+ equality: Object.is
2966
+ }],
2967
+ create: () => {
2968
+ let indexes = /* @__PURE__ */ new Map();
2969
+ const rebuild = (driver) => {
2970
+ const nextIndexes = /* @__PURE__ */ new Map();
2971
+ const ids = driver.read.ids();
2972
+ const values = Object.freeze(ids.map((key, index) => {
2973
+ nextIndexes.set(key, index);
2974
+ return driver.read.get(key);
2975
+ }));
2976
+ indexes = nextIndexes;
2977
+ return values;
2978
+ };
2979
+ const rebuildIncremental = (driver, change, previous) => {
2980
+ const changedValues = /* @__PURE__ */ new Map();
2981
+ for (const entry of change.added) changedValues.set(entry.key, entry.after);
2982
+ for (const entry of change.updated) changedValues.set(entry.key, entry.after);
2983
+ const nextIndexes = /* @__PURE__ */ new Map();
2984
+ const values = Object.freeze(driver.read.ids().map((key, index) => {
2985
+ nextIndexes.set(key, index);
2986
+ if (changedValues.has(key)) return changedValues.get(key);
2987
+ const previousIndex = indexes.get(key);
2988
+ return previousIndex === void 0 ? driver.read.get(key) : previous[previousIndex];
2989
+ }));
2990
+ indexes = nextIndexes;
2991
+ return values;
2992
+ };
2993
+ return {
2994
+ evaluate: (evaluation) => {
2995
+ const driver = evaluation.sources[0];
2996
+ const output = evaluation.outputs[0];
2997
+ if (driver.kind !== "collection" || output.kind !== "value") throw new Error("derive.keyed.values requires collection input and value output.");
2998
+ const change = driver.change;
2999
+ if (evaluation.reset || !change || change.kind === "reset") {
3000
+ output.output.set(rebuild(driver));
3001
+ return;
3002
+ }
3003
+ const previous = output.previous;
3004
+ if (!previous) {
3005
+ output.output.set(rebuild(driver));
3006
+ return;
3007
+ }
3008
+ if (collectionHasStructuralChange(change)) {
3009
+ output.output.set(rebuildIncremental(driver, change, previous));
3010
+ return;
3011
+ }
3012
+ if (!change.updated.length) return;
3013
+ const next = [...previous];
3014
+ for (const entry of change.updated) {
3015
+ const index = indexes.get(entry.key);
3016
+ if (index === void 0) {
3017
+ output.output.set(rebuild(driver));
3018
+ return;
3019
+ }
3020
+ next[index] = entry.after;
3021
+ }
3022
+ output.output.set(Object.freeze(next));
3023
+ },
3024
+ release: () => indexes.clear()
3025
+ };
3026
+ },
3027
+ name: "derive.keyed.values"
2983
3028
  });
2984
- return createCollectionChange({
2985
- added,
2986
- updated,
2987
- removed,
2988
- beforeOrder: [...previous.keys()],
2989
- afterOrder: [...current.keys()]
3029
+ return projection;
3030
+ };
3031
+ function createKeyedFilter(source, dependenciesOrPredicate, maybePredicate) {
3032
+ const dependencySpecs = typeof dependenciesOrPredicate === "function" ? void 0 : dependenciesOrPredicate;
3033
+ const predicate = dependencySpecs === void 0 ? dependenciesOrPredicate : maybePredicate;
3034
+ if (typeof predicate !== "function") throw new TypeError("Keyed filter requires a predicate.");
3035
+ return createKeyedTransform(source, dependencySpecs, (value, key, dependencies) => {
3036
+ const included = dependencySpecs === void 0 ? predicate(value, key) : predicate(value, key, dependencies);
3037
+ assertSynchronous(included);
3038
+ if (typeof included !== "boolean") throw new TypeError("Keyed filter predicate must return boolean.");
3039
+ return included ? value : absent;
3040
+ }, Object.is, "derive.keyed.filter");
3041
+ }
3042
+ function createKeyedCompact(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
3043
+ const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
3044
+ const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
3045
+ const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
3046
+ if (typeof select !== "function") throw new TypeError("Keyed compact requires a selector.");
3047
+ return createKeyedTransform(source, dependencySpecs, (value, key, dependencies) => {
3048
+ const next = dependencySpecs === void 0 ? select(value, key) : select(value, key, dependencies);
3049
+ assertSynchronous(next);
3050
+ return next === void 0 ? absent : next;
3051
+ }, equality, "derive.keyed.compact");
3052
+ }
3053
+ const createKeyedSubset = (source, orderedKeys) => {
3054
+ assertKeyedProjection(source, "Keyed subset source");
3055
+ const keyProjection = isProjection(orderedKeys) ? orderedKeys : void 0;
3056
+ if (keyProjection && outputKind(keyProjection) !== "value") throw new TypeError("Keyed subset ordered keys must be a value projection.");
3057
+ const staticSelection = keyProjection ? void 0 : (() => {
3058
+ const compiled = compileOrderedKeys(orderedKeys, "Keyed subset ordered keys");
3059
+ return {
3060
+ keys: Object.freeze([...compiled.keys]),
3061
+ requested: compiled.requested
3062
+ };
3063
+ })();
3064
+ const [projection] = defineProcessor({
3065
+ dependencies: keyProjection ? [source, keyProjection] : [source],
3066
+ outputs: [{
3067
+ kind: "collection",
3068
+ equality: Object.is
3069
+ }],
3070
+ create: () => {
3071
+ let keys = staticSelection?.keys;
3072
+ let requested = staticSelection?.requested;
3073
+ let keyRevision = -1;
3074
+ return {
3075
+ evaluate: (evaluation) => {
3076
+ const driver = evaluation.sources[0];
3077
+ const output = evaluation.outputs[0];
3078
+ if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("derive.keyed.subset requires collection input and output.");
3079
+ let keysChanged = false;
3080
+ if (keyProjection) {
3081
+ const sourceContext = evaluation.sources[1];
3082
+ if (sourceContext?.kind !== "value") throw new TypeError("Keyed subset ordered keys resolved to a collection.");
3083
+ if (keys === void 0 || sourceContext.reset || keyRevision !== sourceContext.revision) {
3084
+ const compiled = compileOrderedKeys(sourceContext.value, "Keyed subset ordered keys");
3085
+ keys = compiled.keys;
3086
+ requested = compiled.requested;
3087
+ keyRevision = sourceContext.revision;
3088
+ keysChanged = true;
3089
+ }
3090
+ }
3091
+ if (!keys || !requested) throw new Error("Keyed subset keys were not initialized.");
3092
+ const activeKeys = keys;
3093
+ const activeRequested = requested;
3094
+ const orderedPresentKeys = () => activeKeys.filter((key) => driver.read.has(key));
3095
+ const rebuild = () => {
3096
+ const nextKeys = orderedPresentKeys();
3097
+ for (const key of nextKeys) output.output.set(key, driver.read.get(key));
3098
+ output.output.order(nextKeys);
3099
+ };
3100
+ const reconcileKeys = () => {
3101
+ const nextKeys = orderedPresentKeys();
3102
+ for (const key of output.previous.ids()) if (!activeRequested.has(key) || !driver.read.has(key)) output.output.remove(key);
3103
+ for (const key of nextKeys) if (!output.previous.has(key)) output.output.set(key, driver.read.get(key));
3104
+ output.output.order(nextKeys);
3105
+ };
3106
+ const change = driver.change;
3107
+ if (evaluation.reset || change?.kind === "reset") rebuild();
3108
+ else if (keysChanged) {
3109
+ reconcileKeys();
3110
+ if (change) for (const entry of change.updated) {
3111
+ const key = entry.key;
3112
+ if (activeRequested.has(key) && output.previous.has(key)) output.output.set(key, entry.after);
3113
+ }
3114
+ } else if (change) {
3115
+ let updateOrder = false;
3116
+ for (const entry of change.removed) {
3117
+ const key = entry.key;
3118
+ if (!activeRequested.has(key)) continue;
3119
+ output.output.remove(key);
3120
+ updateOrder = true;
3121
+ }
3122
+ for (const entry of change.added) {
3123
+ const key = entry.key;
3124
+ if (!activeRequested.has(key)) continue;
3125
+ output.output.set(key, entry.after);
3126
+ updateOrder = true;
3127
+ }
3128
+ for (const entry of change.updated) {
3129
+ const key = entry.key;
3130
+ if (activeRequested.has(key)) output.output.set(key, entry.after);
3131
+ }
3132
+ if (updateOrder) output.output.order(orderedPresentKeys());
3133
+ }
3134
+ },
3135
+ release: () => {
3136
+ if (keyProjection) {
3137
+ keys = void 0;
3138
+ requested = void 0;
3139
+ keyRevision = -1;
3140
+ }
3141
+ }
3142
+ };
3143
+ },
3144
+ name: "derive.keyed.subset"
2990
3145
  });
3146
+ return projection;
2991
3147
  };
2992
- const collectionChangedKeys = (change) => change.kind === "reset" ? /* @__PURE__ */ new Set() : new Set([
2993
- ...change.added.map((entry) => entry.key),
2994
- ...change.updated.map((entry) => entry.key),
2995
- ...change.removed.map((entry) => entry.key)
2996
- ]);
2997
- const collectionHasStructuralChange = (change) => change.kind === "reset" || Boolean(change.added.length || change.removed.length || change.order);
2998
- const collectionHasAnyChange = (change) => change.kind === "reset" || Boolean(change.added.length || change.updated.length || change.removed.length || change.order);
3148
+ const keyedDerive = Object.assign(createKeyedDerive, {
3149
+ keys: createKeyedKeys,
3150
+ values: createKeyedValues,
3151
+ subset: createKeyedSubset,
3152
+ filter: createKeyedFilter,
3153
+ compact: createKeyedCompact
3154
+ });
3155
+ //#endregion
3156
+ //#region core/src/projection/derive.ts
3157
+ function createValueDerive(dependencies, compute, equality = Object.is) {
3158
+ const compiled = compileProjectionDependencies(dependencies, "Derive");
3159
+ const names = compiled.names;
3160
+ const ordered = compiled.projections;
3161
+ const [projection] = defineProcessor({
3162
+ dependencies: ordered,
3163
+ outputs: [{
3164
+ kind: "value",
3165
+ equality
3166
+ }],
3167
+ create: () => ({ evaluate: (evaluation) => {
3168
+ const output = evaluation.outputs[0];
3169
+ if (output.kind !== "value") throw new Error("derive requires a value output.");
3170
+ const values = Object.create(null);
3171
+ for (let index = 0; index < names.length; index++) values[names[index]] = snapshotDependencyValue(evaluation.sources[index]);
3172
+ const next = compute(Object.freeze(values));
3173
+ assertSynchronous(next);
3174
+ output.output.set(next);
3175
+ } })
3176
+ });
3177
+ return projection;
3178
+ }
3179
+ const derive = Object.assign(createValueDerive, { keyed: keyedDerive });
2999
3180
  //#endregion
3000
3181
  //#region core/src/projection/collection/index.ts
3001
3182
  const height = (node) => node?.height ?? 0;
@@ -3208,15 +3389,36 @@ const createCollectionOutput = (binding) => {
3208
3389
  profile.projection("touchedKeys", staged.size);
3209
3390
  const added = /* @__PURE__ */ new Set();
3210
3391
  const removed = /* @__PURE__ */ new Set();
3211
- const updated = /* @__PURE__ */ new Set();
3392
+ const addedEntries = [];
3393
+ const updatedEntries = [];
3394
+ const removedEntries = [];
3212
3395
  for (const [key, entry] of staged) {
3213
3396
  const existed = published.has(key);
3214
3397
  if (entry.present) {
3215
- if (!existed) added.add(key);
3216
- else if (!isEqual(published.get(key), entry.value)) updated.add(key);
3398
+ if (!existed) {
3399
+ added.add(key);
3400
+ addedEntries.push({
3401
+ key,
3402
+ after: entry.value
3403
+ });
3404
+ continue;
3405
+ }
3406
+ const before = published.get(key);
3407
+ if (!isEqual(before, entry.value)) updatedEntries.push({
3408
+ key,
3409
+ before,
3410
+ after: entry.value
3411
+ });
3217
3412
  else if (!nextReset) staged.delete(key);
3218
- } else if (existed) removed.add(key);
3219
- else staged.delete(key);
3413
+ continue;
3414
+ }
3415
+ if (existed) {
3416
+ removed.add(key);
3417
+ removedEntries.push({
3418
+ key,
3419
+ before: published.get(key)
3420
+ });
3421
+ } else staged.delete(key);
3220
3422
  }
3221
3423
  const ids = published.ids();
3222
3424
  if (explicitOrder || added.size || removed.size) {
@@ -3224,35 +3426,7 @@ const createCollectionOutput = (binding) => {
3224
3426
  if (new Set(order).size !== order.length || order.length !== published.size() + added.size - removed.size || order.some((key) => !hasNext(key))) throw new TypeError("Projection order must contain every key exactly once.");
3225
3427
  nextIds = ids.length === order.length && ids.every((key, index) => key === order[index]) ? ids : Object.freeze([...order]);
3226
3428
  } else nextIds = ids;
3227
- const changedKeys = /* @__PURE__ */ new Set();
3228
- added.forEach((key) => changedKeys.add(key));
3229
- removed.forEach((key) => changedKeys.add(key));
3230
- updated.forEach((key) => changedKeys.add(key));
3231
- profile.projection("changedKeys", changedKeys.size);
3232
- const addedEntries = [];
3233
- const updatedEntries = [];
3234
- const removedEntries = [];
3235
- for (const key of changedKeys) {
3236
- const beforePresent = published.has(key);
3237
- const afterPresent = hasNext(key);
3238
- if (beforePresent === afterPresent) {
3239
- if (!beforePresent) continue;
3240
- if (isEqual !== Object.is && isEqual(published.get(key), getNext(key))) continue;
3241
- }
3242
- if (!beforePresent) addedEntries.push({
3243
- key,
3244
- after: getNext(key)
3245
- });
3246
- else if (!afterPresent) removedEntries.push({
3247
- key,
3248
- before: published.get(key)
3249
- });
3250
- else updatedEntries.push({
3251
- key,
3252
- before: published.get(key),
3253
- after: getNext(key)
3254
- });
3255
- }
3429
+ profile.projection("changedKeys", addedEntries.length + updatedEntries.length + removedEntries.length);
3256
3430
  change = nextReset ? Object.freeze({ kind: "reset" }) : createCollectionChange({
3257
3431
  added: addedEntries,
3258
3432
  updated: updatedEntries,
@@ -3617,10 +3791,9 @@ const trackedMap = (source) => {
3617
3791
  };
3618
3792
  const selectionAffects = (selection, change) => {
3619
3793
  if (!change || change.kind === "reset") return true;
3620
- if (selection.all && collectionHasAnyChange(change)) return true;
3794
+ if (selection.all) return true;
3621
3795
  if (selection.structure && collectionHasStructuralChange(change)) return true;
3622
- const changedKeys = collectionChangedKeys(change);
3623
- for (const key of selection.keys) if (changedKeys.has(key)) return true;
3796
+ for (const key of collectionChange.keys(change)) if (selection.keys.has(key)) return true;
3624
3797
  return false;
3625
3798
  };
3626
3799
  const createDirectReadable = (source) => Object.freeze({
@@ -4162,7 +4335,7 @@ const stageCollectionRead = (state, read, active, options) => {
4162
4335
  evaluation.output.order(ids);
4163
4336
  } else {
4164
4337
  const previous = state.current();
4165
- const keys = options.candidates ? [...new Set(options.candidates)] : [...new Set([...previous.ids(), ...read.ids()])];
4338
+ const keys = options.candidates ? [...options.candidates] : [...new Set([...previous.ids(), ...read.ids()])];
4166
4339
  if (!options.candidates) profile.collectionView.idsScanned(keys.length);
4167
4340
  for (const key of keys) if (read.has(key)) {
4168
4341
  profile.collectionView.mapped();
@@ -4851,6 +5024,7 @@ const materializeInputSource = (scheduler, definition) => {
4851
5024
  scheduler.assertIdle();
4852
5025
  const staged = /* @__PURE__ */ new Map();
4853
5026
  const operations = [];
5027
+ let structural = false;
4854
5028
  let active = true;
4855
5029
  const draft = Object.freeze({
4856
5030
  get: (key) => {
@@ -4866,6 +5040,8 @@ const materializeInputSource = (scheduler, definition) => {
4866
5040
  set: (key, value) => {
4867
5041
  assertScope(() => active);
4868
5042
  if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
5043
+ const previous = staged.get(key);
5044
+ structural ||= previous ? !previous.present : !values.has(key);
4869
5045
  staged.set(key, {
4870
5046
  present: true,
4871
5047
  value
@@ -4879,8 +5055,11 @@ const materializeInputSource = (scheduler, definition) => {
4879
5055
  remove: (key) => {
4880
5056
  assertScope(() => active);
4881
5057
  if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
5058
+ const previous = staged.get(key);
5059
+ const present = previous ? previous.present : values.has(key);
5060
+ structural ||= present;
4882
5061
  staged.set(key, { present: false });
4883
- operations.push({
5062
+ if (present) operations.push({
4884
5063
  kind: "remove",
4885
5064
  key
4886
5065
  });
@@ -4891,32 +5070,25 @@ const materializeInputSource = (scheduler, definition) => {
4891
5070
  } finally {
4892
5071
  active = false;
4893
5072
  }
5073
+ if (!operations.length) return;
4894
5074
  const candidates = /* @__PURE__ */ new Set();
4895
- let structural = false;
4896
- const accepted = [];
4897
- const overlay = /* @__PURE__ */ new Map();
4898
- for (const operation of operations) {
4899
- const overlayValue = overlay.get(operation.key);
4900
- const present = overlayValue ? overlayValue.present : values.has(operation.key);
4901
- if (operation.kind === "set") {
4902
- const previous = overlayValue ? overlayValue.present ? overlayValue.value : void 0 : values.get(operation.key);
4903
- if (present && source.equality(previous, operation.value)) continue;
4904
- structural ||= !present;
4905
- overlay.set(operation.key, {
4906
- present: true,
4907
- value: operation.value
4908
- });
4909
- } else {
4910
- if (!present) continue;
4911
- structural = true;
4912
- overlay.set(operation.key, { present: false });
4913
- }
4914
- accepted.push(operation);
4915
- candidates.add(operation.key);
5075
+ const retained = /* @__PURE__ */ new Map();
5076
+ for (const [key, entry] of staged) {
5077
+ const existed = values.has(key);
5078
+ if (entry.present) {
5079
+ if (!existed) {
5080
+ candidates.add(key);
5081
+ continue;
5082
+ }
5083
+ const previous = values.get(key);
5084
+ if (source.equality(previous, entry.value)) retained.set(key, previous);
5085
+ else candidates.add(key);
5086
+ } else if (existed) candidates.add(key);
4916
5087
  }
4917
- if (!accepted.length) return;
4918
- for (const operation of accepted) if (operation.kind === "set") values.set(operation.key, operation.value);
5088
+ if (!candidates.size && !structural) return;
5089
+ for (const operation of operations) if (operation.kind === "set") values.set(operation.key, operation.value);
4919
5090
  else values.delete(operation.key);
5091
+ for (const [key, value] of retained) if (values.has(key)) values.set(key, value);
4920
5092
  boundary.mark({
4921
5093
  candidates,
4922
5094
  orderMayChange: structural