reselect-kit 1.0.0

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.
Files changed (67) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +473 -0
  3. package/dist/cjs/index.js +6 -0
  4. package/dist/cjs/reselectKit.development.cjs +683 -0
  5. package/dist/cjs/reselectKit.development.cjs.map +1 -0
  6. package/dist/cjs/reselectKit.production.min.cjs +2 -0
  7. package/dist/cjs/reselectKit.production.min.cjs.map +1 -0
  8. package/dist/reselectKit.d.ts +248 -0
  9. package/dist/reselectKit.mjs +678 -0
  10. package/dist/reselectKit.mjs.map +1 -0
  11. package/package.json +70 -0
  12. package/src/__data__/state.ts +97 -0
  13. package/src/__tests__/createAdaptedSelector.test.ts +134 -0
  14. package/src/__tests__/createBoundSelector.test.ts +351 -0
  15. package/src/__tests__/createCachedSequenceSelector.test.ts +24 -0
  16. package/src/__tests__/createCachedStructuredSelector.test.ts +29 -0
  17. package/src/__tests__/createChainSelector.test.ts +404 -0
  18. package/src/__tests__/createEmptySelector.test.ts +99 -0
  19. package/src/__tests__/createPathSelector.test.ts +81 -0
  20. package/src/__tests__/createPropSelector.test.ts +36 -0
  21. package/src/__tests__/createSegmentSelector.test.ts +31 -0
  22. package/src/__tests__/createSequenceSelector.test.ts +62 -0
  23. package/src/_helpers/__tests__/arePathsEqual.test.ts +27 -0
  24. package/src/_helpers/__tests__/getObjectPaths.test.ts +56 -0
  25. package/src/_helpers/__tests__/temporaryAssign.test.ts +59 -0
  26. package/src/_helpers/arePathsEqual.ts +15 -0
  27. package/src/_helpers/defineDynamicSelectorName.ts +10 -0
  28. package/src/_helpers/generateSelectorKey.ts +15 -0
  29. package/src/_helpers/getObjectPaths.ts +13 -0
  30. package/src/_helpers/getSelectorName.ts +7 -0
  31. package/src/_helpers/isCachedSelector.ts +9 -0
  32. package/src/_helpers/isObject.ts +3 -0
  33. package/src/_helpers/stringifyFunction.ts +3 -0
  34. package/src/_helpers/temporaryAssign.ts +80 -0
  35. package/src/_hooks/__tests__/useMemoWith.test.ts +31 -0
  36. package/src/_hooks/__tests__/useSelector.test.ts +87 -0
  37. package/src/_hooks/useMemoWith.ts +11 -0
  38. package/src/_hooks/useSelector.ts +34 -0
  39. package/src/cache/TreeCache.ts +126 -0
  40. package/src/cache/__tests__/TreeCacheObject.test.ts +188 -0
  41. package/src/cache/__tests__/intervalMapCache.test.ts +133 -0
  42. package/src/cache/intervalMapCache.ts +97 -0
  43. package/src/createAdaptedSelector.ts +75 -0
  44. package/src/createBoundSelector.ts +197 -0
  45. package/src/createCachedSequenceSelector.ts +18 -0
  46. package/src/createCachedStructuredSelector.ts +44 -0
  47. package/src/createChainSelector.ts +230 -0
  48. package/src/createEmptySelector.ts +27 -0
  49. package/src/createPathSelector.ts +208 -0
  50. package/src/createPropSelector.ts +28 -0
  51. package/src/createSegmentSelector.ts +6 -0
  52. package/src/createSequenceSelector.ts +15 -0
  53. package/src/createStructuredSelector.ts +3 -0
  54. package/src/debug/__tests__/debug.test.ts +20 -0
  55. package/src/debug/debug.ts +7 -0
  56. package/src/debug/withDebugName.ts +28 -0
  57. package/src/index.ts +26 -0
  58. package/src/keys/__tests__/arrayComposeKeySelectors.test.ts +52 -0
  59. package/src/keys/__tests__/createKeySelectorCreator.test.ts +246 -0
  60. package/src/keys/__tests__/stringComposeKeySelectors.test.ts +52 -0
  61. package/src/keys/arrayComposeKeySelectors.ts +20 -0
  62. package/src/keys/createKeySelectorComposer.ts +68 -0
  63. package/src/keys/createKeySelectorCreator.ts +106 -0
  64. package/src/keys/defaultKeySelector.ts +1 -0
  65. package/src/keys/stringComposeKeySelectors.ts +15 -0
  66. package/src/types.ts +43 -0
  67. package/src/vitest-globals.d.ts +1 -0
