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