@@ -0,0 +1,678 @@
1
+ import { FlatObjectCache, createCachedSelector } from "@veksa/re-reselect";
2
+ import { createSelector, createStructuredSelector } from "@veksa/reselect";
3
+ import { shallowEqual, useSelector as useSelector$1 } from "@veksa/react-redux";
4
+ import { useCallback, useMemo, useRef } from "react";
5
+ //#region src/createSegmentSelector.ts
6
+ function createSegmentSelector(fn, initial) {
7
+ return (state) => {
8
+ var _fn;
9
+ return (_fn = fn(state)) !== null && _fn !== void 0 ? _fn : initial;
10
+ };
11
+ }
12
+ //#endregion
13
+ //#region src/_helpers/isObject.ts
14
+ const isObject = (value) => {
15
+ return value !== null && typeof value === "object";
16
+ };
17
+ //#endregion
18
+ //#region src/_helpers/defineDynamicSelectorName.ts
19
+ const defineDynamicSelectorName = (selector, selectorNameGetter) => {
20
+ let overriddenSelectorName;
21
+ Object.defineProperty(selector, "selectorName", {
22
+ configurable: true,
23
+ get: () => {
24
+ var _overriddenSelectorNa;
25
+ return (_overriddenSelectorNa = overriddenSelectorName) !== null && _overriddenSelectorNa !== void 0 ? _overriddenSelectorNa : selectorNameGetter();
26
+ },
27
+ set: (value) => {
28
+ overriddenSelectorName = value;
29
+ }
30
+ });
31
+ };
32
+ //#endregion
33
+ //#region src/debug/debug.ts
34
+ let debugMode = false;
35
+ const isDebugMode = () => debugMode;
36
+ const setDebugMode = (value) => {
37
+ debugMode = value;
38
+ };
39
+ //#endregion
40
+ //#region src/debug/withDebugName.ts
41
+ /**
42
+ * Runs `callback` only outside production and only while debug mode is enabled.
43
+ * Centralizes the `NODE_ENV` / `isDebugMode` guard that the selector factories
44
+ * would otherwise repeat around every debug-name assignment.
45
+ */
46
+ const runInDebug = (callback) => {
47
+ /* istanbul ignore else */
48
+ if (process.env.NODE_ENV !== "production") {
49
+ /* istanbul ignore else */
50
+ if (isDebugMode()) callback();
51
+ }
52
+ };
53
+ /**
54
+ * Assigns a lazily-computed debug name to `selector`, guarded by
55
+ * {@link runInDebug}. Covers the common single-name case; use `runInDebug`
56
+ * directly when a block needs to name several selectors at once.
57
+ */
58
+ const withDebugName = (selector, selectorNameGetter) => {
59
+ runInDebug(() => {
60
+ defineDynamicSelectorName(selector, selectorNameGetter);
61
+ });
62
+ };
63
+ //#endregion
64
+ //#region src/_helpers/getSelectorName.ts
65
+ const getSelectorName = (selector) => {
66
+ if ("selectorName" in selector && selector.selectorName) return selector.selectorName;
67
+ return selector.name;
68
+ };
69
+ //#endregion
70
+ //#region src/createPathSelector.ts
71
+ const defaultVoidFunction = () => {};
72
+ /** @internal */
73
+ const innerCreatePathSelector = (baseSelector, path = [], applyMeta = defaultVoidFunction) => {
74
+ const pathLength = path.length;
75
+ const proxyTarget = (defaultValue) => {
76
+ function resultSelector(state, props) {
77
+ let result;
78
+ if (arguments.length === 2) result = baseSelector(state, props);
79
+ else if (arguments.length === 1) result = baseSelector(state);
80
+ else result = baseSelector.apply(null, arguments);
81
+ for (let i = 0; i < pathLength && isObject(result); i += 1) result = result[path[i]];
82
+ return result === void 0 ? defaultValue : result;
83
+ }
84
+ Object.assign(resultSelector, baseSelector, { path });
85
+ resultSelector.dependencies = [baseSelector];
86
+ applyMeta(resultSelector);
87
+ withDebugName(resultSelector, () => {
88
+ return [getSelectorName(baseSelector), ...path].join(".");
89
+ });
90
+ return resultSelector;
91
+ };
92
+ return new Proxy(proxyTarget, { get: (_target, key) => {
93
+ return innerCreatePathSelector(baseSelector, [...path, String(key)], applyMeta);
94
+ } });
95
+ };
96
+ function createPathSelector(baseSelector) {
97
+ return innerCreatePathSelector(baseSelector);
98
+ }
99
+ //#endregion
100
+ //#region src/createPropSelector.ts
101
+ const propSelectorSymbol = Symbol.for("PropSelector");
102
+ const isPropSelector = (selector) => {
103
+ return selector instanceof Object && propSelectorSymbol in selector;
104
+ };
105
+ function createPropSelector() {
106
+ const propsSelector = (_state, props) => props;
107
+ const applyMeta = (selector) => {
108
+ Object.defineProperty(selector, propSelectorSymbol, { value: true });
109
+ };
110
+ return innerCreatePathSelector(propsSelector, [], applyMeta);
111
+ }
112
+ //#endregion
113
+ //#region src/keys/createKeySelectorComposer.ts
114
+ const composedKeySelectorSymbol = Symbol.for("ComposedKeySelector");
115
+ function isComposedKeySelector(keySelector) {
116
+ return "dependencies" in keySelector && composedKeySelectorSymbol in keySelector;
117
+ }
118
+ function createKeySelectorComposer(baseKeySelectorComposer) {
119
+ return ((...keySelectors) => {
120
+ const resultSelector = baseKeySelectorComposer(...keySelectors);
121
+ resultSelector.dependencies = keySelectors;
122
+ Object.defineProperty(resultSelector, composedKeySelectorSymbol, { value: true });
123
+ return resultSelector;
124
+ });
125
+ }
126
+ //#endregion
127
+ //#region src/_helpers/arePathsEqual.ts
128
+ const arePathsEqual = (path, anotherPath) => {
129
+ if (path.length !== anotherPath.length) return false;
130
+ for (let i = 0; i < path.length; i += 1) if (path[i] !== anotherPath[i]) return false;
131
+ return true;
132
+ };
133
+ //#endregion
134
+ //#region src/keys/defaultKeySelector.ts
135
+ const defaultKeySelector = () => "<DefaultKey>";
136
+ //#endregion
137
+ //#region src/_helpers/isCachedSelector.ts
138
+ const isCachedSelector = (selector) => {
139
+ return selector instanceof Object && "keySelector" in selector && typeof selector.keySelector === "function";
140
+ };
141
+ //#endregion
142
+ //#region src/keys/createKeySelectorCreator.ts
143
+ const areSelectorsEqual = (selector, another) => {
144
+ if (selector === another) return true;
145
+ if (isPropSelector(selector) && isPropSelector(another)) return arePathsEqual(selector.path, another.path);
146
+ return false;
147
+ };
148
+ const flatKeySelectors = (keySelectors) => {
149
+ const result = [];
150
+ for (let i = 0; i < keySelectors.length; i += 1) {
151
+ const keySelector = keySelectors[i];
152
+ if (isComposedKeySelector(keySelector)) result.push(...flatKeySelectors(keySelector.dependencies));
153
+ else result.push(keySelector);
154
+ }
155
+ return result;
156
+ };
157
+ const uniqKeySelectors = (keySelectors) => {
158
+ const result = [];
159
+ for (let i = 0; i < keySelectors.length; i += 1) {
160
+ const keySelector = keySelectors[i];
161
+ if (!result.some((resultKeySelector) => areSelectorsEqual(keySelector, resultKeySelector))) result.push(keySelector);
162
+ }
163
+ return result;
164
+ };
165
+ const excludeDefaultSelectors = (keySelectors) => {
166
+ const result = [];
167
+ for (let i = 0; i < keySelectors.length; i += 1) {
168
+ const keySelector = keySelectors[i];
169
+ if (keySelector !== defaultKeySelector) result.push(keySelector);
170
+ }
171
+ return result;
172
+ };
173
+ function createKeySelectorCreator(keySelectorComposer) {
174
+ return ({ inputSelectors, keySelector }) => {
175
+ let keySelectors = [];
176
+ if (keySelector) keySelectors.push(keySelector);
177
+ inputSelectors.forEach((selector) => {
178
+ if (isCachedSelector(selector)) keySelectors.push(selector.keySelector);
179
+ });
180
+ keySelectors = flatKeySelectors(keySelectors);
181
+ keySelectors = uniqKeySelectors(keySelectors);
182
+ keySelectors = excludeDefaultSelectors(keySelectors);
183
+ if (keySelectors.length === 0) return defaultKeySelector;
184
+ if (keySelectors.length === 1) {
185
+ const [resultSelector] = keySelectors;
186
+ return resultSelector;
187
+ }
188
+ return keySelectorComposer(...keySelectors);
189
+ };
190
+ }
191
+ //#endregion
192
+ //#region src/keys/stringComposeKeySelectors.ts
193
+ const stringComposeKeySelectors = createKeySelectorComposer((...keySelectors) => (state, props) => {
194
+ let key = keySelectors[0](state, props);
195
+ for (let i = 1; i < keySelectors.length; i += 1) {
196
+ key += ":";
197
+ key += keySelectors[i](state, props);
198
+ }
199
+ return key;
200
+ });
201
+ //#endregion
202
+ //#region src/_helpers/temporaryAssign.ts
203
+ const noop = () => void 0;
204
+ function temporaryAssign(target, source) {
205
+ if (typeof target !== "object" || target === null) return {
206
+ result: source,
207
+ rollback: noop
208
+ };
209
+ let temporary = null;
210
+ let deleteProps = null;
211
+ for (const prop in source) if (Object.prototype.hasOwnProperty.call(source, prop)) {
212
+ const targetProp = target[prop];
213
+ const sourceProp = source[prop];
214
+ if (targetProp !== sourceProp) {
215
+ if (Object.prototype.hasOwnProperty.call(target, prop)) {
216
+ if (temporary === null) temporary = {};
217
+ temporary[prop] = targetProp;
218
+ } else {
219
+ if (deleteProps === null) deleteProps = [];
220
+ deleteProps.push(prop);
221
+ }
222
+ target[prop] = sourceProp;
223
+ }
224
+ }
225
+ if (temporary === null && deleteProps === null) return {
226
+ result: target,
227
+ rollback: noop
228
+ };
229
+ const restore = temporary;
230
+ const remove = deleteProps;
231
+ const rollback = () => {
232
+ if (restore !== null) {
233
+ for (const prop in restore) if (Object.prototype.hasOwnProperty.call(restore, prop)) target[prop] = restore[prop];
234
+ }
235
+ if (remove !== null) {
236
+ for (const prop of remove) if (Object.prototype.hasOwnProperty.call(target, prop)) delete target[prop];
237
+ }
238
+ };
239
+ return {
240
+ result: target,
241
+ rollback
242
+ };
243
+ }
244
+ //#endregion
245
+ //#region src/_helpers/getObjectPaths.ts
246
+ const getObjectPaths = (value, parentPath = []) => {
247
+ if (!isObject(value)) return [parentPath];
248
+ return Object.keys(value).reduce((result, key) => {
249
+ const paths = getObjectPaths(value[key], parentPath.concat([key]));
250
+ return result.concat(paths);
251
+ }, []);
252
+ };
253
+ //#endregion
254
+ //#region src/createBoundSelector.ts
255
+ const generateBindingName = (binding) => {
256
+ const structure = Object.keys(binding).reduce((result, key) => ({
257
+ ...result,
258
+ [key]: "[*]"
259
+ }), {});
260
+ return `${Object.keys(structure).join()} -> ${Object.values(structure).join()}`;
261
+ };
262
+ const innerCreateBoundSelector = (baseSelector, binding) => {
263
+ const boundSelector = (state, props) => baseSelector(state, {
264
+ ...props,
265
+ ...binding
266
+ });
267
+ return boundSelector;
268
+ };
269
+ const createBoundInnerSelector = (baseSelector, binding, options = {}) => {
270
+ var _ref, _options$keySelectorC;
271
+ const bindingStrategy = (_ref = options.bindingStrategy) !== null && _ref !== void 0 ? _ref : innerCreateBoundSelector;
272
+ const keySelectorComposer = (_options$keySelectorC = options.keySelectorComposer) !== null && _options$keySelectorC !== void 0 ? _options$keySelectorC : stringComposeKeySelectors;
273
+ const boundSelector = bindingStrategy(baseSelector, binding);
274
+ Object.assign(boundSelector, baseSelector);
275
+ boundSelector.dependencies = [baseSelector];
276
+ withDebugName(boundSelector, () => {
277
+ return `${getSelectorName(baseSelector)} (${generateBindingName(binding)})`;
278
+ });
279
+ if (isCachedSelector(baseSelector)) {
280
+ const cachedBoundSelector = boundSelector;
281
+ if (baseSelector.getMatchingSelector) cachedBoundSelector.getMatchingSelector = bindingStrategy(baseSelector.getMatchingSelector, binding);
282
+ if (baseSelector.removeMatchingSelector) cachedBoundSelector.removeMatchingSelector = bindingStrategy(baseSelector.removeMatchingSelector, binding);
283
+ const baseKeySelector = baseSelector.keySelector;
284
+ if (isObject(binding)) {
285
+ const paths = getObjectPaths(binding);
286
+ let keySelectors = isComposedKeySelector(baseKeySelector) ? baseKeySelector.dependencies : [baseKeySelector];
287
+ keySelectors = excludeDefaultSelectors(keySelectors);
288
+ keySelectors = keySelectors.filter((keySelector) => !isPropSelector(keySelector) || !paths.some((path) => arePathsEqual(keySelector.path, path))).map((keySelector) => isPropSelector(keySelector) ? keySelector : bindingStrategy(keySelector, binding));
289
+ if (keySelectors.length === 0) cachedBoundSelector.keySelector = defaultKeySelector;
290
+ else if (keySelectors.length === 1) {
291
+ const [keySelector] = keySelectors;
292
+ cachedBoundSelector.keySelector = keySelector;
293
+ } else cachedBoundSelector.keySelector = keySelectorComposer(...keySelectors);
294
+ } else cachedBoundSelector.keySelector = bindingStrategy(baseKeySelector, binding);
295
+ }
296
+ return boundSelector;
297
+ };
298
+ const createBoundSelector = (baseSelector, binding) => {
299
+ return createBoundInnerSelector(baseSelector, binding, { bindingStrategy: (selector, bindingProps) => {
300
+ const source = bindingProps;
301
+ const keys = Object.keys(source);
302
+ /**
303
+ * Almost every binding is a single key — `bound(selector, { positionId })` —
304
+ * and specialising it removes the two allocations the generic path costs per
305
+ * call: the `{ result, rollback }` object and the `rollback` closure. On a
306
+ * graph with a bound selector per row those are the calls that dominate.
307
+ *
308
+ * The `delete` in the restore stays deliberately. Leaving the key behind as
309
+ * `undefined` would be cheaper — it avoids putting the caller's props object
310
+ * into dictionary mode — but it also changes the object's shape, and
311
+ * `useSelector` memoizes props shallowly: a props object that has grown an
312
+ * extra key never again compares equal to a freshly built one, so the
313
+ * memoization the binding exists to protect would be defeated.
314
+ */
315
+ if (keys.length === 1) {
316
+ const key = keys[0];
317
+ const value = source[key];
318
+ return (state, props) => {
319
+ const target = props;
320
+ if (typeof target !== "object" || target === null) return selector(state, source);
321
+ const previous = target[key];
322
+ if (previous === value) return selector(state, props);
323
+ const had = Object.prototype.hasOwnProperty.call(target, key);
324
+ target[key] = value;
325
+ try {
326
+ return selector(state, props);
327
+ } finally {
328
+ if (had) target[key] = previous;
329
+ else delete target[key];
330
+ }
331
+ };
332
+ }
333
+ return (state, props) => {
334
+ const { result: nextProps, rollback } = temporaryAssign(props, source);
335
+ const result = selector(state, nextProps);
336
+ rollback();
337
+ return result;
338
+ };
339
+ } });
340
+ };
341
+ //#endregion
342
+ //#region src/createEmptySelector.ts
343
+ /**
344
+ * Creates a selector that always resolves to `undefined`. Used as the "no link"
345
+ * branch of a chain (e.g. an optional relation that isn't present).
346
+ *
347
+ * When the base selector is cached, the empty selector is tagged with
348
+ * {@link defaultKeySelector} so that it still passes {@link isCachedSelector}
349
+ * and takes part in key-selector composition. The default key is deliberately
350
+ * the only cache-related metadata copied: an empty selector has no result to
351
+ * memoize, so it needs no `cache` / `getMatchingSelector` of its own, and the
352
+ * default key is dropped by `excludeDefaultSelectors` during composition.
353
+ */
354
+ const createEmptySelector = (baseSelector) => {
355
+ const emptySelector = () => void 0;
356
+ if (isCachedSelector(baseSelector)) emptySelector.keySelector = defaultKeySelector;
357
+ return emptySelector;
358
+ };
359
+ //#endregion
360
+ //#region src/createAdaptedSelector.ts
361
+ const generateMappingName = (mapping) => {
362
+ if (mapping.name) return mapping.name;
363
+ const structure = mapping(new Proxy({}, { get: (_target, key) => key }));
364
+ return `${Object.keys(structure).join()} -> ${Object.values(structure).join()}`;
365
+ };
366
+ const innerCreateAdaptedSelector = (baseSelector, mapping) => (state, props) => {
367
+ return baseSelector(state, mapping(props));
368
+ };
369
+ const createAdaptedSelector = (baseSelector, mapping) => {
370
+ const adaptedSelector = innerCreateAdaptedSelector(baseSelector, mapping);
371
+ Object.assign(adaptedSelector, baseSelector);
372
+ adaptedSelector.dependencies = [baseSelector];
373
+ withDebugName(adaptedSelector, () => {
374
+ return `${getSelectorName(baseSelector)} (${generateMappingName(mapping)})`;
375
+ });
376
+ if (isCachedSelector(baseSelector)) {
377
+ const cachedAdaptedSelector = adaptedSelector;
378
+ if (baseSelector.getMatchingSelector) cachedAdaptedSelector.getMatchingSelector = innerCreateAdaptedSelector(baseSelector.getMatchingSelector, mapping);
379
+ if (baseSelector.removeMatchingSelector) cachedAdaptedSelector.removeMatchingSelector = innerCreateAdaptedSelector(baseSelector.removeMatchingSelector, mapping);
380
+ cachedAdaptedSelector.keySelector = innerCreateAdaptedSelector(baseSelector.keySelector, mapping);
381
+ }
382
+ return adaptedSelector;
383
+ };
384
+ //#endregion
385
+ //#region src/_helpers/stringifyFunction.ts
386
+ const stringifyFunction = (source) => {
387
+ return String(source).replace(/\s+/g, " ").trim();
388
+ };
389
+ //#endregion
390
+ //#region src/_helpers/generateSelectorKey.ts
391
+ const generateSelectorKey = (selector) => {
392
+ var _dependencies;
393
+ const dependencies = (_dependencies = selector.dependencies) !== null && _dependencies !== void 0 ? _dependencies : [];
394
+ let result = stringifyFunction(selector);
395
+ for (let i = dependencies.length - 1; i >= 0; i -= 1) {
396
+ const dependency = dependencies[i];
397
+ result += stringifyFunction(dependency);
398
+ }
399
+ return result;
400
+ };
401
+ //#endregion
402
+ //#region src/createChainSelector.ts
403
+ function createChainSelector(selector, options, prevChain) {
404
+ const chain = (fn, chainOptions) => {
405
+ const combinedOptions = {
406
+ ...options,
407
+ ...chainOptions
408
+ };
409
+ const keySelector = isCachedSelector(selector) ? selector.keySelector : defaultKeySelector;
410
+ const { createSelectorOptions = () => ({}), keySelectorComposer = stringComposeKeySelectors } = combinedOptions;
411
+ const keySelectorCreator = createKeySelectorCreator(keySelectorComposer);
412
+ const higherOrderSelector = createCachedSelector([selector], fn)({
413
+ ...createSelectorOptions(),
414
+ keySelector
415
+ });
416
+ withDebugName(higherOrderSelector, () => {
417
+ return `higher order for ${getSelectorName(selector)} (${stringifyFunction(fn)})`;
418
+ });
419
+ const combinedSelector = (state, props) => {
420
+ const derivedSelector = higherOrderSelector(state, props);
421
+ runInDebug(() => {
422
+ combinedSelector.dependencies = [higherOrderSelector, derivedSelector];
423
+ if (!getSelectorName(derivedSelector)) defineDynamicSelectorName(derivedSelector, () => {
424
+ return `derived from ${getSelectorName(selector)} (${generateSelectorKey(derivedSelector)})`;
425
+ });
426
+ defineDynamicSelectorName(combinedSelector, () => {
427
+ return `${getSelectorName(selector)} (chained by ${getSelectorName(derivedSelector)})`;
428
+ });
429
+ });
430
+ return derivedSelector(state, props);
431
+ };
432
+ combinedSelector.dependencies = [higherOrderSelector];
433
+ combinedSelector.cache = higherOrderSelector.cache;
434
+ /**
435
+ * The derived selector's key selector, memoized on the derived selector itself.
436
+ *
437
+ * This used to be a second `createCachedSelector` over `[higherOrderSelector]`,
438
+ * which meant every key computation paid a whole extra cached-selector layer —
439
+ * its own key computation, its own cache lookup, and its own evaluation of
440
+ * `higherOrderSelector` — to produce a value that depends on nothing but the
441
+ * derived selector. A `WeakMap` keyed on that selector is the same mapping with
442
+ * none of the layer, and it shares across cache keys rather than per key.
443
+ *
444
+ * Note what is deliberately *not* memoized: the `(state, props)` pair. `props`
445
+ * is mutated in place by `createBoundSelector`, so two calls can pass the same
446
+ * object reference with different contents, and a reference-keyed cache would
447
+ * hand back a stale selector.
448
+ */
449
+ const derivedKeySelectors = /* @__PURE__ */ new WeakMap();
450
+ combinedSelector.keySelector = (state, props) => {
451
+ const derivedSelector = higherOrderSelector(state, props);
452
+ let derivedKeySelector = derivedKeySelectors.get(derivedSelector);
453
+ if (derivedKeySelector === void 0) {
454
+ derivedKeySelector = keySelectorCreator({ inputSelectors: [higherOrderSelector, derivedSelector] });
455
+ derivedKeySelectors.set(derivedSelector, derivedKeySelector);
456
+ }
457
+ return derivedKeySelector(state, props);
458
+ };
459
+ withDebugName(combinedSelector, () => {
460
+ return `${getSelectorName(selector)} (will be chained ${stringifyFunction(fn)})`;
461
+ });
462
+ return createChainSelector(combinedSelector, combinedOptions, Object.assign(fn, { parentChain: prevChain }));
463
+ };
464
+ const map = (fn, mapOptions) => {
465
+ const mapSelector = (result) => {
466
+ const output = fn(result);
467
+ const mapSelector = () => output;
468
+ withDebugName(mapSelector, () => {
469
+ return `mapped from ${getSelectorName(selector)} (${stringifyFunction(fn)})`;
470
+ });
471
+ return mapSelector;
472
+ };
473
+ return chain(mapSelector, mapOptions);
474
+ };
475
+ const build = () => {
476
+ return Object.assign(selector, { chainHierarchy: prevChain });
477
+ };
478
+ return {
479
+ chain,
480
+ map,
481
+ build
482
+ };
483
+ }
484
+ //#endregion
485
+ //#region src/createSequenceSelector.ts
486
+ function createSequenceSelector(selectors, selectorCreator = createSelector) {
487
+ return selectorCreator(selectors, (...results) => results);
488
+ }
489
+ //#endregion
490
+ //#region src/createCachedSequenceSelector.ts
491
+ function createCachedSequenceSelector(selectors) {
492
+ return createSequenceSelector(selectors, createCachedSelector);
493
+ }
494
+ //#endregion
495
+ //#region src/createStructuredSelector.ts
496
+ const createStructuredSelector$1 = createStructuredSelector;
497
+ //#endregion
498
+ //#region src/createCachedStructuredSelector.ts
499
+ function createCachedStructuredSelector(selectors) {
500
+ return createStructuredSelector$1(selectors, createCachedSelector);
501
+ }
502
+ //#endregion
503
+ //#region src/keys/arrayComposeKeySelectors.ts
504
+ const arrayComposeKeySelectors = createKeySelectorComposer((...keySelectors) => (state, props) => {
505
+ let keyPath = [];
506
+ for (let i = 0; i < keySelectors.length; i += 1) {
507
+ const key = keySelectors[i](state, props);
508
+ if (Array.isArray(key)) keyPath = keyPath.concat(key);
509
+ else keyPath.push(key);
510
+ }
511
+ return keyPath;
512
+ });
513
+ //#endregion
514
+ //#region src/cache/intervalMapCache.ts
515
+ /**
516
+ * Buckets used to live in a module-level `Record<string, …>` keyed by an
517
+ * incrementing numeric id, which meant every `get` and `set` coerced that number to
518
+ * a string and re-read the global record — four times per cache hit in `get`.
519
+ * Holding the entries on the instance removes both, and a `Map` keeps non-string
520
+ * keys as they are instead of stringifying them.
521
+ */
522
+ const buckets = /* @__PURE__ */ new Set();
523
+ const cacheLifetime = 1e4;
524
+ let garbageCollectorStarted = false;
525
+ const runGarbageCollector = () => {
526
+ const currentTime = Date.now();
527
+ buckets.forEach((bucket) => {
528
+ if (bucket.sweep(currentTime)) buckets.delete(bucket);
529
+ });
530
+ window.setTimeout(runGarbageCollector, cacheLifetime);
531
+ };
532
+ const initGarbageCollector = () => {
533
+ if (garbageCollectorStarted) return;
534
+ if (typeof window !== "undefined") {
535
+ garbageCollectorStarted = true;
536
+ window.setTimeout(runGarbageCollector, cacheLifetime);
537
+ }
538
+ };
539
+ var IntervalMapCache = class {
540
+ constructor() {
541
+ this.entries = /* @__PURE__ */ new Map();
542
+ }
543
+ set(key, data) {
544
+ const entry = this.entries.get(key);
545
+ if (entry === void 0) {
546
+ buckets.add(this);
547
+ this.entries.set(key, {
548
+ data,
549
+ time: Date.now()
550
+ });
551
+ } else {
552
+ entry.data = data;
553
+ entry.time = Date.now();
554
+ }
555
+ }
556
+ get(key) {
557
+ const entry = this.entries.get(key);
558
+ if (entry === void 0) return;
559
+ entry.time = Date.now();
560
+ return entry.data;
561
+ }
562
+ remove(key) {
563
+ this.entries.delete(key);
564
+ }
565
+ clear() {
566
+ this.entries.clear();
567
+ buckets.delete(this);
568
+ }
569
+ /**
570
+ * Drops entries untouched for longer than the lifetime. Returns whether the
571
+ * bucket is now empty, which is what lets the collector unregister it.
572
+ */
573
+ sweep(currentTime) {
574
+ this.entries.forEach((entry, key) => {
575
+ if (currentTime - entry.time > cacheLifetime) this.entries.delete(key);
576
+ });
577
+ return this.entries.size === 0;
578
+ }
579
+ };
580
+ //#endregion
581
+ //#region src/cache/TreeCache.ts
582
+ const normalizeKey = (key) => Array.isArray(key) ? key : [key];
583
+ var TreeCacheNode = class {};
584
+ var TreeCache = class {
585
+ constructor(options) {
586
+ var _options$cacheObjectC;
587
+ this.cacheObjectCreator = (_options$cacheObjectC = options.cacheObjectCreator) !== null && _options$cacheObjectC !== void 0 ? _options$cacheObjectC : (() => new FlatObjectCache());
588
+ const root = new TreeCacheNode();
589
+ root.cache = this.cacheObjectCreator();
590
+ this.root = root;
591
+ }
592
+ clear() {
593
+ const root = new TreeCacheNode();
594
+ root.cache = this.cacheObjectCreator();
595
+ this.root = root;
596
+ }
597
+ get(key) {
598
+ if (!Array.isArray(key)) {
599
+ var _this$root$cache;
600
+ const cacheResponse = (_this$root$cache = this.root.cache) === null || _this$root$cache === void 0 ? void 0 : _this$root$cache.get(key);
601
+ return cacheResponse instanceof TreeCacheNode ? cacheResponse.selectorFn : void 0;
602
+ }
603
+ let currentNode = this.root;
604
+ for (let i = 0; i < key.length; i += 1) {
605
+ var _currentNode$cache;
606
+ const item = key[i];
607
+ const cacheResponse = (_currentNode$cache = currentNode.cache) === null || _currentNode$cache === void 0 ? void 0 : _currentNode$cache.get(item);
608
+ if (cacheResponse instanceof TreeCacheNode) currentNode = cacheResponse;
609
+ else return;
610
+ }
611
+ return currentNode.selectorFn;
612
+ }
613
+ set(key, selectorFn) {
614
+ const keyPath = normalizeKey(key);
615
+ let currentNode = this.root;
616
+ for (let i = 0; i < keyPath.length; i += 1) {
617
+ const item = keyPath[i];
618
+ if (!currentNode.cache) currentNode.cache = this.cacheObjectCreator();
619
+ const cacheResponse = currentNode.cache.get(item);
620
+ if (cacheResponse instanceof TreeCacheNode) currentNode = cacheResponse;
621
+ else {
622
+ const node = new TreeCacheNode();
623
+ currentNode.cache.set(item, node);
624
+ currentNode = node;
625
+ }
626
+ }
627
+ currentNode.selectorFn = selectorFn;
628
+ }
629
+ remove(key) {
630
+ const keyPath = normalizeKey(key);
631
+ let currentNode = this.root;
632
+ for (let i = 0; i < keyPath.length; i += 1) {
633
+ var _currentNode$cache2;
634
+ const item = keyPath[i];
635
+ const cacheResponse = (_currentNode$cache2 = currentNode.cache) === null || _currentNode$cache2 === void 0 ? void 0 : _currentNode$cache2.get(item);
636
+ if (cacheResponse instanceof TreeCacheNode) currentNode = cacheResponse;
637
+ else return;
638
+ }
639
+ currentNode.selectorFn = void 0;
640
+ }
641
+ /**
642
+ * Called by re-reselect on every cache operation, so neither the wrapping array
643
+ * nor the `every` callback is allocated here.
644
+ */
645
+ isValidCacheKey(key) {
646
+ const rootCache = this.root.cache;
647
+ if ((rootCache === null || rootCache === void 0 ? void 0 : rootCache.isValidCacheKey) === void 0) return true;
648
+ if (!Array.isArray(key)) return rootCache.isValidCacheKey(key);
649
+ for (let i = 0; i < key.length; i += 1) if (!rootCache.isValidCacheKey(key[i])) return false;
650
+ return true;
651
+ }
652
+ };
653
+ //#endregion
654
+ //#region src/_hooks/useMemoWith.ts
655
+ const useMemoWith = (value, comparator) => {
656
+ const ref = useRef(value);
657
+ if (!comparator(value, ref.current)) ref.current = value;
658
+ return ref.current;
659
+ };
660
+ //#endregion
661
+ //#region src/_hooks/useSelector.ts
662
+ function useSelector(selector, ...params) {
663
+ const props = params[0];
664
+ const memoizedProps = useMemoWith(props, shallowEqual);
665
+ const unfrozenProps = useMemo(() => {
666
+ return Object.isFrozen(memoizedProps) ? { ...memoizedProps } : memoizedProps;
667
+ }, [memoizedProps]);
668
+ return useSelector$1(useCallback((state) => {
669
+ return selector(state, unfrozenProps);
670
+ }, [selector, unfrozenProps]), { devModeChecks: {
671
+ stabilityCheck: "never",
672
+ identityFunctionCheck: "never"
673
+ } });
674
+ }
675
+ //#endregion
676
+ export { IntervalMapCache, TreeCache, arrayComposeKeySelectors, createAdaptedSelector, createBoundSelector, createCachedSequenceSelector, createCachedStructuredSelector, createChainSelector, createEmptySelector, createKeySelectorComposer, createKeySelectorCreator, createPathSelector, createPropSelector, createSegmentSelector, defaultKeySelector, initGarbageCollector, isDebugMode, setDebugMode, stringComposeKeySelectors, useSelector };
677
+
678
+ //# sourceMappingURL=reselectKit.mjs.map