diff --git a/README.md b/README.md index 0074f3d77..34bd845b3 100644 --- a/README.md +++ b/README.md @@ -1,707 +1,1782 @@ # Reselect +[![npm package][npm-badge]][npm][![Coveralls][coveralls-badge]][coveralls][![GitHub Workflow Status][build-badge]][build]![TypeScript][typescript-badge] + A library for creating memoized "selector" functions. Commonly used with Redux, but usable with any plain JS immutable data as well. -- Selectors can compute derived data, allowing Redux to store the minimal possible state. +- Selectors can compute derived data, allowing [Redux] to store the minimal possible state. - Selectors are efficient. A selector is not recomputed unless one of its arguments changes. - Selectors are composable. They can be used as input to other selectors. -The **Redux docs usage page on [Deriving Data with Selectors](https://redux.js.org/usage/deriving-data-selectors)** covers the purpose and motivation for selectors, why memoized selectors are useful, typical Reselect usage patterns, and using selectors with React-Redux. +The **Redux docs usage page on [Deriving Data with Selectors](https://redux.js.org/usage/deriving-data-selectors)** covers the purpose and motivation for selectors, why memoized selectors are useful, typical Reselect usage patterns, and using selectors with [React-Redux]. + +## Installation + +### Redux Toolkit + +While Reselect is not exclusive to [Redux], it is already included by default in [the official Redux Toolkit package](https://redux-toolkit.js.org) - no further installation needed. + +```ts +import { createSelector } from '@reduxjs/toolkit' +``` + +### Standalone + +For standalone usage, install the `reselect` package: + +```bash +# NPM +npm install reselect + +# Yarn +yarn add reselect + +# Bun +bun add reselect + +# PNPM +pnpm add reselect +``` + +--- + +## Basic Usage + +Reselect exports a [`createSelector`] API, which generates memoized selector functions. [`createSelector`] accepts one or more [input selectors], which extract values from arguments, and a [result function] function that receives the extracted values and should return a derived value. If the generated [output selector] is called multiple times, the output will only be recalculated when the extracted values have changed. + +You can play around with the following **example** in [this CodeSandbox](https://codesandbox.io/s/reselect-example-g3k9gf?file=/src/index.js): + +```ts +import { createSelector } from 'reselect' + +interface RootState { + todos: { id: number; completed: boolean }[] + alerts: { id: number; read: boolean }[] +} + +const state: RootState = { + todos: [ + { id: 0, completed: false }, + { id: 1, completed: true } + ], + alerts: [ + { id: 0, read: false }, + { id: 1, read: true } + ] +} + +const selectCompletedTodos = (state: RootState) => { + console.log('selector ran') + return state.todos.filter(todo => todo.completed === true) +} + +selectCompletedTodos(state) // selector ran +selectCompletedTodos(state) // selector ran +selectCompletedTodos(state) // selector ran + +const memoizedSelectCompletedTodos = createSelector( + [(state: RootState) => state.todos], + todos => { + console.log('memoized selector ran') + return todos.filter(todo => todo.completed === true) + } +) + +memoizedSelectCompletedTodos(state) // memoized selector ran +memoizedSelectCompletedTodos(state) +memoizedSelectCompletedTodos(state) + +console.log(selectCompletedTodos(state) === selectCompletedTodos(state)) //=> false + +console.log( + memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state) +) //=> true +``` + +As you can see from the example above, `memoizedSelectCompletedTodos` does not run the second or third time, but we still get the same return value as last time. + +In addition to skipping unnecessary recalculations, `memoizedSelectCompletedTodos` returns the existing result reference if there is no recalculation. This is important for libraries like [React-Redux] or [React] that often rely on reference equality checks to optimize UI updates. + +--- + +## Table of Contents + +- [Installation](#installation) + - [Redux Toolkit](#redux-toolkit) + - [Standalone](#standalone) +- [Basic Usage](#basic-usage) +- [Terminology](#terminology) +- [How Does Reselect Work?](#how-does-reselect-work) + - [Cascading Memoization](#cascading-memoization) + - [Why Reselect Is Often Used With Redux](#why-reselect-is-often-used-with-redux) +- [API](#api) + - [**`createSelector`**][`createSelector`] + - [**`createSelectorCreator`**][`createSelectorCreator`] + - [**`createStructuredSelector`**][`createStructuredSelector`] + - [**`defaultMemoize`**][`defaultMemoize`] + - [**`weakMapMemoize`**][`weakMapMemoize`] + - [**`unstable_autotrackMemoize`**][`unstable_autotrackMemoize`] +- [Debugging Tools](#debuggingtools) + - [Development-Only Stability Checks](#development-only-stability-checks) + - [Output Selector Fields](#output-selector-fields) +- [What's New in 5.0.0?](#v5summary) +- [Optimizing Reselect](#optimizing-reselect) +- [FAQ](#faq) + - [Why isn’t my selector recomputing when the input state changes?](#why-isnt-my-selector-recomputing-when-the-input-state-changes) + - [Why is my selector recomputing when the input state stays the same?](#why-is-my-selector-recomputing-when-the-input-state-stays-the-same) + - [Can I use Reselect without Redux?](#can-i-use-reselect-without-redux) + - [How do I create a selector that takes an argument?](#how-do-i-create-a-selector-that-takes-an-argument) + - [Can the memoization behavior be customized?](#can-the-memoization-behavior-be-customized) + - [How do I test a selector?](#how-do-i-test-a-selector) + - [Can I share a selector across multiple component instances?](#can-i-share-a-selector-across-multiple-component-instances) + - [Are there TypeScript Typings?](#are-there-typescript-typings) + - [I am seeing a TypeScript error: `Type instantiation is excessively deep and possibly infinite`](#i-am-seeing-a-typescript-error-type-instantiation-is-excessively-deep-and-possibly-infinite) + - [How can I make a curried selector?](#how-can-i-make-a-curried-selector) + - [How can I make pre-typed version of `createSelector` for my root state?](#how-can-i-make-pre-typed-version-of-createselector-for-my-root-state) + - [What if I want to use `createSelector` without memoization?](#what-if-i-want-to-use-createselector-without-memoization) +- [External References](#external-references) +- [Related Projects](#related-projects) +- [License](#license) +- [Prior Art and Inspiration](#prior-art-and-inspiration) + +--- + +## Terminology + +- [**Selector Function**](#selector-function): A function that accepts one or more JavaScript values as arguments, and derives a result. When used with [Redux], the first argument is typically the entire Redux store state. +- [**input selectors**](#input-selectors): Basic selector functions used as building blocks for creating a memoized selector. They are passed as the first argument(s) to [`createSelector`], and are called with all selector arguments. They are responsible for extracting and providing necessary values to the [result function]. +- [**Output Selector**](#output-selector): The actual memoized selectors created by [`createSelector`]. +- [**Result Function**](#result-function): The function that comes after the [input selectors]. It takes the [input selectors]' return values as arguments and returns a result. +- [**`Dependencies`**](#dependencies): Same as [input selectors]. They are what the [output selector] "depends" on. + +The below example serves as a visual aid: + +```ts +const outputSelector = createSelector( + [inputSelector1, inputSelector2, inputSelector3], // synonymous with `dependencies`. + resultFunc // Result function +) +``` + +
[ ↑ Back to top ↑ ]
+ +--- + +## How Does Reselect Work? + +Reselect, at its core, is a library for creating memoized selectors in JavaScript applications. Its primary role is to efficiently compute derived data based on provided inputs. A key aspect of Reselect's internal mechanism is how it orchestrates the flow of arguments from the final selector to its constituent [input selectors]. + +```ts +const finalSelector = (...args) => { + const extractedValues = inputSelectors.map(inputSelector => + inputSelector(...args) + ) + return resultFunc(...extractedValues) +} +``` + +In this pattern, the `finalSelector` is composed of several [input selectors], **all receiving the same arguments as the final selector**. Each input selector processes its part of the data, and the results are then combined and further processed by the [result function]. Understanding this argument flow is crucial for appreciating how Reselect optimizes data computation and minimizes unnecessary recalculations. + + + +### Cascading Memoization + +Reselect uses a two-stage "cascading" approach to memoizing functions: + +
Detailed Explanation: Cascading Memoization + +The way Reselect works can be broken down into multiple parts: + +1. **Initial Run**: On the first call, Reselect runs all the [input selectors], gathers their results, and passes them to the [result function]. + +2. **Subsequent Runs**: For subsequent calls, Reselect performs two levels of checks: + + - **First Level**: It compares the current arguments with the previous ones (done by `argsMemoize`). + + - If they're the same, it returns the cached result without running the [input selectors] or the [result function]. + + - If they differ, it proceeds ("cascades") to the second level. + + - **Second Level**: It runs the [input selectors] and compares their current results with the previous ones (done by `memoize`). + > [!NOTE] + > If any one of the [input selectors] return a different result, all [input selectors] will recalculate. + - If the results are the same, it returns the cached result without running the [result function]. + - If the results differ, it runs the [result function]. + +This behavior is what we call **_Cascading Double-Layer Memoization_**. + +#### Reselect Vs Standard Memoization + +##### Standard Memoization + +![normal-memoization-function](docs/assets//normal-memoization-function.png) + +_Standard memoization only compares arguments. If they're the same, it returns the cached result._ + +##### Memoization with Reselect + +![reselect-memoization](docs/assets//reselect-memoization.png) + +_Reselect adds a second layer of checks with the [input selectors]. This is crucial in [Redux] applications where state references change frequently._ + +A normal [memoization] function will compare the arguments, and if they are the same as last time, it will skip running the function and return the cached result. However, Reselect enhances this by introducing a second tier of checks via its [input selectors]. It's possible that the arguments passed to these [input selectors] may change, yet their results remain the same. When this occurs, Reselect avoids re-executing the [result function], and returns the cached result. + +This feature becomes crucial in [Redux] applications, where the `state` changes its reference anytime an `action` is dispatched. + +> [!NOTE] +> The [input selectors] take the same arguments as the [output selector]. + +
+ +### Why Reselect Is Often Used With [Redux] + +While Reselect can be used independently from Redux, it is a standard tool used in most Redux applications to help optimize calculations and UI updates: + +
Detailed Explanation: Reselect and Redux Optimization + +Imagine you have a selector like this: + +```ts +const selectCompletedTodos = (state: RootState) => + state.todos.filter(todo => todo.completed === true) +``` + +So you decide to memoize it: + +```ts +const selectCompletedTodos = someMemoizeFunction((state: RootState) => + state.todos.filter(todo => todo.completed === true) +) +``` + +Then you update `state.alerts`: + +```ts +store.dispatch(toggleRead(0)) +``` + +Now when you call `selectCompletedTodos`, it re-runs, because we have effectively broken memoization. + +```ts +selectCompletedTodos(store.getState()) +// Will not run, and the cached result will be returned. +selectCompletedTodos(store.getState()) +store.dispatch(toggleRead(0)) +// It recalculates. +selectCompletedTodos(store.getState()) +``` + +But why? `selectCompletedTodos` only needs to access `state.todos`, and has nothing to do with `state.alerts`, so why have we broken memoization? Well that's because in [Redux] anytime you make a change to the root `state`, it gets shallowly updated, which means its reference changes, therefore a normal memoization function will always fail the comparison check on the arguments. + +But with Reselect, we can do something like this: + +```ts +const selectCompletedTodos = createSelector( + [(state: RootState) => state.todos], + todos => todos.filter(todo => todo.completed === true) +) +``` + +And now we have achieved memoization: + +```ts +selectCompletedTodos(store.getState()) +// Will not run, and the cached result will be returned. +selectCompletedTodos(store.getState()) +store.dispatch(toggleRead(0)) +// The `input selectors` will run, but the `result function` is +// skipped and the cached result will be returned. +selectCompletedTodos(store.getState()) +``` + +Even when the overall `state` changes, Reselect ensures efficient memoization through its unique approach. The [result function] doesn't re-run if the relevant part of the `state` (in this case `state.todos`), remains unchanged. This is due to Reselect's [**_Cascading Double-Layer Memoization_**][**_Cascading Memoization_**]. The first layer checks the entire `state`, and the second layer checks the results of the [input selectors]. If the first layer fails (due to a change in the overall `state`) but the second layer succeeds (because `state.todos` is unchanged), Reselect skips recalculating the [result function]. This dual-check mechanism makes Reselect particularly effective in [Redux] applications, ensuring computations are only done when truly necessary. + +
+ +
[ ↑ Back to top ↑ ]
+ +--- + +## API + + + +### createSelector(...inputSelectors | [inputSelectors], resultFunc, createSelectorOptions?) + +Description + +Accepts one or more "[input selectors]" (either as separate arguments or a single array), +a single "[result function]", and an optional options object, and +generates a memoized selector function. + +Parameters + +| Name | Description | +| :----------------------- | :-------------------------------------------------------------------------------- | +| `inputSelectors` | An array of [input selectors], can also be passed as separate arguments. | +| `resultFunc` | A function that takes the results of the [input selectors] as separate arguments. | +| `createSelectorOptions?` | An optional options object that allows for further customization per selector. | + +Returns + +A memoized [output selector]. + +
Type parameters + +| Name | Description | +| :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `InputSelectors` | The type of the [input selectors] array. | +| `Result` | The return type of the [result function] as well as the [output selector]. | +| `OverrideMemoizeFunction` | The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into [`createSelectorCreator`]. | +| `OverrideArgsMemoizeFunction` | The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into [`createSelectorCreator`]. | + +
+ +
[ ↑ Back to top ↑ ]
+ +--- + + + +### createSelectorCreator(memoize | options, ...memoizeOptions) + +Description + +Accepts either a `memoize` function and `...memoizeOptions` rest parameter, or since 5.0.0 an `options` object containing a `memoize` function and creates a custom selector creator function. + +Parameters (since 5.0.0) + +| Name | Description | +| :----------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `options` | An options object containing the `memoize` function responsible for memoizing the `resultFunc` inside [`createSelector`] (e.g., `defaultMemoize` or `weakMapMemoize`). It also provides additional options for customizing memoization. While the `memoize` property is mandatory, the rest are optional. | +| `options.argsMemoize?` | The optional memoize function that is used to memoize the arguments passed into the [output selector] generated by [`createSelector`] (e.g., `defaultMemoize` or `weakMapMemoize`).
**`Default`** `defaultMemoize` | +| `options.argsMemoizeOptions?` | Optional configuration options for the `argsMemoize` function. These options are passed to the `argsMemoize` function as the second argument.
since 5.0.0 | +| `options.inputStabilityCheck?` | Overrides the global input stability check for the selector. Possible values are:
`once` - Run only the first time the selector is called.
`always` - Run every time the selector is called.
`never` - Never run the input stability check.
**`Default`** = `'once'`
since 5.0.0 | +| `options.memoize` | The memoize function that is used to memoize the `resultFunc` inside [`createSelector`] (e.g., `defaultMemoize` or `weakMapMemoize`). since 5.0.0 | +| `options.memoizeOptions?` | Optional configuration options for the `memoize` function. These options are passed to the `memoize` function as the second argument.
since 5.0.0 | + +Parameters + +| Name | Description | +| :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | +| `memoize` | The `memoize` function responsible for memoizing the `resultFunc` inside [`createSelector`] (e.g., `defaultMemoize` or `weakMapMemoize`). | +| `...memoizeOptionsFromArgs` | Optional configuration options for the memoization function. These options are then passed to the memoize function as the second argument onwards. | + +Returns + +A customized [`createSelector`] function. + +
Type parameters + +| Name | Description | +| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `MemoizeFunction` | The type of the memoize function that is used to memoize the `resultFunc` inside [`createSelector`] (e.g., `defaultMemoize` or `weakMapMemoize`). | +| `ArgsMemoizeFunction` | The type of the optional memoize function that is used to memoize the arguments passed into the [output selector] generated by [`createSelector`] (e.g., `defaultMemoize` or `weakMapMemoize`). If none is explicitly provided, `defaultMemoize` will be used. | + +
+ +
Examples + +##### Using `options` (since 5.0.0) + +```ts +const customCreateSelector = createSelectorCreator({ + memoize: customMemoize, // Function to be used to memoize `resultFunc` + memoizeOptions: [memoizeOption1, memoizeOption2], // Options passed to `customMemoize` as the second argument onwards + argsMemoize: customArgsMemoize, // Function to be used to memoize the selector's arguments + argsMemoizeOptions: [argsMemoizeOption1, argsMemoizeOption2] // Options passed to `customArgsMemoize` as the second argument onwards +}) + +const customSelector = customCreateSelector( + [inputSelector1, inputSelector2], + resultFunc // `resultFunc` will be passed as the first argument to `customMemoize` +) + +customSelector( + ...selectorArgs // Will be memoized by `customArgsMemoize` +) +``` + +
[ ↑ Back to top ↑ ]
+ +--- + +##### Using `memoize` and `...memoizeOptions` + +`createSelectorCreator` can be used to make a customized version of [`createSelector`]. + +The `memoize` argument is a memoization function to replace `defaultMemoize`. + +The `...memoizeOptions` rest parameters are zero or more configuration options to be passed to `memoizeFunc`. The selectors `resultFunc` is passed as the first argument to `memoize` and the `memoizeOptions` are passed as the second argument onwards: + +```ts +const customSelectorCreator = createSelectorCreator( + customMemoize, // Function to be used to memoize `resultFunc` + option1, // `option1` will be passed as second argument to `customMemoize` + option2, // `option2` will be passed as third argument to `customMemoize` + option3 // `option3` will be passed as fourth argument to `customMemoize` +) + +const customSelector = customSelectorCreator( + [inputSelector1, inputSelector2], + resultFunc // `resultFunc` will be passed as first argument to `customMemoize` +) +``` + +Internally `customSelector` calls the memoize function as follows: + +```ts +customMemoize(resultFunc, option1, option2, option3) +``` + +##### Additional Examples + +###### Customize `equalityCheck` for `defaultMemoize` + +```js +import { createSelectorCreator, defaultMemoize } from 'reselect' +import isEqual from 'lodash.isequal' + +// create a "selector creator" that uses lodash.isequal instead of === +const createDeepEqualSelector = createSelectorCreator(defaultMemoize, isEqual) + +// use the new "selector creator" to create a selector +const selectSum = createDeepEqualSelector( + [state => state.values.filter(val => val < 5)], + values => values.reduce((acc, val) => acc + val, 0) +) +``` + +###### Use memoize function from Lodash for an unbounded cache + +```js +import { createSelectorCreator } from 'reselect' +import memoize from 'lodash.memoize' + +const hashFn = (...args) => + args.reduce((acc, val) => acc + '-' + JSON.stringify(val), '') + +const customSelectorCreator = createSelectorCreator(memoize, hashFn) + +const selector = customSelectorCreator( + [state => state.a, state => state.b], + (a, b) => a + b +) +``` + +
+ +
[ ↑ Back to top ↑ ]
+ +--- + + + +### createStructuredSelector({ inputSelectorsObject }, selectorCreator = createSelector) + +Description + +A convenience function that simplifies returning an object made up of selector results. + +Parameters + +| Name | Description | +| :--------------------- | :--------------------------------------------------------------------- | +| `inputSelectorsObject` | A key value pair consisting of input selectors. | +| `selectorCreator?` | A custom selector creator function. It defaults to [`createSelector`]. | + +Returns + +A memoized structured selector. + +
Type parameters + +| Name | Description | +| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `InputSelectorsObject` | The shape of the [input selectors] object. | +| `MemoizeFunction` | The type of the memoize function that is used to create the structured selector. It defaults to `defaultMemoize`. | +| `ArgsMemoizeFunction` | The type of the of the memoize function that is used to memoize the arguments passed into the generated structured selector. It defaults to `defaultMemoize`. | + +
+ +
Examples + +##### Modern Use Case + +```ts +import { createSelector, createStructuredSelector } from 'reselect' + +interface RootState { + todos: { + id: number + completed: boolean + title: string + description: string + }[] + alerts: { id: number; read: boolean }[] +} + +// This: +const structuredSelector = createStructuredSelector( + { + todos: (state: RootState) => state.todos, + alerts: (state: RootState) => state.alerts, + todoById: (state: RootState, id: number) => state.todos[id] + }, + createSelector +) + +// Is essentially the same as this: +const selector = createSelector( + [ + (state: RootState) => state.todos, + (state: RootState) => state.alerts, + (state: RootState, id: number) => state.todos[id] + ], + (todos, alerts, todoById) => { + return { + todos, + alerts, + todoById + } + } +) +``` + +In your component: + +```tsx +interface Props { + id: number +} + +const MyComponent: FC = ({ id }) => { + const { todos, alerts, todoById } = useSelector(state => + structuredSelector(state, id) + ) + + return ( +
+ Next to do is: +

{todoById.title}

+

Description: {todoById.description}

+
    +

    All other to dos:

    + {todos.map(todo => ( +
  • {todo.title}
  • + ))} +
+
+ ) +} +``` + +##### Simple Use Case + +```ts +const selectA = state => state.a +const selectB = state => state.b + +// The result function in the following selector +// is simply building an object from the input selectors +const structuredSelector = createSelector(selectA, selectB, (a, b) => ({ + a, + b +})) + +const result = structuredSelector({ a: 1, b: 2 }) // will produce { x: 1, y: 2 } +``` + +
+ +
[ ↑ Back to top ↑ ]
+ +--- + +### Memoization Functions + +Reselect comes with a selection of memoization functions, each uniquely designed to address different scenarios and performance requirements. By effectively leveraging these functions, you can significantly enhance the efficiency and responsiveness of your applications. + + + +#### defaultMemoize(func, equalityCheckOrOptions = defaultEqualityCheck) + +Description + +The standard memoize function used by [`createSelector`]. + +It has a default cache size of 1. This means it always recalculates when the value of an argument changes. However, this can be customized as needed with a specific max cache size (since 4.1.0). + +It determines if an argument has changed by calling the `equalityCheck` function. As `defaultMemoize` is designed to be used with immutable data, the default `equalityCheck` function checks for changes using [reference equality][Reference Equality Check]: + +```ts +const defaultEqualityCheck = (previousValue: any, currentValue: any) => { + return previousValue === currentValue +} +``` + +Parameters + +| Name | Description | +| :----------------------- | :---------------------------------------------------------- | +| `func` | The function to be memoized. | +| `equalityCheckOrOptions` | Either an `equality check` function or an `options` object. | + +Since 4.1.0, `defaultMemoize` also accepts an options object as its first argument instead of an `equalityCheck` function. The `options` object may contain: + +```ts +type EqualityFn = (a: any, b: any) => boolean + +interface DefaultMemoizeOptions { + equalityCheck?: EqualityFn + resultEqualityCheck?: EqualityFn + maxSize?: number +} +``` + +| Name | Description | +| :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `equalityCheck` | Used to compare the individual arguments of the provided calculation function.
**`Default`** = `defaultEqualityCheck` | +| `resultEqualityCheck` | If provided, used to compare a newly generated output value against previous values in the cache. If a match is found, the old value is returned. This addresses the common todos.map(todo => todo.id) use case, where an update to another field in the original data causes a recalculation due to changed references, but the output is still effectively the same. | +| `maxSize` | The cache size for the selector. If greater than 1, the selector will use an LRU cache internally.
**`Default`** = 1 | + +> [!WARNING] +> If `resultEqualityCheck` is used inside `argsMemoizeOptions` it has no effect. + +Returns + +A memoized function with a `.clearCache()` method attached. + +
Type parameters + +| Name | Description | +| :----- | :----------------------------------------- | +| `Func` | The type of the function that is memoized. | + +
+ +
Examples + +###### Using `defaultMemoize` with [`createSelector`] + +```ts +import { shallowEqual } from 'react-redux' +import { createSelector } from 'reselect' + +const selectTodoIds = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(todo => todo.id), + { + memoizeOptions: { + equalityCheck: shallowEqual, + resultEqualityCheck: shallowEqual, + maxSize: 10 + }, + argsMemoizeOptions: { + equalityCheck: shallowEqual, + resultEqualityCheck: shallowEqual, + maxSize: 10 + } + } +) +``` + +###### Using `defaultMemoize` with [`createSelectorCreator`] + +```ts +import { shallowEqual } from 'react-redux' +import { createSelectorCreator, defaultMemoize } from 'reselect' + +const createSelectorShallowEqual = createSelectorCreator({ + memoize: defaultMemoize, + memoizeOptions: { + equalityCheck: shallowEqual, + resultEqualityCheck: shallowEqual, + maxSize: 10 + }, + argsMemoize: defaultMemoize, + argsMemoizeOptions: { + equalityCheck: shallowEqual, + resultEqualityCheck: shallowEqual, + maxSize: 10 + } +}) + +const selectTodoIds = createSelectorShallowEqual( + [(state: RootState) => state.todos], + todos => todos.map(todo => todo.id) +) +``` + +
+ +
[ ↑ Back to top ↑ ]
+ +--- + + + +#### weakMapMemoize(func) - (since 5.0.0) + +Description + +[`defaultMemoize`] has to be explicitly configured to have a cache size larger than 1, and uses an LRU cache internally. + +`weakMapMemoize` creates a tree of [`WeakMap`]-based cache nodes based on the identity of the arguments it's been called with (in this case, the extracted values from your input selectors). **This allows `weakMapMemoize` to have an effectively infinite cache size**. Cache results will be kept in memory as long as references to the arguments still exist, and then cleared out as the arguments are garbage-collected. + +
Design Tradeoffs + +- Pros: + + - It has an effectively infinite cache size, but you have no control over + how long values are kept in cache as it's based on garbage collection and [`WeakMap`]s. + +- Cons: + - There's currently no way to alter the argument comparisons. They're based on [strict reference equality][Reference Equality Check]. + +
+ +
Use Cases + +- This memoizer is likely best used for cases where you need to call the + same selector instance with many different arguments, such as a single + selector instance that is used in a list item component and called with + item IDs like: + +```ts +useSelector(state => selectSomeData(state, id)) +``` + +Prior to `weakMapMemoize`, you had this problem: + +```ts +interface RootState { + items: { id: number; category: string; name: string }[] +} + +const selectItemsByCategory = createSelector( + [ + (state: RootState) => state.items, + (state: RootState, category: string) => category + ], + (items, category) => items.filter(item => item.category === category) +) + +selectItemsByCategory(state, 'Electronics') // Selector runs +selectItemsByCategory(state, 'Electronics') +selectItemsByCategory(state, 'Stationery') // Selector runs +selectItemsByCategory(state, 'Electronics') // Selector runs again! +``` + +Before you could solve this in a number of different ways: + +1. Set the `maxSize` with [`defaultMemoize`]: + +```ts +const selectItemsByCategory = createSelector( + [ + (state: RootState) => state.items, + (state: RootState, category: string) => category + ], + (items, category) => items.filter(item => item.category === category), + { + memoizeOptions: { + maxSize: 10 + } + } +) +``` + +But this required having to know the cache size ahead of time. + +2. Create unique selector instances using [`useMemo`]. + +```tsx +const makeSelectItemsByCategory = (category: string) => + createSelector([(state: RootState) => state.items], items => + items.filter(item => item.category === category) + ) + +interface Props { + category: string +} + +const MyComponent: FC = ({ category }) => { + const selectItemsByCategory = useMemo( + () => makeSelectItemsByCategory(category), + [category] + ) + + const itemsByCategory = useSelector(selectItemsByCategory) + + return ( +
+ {itemsByCategory.map(item => ( +
{item.name}
+ ))} +
+ ) +} +``` + +3. Using [`useCallback`]. + +```tsx +const selectItemsByCategory = createSelector( + [ + (state: RootState) => state.items, + (state: RootState, category: string) => category + ], + (items, category) => items.filter(item => item.category === category) +) + +const MyComponent: FC = ({ category }) => { + const selectItemsByCategoryMemoized = useCallback(selectItemsByCategory, []) + + const itemsByCategory = useSelector(state => + selectItemsByCategoryMemoized(state, category) + ) + + return ( +
+ {itemsByCategory.map(item => ( +
{item.name}
+ ))} +
+ ) +} +``` + +4. Use [`re-reselect`]: + +```ts +import { createCachedSelector } from 're-reselect' + +const selectItemsByCategory = createCachedSelector( + [ + (state: RootState) => state.items, + (state: RootState, category: string) => category + ], + (items, category) => items.filter(item => item.category === category) +)((state: RootState, category: string) => category) +``` + +Starting in 5.0.0, you can eliminate this problem using `weakMapMemoize`. + +```ts +const selectItemsByCategory = createSelector( + [ + (state: RootState) => state.items, + (state: RootState, category: string) => category + ], + (items, category) => items.filter(item => item.category === category), + { + memoize: weakMapMemoize, + argsMemoize: weakMapMemoize + } +) + +selectItemsByCategory(state, 'Electronics') // Selector runs +selectItemsByCategory(state, 'Electronics') // Cached +selectItemsByCategory(state, 'Stationery') // Selector runs +selectItemsByCategory(state, 'Electronics') // Still cached! +``` + +This solves the problem of having to know and set the cache size prior to creating a memoized selector. Because `weakMapMemoize` essentially provides a dynamic cache size out of the box. + +
+ +Parameters + +| Name | Description | +| :----- | :--------------------------- | +| `func` | The function to be memoized. | + +Returns + +A memoized function with a `.clearCache()` method attached. + +
Type parameters + +| Name | Description | +| :----- | :----------------------------------------- | +| `Func` | The type of the function that is memoized. | + +
+ +
Examples + +###### Using `weakMapMemoize` with [`createSelector`] + +```ts +import { createSelector, weakMapMemoize } from 'reselect' + +const selectItemsByCategory = createSelector( + [ + (state: RootState) => state.items, + (state: RootState, category: string) => category + ], + (items, category) => items.filter(item => item.category === category), + { + memoize: weakMapMemoize, + argsMemoize: weakMapMemoize + } +) + +selectItemsByCategory(state, 'Electronics') // Selector runs +selectItemsByCategory(state, 'Electronics') +selectItemsByCategory(state, 'Stationery') // Selector runs +selectItemsByCategory(state, 'Electronics') +``` + +###### Using `weakMapMemoize` with [`createSelectorCreator`] + +```ts +import { createSelectorCreator, weakMapMemoize } from 'reselect' + +const createSelectorWeakMap = createSelectorCreator({ + memoize: weakMapMemoize, + argsMemoize: weakMapMemoize +}) + +const selectItemsByCategory = createSelectorWeakMap( + [ + (state: RootState) => state.items, + (state: RootState, category: string) => category + ], + (items, category) => items.filter(item => item.category === category) +) + +selectItemsByCategory(state, 'Electronics') // Selector runs +selectItemsByCategory(state, 'Electronics') +selectItemsByCategory(state, 'Stationery') // Selector runs +selectItemsByCategory(state, 'Electronics') +``` + +
+ +
[ ↑ Back to top ↑ ]
+ +--- + + + +#### unstable_autotrackMemoize(func) - (since 5.0.0) + +Description + +Uses an "auto-tracking" approach inspired by the work of the Ember Glimmer team. It uses a Proxy to wrap arguments and track accesses to nested fields in your selector on first read. Later, when the selector is called with new arguments, it identifies which accessed fields have changed and only recalculates the result if one or more of those accessed fields have changed. This allows it to be more precise than the shallow equality checks in `defaultMemoize`. + +> [!WARNING] +> This API is still experimental and undergoing testing. + +
Design Tradeoffs + +- Pros: + + - It is likely to avoid excess calculations and recalculate fewer times than `defaultMemoize` will, which may also result in fewer component re-renders. + +- Cons: + + - It only has a cache size of 1. + - It is slower than `defaultMemoize`, because it has to do more work. (How much slower is dependent on the number of accessed fields in a selector, number of calls, frequency of input changes, etc) + - It can have some unexpected behavior. Because it tracks nested field accesses, cases where you don't access a field will not recalculate properly. For example, a badly-written selector like: + + ```ts + createSelector([state => state.todos], todos => todos) + ``` + + that just immediately returns the extracted value will never update, because it doesn't see any field accesses to check. + +
+ +
Use Cases + +- It is likely best used for cases where you need to access specific nested fields in data, and avoid recalculating if other fields in the same data objects are immutably updated. + +
+ +Parameters + +| Name | Description | +| :----- | :--------------------------- | +| `func` | The function to be memoized. | + +Returns + +A memoized function with a `.clearCache()` method attached. + +
Type parameters + +| Name | Description | +| :----- | :----------------------------------------- | +| `Func` | The type of the function that is memoized. | + +
+ +
Examples + +###### Using `unstable_autotrackMemoize` with [`createSelector`] + +```ts +import { unstable_autotrackMemoize, createSelector } from 'reselect' + +const selectTodoIds = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(todo => todo.id), + { memoize: unstable_autotrackMemoize } +) +``` + +###### Using `unstable_autotrackMemoize` with [`createSelectorCreator`] + +```ts +import { unstable_autotrackMemoize, createSelectorCreator } from 'reselect' + +const createSelectorAutotrack = createSelectorCreator({ + memoize: unstable_autotrackMemoize +}) + +const selectTodoIds = createSelectorAutotrack( + [(state: RootState) => state.todos], + todos => todos.map(todo => todo.id) +) +``` + +
+ +
[ ↑ Back to top ↑ ]
+ +--- + + + +## Debugging Tools + + + +### Development-Only Stability Checks + +Reselect includes extra checks in development mode to help catch and warn about mistakes in selector behavior. + + + +#### `inputStabilityCheck` + +Due to how [**_Cascading Memoization_**] works in Reselect, it is crucial that your [input selectors] do not return a new reference on each run. If an [input selector][input selectors] always returns a new reference, like + +```ts +state => ({ a: state.a, b: state.b }) +``` + +or + +```ts +state => state.todos.map(todo => todo.id) +``` + +that will cause the selector to never memoize properly. +Since this is a common mistake, we've added a development mode check to catch this. By default, [`createSelector`] will now run the [input selectors] twice during the first call to the selector. If the result appears to be different for the same call, it will log a warning with the arguments and the two different sets of extracted input values. + +```ts +type StabilityCheckFrequency = 'always' | 'once' | 'never' +``` + +| Possible Values | Description | +| :-------------- | :---------------------------------------------- | +| `once` | Run only the first time the selector is called. | +| `always` | Run every time the selector is called. | +| `never` | Never run the input stability check. | + +> [!IMPORTANT] +> The input stability check is automatically disabled in production environments. + +You can configure this behavior in two ways: + + + +##### 1. Globally through `setInputStabilityCheckEnabled`: + +A `setInputStabilityCheckEnabled` function is exported from Reselect, which should be called with the desired setting. + +```ts +import { setInputStabilityCheckEnabled } from 'reselect' + +// Run only the first time the selector is called. (default) +setInputStabilityCheckEnabled('once') + +// Run every time the selector is called. +setInputStabilityCheckEnabled('always') + +// Never run the input stability check. +setInputStabilityCheckEnabled('never') +``` + +##### 2. Per selector by passing an `inputStabilityCheck` option directly to [`createSelector`]: + +```ts +// Create a selector that double-checks the results of [`input selectors`][Input Selectors] every time it runs. +const selectCompletedTodosLength = createSelector( + [ + // This `input selector` will not be memoized properly since it always returns a new reference. + (state: RootState) => + state.todos.filter(({ completed }) => completed === true) + ], + completedTodos => completedTodos.length, + // Will override the global setting. + { inputStabilityCheck: 'always' } +) +``` + +> [!WARNING] +> This will override the global input stability check set by calling `setInputStabilityCheckEnabled`. -[![GitHub Workflow Status][build-badge]][build] -[![npm package][npm-badge]][npm] -[![Coveralls][coveralls-badge]][coveralls] + -## Installation +### Output Selector Fields -### Redux Toolkit +The output selectors created by createSelector have several additional properties attached to them: -While Reselect is not exclusive to Redux, it is already included by default in [the official Redux Toolkit package](https://redux-toolkit.js.org) - no further installation needed. +| Name | Description | +| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `resultFunc` | The final function passed to [`createSelector`]. | +| `memoizedResultFunc` | The memoized version of `resultFunc`. | +| `lastResult` | Returns the last result calculated by `memoizedResultFunc`. | +| `dependencies` | The array of the input selectors used by [`createSelector`] to compose `resultFunc`. | +| `recomputations` | Counts the number of times `memoizedResultFunc` has been recalculated. | +| `resetRecomputations` | Resets the count of `recomputations` count to 0. | +| `dependencyRecomputations` | Counts the number of times the [input selectors] ([`dependencies`]) have been recalculated. This is distinct from `recomputations`, which tracks the recalculations of the [result function]. | +| `resetDependencyRecomputations` | Resets the `dependencyRecomputations` count to 0. | +| `memoize` | Function used to memoize the `resultFunc`. | +| `argsMemoize` | Function used to memoize the arguments passed into the [output selector]. | -```js -import { createSelector } from '@reduxjs/toolkit' -``` +
[ ↑ Back to top ↑ ]
-### Standalone +--- -For standalone usage, install the `reselect` package: + -```bash -npm install reselect +## What's New in 5.0.0? -yarn add reselect -``` +Version 5.0.0 introduces several new features and improvements: -## Basic Usage +- **Customization Enhancements**: -Reselect exports a `createSelector` API, which generates memoized selector functions. `createSelector` accepts one or more "input" selectors, which extract values from arguments, and an "output" selector that receives the extracted values and should return a derived value. If the generated selector is called multiple times, the output will only be recalculated when the extracted values have changed. + - Added the ability to pass an options object to [`createSelectorCreator`], allowing for customized `memoize` and `argsMemoize` functions, alongside their respective options (`memoizeOptions` and `argsMemoizeOptions`). + - The [`createSelector`] function now supports direct customization of `memoize` and `argsMemoize` within its options object. -You can play around with the following **example** in [this CodeSandbox](https://codesandbox.io/s/objective-waterfall-1z5y8?file=/src/index.js): +- **Memoization Functions**: -```js -import { createSelector } from 'reselect' + - Introduced new experimental memoization functions: `weakMapMemoize` and `unstable_autotrackMemoize`. + - Incorporated `memoize` and `argsMemoize` into the [output selector fields] for debugging purposes. -const selectShopItems = state => state.shop.items -const selectTaxPercent = state => state.shop.taxPercent +- **TypeScript Support and Performance**: -const selectSubtotal = createSelector(selectShopItems, items => - items.reduce((subtotal, item) => subtotal + item.value, 0) -) + - Discontinued support for TypeScript versions below 4.7, aligning with modern TypeScript features. + - Significantly improved TypeScript performance for nesting [output selector]s. The nesting limit has increased from approximately 8 to around 30 [output selector]s, greatly reducing the occurrence of the infamous `Type instantiation is excessively deep and possibly infinite` error. -const selectTax = createSelector( - selectSubtotal, - selectTaxPercent, - (subtotal, taxPercent) => subtotal * (taxPercent / 100) -) +- **Selector API Enhancements**: -const selectTotal = createSelector( - selectSubtotal, - selectTax, - (subtotal, tax) => ({ total: subtotal + tax }) -) + - Removed the second overload of `createStructuredSelector` due to its susceptibility to runtime errors. + - Added the `TypedStructuredSelectorCreator` utility type (_currently a work-in-progress_) to facilitate the creation of a pre-typed version of `createStructuredSelector` for your root state. -const exampleState = { - shop: { - taxPercent: 8, - items: [ - { name: 'apple', value: 1.2 }, - { name: 'orange', value: 0.95 } - ] - } -} +- **Additional Functionalities**: -console.log(selectSubtotal(exampleState)) // 2.15 -console.log(selectTax(exampleState)) // 0.172 -console.log(selectTotal(exampleState)) // { total: 2.322 } -``` + - Added `dependencyRecomputations` and `resetDependencyRecomputations` to the [output selector fields]. These additions provide greater control and insight over [input selectors], complementing the new `argsMemoize` API. + - Introduced `inputStabilityCheck`, a development tool that runs the [input selectors] twice using the same arguments and triggers a warning If they return differing results for the same call. -## Table of Contents +These updates aim to enhance flexibility, performance, and developer experience. For detailed usage and examples, refer to the updated documentation sections for each feature. -- [Installation](#installation) - - [Redux Toolkit](#redux-toolkit) - - [Standalone](#standalone) -- [Basic Usage](#basic-usage) -- [API](#api) - - [createSelector(...inputSelectors | [inputSelectors], resultFunc, selectorOptions?)](#createselectorinputselectors--inputselectors-resultfunc-selectoroptions) - - [defaultMemoize(func, equalityCheckOrOptions = defaultEqualityCheck)](#defaultmemoizefunc-equalitycheckoroptions--defaultequalitycheck) - - [createSelectorCreator(memoize, ...memoizeOptions)](#createselectorcreatormemoize-memoizeoptions) - - [Customize `equalityCheck` for `defaultMemoize`](#customize-equalitycheck-for-defaultmemoize) - - [Use memoize function from Lodash for an unbounded cache](#use-memoize-function-from-lodash-for-an-unbounded-cache) - - [createStructuredSelector({inputSelectors}, selectorCreator = createSelector)](#createstructuredselectorinputselectors-selectorcreator--createselector) -- [Development-only checks](#development-only-checks) - - [`inputStabilityCheck`](#inputstabilitycheck) - - [Global configuration](#global-configuration) - - [Per-selector configuration](#per-selector-configuration) -- [FAQ](#faq) - - [Q: Why isn’t my selector recomputing when the input state changes?](#q-why-isnt-my-selector-recomputing-when-the-input-state-changes) - - [Q: Why is my selector recomputing when the input state stays the same?](#q-why-is-my-selector-recomputing-when-the-input-state-stays-the-same) - - [Q: Can I use Reselect without Redux?](#q-can-i-use-reselect-without-redux) - - [Q: How do I create a selector that takes an argument?](#q-how-do-i-create-a-selector-that-takes-an-argument) - - [Q: The default memoization function is no good, can I use a different one?](#q-the-default-memoization-function-is-no-good-can-i-use-a-different-one) - - [Q: How do I test a selector?](#q-how-do-i-test-a-selector) - - [Q: Can I share a selector across multiple component instances?](#q-can-i-share-a-selector-across-multiple-component-instances) - - [Q: Are there TypeScript Typings?](#q-are-there-typescript-typings) - - [Q: How can I make a curried selector?](#q-how-can-i-make-a-curried-selector) -- [Related Projects](#related-projects) - - [re-reselect](#re-reselect) - - [reselect-tools](#reselect-tools) - - [reselect-debugger](#reselect-debugger) -- [License](#license) -- [Prior Art and Inspiration](#prior-art-and-inspiration) +- **Breaking Changes**: -## API + - Removed `ParametricSelector` and `OutputParametricSelector` types. Their functionalities are now integrated into `Selector` and `OutputSelector` respectively, which inherently support additional parameters. -### createSelector(...inputSelectors | [inputSelectors], resultFunc, selectorOptions?) + -Accepts one or more "input selectors" (either as separate arguments or a single array), a single "output selector" / "result function", and an optional options object, and generates a memoized selector function. +
[ ↑ Back to top ↑ ]
-When the selector is called, each input selector will be called with all of the provided arguments. The extracted values are then passed as separate arguments to the output selector, which should calculate and return a final result. The inputs and result are cached for later use. +--- -If the selector is called again with the same arguments, the previously cached result is returned instead of recalculating a new result. + -`createSelector` determines if the value returned by an input-selector has changed between calls using reference equality (`===`). Inputs to selectors created with `createSelector` should be immutable. +## Optimizing Reselect -By default, selectors created with `createSelector` have a cache size of 1. This means they always recalculate when the value of an input-selector changes, as a selector only stores the preceding value of each input-selector. This can be customized by passing a `selectorOptions` object with a `memoizeOptions` field containing options for the built-in `defaultMemoize` memoization function . +### Common Mistakes -```js -const selectValue = createSelector( - state => state.values.value1, - state => state.values.value2, - (value1, value2) => value1 + value2 -) +
Click to expand -// You can also pass an array of selectors -const selectTotal = createSelector( - [state => state.values.value1, state => state.values.value2], - (value1, value2) => value1 + value2 -) +A somewhat common mistake is to write an [input selector][input selectors] that extracts a value or does some derivation, and a [result function] that just returns its result: -// Selector behavior can be customized -const customizedSelector = createSelector( - state => state.a, - state => state.b, - (a, b) => a + b, - { - // New in 4.1: Pass options through to the built-in `defaultMemoize` function - memoizeOptions: { - equalityCheck: (a, b) => a === b, - maxSize: 10, - resultEqualityCheck: shallowEqual - } - } +```ts +// ❌ BROKEN: this will not memoize correctly, and does nothing useful! +const brokenSelector = createSelector( + [(state: RootState) => state.todos], + todos => todos ) ``` -Selectors are typically called with a Redux `state` value as the first argument, and the input selectors extract pieces of the `state` object for use in calculations. However, it's also common to want to pass additional arguments, such as a value to filter by. Since input selectors are given all arguments, they can extract the additional arguments and pass them to the output selector: +Any [result function] that just returns its inputs is incorrect! The [result function] should always have the transformation logic. -```js -const selectItemsByCategory = createSelector( - [ - // Usual first input - extract value from `state` - state => state.items, - // Take the second arg, `category`, and forward to the output selector - (state, category) => category - ], - // Output selector gets (`items, category)` as args - (items, category) => items.filter(item => item.category === category) +Similarly: + +```ts +// ❌ BROKEN: this will not memoize correctly! +const brokenSelector = createSelector( + [(state: RootState) => state], + state => state.todos ) ``` -### defaultMemoize(func, equalityCheckOrOptions = defaultEqualityCheck) - -`defaultMemoize` memoizes the function passed in the func parameter. It is the standard memoize function used by `createSelector`. +
-`defaultMemoize` has a default cache size of 1. This means it always recalculates when the value of an argument changes. However, this can be customized as needed with a specific max cache size (new in 4.1). +### Handling Empty Array Results -`defaultMemoize` determines if an argument has changed by calling the `equalityCheck` function. As `defaultMemoize` is designed to be used with immutable data, the default `equalityCheck` function checks for changes using reference equality: +
Click to expand -```js -function defaultEqualityCheck(previousVal, currentVal) { - return currentVal === previousVal -} -``` +To reduce recalculations, use a predefined empty array when `array.filter` or similar methods result in an empty array. -As of Reselect 4.1, `defaultMemoize` also accepts an options object as its first argument instead of `equalityCheck`. The options object may contain: +So you can have a pattern like this: ```ts -interface DefaultMemoizeOptions { - equalityCheck?: EqualityFn - resultEqualityCheck?: EqualityFn - maxSize?: number +interface RootState { + todos: { + id: number + title: string + description: string + completed: boolean + }[] } -``` -Available options are: +const EMPTY_ARRAY: [] = [] -- `equalityCheck`: used to compare the individual arguments of the provided calculation function -- `resultEqualityCheck`: if provided, used to compare a newly generated output value against previous values in the cache. If a match is found, the old value is returned. This address the common `todos.map(todo => todo.id)` use case, where an update to another field in the original data causes a recalculate due to changed references, but the output is still effectively the same. -- `maxSize`: the cache size for the selector. If `maxSize` is greater than 1, the selector will use an LRU cache internally +const selectCompletedTodos = createSelector( + [(state: RootState) => state.todos], + todos => { + const completedTodos = todos.filter(todo => todo.completed === true) + return completedTodos.length === 0 ? EMPTY_ARRAY : completedTodos + } +) +``` -The returned memoized function will have a `.clearCache()` method attached. +Or to avoid repetition, you can create a wrapper function and reuse it: -`defaultMemoize` can also be used with `createSelectorCreator` to create a new selector factory that always has the same settings for each selector. +```ts +const EMPTY_ARRAY: [] = [] -### createSelectorCreator(memoize, ...memoizeOptions) +export const fallbackToEmptyArray = (array: T[]) => { + return array.length === 0 ? EMPTY_ARRAY : array +} -`createSelectorCreator` can be used to make a customized version of `createSelector`. +const selectCompletedTodos = createSelector( + [(state: RootState) => state.todos], + todos => { + return fallbackToEmptyArray(todos.filter(todo => todo.completed === true)) + } +) +``` -The `memoize` argument is a memoization function to replace `defaultMemoize`. +This way if the [result function] returns an empty array twice in a row, your component will not re-render due to a stable empty array reference: -The `...memoizeOptions` rest parameters are zero or more configuration options to be passed to `memoizeFunc`. The selectors `resultFunc` is passed as the first argument to `memoize` and the `memoizeOptions` are passed as the second argument onwards: +```ts +const completedTodos = selectCompletedTodos(store.getState()) -```js -const customSelectorCreator = createSelectorCreator( - customMemoize, // function to be used to memoize resultFunc - option1, // option1 will be passed as second argument to customMemoize - option2, // option2 will be passed as third argument to customMemoize - option3 // option3 will be passed as fourth argument to customMemoize -) +store.dispatch(addTodo()) -const customSelector = customSelectorCreator( - input1, - input2, - resultFunc // resultFunc will be passed as first argument to customMemoize -) +console.log(completedTodos === selectCompletedTodos(store.getState())) //=> true ``` -Internally `customSelector` calls the memoize function as follows: +
-```js -customMemoize(resultFunc, option1, option2, option3) -``` +### Best Practices -Here are some examples of how you might use `createSelectorCreator`: +
Click to expand -#### Customize `equalityCheck` for `defaultMemoize` +There are a few details that will help you skip running as many functions as possible and get the best possible performance out of Reselect: -```js -import { createSelectorCreator, defaultMemoize } from 'reselect' -import isEqual from 'lodash.isequal' +- Due to the [**_Cascading Memoization_**] in Reselect, The first layer of checks is upon the arguments that are passed to the [output selector], therefore it's best to maintain the same reference for the arguments as much as possible. +- In [Redux], your state will change reference when updated. But it's best to keep the additional arguments as simple as possible, you can pass in objects or array as long as their reference does not change. Or you can pass in primitives like numbers for ids. +- Keep your [input selectors] as simple as possible. It's best if they mostly consist of field accessors like `state => state.todos` or argument providers like `(state, id) => id`. You should not be doing any sort of calculation inside [input selectors], and you should definitely not be returning an object or array with a new reference each time. +- The [result function] is only re-run as a last resort. So make sure to put any and all calculations inside your [result function]. That way, Reselect will only run those calculations if all other checks fail. -// create a "selector creator" that uses lodash.isequal instead of === -const createDeepEqualSelector = createSelectorCreator(defaultMemoize, isEqual) +This: -// use the new "selector creator" to create a selector -const selectSum = createDeepEqualSelector( - state => state.values.filter(val => val < 5), - values => values.reduce((acc, val) => acc + val, 0) +```ts +// ✔️ This is optimal because we have less calculations in input selectors and more in the result function. +const selectorGood = createSelector( + [(state: RootState) => state.todos], + todos => someExpensiveComputation(todos) ) ``` -#### Use memoize function from Lodash for an unbounded cache - -```js -import { createSelectorCreator } from 'reselect' -import memoize from 'lodash.memoize' +Is preferable to this: -let called = 0 -const hashFn = (...args) => - args.reduce((acc, val) => acc + '-' + JSON.stringify(val), '') -const customSelectorCreator = createSelectorCreator(memoize, hashFn) -const selector = customSelectorCreator( - state => state.a, - state => state.b, - (a, b) => { - called++ - return a + b - } +```ts +// ❌ This is not optimal! +const selectorBad = createSelector( + [(state: RootState) => someExpensiveComputation(state.todos)], + someOtherCalculation ) ``` -### createStructuredSelector({inputSelectors}, selectorCreator = createSelector) +
-`createStructuredSelector` is a convenience function for a common pattern that arises when using Reselect. The selector passed to a `connect` decorator often just takes the values of its input-selectors and maps them to keys in an object: +
[ ↑ Back to top ↑ ]
-```js -const selectA = state => state.a -const selectB = state => state.b +--- -// The result function in the following selector -// is simply building an object from the input selectors -const structuredSelector = createSelector(selectA, selectB, (a, b) => ({ - a, - b -})) -``` +## FAQ -`createStructuredSelector` takes an object whose properties are input-selectors and returns a structured selector. The structured selector returns an object with the same keys as the `inputSelectors` argument, but with the selectors replaced with their values. +### Why isn’t my selector recomputing when the input state changes? -```js -const selectA = state => state.a -const selectB = state => state.b +Check that your memoization function is compatible with your state update function (i.e. the reducer if you are using [Redux]). For example, a selector created with [`createSelector`] will not work with a state update function that mutates an existing object instead of creating a new one each time. [`createSelector`] uses an identity check (`===`) to detect that an input has changed, so mutating an existing object will not trigger the selector to recompute because mutating an object does not change its identity. Note that if you are using [Redux], mutating the state object is [almost certainly a mistake](http://redux.js.org/docs/Troubleshooting.html). -const structuredSelector = createStructuredSelector({ - x: selectA, - y: selectB -}) +### Why is my selector recomputing when the input state stays the same? -const result = structuredSelector({ a: 1, b: 2 }) // will produce { x: 1, y: 2 } -``` +To address unexpected recomputations in your selector, first ensure that `inputStabilityCheck` is set to either `'always'` or `'once'`. This setting aids in debugging by monitoring the stability of your inputs. Additionally, utilize [output selector fields] such as `recomputations`, `resetRecomputations`, `dependencyRecomputations`, and `resetDependencyRecomputations`. These tools help identify the source of the issue. -Structured selectors can be nested: +Keep an eye on the `dependencyRecomputations` count. If it increases while `recomputations` remains the same, it suggests that your arguments are changing references but your [input selectors] are stable which is typically the desired behavior. -```js -const nestedSelector = createStructuredSelector({ - subA: createStructuredSelector({ - selectorA, - selectorB - }), - subB: createStructuredSelector({ - selectorC, - selectorD - }) -}) +
Detailed Explanation: Selector Recomputations + +To delve deeper, you can determine which arguments are changing references too frequently by using the `argsMemoizeOptions` and `equalityCheck`. Consider the following example: + +```ts +interface RootState { + todos: { id: number; completed: boolean }[] + alerts: { id: number; read: boolean; type: string }[] +} + +const selectAlertsByType = createSelector( + [ + (state: RootState) => state.alerts, + (state: RootState, type: string) => type + ], + (alerts, type) => alerts.filter(todo => todo.type === type), + { + argsMemoizeOptions: { + // This will check the arguments passed to the output selector. + equalityCheck: (a, b) => { + if (a !== b) { + console.log('Changed argument:', a, 'to', b) + } + return a === b + } + } + } +) ``` -## Development-only checks +
-### `inputStabilityCheck` +### Can I use Reselect without [Redux]? -In development, an extra check is conducted on your input selectors. It runs your input selectors an extra time with the same parameters, and warns in console if they return a different result (based on your `memoize` method). +Yes. Reselect has no dependencies on any other package, so although it was designed to be used with [Redux] it can be used independently. It can be used with any plain JS data, such as typical [React] state values, as long as that data is being updated immutably. -This is important, as an input selector returning a materially different result with the same parameters means that the output selector will be run unnecessarily, thus (potentially) creating a new result and causing rerenders. +### How do I create a selector that takes an argument? -```js -const addNumbers = createSelector( - // this input selector will always return a new reference when run - // so cache will never be used - (a, b) => ({ a, b }), - ({ a, b }) => ({ total: a + b }) -) -// instead, you should have an input selector for each stable piece of data -const addNumbersStable = createSelector( - (a, b) => a, - (a, b) => b, - (a, b) => ({ - total: a + b - }) +Each of the [input selectors] you provide will be called with all of the selector's arguments. You can add additional input selectors to extract arguments and forward them to the [result function], like this: + +```ts +const selectTodosByCategory = createSelector( + (state: RootState) => state.todos, + // Extract the second argument to pass it on + (state: RootState, category: string) => category, + (todos, category) => todos.filter(t => t.category === category) ) ``` -By default, this will only happen when the selector is first called. You can configure the check globally or per selector, to change it to always run when the selector is called, or to never run. +
Detailed Explanation: Selectors and Arguments -_This check is disabled for production environments._ +When creating a selector that accepts arguments in Reselect, it's important to structure your input and [output selector]s appropriately. Here are key points to consider: -#### Global configuration +1. **Consistency in Arguments**: Ensure that all positional arguments across [input selectors] are of the same type for consistency. -A `setInputStabilityCheckEnabled` function is exported from `reselect`, which should be called with the desired setting. +2. **Selective Argument Usage**: Design each selector to use only its relevant argument(s) and ignore the rest. This is crucial because all [input selectors] receive the same arguments that are passed to the [output selector]. -```js -import { setInputStabilityCheckEnabled } from 'reselect' +Suppose we have the following state structure: -// run when selector is first called (default) -setInputStabilityCheckEnabled('once') +```ts +interface RootState { + items: { + id: number + category: string + vendor: { id: number; name: string } + }[] + // ... other state properties ... +} +``` -// always run -setInputStabilityCheckEnabled('always') +To create a selector that filters `items` based on a `category` and excludes a specific `id`, you can set up your selectors as follows: -// never run -setInputStabilityCheckEnabled('never') +```ts +const selectAvailableItems = createSelector( + [ + // First input selector extracts items from the state + (state: RootState) => state.items, + // Second input selector forwards the category argument + (state: RootState, category: string) => category, + // Third input selector forwards the ID argument + (state: RootState, category: string, id: number) => id + ], + // Output selector uses the extracted items, category, and ID + (items, category, id) => + items.filter(item => item.category === category && item.id !== id) +) ``` -#### Per-selector configuration - -A value can be passed as part of the selector options object, which will override the global setting for the given selector. +Internally Reselect is doing this: ```ts -const selectPersonName = createSelector( - selectPerson, - person => person.firstName + ' ' + person.lastName, - // `inputStabilityCheck` accepts the same settings - // as `setInputStabilityCheckEnabled` - { inputStabilityCheck: 'never' } -) +// Input selector #1 +const items = (state: RootState, category: string, id: number) => state.items +// Input selector #2 +const category = (state: RootState, category: string, id: number) => category +// Input selector #3 +const id = (state: RootState, category: string, id: number) => id +// result of output selector +const finalResult = + // The result function + items.filter(item => item.category === category && item.id !== id) ``` -## FAQ +In this example, `selectItemId` expects that its second argument will be some simple value, while `selectVendorName` expects that the second argument is an object. If you call `selectItemById(state, 42)`, `selectVendorName` will break because it's trying to access `42.name`. Reselect's TS types should detect this and prevent compilation: -### Q: Why isn’t my selector recomputing when the input state changes? +```ts +const selectItems = (state: RootState) => state.items -A: Check that your memoization function is compatible with your state update function (i.e. the reducer if you are using Redux). For example, a selector created with `createSelector` will not work with a state update function that mutates an existing object instead of creating a new one each time. `createSelector` uses an identity check (`===`) to detect that an input has changed, so mutating an existing object will not trigger the selector to recompute because mutating an object does not change its identity. Note that if you are using Redux, mutating the state object is [almost certainly a mistake](http://redux.js.org/docs/Troubleshooting.html). +// expects a number as the second argument +const selectItemId = (state: RootState, itemId: number) => itemId -The following example defines a simple selector that determines if the first todo item in an array of todos has been completed: +// expects an object as the second argument +const selectVendorName = ( + state: RootState, + vendor: { id: number; name: string } +) => vendor.name -```js -const selectIsFirstTodoComplete = createSelector( - state => state.todos[0], - todo => todo && todo.completed +const selectItemById = createSelector( + [selectItems, selectItemId, selectVendorName], + (items, itemId, vendorName) => items[itemId] ) ``` -The following state update function **will not** work with `selectIsFirstTodoComplete`: - -```js -export default function todos(state = initialState, action) { - switch (action.type) { - case COMPLETE_ALL: - const areAllMarked = state.every(todo => todo.completed) - // BAD: mutating an existing object - return state.map(todo => { - todo.completed = !areAllMarked - return todo - }) - - default: - return state - } -} -``` +
-The following state update function **will** work with `selectIsFirstTodoComplete`: +### Can the memoization behavior be customized? -```js -export default function todos(state = initialState, action) { - switch (action.type) { - case COMPLETE_ALL: - const areAllMarked = state.every(todo => todo.completed) - // GOOD: returning a new object each time with Object.assign - return state.map(todo => - Object.assign({}, todo, { - completed: !areAllMarked - }) - ) - - default: - return state - } -} -``` +Yes. The built-in `defaultMemoize` memoizer works great for a lot of use cases, but it can be customized or swapped out for a different memoizer. See [these examples](#customize-equalitycheck-for-defaultmemoize). -If you are not using Redux and have a requirement to work with mutable data, you can use `createSelectorCreator` to replace the default memoization function and/or use a different equality check function. See [here](#use-memoize-function-from-lodash-for-an-unbounded-cache) and [here](#customize-equalitycheck-for-defaultmemoize) for examples. +### How do I test a selector? -### Q: Why is my selector recomputing when the input state stays the same? +Selectors are pure functions - for a given input, a selector should always produce the same result. For this reason they are simple to unit test: call the selector with a set of inputs, and assert that the result value matches an expected shape. -A: Check that your memoization function is compatible with your state update function (i.e. the reducer if you are using Redux). For example, a selector created with `createSelector` that recomputes unexpectedly may be receiving a new object on each update whether the values it contains have changed or not. `createSelector` uses an identity check (`===`) to detect that an input has changed, so returning a new object on each update means that the selector will recompute on each update. +
Detailed Explanation: Testing Selectors -```js -import { REMOVE_OLD } from '../constants/ActionTypes' +```ts +interface RootState { + todos: { id: number; completed: boolean }[] + alerts: { id: number; read: boolean }[] +} -const initialState = [ - { - text: 'Use Redux', - completed: false, - id: 0, - timestamp: Date.now() - } -] - -export default function todos(state = initialState, action) { - switch (action.type) { - case REMOVE_OLD: - return state.filter(todo => { - return todo.timestamp + 30 * 24 * 60 * 60 * 1000 > Date.now() - }) - default: - return state - } +const state: RootState = { + todos: [ + { id: 0, completed: false }, + { id: 1, completed: true } + ], + alerts: [ + { id: 0, read: false }, + { id: 1, read: true } + ] } -``` -The following selector is going to recompute every time REMOVE_OLD is invoked because Array.filter always returns a new object. However, in the majority of cases the REMOVE_OLD action will not change the list of todos so the recomputation is unnecessary. +// With `Vitest` or `Jest` +test('selector unit test', () => { + const selectTodoIds = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(({ id }) => id) + ) + const firstResult = selectTodoIds(state) + const secondResult = selectTodoIds(state) + // Reference equality should pass. + expect(firstResult).toBe(secondResult) + // Deep equality should also pass. + expect(firstResult).toStrictEqual(secondResult) + selectTodoIds(state) + selectTodoIds(state) + selectTodoIds(state) + // The `Result Function` should not recalculate. + expect(selectTodoIds.recomputations()).toBe(1) + // `input selectors` should not recalculate. + expect(selectTodoIds.dependencyRecomputations()).toBe(1) +}) -```js -import { createSelector } from 'reselect' +// With `Chai` +test('selector unit test', () => { + const selectTodoIds = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(({ id }) => id) + ) + const firstResult = selectTodoIds(state) + const secondResult = selectTodoIds(state) + // Reference equality should pass. + expect(firstResult).to.equal(secondResult) + // Deep equality should also pass. + expect(firstResult).to.deep.equal(secondResult) + selectTodoIds(state) + selectTodoIds(state) + selectTodoIds(state) + // The `result function` should not recalculate. + expect(selectTodoIds.recomputations()).to.equal(1) + // `input selectors` should not recalculate. + expect(selectTodoIds.dependencyRecomputations()).to.equal(1) +}) +``` -const todosSelector = state => state.todos +
-export const selectVisibleTodos = createSelector( - todosSelector, - (todos) => { - ... - } -) -``` +### Can I share a selector across multiple component instances? -You can eliminate unnecessary recomputations by returning a new object from the state update function only when a deep equality check has found that the list of todos has actually changed: +Yes, although if they pass in different arguments, you will need to handle that in order for memoization to work consistently: -```js -import { REMOVE_OLD } from '../constants/ActionTypes' -import isEqual from 'lodash.isequal' +- Pass a larger `maxSize` if using `defaultMemoize` ( as of 4.1.0+) +- Use [`weakMapMemoize`](#weakmapmemoize) (as of 5.0.0+) -const initialState = [ - { - text: 'Use Redux', - completed: false, - id: 0, - timestamp: Date.now() - } -] - -export default function todos(state = initialState, action) { - switch (action.type) { - case REMOVE_OLD: - const updatedState = state.filter(todo => { - return todo.timestamp + 30 * 24 * 60 * 60 * 1000 > Date.now() - }) - return isEqual(updatedState, state) ? state : updatedState - default: - return state - } -} -``` +### Are there TypeScript Typings? -Alternatively, the default `equalityCheck` function in the selector can be replaced by a deep equality check: +Yes! Reselect is now written in TypeScript itself, so they should Just Work™. -```js -import { createSelectorCreator, defaultMemoize } from 'reselect' -import isEqual from 'lodash.isequal' +### I am seeing a TypeScript error: `Type instantiation is excessively deep and possibly infinite` -const selectTodos = state => state.todos +Starting in 5.0.0 you should be able to nest up to 30 selectors, but in case you still run into this issue, you can refer to [this +comment](https://github.com/reduxjs/reselect/issues/534#issuecomment-956708953) for a discussion of the problem, as +relating to nested selectors. -// create a "selector creator" that uses lodash.isequal instead of === -const createDeepEqualSelector = createSelectorCreator( - defaultMemoize, - isEqual -) +### How can I make a [curried](https://github.com/hemanth/functional-programming-jargon#currying) selector? -// use the new "selector creator" to create a selector -const mySelector = createDeepEqualSelector( - todosSelector, - (todos) => { - ... - } -) +Selectors that take arguments are commonly used inside of React-Redux's `useSelector` by using a closure to pass along the extra arguments: + +```ts +function TodosList({ category }) { + const filteredTodos = useSelector(state => + selectTodosByCategory(state, category) + ) +} ``` -Always check that the cost of an alternative `equalityCheck` function or deep equality check in the state update function is not greater than the cost of recomputing every time. If recomputing every time does work out to be the cheaper option, it may be that for this case Reselect is not giving you any benefit over passing a plain `mapStateToProps` function to `connect`. +If you prefer to use a curried form instead, you can create a curried selector with this recipe: -### Q: Can I use Reselect without Redux? +
Detailed Explanation: Creating Curried Selectors -A: Yes. Reselect has no dependencies on any other package, so although it was designed to be used with Redux it can be used independently. It can be used with any plain JS data, such as typical React state values, as long as that data is being updated immutably. +You can try this pattern: + +```ts +const currySelector = < + State, + Result, + Params extends readonly any[], + AdditionalFields +>( + selector: ((state: State, ...args: Params) => Result) & AdditionalFields +) => { + const curriedSelector = (...args: Params) => { + return (state: State) => { + return selector(state, ...args) + } + } + return Object.assign(curriedSelector, selector) +} -### Q: How do I create a selector that takes an argument? +const selectTodoByIdCurried = currySelector( + createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id) + ) +) +``` -Conceptually, Reselect works like this internally: +Or for reusability you can do this: ```ts -const finalSelector = (...args) => { - const extractedValues = inputFunctions.map(input => input(...args)); - return output(...extractedValues); +import type { defaultMemoize, SelectorArray, UnknownMemoizer } from 'reselect' +import { createSelector } from 'reselect' + +export const createCurriedSelector = < + InputSelectors extends SelectorArray, + Result, + OverrideMemoizeFunction extends UnknownMemoizer = typeof defaultMemoize, + OverrideArgsMemoizeFunction extends UnknownMemoizer = typeof defaultMemoize +>( + ...args: Parameters< + typeof createSelector< + InputSelectors, + Result, + OverrideMemoizeFunction, + OverrideArgsMemoizeFunction + > + > +) => { + return currySelector(createSelector(...args)) } ``` -In other words, all the arguments passed to the selector function are immediately passed to all of the inputs. - -As shown in the API reference section above, provide input selectors that extract the arguments and forward them to the output selector for calculation: +This: -```js -const selectItemsByCategory = createSelector( - [ - // Usual first input - extract value from `state` - state => state.items, - // Take the second arg, `category`, and forward to the output selector - (state, category) => category - ], - // Output selector gets (`items, category)` as args - (items, category) => items.filter(item => item.category === category) +```ts +const selectTodoById = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id) ) + +selectTodoById(state, 0) ``` +Is the same as this: -More generally, you can have N arguments passed to the selector, and you can have M input functions extracting values from any of those arguments. All M extracted values get passed to the output function. +```ts +selectTodoByIdCurried(0)(state) +``` -### Q: The default memoization function is no good, can I use a different one? +As before you had to do this: -A: We think it works great for a lot of use cases, but sure. See [these examples](#customize-equalitycheck-for-defaultmemoize). +```ts +const todoById = useSelector(state => selectTodoById(state, id)) +``` -### Q: How do I test a selector? +Now you can do this: -A: For a given input, a selector should always produce the same output. For this reason they are simple to unit test. +```ts +const todoById = useSelector(selectTodoByIdCurried(id)) +``` -```js -const selector = createSelector( - state => state.a, - state => state.b, - (a, b) => ({ - c: a * 2, - d: b * 3 - }) -) +Another thing you can do if you are using [React-Redux] is create a custom hook factory function: -test('selector unit test', () => { - assert.deepEqual(selector({ a: 1, b: 2 }), { c: 2, d: 6 }) - assert.deepEqual(selector({ a: 2, b: 3 }), { c: 4, d: 9 }) -}) +```ts +import { useSelector } from 'react-redux' + +export const createParametricSelectorHook = < + Result, + Params extends readonly unknown[] +>( + selector: (state: RootState, ...params: Params) => Result +) => { + return (...args: Params) => { + return useSelector(state => selector(state, ...args)) + } +} + +const useSelectTodo = createParametricSelectorHook(selectTodoById) ``` -It may also be useful to check that the memoization function for a selector works correctly with the state update function (i.e. the reducer if you are using Redux). Each selector has a `recomputations` method that will return the number of times it has been recomputed: +And then inside your component: -```js -suite('selector', () => { - let state = { a: 1, b: 2 } - - const reducer = (state, action) => ({ - a: action(state.a), - b: action(state.b) - }) - - const selector = createSelector( - state => state.a, - state => state.b, - (a, b) => ({ - c: a * 2, - d: b * 3 - }) - ) +```tsx +import type { FC } from 'react' - const plusOne = x => x + 1 - const id = x => x - - test('selector unit test', () => { - state = reducer(state, plusOne) - assert.deepEqual(selector(state), { c: 4, d: 9 }) - state = reducer(state, id) - assert.deepEqual(selector(state), { c: 4, d: 9 }) - assert.equal(selector.recomputations(), 1) - state = reducer(state, plusOne) - assert.deepEqual(selector(state), { c: 6, d: 12 }) - assert.equal(selector.recomputations(), 2) - }) -}) +interface Props { + id: number +} + +const MyComponent: FC = ({ id }) => { + const todo = useSelectTodo(id) + return
{todo.title}
+} ``` -Additionally, selectors keep a reference to the last result function as `.resultFunc`. If you have selectors composed of many other selectors this can help you test each selector without coupling all of your tests to the shape of your state. +
-For example if you have a set of selectors like this: +### How can I make pre-typed version of [`createSelector`](#createselector) for my root state? -**selectors.js** +When used with Redux, it's typical to have all input selectors take `(state: RootState)` as their first argument. Creating a pre-typed version of `createSelector` can shorten that repetition. -```js -export const selectFirst = createSelector( ... ) -export const selectSecond = createSelector( ... ) -export const selectThird = createSelector( ... ) +
Detailed Explanation: Pre-Typed `createSelector` -export const myComposedSelector = createSelector( - selectFirst, - selectSecond, - selectThird, - (first, second, third) => first * second < third -) -``` +You can create a custom typed version of [`createSelector`] by defining a utility type that extends the original [`createSelector`] function. Here's an example: -And then a set of unit tests like this: +```ts +import type { + OutputSelector, + Selector, + SelectorArray, + UnknownMemoizer +} from 'reselect' +import { createSelector } from 'reselect' -**test/selectors.js** +interface RootState { + todos: { id: number; completed: boolean }[] + alerts: { id: number; read: boolean }[] +} -```js -// tests for the first three selectors... -test("selectFirst unit test", () => { ... }) -test("selectSecond unit test", () => { ... }) -test("selectThird unit test", () => { ... }) - -// We have already tested the previous -// three selector outputs so we can just call `.resultFunc` -// with the values we want to test directly: -test("myComposedSelector unit test", () => { - // here instead of calling selector() - // we just call selector.resultFunc() - assert(myComposedSelector.resultFunc(1, 2, 3), true) - assert(myComposedSelector.resultFunc(2, 2, 1), false) -}) +export type TypedCreateSelector< + State, + MemoizeFunction extends UnknownMemoizer = typeof defaultMemoize, + ArgsMemoizeFunction extends UnknownMemoizer = typeof defaultMemoize +> = < + InputSelectors extends readonly Selector[], + Result, + OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction, + OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction +>( + ...createSelectorArgs: Parameters< + typeof createSelector< + InputSelectors, + Result, + OverrideMemoizeFunction, + OverrideArgsMemoizeFunction + > + > +) => ReturnType< + typeof createSelector< + InputSelectors, + Result, + OverrideMemoizeFunction, + OverrideArgsMemoizeFunction + > +> + +export const createAppSelector: TypedCreateSelector = createSelector ``` -Finally, each selector has a `resetRecomputations` method that sets -recomputations back to 0. The intended use is for a complex selector that may -have many independent tests and you don't want to manually manage the -computation count or create a "dummy" selector for each test. +> [!WARNING]: This approach currently only supports [input selectors] provided as a single array. -### Q: Can I share a selector across multiple component instances? +
-A: Yes, although it requires some planning. +### What if I want to use [`createSelector`](#createselector) without memoization? -As of Reselect 4.1, you can create a selector with a cache size greater than one by passing in a `maxSize` option under `memoizeOptions` for use with the built-in `defaultMemoize`. +There may be rare cases when you might want to use `createSelector` for its composition syntax, but without any memoization applied. In that case, create an [`identity function`][Identity Function] and use it as the memoizers: -Otherwise, selectors created using `createSelector` only have a cache size of one. This can make them unsuitable for sharing across multiple instances if the arguments to the selector are different for each instance of the component. There are a couple of ways to get around this: +```ts +const identity = any>(func: Func) => func -- Create a factory function which returns a new selector for each instance of the component. This can be called in a React component inside the `useMemo` hook to generate a unique selector instance per component. -- Create a custom selector with a cache size greater than one using `createSelectorCreator` +const createNonMemoizedSelector = createSelectorCreator({ + memoize: identity, + argsMemoize: identity +}) +``` -### Q: Are there TypeScript Typings? +
[ ↑ Back to top ↑ ]
-A: Yes! Reselect is now written in TS itself, so they should Just Work™. +--- -### Q: I am seeing a TypeScript error: `Type instantiation is excessively deep and possibly infinite` +## External References -A: This can often occur with deeply recursive types, which occur in this library. Please see [this -comment](https://github.com/reduxjs/reselect/issues/534#issuecomment-956708953) for a discussion of the problem, as -relating to nested selectors. +
Click to expand -### Q: How can I make a [curried](https://github.com/hemanth/functional-programming-jargon#currying) selector? +- [**`WeakMap`**][`WeakMap`] +- [**`Reference Equality Check`**][Reference Equality Check] +- [**`Memoization`**][Memoization] +- [**`Identity Function`**][Identity Function] -A: Try these [helper functions](https://github.com/reduxjs/reselect/issues/159#issuecomment-238724788) courtesy of [MattSPalmer](https://github.com/MattSPalmer) +
## Related Projects ### [re-reselect](https://github.com/toomuchdesign/re-reselect) -Enhances Reselect selectors by wrapping `createSelector` and returning a memoized collection of selectors indexed with the cache key returned by a custom resolver function. +Enhances Reselect selectors by wrapping [`createSelector`] and returning a memoized collection of selectors indexed with the cache key returned by a custom resolver function. Useful to reduce selectors recalculation when the same selector is repeatedly called with one/few different arguments. ### [reselect-tools](https://github.com/skortchmark9/reselect-tools) -[Chrome extension](https://chrome.google.com/webstore/detail/reselect-devtools/cjmaipngmabglflfeepmdiffcijhjlbb?hl=en) and [companion lib](https://github.com/skortchmark9/reselect-tools) for debugging selectors. - - Measure selector recomputations across the app and identify performance bottlenecks - Check selector dependencies, inputs, outputs, and recomputations at any time with the chrome extension - Statically export a JSON representation of your selector graph for further analysis @@ -720,17 +1795,59 @@ Inspired by Reselect Tools, so it also has all functionality from this library a - Selectors Output (In case if selector not dependent from external arguments) - Shows "Not Memoized (NM)" selectors +
[ ↑ Back to top ↑ ]
+ +--- + ## License MIT ## Prior Art and Inspiration +
Click to expand + Originally inspired by getters in [NuclearJS](https://github.com/optimizely/nuclear-js.git), [subscriptions](https://github.com/Day8/re-frame#just-a-read-only-cursor) in [re-frame](https://github.com/Day8/re-frame) and this [proposal](https://github.com/reduxjs/redux/pull/169) from [speedskater](https://github.com/speedskater). -[build-badge]: https://img.shields.io/github/workflow/status/reduxjs/redux-thunk/Tests +[typescript-badge]: https://img.shields.io/badge/TypeScript-v4%2E7%2B-007ACC?style=for-the-badge&logo=TypeScript&logoColor=black&labelColor=blue&color=gray +[build-badge]: https://img.shields.io/github/actions/workflow/status/reduxjs/reselect/build-and-test-types.yml?branch=master&style=for-the-badge [build]: https://github.com/reduxjs/reselect/actions/workflows/build-and-test-types.yml -[npm-badge]: https://img.shields.io/npm/v/reselect.svg?style=flat-square +[npm-badge]: https://img.shields.io/npm/v/reselect.svg?style=for-the-badge [npm]: https://www.npmjs.org/package/reselect -[coveralls-badge]: https://img.shields.io/coveralls/reduxjs/reselect/master.svg?style=flat-square +[coveralls-badge]: https://img.shields.io/coveralls/reduxjs/reselect/master.svg?style=for-the-badge [coveralls]: https://coveralls.io/github/reduxjs/reselect + + + +[`WeakMap`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap 'WeakMap' +[Reference Equality Check]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality 'Reference Equality Check' +[Memoization]: https://en.wikipedia.org/wiki/Memoization 'Memoization' +[Identity Function]: https://en.wikipedia.org/wiki/Identity_function 'Identity Function' +[`useMemo`]: https://react.dev/reference/react/useMemo#usememo 'useMemo' +[`useCallback`]: https://react.dev/reference/react/useCallback#usecallback 'useCallback' +[`re-reselect`]: https://github.com/toomuchdesign/re-reselect 're-reselect' +[Redux]: https://redux.js.org 'Redux' +[React]: https://react.dev 'React' +[React-Redux]: https://react-redux.js.org 'React-Redux' + + + +[selector]: #selector-function 'Selector Function' +[input selectors]: #input-selectors 'Input Selectors' +[output selector]: #output-selector 'Output Selector' +[result function]: #result-function 'Result Function' +[`dependencies`]: #dependencies 'Dependencies' +[**_Cascading Memoization_**]: #cascading-memoization 'Cascading Memoization' +[output selector fields]: #output-selector-fields 'Output Selector Fields' +[`createSelector`]: #createselectorinputselectors--inputselectors-resultfunc-createselectoroptions 'createSelector' +[`createSelectorCreator`]: #createselectorcreatormemoize--options-memoizeoptions 'createSelectorCreator' +[`defaultMemoize`]: #defaultmemoizefunc-equalitycheckoroptions--defaultequalitycheck 'defaultMemoize' +[`weakMapMemoize`]: #weakmapmemoizefunc---since-500 'weakMapMemoize' +[`unstable_autotrackMemoize`]: #unstable_autotrackmemoizefunc---since-500 'unstable_autotrackMemoize' +[`createStructuredSelector`]: #createstructuredselector-inputSelectorsObject--selectorcreator--createselector 'createStructuredSelector' + +
+ +
[ ↑ Back to top ↑ ]
+ +--- diff --git a/docs/assets/diagrams/normal-memoization-function.drawio b/docs/assets/diagrams/normal-memoization-function.drawio new file mode 100644 index 000000000..479edfd6e --- /dev/null +++ b/docs/assets/diagrams/normal-memoization-function.drawio @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/assets/diagrams/reselect-memoization.drawio b/docs/assets/diagrams/reselect-memoization.drawio new file mode 100644 index 000000000..3bdab1502 --- /dev/null +++ b/docs/assets/diagrams/reselect-memoization.drawio @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/assets/normal-memoization-function.png b/docs/assets/normal-memoization-function.png new file mode 100644 index 000000000..bffa8ede9 Binary files /dev/null and b/docs/assets/normal-memoization-function.png differ diff --git a/docs/assets/reselect-memoization.png b/docs/assets/reselect-memoization.png new file mode 100644 index 000000000..f31afb834 Binary files /dev/null and b/docs/assets/reselect-memoization.png differ diff --git a/src/autotrackMemoize/autotrackMemoize.ts b/src/autotrackMemoize/autotrackMemoize.ts index e5d698a0e..91113aa51 100644 --- a/src/autotrackMemoize/autotrackMemoize.ts +++ b/src/autotrackMemoize/autotrackMemoize.ts @@ -65,6 +65,8 @@ import { createCache } from './autotracking' * * @template Func - The type of the function that is memoized. * + * @see {@link https://github.com/reduxjs/reselect#unstable_autotrackmemoizefunc---since-500 autotrackMemoize} + * * @since 5.0.0 * @public * @experimental diff --git a/src/createSelectorCreator.ts b/src/createSelectorCreator.ts index 85525ca9c..3045472d9 100644 --- a/src/createSelectorCreator.ts +++ b/src/createSelectorCreator.ts @@ -45,7 +45,7 @@ export interface CreateSelectorFunction< * @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into `createSelectorCreator`. * @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`. * - * @see {@link https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc-selectoroptions createSelector} + * @see {@link https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc-createselectoroptions createSelector} */ ( ...createSelectorArgs: [ @@ -71,7 +71,7 @@ export interface CreateSelectorFunction< * @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into `createSelectorCreator`. * @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`. * - * @see {@link https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc-selectoroptions createSelector} + * @see {@link https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc-createselectoroptions createSelector} */ < InputSelectors extends SelectorArray, @@ -112,7 +112,7 @@ export interface CreateSelectorFunction< * @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into `createSelectorCreator`. * @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`. * - * @see {@link https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc-selectoroptions createSelector} + * @see {@link https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc-createselectoroptions createSelector} */ < InputSelectors extends SelectorArray, @@ -149,7 +149,7 @@ let globalStabilityCheck: StabilityCheckFrequency = 'once' * This function allows you to override this setting for all of your selectors. * * **Note**: This setting can still be overridden per selector inside `createSelector`'s `options` object. - * See {@link https://github.com/reduxjs/reselect#per-selector-configuration per-selector-configuration} + * See {@link https://github.com/reduxjs/reselect#2-per-selector-by-passing-an-inputstabilitycheck-option-directly-to-createselector per-selector-configuration} * and {@linkcode CreateSelectorOptions.inputStabilityCheck inputStabilityCheck} for more details. * * _The input stability check does not run in production builds._ @@ -171,8 +171,8 @@ import { OutputSelectorFields, Mapped } from './types'; * // Never run the input stability check. * setInputStabilityCheckEnabled('never') * ``` - * @see {@link https://github.com/reduxjs/reselect#development-only-checks development-only-checks} - * @see {@link https://github.com/reduxjs/reselect#global-configuration global-configuration} + * @see {@link https://github.com/reduxjs/reselect#debugging-tools debugging-tools} + * @see {@link https://github.com/reduxjs/reselect#1-globally-through-setinputstabilitycheckenabled global-configuration} * * @since 5.0.0 * @public @@ -211,7 +211,7 @@ export function setInputStabilityCheckEnabled( * @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `defaultMemoize` or `weakMapMemoize`). * @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `defaultMemoize` or `weakMapMemoize`). If none is explicitly provided, `defaultMemoize` will be used. * - * @see {@link https://github.com/reduxjs/reselect#createselectorcreatormemoize-memoizeoptions createSelectorCreator} + * @see {@link https://github.com/reduxjs/reselect#createselectorcreatormemoize--options-memoizeoptions createSelectorCreator} * * @since 5.0.0 * @public @@ -251,7 +251,7 @@ export function createSelectorCreator< * * @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `defaultMemoize` or `weakMapMemoize`). * - * @see {@link https://github.com/reduxjs/reselect#createselectorcreatormemoize-memoizeoptions createSelectorCreator} + * @see {@link https://github.com/reduxjs/reselect#createselectorcreatormemoize--options-memoizeoptions createSelectorCreator} * * @public */ @@ -459,7 +459,7 @@ export function createSelectorCreator< * a single "result function" / "combiner", and an optional options object, and * generates a memoized selector function. * - * @see {@link https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc-selectoroptions createSelector} + * @see {@link https://github.com/reduxjs/reselect#createselectorinputselectors--inputselectors-resultfunc-createselectoroptions createSelector} * * @public */ diff --git a/src/createStructuredSelector.ts b/src/createStructuredSelector.ts index 11462b925..3ca2ae2fc 100644 --- a/src/createStructuredSelector.ts +++ b/src/createStructuredSelector.ts @@ -59,7 +59,7 @@ interface SelectorsObject { * The structured selector can take multiple input selectors * and map their output to an object with specific keys. * - * @see {@link https://github.com/reduxjs/reselect#createstructuredselectorinputselectors-selectorcreator--createselector createStructuredSelector} + * @see {@link https://github.com/reduxjs/reselect#createstructuredselector-inputselectorsobject--selectorcreator--createselector createStructuredSelector} * * @public */ @@ -171,7 +171,7 @@ export interface StructuredSelectorCreator { * @template MemoizeFunction - The type of the memoize function that is used to create the structured selector. It defaults to `defaultMemoize`. * @template ArgsMemoizeFunction - The type of the of the memoize function that is used to memoize the arguments passed into the generated structured selector. It defaults to `defaultMemoize`. * - * @see {@link https://github.com/reduxjs/reselect#createstructuredselectorinputselectors-selectorcreator--createselector createStructuredSelector} + * @see {@link https://github.com/reduxjs/reselect#createstructuredselector-inputselectorsobject--selectorcreator--createselector createStructuredSelector} */ < InputSelectorsObject extends SelectorsObject, @@ -234,7 +234,7 @@ export interface StructuredSelectorCreator { * })) * ``` * - * @see {@link https://github.com/reduxjs/reselect#createstructuredselectorinputselectors-selectorcreator--createselector createStructuredSelector} + * @see {@link https://github.com/reduxjs/reselect#createstructuredselector-inputselectorsobject--selectorcreator--createselector createStructuredSelector} * * @public */ diff --git a/src/types.ts b/src/types.ts index c79719a37..775d9c370 100644 --- a/src/types.ts +++ b/src/types.ts @@ -75,9 +75,9 @@ export interface CreateSelectorOptions< * * @default 'once' * - * @see {@link https://github.com/reduxjs/reselect#development-only-checks development-only-checks} + * @see {@link https://github.com/reduxjs/reselect#debugging-tools debugging-tools} * @see {@link https://github.com/reduxjs/reselect#inputstabilitycheck inputStabilityCheck} - * @see {@link https://github.com/reduxjs/reselect#per-selector-configuration per-selector-configuration} + * @see {@link https://github.com/reduxjs/reselect#2-per-selector-by-passing-an-inputstabilitycheck-option-directly-to-createselector per-selector-configuration} * * @since 5.0.0 */ diff --git a/src/weakMapMemoize.ts b/src/weakMapMemoize.ts index 5311c9078..f92061616 100644 --- a/src/weakMapMemoize.ts +++ b/src/weakMapMemoize.ts @@ -91,6 +91,8 @@ function createCacheNode(): CacheNode { * * @template Func - The type of the function that is memoized. * + * @see {@link https://github.com/reduxjs/reselect#weakmapmemoizefunc---since-500 weakMapMemoize} + * * @since 5.0.0 * @public * @experimental diff --git a/test/examples.test.ts b/test/examples.test.ts new file mode 100644 index 000000000..f487097e7 --- /dev/null +++ b/test/examples.test.ts @@ -0,0 +1,220 @@ +import type { + OutputSelector, + Selector, + SelectorArray, + UnknownMemoizer +} from 'reselect' +import { + createSelector, + createSelectorCreator, + defaultMemoize, + unstable_autotrackMemoize as autotrackMemoize, + weakMapMemoize +} from 'reselect' +import { test } from 'vitest' +import type { RootState } from './testUtils' +import { addTodo, setupStore } from './testUtils' + +const store = setupStore() + +const EMPTY_ARRAY: [] = [] + +export const fallbackToEmptyArray = (array: T[]) => { + return array.length === 0 ? EMPTY_ARRAY : array +} + +const selectCompletedTodos = createSelector( + [(state: RootState) => state.todos], + todos => { + return fallbackToEmptyArray(todos.filter(todo => todo.completed === true)) + } +) + +const completedTodos = selectCompletedTodos(store.getState()) + +store.dispatch(addTodo({ title: '', description: '' })) + +test('empty array', () => { + expect(completedTodos).toBe(selectCompletedTodos(store.getState())) +}) + +test('identity', () => { + const identity = any>(func: Func) => func + const createNonMemoizedSelector = createSelectorCreator({ + memoize: identity, + argsMemoize: identity + }) + const nonMemoizedSelector = createNonMemoizedSelector( + [(state: RootState) => state.todos], + todos => todos.filter(todo => todo.completed === true), + { inputStabilityCheck: 'never' } + ) + + nonMemoizedSelector(store.getState()) + nonMemoizedSelector(store.getState()) + nonMemoizedSelector(store.getState()) + + expect(nonMemoizedSelector.recomputations()).toBe(3) +}) + +test.todo('Top Level Selectors', () => { + type TopLevelSelectors = { + [K in keyof State as K extends string + ? `select${Capitalize}` + : never]: Selector + } + + const topLevelSelectors: TopLevelSelectors = { + selectAlerts: state => state.alerts, + selectTodos: state => state.todos, + selectUsers: state => state.users + } +}) + +test.todo('Find Fastest Selector', () => { + const store = setupStore() + const selectTodoIds = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(({ id }) => id) + ) + const findFastestSelector = ( + selector: S, + ...selectorArgs: Parameters + ) => { + const memoizeFuncs = [defaultMemoize, weakMapMemoize, autotrackMemoize] + const results = memoizeFuncs + .map(memoize => { + const alternateSelector = createSelector( + selector.dependencies as [...SelectorArray], + selector.resultFunc, + { memoize } + ) + const start = performance.now() + alternateSelector.apply(null, selectorArgs) + const time = performance.now() - start + return { name: memoize.name, time, selector: alternateSelector } + }) + .sort((a, b) => a.time - b.time) + const fastest = results.reduce((minResult, currentResult) => + currentResult.time < minResult.time ? currentResult : minResult + ) + const ratios = results + .filter(({ time }) => time !== fastest.time) + .map( + ({ time, name }) => + `\x1B[33m \x1B[1m${ + time / fastest.time + }\x1B[0m times faster than \x1B[1;41m${name}\x1B[0m.` + ) + if (fastest.selector.memoize.name !== selector.memoize.name) { + console.warn( + `The memoization method for \x1B[1;41m${ + selector.name + }\x1B[0m is \x1B[31m${ + selector.memoize.name + }\x1B[0m!\nChange it to \x1B[32m\x1B[1m${ + fastest.selector.memoize.name + }\x1B[0m to be more efficient.\nYou should use \x1B[32m\x1B[1m${ + fastest.name + }\x1B[0m because it is${ratios.join('\nand\n')}` + ) + } + return { results, fastest } as const + } +}) + +test('TypedCreateSelector', () => { + type TypedCreateSelector< + State, + MemoizeFunction extends UnknownMemoizer = typeof defaultMemoize, + ArgsMemoizeFunction extends UnknownMemoizer = typeof defaultMemoize + > = < + InputSelectors extends readonly Selector[], + Result, + OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction, + OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction + >( + ...createSelectorArgs: Parameters< + typeof createSelector< + InputSelectors, + Result, + OverrideMemoizeFunction, + OverrideArgsMemoizeFunction + > + > + ) => ReturnType< + typeof createSelector< + InputSelectors, + Result, + OverrideMemoizeFunction, + OverrideArgsMemoizeFunction + > + > + const createAppSelector: TypedCreateSelector = createSelector + const selector = createAppSelector( + [state => state.todos, (state, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id)?.completed + ) +}) + +test('createCurriedSelector copy paste pattern', () => { + const state = store.getState() + const currySelector = < + State, + Result, + Params extends readonly any[], + AdditionalFields + >( + selector: ((state: State, ...args: Params) => Result) & AdditionalFields + ) => { + const curriedSelector = (...args: Params) => { + return (state: State) => { + return selector(state, ...args) + } + } + return Object.assign(curriedSelector, selector) + } + + const createCurriedSelector = < + InputSelectors extends SelectorArray, + Result, + OverrideMemoizeFunction extends UnknownMemoizer = typeof defaultMemoize, + OverrideArgsMemoizeFunction extends UnknownMemoizer = typeof defaultMemoize + >( + ...args: Parameters< + typeof createSelector< + InputSelectors, + Result, + OverrideMemoizeFunction, + OverrideArgsMemoizeFunction + > + > + ) => { + return currySelector(createSelector(...args)) + } + const selectTodoById = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id) + ) + const selectTodoByIdCurried = createCurriedSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id) + ) + expect(selectTodoById(state, 0)).toStrictEqual( + selectTodoByIdCurried(0)(state) + ) + expect(selectTodoById.argsMemoize).toBe(selectTodoByIdCurried.argsMemoize) + expect(selectTodoById.lastResult()).toBeDefined() + expect(selectTodoByIdCurried.lastResult()).toBeDefined() + expect(selectTodoById.lastResult()).toBe(selectTodoByIdCurried.lastResult()) + expect(selectTodoById.memoize).toBe(selectTodoByIdCurried.memoize) + expect(selectTodoById.memoizedResultFunc(state.todos, 0)).toBe( + selectTodoByIdCurried.memoizedResultFunc(state.todos, 0) + ) + expect(selectTodoById.recomputations()).toBe( + selectTodoByIdCurried.recomputations() + ) + expect(selectTodoById.resultFunc(state.todos, 0)).toBe( + selectTodoByIdCurried.resultFunc(state.todos, 0) + ) +}) diff --git a/test/reselect.bench.ts b/test/reselect.bench.ts index 419ee1562..d212c821d 100644 --- a/test/reselect.bench.ts +++ b/test/reselect.bench.ts @@ -1,217 +1,217 @@ -import { createSelector } from '@reduxjs/toolkit' -import { bench } from 'vitest' -import { autotrackMemoize } from '../src/autotrackMemoize/autotrackMemoize' -import { weakMapMemoize } from '../src/weakMapMemoize' - -const options: NonNullable[2]> = { - iterations: 1_000_000, - time: 100 -} - -describe('bench', () => { - interface State { - todos: { - id: number - completed: boolean - }[] - } - const state: State = { - todos: [ - { id: 0, completed: false }, - { id: 1, completed: false }, - { id: 2, completed: false }, - { id: 3, completed: false }, - { id: 4, completed: false }, - { id: 5, completed: false }, - { id: 6, completed: false }, - { id: 7, completed: false }, - { id: 8, completed: false }, - { id: 9, completed: false }, - { id: 10, completed: false }, - { id: 11, completed: false }, - { id: 12, completed: false }, - { id: 13, completed: false }, - { id: 14, completed: false }, - { id: 15, completed: false }, - { id: 16, completed: false }, - { id: 17, completed: false }, - { id: 18, completed: false }, - { id: 19, completed: false }, - { id: 20, completed: false }, - { id: 21, completed: false }, - { id: 22, completed: false }, - { id: 23, completed: false }, - { id: 24, completed: false }, - { id: 25, completed: false }, - { id: 26, completed: false }, - { id: 27, completed: false }, - { id: 28, completed: false }, - { id: 29, completed: false }, - { id: 30, completed: false }, - { id: 31, completed: false }, - { id: 32, completed: false }, - { id: 33, completed: false }, - { id: 34, completed: false }, - { id: 35, completed: false }, - { id: 36, completed: false }, - { id: 37, completed: false }, - { id: 38, completed: false }, - { id: 39, completed: false }, - { id: 40, completed: false }, - { id: 41, completed: false }, - { id: 42, completed: false }, - { id: 43, completed: false }, - { id: 44, completed: false }, - { id: 45, completed: false }, - { id: 46, completed: false }, - { id: 47, completed: false }, - { id: 48, completed: false }, - { id: 49, completed: false }, - { id: 50, completed: false }, - { id: 51, completed: false }, - { id: 52, completed: false }, - { id: 53, completed: false }, - { id: 54, completed: false }, - { id: 55, completed: false }, - { id: 56, completed: false }, - { id: 57, completed: false }, - { id: 58, completed: false }, - { id: 59, completed: false }, - { id: 60, completed: false }, - { id: 61, completed: false }, - { id: 62, completed: false }, - { id: 63, completed: false }, - { id: 64, completed: false }, - { id: 65, completed: false }, - { id: 66, completed: false }, - { id: 67, completed: false }, - { id: 68, completed: false }, - { id: 69, completed: false }, - { id: 70, completed: false }, - { id: 71, completed: false }, - { id: 72, completed: false }, - { id: 73, completed: false }, - { id: 74, completed: false }, - { id: 75, completed: false }, - { id: 76, completed: false }, - { id: 77, completed: false }, - { id: 78, completed: false }, - { id: 79, completed: false }, - { id: 80, completed: false }, - { id: 81, completed: false }, - { id: 82, completed: false }, - { id: 83, completed: false }, - { id: 84, completed: false }, - { id: 85, completed: false }, - { id: 86, completed: false }, - { id: 87, completed: false }, - { id: 88, completed: false }, - { id: 89, completed: false }, - { id: 90, completed: false }, - { id: 91, completed: false }, - { id: 92, completed: false }, - { id: 93, completed: false }, - { id: 94, completed: false }, - { id: 95, completed: false }, - { id: 96, completed: false }, - { id: 97, completed: false }, - { id: 98, completed: false }, - { id: 99, completed: false } - ] - } - const selectorDefault = createSelector( - (state: State) => state.todos, - todos => todos.map(t => t.id) - ) - const selectorAutotrack = createSelector( - (state: State) => state.todos, - todos => todos.map(t => t.id), - { memoize: autotrackMemoize } - ) - const selectorWeakMap = createSelector( - (state: State) => state.todos, - todos => todos.map(t => t.id), - { memoize: weakMapMemoize } - ) - const selectorArgsAutotrack = createSelector( - (state: State) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: autotrackMemoize } - ) - const nonMemoizedSelector = (state: State) => state.todos.map(t => t.id) - const selectorArgsWeakMap = createSelector( - (state: State) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize } - ) - const parametricSelector = createSelector( - (state: State) => state.todos, - (state: State, id: number) => id, - (todos, id) => todos[id] - ) - const parametricSelectorWeakMapArgs = createSelector( - (state: State) => state.todos, - (state: State, id: number) => id, - (todos, id) => todos[id], - { - argsMemoize: weakMapMemoize - } - ) - bench( - 'selectorDefault', - () => { - selectorDefault(state) - }, - options - ) - - bench( - 'selectorAutotrack', - () => { - selectorAutotrack(state) - }, - options - ) - bench( - 'selectorWeakMap', - () => { - selectorWeakMap(state) - }, - options - ) - bench( - 'selectorArgsAutotrack', - () => { - selectorArgsAutotrack(state) - }, - options - ) - bench( - 'selectorArgsWeakMap', - () => { - selectorArgsWeakMap(state) - }, - options - ) - bench( - 'non-memoized selector', - () => { - nonMemoizedSelector(state) - }, - options - ) - bench( - 'parametricSelector', - () => { - parametricSelector(state, 0) - }, - options - ) - bench( - 'parametricSelectorWeakMapArgs', - () => { - parametricSelectorWeakMapArgs(state, 0) - }, - options - ) -}) +import { createSelector } from '@reduxjs/toolkit' +import { bench } from 'vitest' +import { autotrackMemoize } from '../src/autotrackMemoize/autotrackMemoize' +import { weakMapMemoize } from '../src/weakMapMemoize' + +const options: NonNullable[2]> = { + iterations: 1_000_000, + time: 100 +} + +describe('bench', () => { + interface State { + todos: { + id: number + completed: boolean + }[] + } + const state: State = { + todos: [ + { id: 0, completed: false }, + { id: 1, completed: false }, + { id: 2, completed: false }, + { id: 3, completed: false }, + { id: 4, completed: false }, + { id: 5, completed: false }, + { id: 6, completed: false }, + { id: 7, completed: false }, + { id: 8, completed: false }, + { id: 9, completed: false }, + { id: 10, completed: false }, + { id: 11, completed: false }, + { id: 12, completed: false }, + { id: 13, completed: false }, + { id: 14, completed: false }, + { id: 15, completed: false }, + { id: 16, completed: false }, + { id: 17, completed: false }, + { id: 18, completed: false }, + { id: 19, completed: false }, + { id: 20, completed: false }, + { id: 21, completed: false }, + { id: 22, completed: false }, + { id: 23, completed: false }, + { id: 24, completed: false }, + { id: 25, completed: false }, + { id: 26, completed: false }, + { id: 27, completed: false }, + { id: 28, completed: false }, + { id: 29, completed: false }, + { id: 30, completed: false }, + { id: 31, completed: false }, + { id: 32, completed: false }, + { id: 33, completed: false }, + { id: 34, completed: false }, + { id: 35, completed: false }, + { id: 36, completed: false }, + { id: 37, completed: false }, + { id: 38, completed: false }, + { id: 39, completed: false }, + { id: 40, completed: false }, + { id: 41, completed: false }, + { id: 42, completed: false }, + { id: 43, completed: false }, + { id: 44, completed: false }, + { id: 45, completed: false }, + { id: 46, completed: false }, + { id: 47, completed: false }, + { id: 48, completed: false }, + { id: 49, completed: false }, + { id: 50, completed: false }, + { id: 51, completed: false }, + { id: 52, completed: false }, + { id: 53, completed: false }, + { id: 54, completed: false }, + { id: 55, completed: false }, + { id: 56, completed: false }, + { id: 57, completed: false }, + { id: 58, completed: false }, + { id: 59, completed: false }, + { id: 60, completed: false }, + { id: 61, completed: false }, + { id: 62, completed: false }, + { id: 63, completed: false }, + { id: 64, completed: false }, + { id: 65, completed: false }, + { id: 66, completed: false }, + { id: 67, completed: false }, + { id: 68, completed: false }, + { id: 69, completed: false }, + { id: 70, completed: false }, + { id: 71, completed: false }, + { id: 72, completed: false }, + { id: 73, completed: false }, + { id: 74, completed: false }, + { id: 75, completed: false }, + { id: 76, completed: false }, + { id: 77, completed: false }, + { id: 78, completed: false }, + { id: 79, completed: false }, + { id: 80, completed: false }, + { id: 81, completed: false }, + { id: 82, completed: false }, + { id: 83, completed: false }, + { id: 84, completed: false }, + { id: 85, completed: false }, + { id: 86, completed: false }, + { id: 87, completed: false }, + { id: 88, completed: false }, + { id: 89, completed: false }, + { id: 90, completed: false }, + { id: 91, completed: false }, + { id: 92, completed: false }, + { id: 93, completed: false }, + { id: 94, completed: false }, + { id: 95, completed: false }, + { id: 96, completed: false }, + { id: 97, completed: false }, + { id: 98, completed: false }, + { id: 99, completed: false } + ] + } + const selectorDefault = createSelector( + (state: State) => state.todos, + todos => todos.map(t => t.id) + ) + const selectorAutotrack = createSelector( + (state: State) => state.todos, + todos => todos.map(t => t.id), + { memoize: autotrackMemoize } + ) + const selectorWeakMap = createSelector( + (state: State) => state.todos, + todos => todos.map(t => t.id), + { memoize: weakMapMemoize } + ) + const selectorArgsAutotrack = createSelector( + (state: State) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: autotrackMemoize } + ) + const nonMemoizedSelector = (state: State) => state.todos.map(t => t.id) + const selectorArgsWeakMap = createSelector( + (state: State) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize } + ) + const parametricSelector = createSelector( + (state: State) => state.todos, + (state: State, id: number) => id, + (todos, id) => todos[id] + ) + const parametricSelectorWeakMapArgs = createSelector( + (state: State) => state.todos, + (state: State, id: number) => id, + (todos, id) => todos[id], + { + argsMemoize: weakMapMemoize + } + ) + bench( + 'selectorDefault', + () => { + selectorDefault(state) + }, + options + ) + + bench( + 'selectorAutotrack', + () => { + selectorAutotrack(state) + }, + options + ) + bench( + 'selectorWeakMap', + () => { + selectorWeakMap(state) + }, + options + ) + bench( + 'selectorArgsAutotrack', + () => { + selectorArgsAutotrack(state) + }, + options + ) + bench( + 'selectorArgsWeakMap', + () => { + selectorArgsWeakMap(state) + }, + options + ) + bench( + 'non-memoized selector', + () => { + nonMemoizedSelector(state) + }, + options + ) + bench( + 'parametricSelector', + () => { + parametricSelector(state, 0) + }, + options + ) + bench( + 'parametricSelectorWeakMapArgs', + () => { + parametricSelectorWeakMapArgs(state, 0) + }, + options + ) +}) diff --git a/test/testUtils.ts b/test/testUtils.ts index 608a2d988..f0d096ea8 100644 --- a/test/testUtils.ts +++ b/test/testUtils.ts @@ -1,196 +1,196 @@ -import type { PayloadAction } from '@reduxjs/toolkit' -import { combineReducers, configureStore, createSlice } from '@reduxjs/toolkit' - -interface Todo { - id: number - title: string - description: string - completed: boolean -} - -interface Alert { - id: number - message: string - type: string - read: boolean -} - -const todoState = [ - { - id: 0, - title: 'Buy groceries', - description: 'Milk, bread, eggs, and fruits', - completed: false - }, - { - id: 1, - title: 'Schedule dentist appointment', - description: 'Check available slots for next week', - completed: false - }, - { - id: 2, - title: 'Convince the cat to get a job', - description: 'Need extra income for cat treats', - completed: false - }, - { - id: 3, - title: 'Figure out if plants are plotting world domination', - description: 'That cactus looks suspicious...', - completed: false - }, - { - id: 4, - title: 'Practice telekinesis', - description: 'Try moving the remote without getting up', - completed: false - }, - { - id: 5, - title: 'Determine location of El Dorado', - description: 'Might need it for the next vacation', - completed: false - }, - { - id: 6, - title: 'Master the art of invisible potato juggling', - description: 'Great party trick', - completed: false - } -] - -const alertState = [ - { - id: 0, - message: 'You have an upcoming meeting at 3 PM.', - type: 'reminder', - read: false - }, - { - id: 1, - message: 'New software update available.', - type: 'notification', - read: false - }, - { - id: 3, - message: - 'The plants have been watered, but keep an eye on that shifty cactus.', - type: 'notification', - read: false - }, - { - id: 4, - message: - 'Telekinesis class has been moved to 5 PM. Please do not bring any spoons.', - type: 'reminder', - read: false - }, - { - id: 5, - message: - 'Expedition to El Dorado is postponed. The treasure map is being updated.', - type: 'notification', - read: false - }, - { - id: 6, - message: - 'Invisible potato juggling championship is tonight. May the best mime win.', - type: 'reminder', - read: false - } -] - -const todoSlice = createSlice({ - name: 'todos', - initialState: todoState, - reducers: { - toggleCompleted: (state, action: PayloadAction) => { - const todo = state.find(todo => todo.id === action.payload) - if (todo) { - todo.completed = !todo.completed - } - }, - - addTodo: (state, action: PayloadAction>) => { - const newId = state.length > 0 ? state[state.length - 1].id + 1 : 0 - state.push({ - ...action.payload, - id: newId, - completed: false - }) - }, - - removeTodo: (state, action: PayloadAction) => { - return state.filter(todo => todo.id !== action.payload) - }, - - updateTodo: (state, action: PayloadAction) => { - const index = state.findIndex(todo => todo.id === action.payload.id) - if (index !== -1) { - state[index] = action.payload - } - }, - - clearCompleted: state => { - return state.filter(todo => !todo.completed) - } - } -}) - -const alertSlice = createSlice({ - name: 'alerts', - initialState: alertState, - reducers: { - markAsRead: (state, action: PayloadAction) => { - const alert = state.find(alert => alert.id === action.payload) - if (alert) { - alert.read = true - } - }, - - addAlert: (state, action: PayloadAction>) => { - const newId = state.length > 0 ? state[state.length - 1].id + 1 : 0 - state.push({ - ...action.payload, - id: newId - }) - }, - - removeAlert: (state, action: PayloadAction) => { - return state.filter(alert => alert.id !== action.payload) - } - } -}) - -const rootReducer = combineReducers({ - [todoSlice.name]: todoSlice.reducer, - [alertSlice.name]: alertSlice.reducer -}) - -export const setupStore = () => configureStore({ reducer: rootReducer }) - -export type AppStore = ReturnType - -export type RootState = ReturnType - -export interface LocalTestContext { - store: AppStore - state: RootState -} - -export const { markAsRead, addAlert, removeAlert } = alertSlice.actions - -export const { - toggleCompleted, - addTodo, - removeTodo, - updateTodo, - clearCompleted -} = todoSlice.actions - -// Since Node 16 does not support `structuredClone` -export const deepClone = (object: T): T => - JSON.parse(JSON.stringify(object)) +import type { PayloadAction } from '@reduxjs/toolkit' +import { combineReducers, configureStore, createSlice } from '@reduxjs/toolkit' + +interface Todo { + id: number + title: string + description: string + completed: boolean +} + +interface Alert { + id: number + message: string + type: string + read: boolean +} + +const todoState = [ + { + id: 0, + title: 'Buy groceries', + description: 'Milk, bread, eggs, and fruits', + completed: false + }, + { + id: 1, + title: 'Schedule dentist appointment', + description: 'Check available slots for next week', + completed: false + }, + { + id: 2, + title: 'Convince the cat to get a job', + description: 'Need extra income for cat treats', + completed: false + }, + { + id: 3, + title: 'Figure out if plants are plotting world domination', + description: 'That cactus looks suspicious...', + completed: false + }, + { + id: 4, + title: 'Practice telekinesis', + description: 'Try moving the remote without getting up', + completed: false + }, + { + id: 5, + title: 'Determine location of El Dorado', + description: 'Might need it for the next vacation', + completed: false + }, + { + id: 6, + title: 'Master the art of invisible potato juggling', + description: 'Great party trick', + completed: false + } +] + +const alertState = [ + { + id: 0, + message: 'You have an upcoming meeting at 3 PM.', + type: 'reminder', + read: false + }, + { + id: 1, + message: 'New software update available.', + type: 'notification', + read: false + }, + { + id: 3, + message: + 'The plants have been watered, but keep an eye on that shifty cactus.', + type: 'notification', + read: false + }, + { + id: 4, + message: + 'Telekinesis class has been moved to 5 PM. Please do not bring any spoons.', + type: 'reminder', + read: false + }, + { + id: 5, + message: + 'Expedition to El Dorado is postponed. The treasure map is being updated.', + type: 'notification', + read: false + }, + { + id: 6, + message: + 'Invisible potato juggling championship is tonight. May the best mime win.', + type: 'reminder', + read: false + } +] + +const todoSlice = createSlice({ + name: 'todos', + initialState: todoState, + reducers: { + toggleCompleted: (state, action: PayloadAction) => { + const todo = state.find(todo => todo.id === action.payload) + if (todo) { + todo.completed = !todo.completed + } + }, + + addTodo: (state, action: PayloadAction>) => { + const newId = state.length > 0 ? state[state.length - 1].id + 1 : 0 + state.push({ + ...action.payload, + id: newId, + completed: false + }) + }, + + removeTodo: (state, action: PayloadAction) => { + return state.filter(todo => todo.id !== action.payload) + }, + + updateTodo: (state, action: PayloadAction) => { + const index = state.findIndex(todo => todo.id === action.payload.id) + if (index !== -1) { + state[index] = action.payload + } + }, + + clearCompleted: state => { + return state.filter(todo => !todo.completed) + } + } +}) + +const alertSlice = createSlice({ + name: 'alerts', + initialState: alertState, + reducers: { + markAsRead: (state, action: PayloadAction) => { + const alert = state.find(alert => alert.id === action.payload) + if (alert) { + alert.read = true + } + }, + + addAlert: (state, action: PayloadAction>) => { + const newId = state.length > 0 ? state[state.length - 1].id + 1 : 0 + state.push({ + ...action.payload, + id: newId + }) + }, + + removeAlert: (state, action: PayloadAction) => { + return state.filter(alert => alert.id !== action.payload) + } + } +}) + +const rootReducer = combineReducers({ + [todoSlice.name]: todoSlice.reducer, + [alertSlice.name]: alertSlice.reducer +}) + +export const setupStore = () => configureStore({ reducer: rootReducer }) + +export type AppStore = ReturnType + +export type RootState = ReturnType + +export interface LocalTestContext { + store: AppStore + state: RootState +} + +export const { markAsRead, addAlert, removeAlert } = alertSlice.actions + +export const { + toggleCompleted, + addTodo, + removeTodo, + updateTodo, + clearCompleted +} = todoSlice.actions + +// Since Node 16 does not support `structuredClone` +export const deepClone = (object: T): T => + JSON.parse(JSON.stringify(object)) diff --git a/tsconfig.json b/tsconfig.json index daa27554a..d36a9f630 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "experimentalDecorators": true, "rootDirs": ["./src"], "rootDir": "./src", - "types": ["vitest/globals"], + "types": ["vitest/globals"], "baseUrl": ".", "paths": { "reselect": ["src/index.ts"], // @remap-prod-remove-line diff --git a/yarn.lock b/yarn.lock index 8004e2ca8..2ba7fc9a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13,11 +13,11 @@ __metadata: linkType: hard "@babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.9.2": - version: 7.23.1 - resolution: "@babel/runtime@npm:7.23.1" + version: 7.23.2 + resolution: "@babel/runtime@npm:7.23.2" dependencies: regenerator-runtime: ^0.14.0 - checksum: 0cd0d43e6e7dc7f9152fda8c8312b08321cda2f56ef53d6c22ebdd773abdc6f5d0a69008de90aa41908d00e2c1facb24715ff121274e689305c858355ff02c70 + checksum: 6c4df4839ec75ca10175f636d6362f91df8a3137f86b38f6cd3a4c90668a0fe8e9281d320958f4fbd43b394988958585a17c3aab2a4ea6bf7316b22916a371fb languageName: node linkType: hard @@ -28,9 +28,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/android-arm64@npm:0.19.5" +"@esbuild/android-arm64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/android-arm64@npm:0.19.6" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -42,9 +42,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/android-arm@npm:0.19.5" +"@esbuild/android-arm@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/android-arm@npm:0.19.6" conditions: os=android & cpu=arm languageName: node linkType: hard @@ -56,9 +56,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/android-x64@npm:0.19.5" +"@esbuild/android-x64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/android-x64@npm:0.19.6" conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -70,9 +70,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/darwin-arm64@npm:0.19.5" +"@esbuild/darwin-arm64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/darwin-arm64@npm:0.19.6" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -84,9 +84,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/darwin-x64@npm:0.19.5" +"@esbuild/darwin-x64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/darwin-x64@npm:0.19.6" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -98,9 +98,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/freebsd-arm64@npm:0.19.5" +"@esbuild/freebsd-arm64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/freebsd-arm64@npm:0.19.6" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -112,9 +112,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/freebsd-x64@npm:0.19.5" +"@esbuild/freebsd-x64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/freebsd-x64@npm:0.19.6" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -126,9 +126,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/linux-arm64@npm:0.19.5" +"@esbuild/linux-arm64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/linux-arm64@npm:0.19.6" conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -140,9 +140,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/linux-arm@npm:0.19.5" +"@esbuild/linux-arm@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/linux-arm@npm:0.19.6" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -154,9 +154,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/linux-ia32@npm:0.19.5" +"@esbuild/linux-ia32@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/linux-ia32@npm:0.19.6" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -168,9 +168,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/linux-loong64@npm:0.19.5" +"@esbuild/linux-loong64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/linux-loong64@npm:0.19.6" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -182,9 +182,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/linux-mips64el@npm:0.19.5" +"@esbuild/linux-mips64el@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/linux-mips64el@npm:0.19.6" conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -196,9 +196,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/linux-ppc64@npm:0.19.5" +"@esbuild/linux-ppc64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/linux-ppc64@npm:0.19.6" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -210,9 +210,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/linux-riscv64@npm:0.19.5" +"@esbuild/linux-riscv64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/linux-riscv64@npm:0.19.6" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -224,9 +224,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/linux-s390x@npm:0.19.5" +"@esbuild/linux-s390x@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/linux-s390x@npm:0.19.6" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -238,9 +238,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/linux-x64@npm:0.19.5" +"@esbuild/linux-x64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/linux-x64@npm:0.19.6" conditions: os=linux & cpu=x64 languageName: node linkType: hard @@ -252,9 +252,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/netbsd-x64@npm:0.19.5" +"@esbuild/netbsd-x64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/netbsd-x64@npm:0.19.6" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard @@ -266,9 +266,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/openbsd-x64@npm:0.19.5" +"@esbuild/openbsd-x64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/openbsd-x64@npm:0.19.6" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -280,9 +280,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/sunos-x64@npm:0.19.5" +"@esbuild/sunos-x64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/sunos-x64@npm:0.19.6" conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -294,9 +294,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/win32-arm64@npm:0.19.5" +"@esbuild/win32-arm64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/win32-arm64@npm:0.19.6" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -308,9 +308,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/win32-ia32@npm:0.19.5" +"@esbuild/win32-ia32@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/win32-ia32@npm:0.19.6" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -322,9 +322,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.19.5": - version: 0.19.5 - resolution: "@esbuild/win32-x64@npm:0.19.5" +"@esbuild/win32-x64@npm:0.19.6": + version: 0.19.6 + resolution: "@esbuild/win32-x64@npm:0.19.6" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -341,15 +341,15 @@ __metadata: linkType: hard "@eslint-community/regexpp@npm:^4.6.1": - version: 4.9.0 - resolution: "@eslint-community/regexpp@npm:4.9.0" - checksum: 82411f0643ab9bfd271bf12c8c75031266b13595d9371585ee3b0d680d918d4abf37c7e94d0da22e45817c9bbc59b79dfcbd672050dfb00af88fb89c80fd420f + version: 4.10.0 + resolution: "@eslint-community/regexpp@npm:4.10.0" + checksum: 2a6e345429ea8382aaaf3a61f865cae16ed44d31ca917910033c02dc00d505d939f10b81e079fa14d43b51499c640138e153b7e40743c4c094d9df97d4e56f7b languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.1.2": - version: 2.1.2 - resolution: "@eslint/eslintrc@npm:2.1.2" +"@eslint/eslintrc@npm:^2.1.3": + version: 2.1.3 + resolution: "@eslint/eslintrc@npm:2.1.3" dependencies: ajv: ^6.12.4 debug: ^4.3.2 @@ -360,25 +360,25 @@ __metadata: js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: bc742a1e3b361f06fedb4afb6bf32cbd27171292ef7924f61c62f2aed73048367bcc7ac68f98c06d4245cd3fabc43270f844e3c1699936d4734b3ac5398814a7 + checksum: 5c6c3878192fe0ddffa9aff08b4e2f3bcc8f1c10d6449b7295a5f58b662019896deabfc19890455ffd7e60a5bd28d25d0eaefb2f78b2d230aae3879af92b89e5 languageName: node linkType: hard -"@eslint/js@npm:8.50.0": - version: 8.50.0 - resolution: "@eslint/js@npm:8.50.0" - checksum: 302478f2acaaa7228729ec6a04f56641590185e1d8cd1c836a6db8a6b8009f80a57349341be9fbb9aa1721a7a569d1be3ffc598a33300d22816f11832095386c +"@eslint/js@npm:8.54.0": + version: 8.54.0 + resolution: "@eslint/js@npm:8.54.0" + checksum: 6d88a6f711ef0133566b5340e3178a178fbb297585766460f195d0a9db85688f1e5cf8559fd5748aeb3131e2096c66595b323d8edab22df015acda68f1ebde92 languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.11": - version: 0.11.11 - resolution: "@humanwhocodes/config-array@npm:0.11.11" +"@humanwhocodes/config-array@npm:^0.11.13": + version: 0.11.13 + resolution: "@humanwhocodes/config-array@npm:0.11.13" dependencies: - "@humanwhocodes/object-schema": ^1.2.1 + "@humanwhocodes/object-schema": ^2.0.1 debug: ^4.1.1 minimatch: ^3.0.5 - checksum: db84507375ab77b8ffdd24f498a5b49ad6b64391d30dd2ac56885501d03964d29637e05b1ed5aefa09d57ac667e28028bc22d2da872bfcd619652fbdb5f4ca19 + checksum: f8ea57b0d7ed7f2d64cd3944654976829d9da91c04d9c860e18804729a33f7681f78166ef4c761850b8c324d362f7d53f14c5c44907a6b38b32c703ff85e4805 languageName: node linkType: hard @@ -389,10 +389,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^1.2.1": - version: 1.2.1 - resolution: "@humanwhocodes/object-schema@npm:1.2.1" - checksum: a824a1ec31591231e4bad5787641f59e9633827d0a2eaae131a288d33c9ef0290bd16fda8da6f7c0fcb014147865d12118df10db57f27f41e20da92369fcb3f1 +"@humanwhocodes/object-schema@npm:^2.0.1": + version: 2.0.1 + resolution: "@humanwhocodes/object-schema@npm:2.0.1" + checksum: 24929487b1ed48795d2f08346a0116cc5ee4634848bce64161fb947109352c562310fd159fc64dda0e8b853307f5794605191a9547f7341158559ca3c8262a45 languageName: node linkType: hard @@ -452,12 +452,12 @@ __metadata: linkType: hard "@jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.19 - resolution: "@jridgewell/trace-mapping@npm:0.3.19" + version: 0.3.20 + resolution: "@jridgewell/trace-mapping@npm:0.3.20" dependencies: "@jridgewell/resolve-uri": ^3.1.0 "@jridgewell/sourcemap-codec": ^1.4.14 - checksum: 956a6f0f6fec060fb48c6bf1f5ec2064e13cd38c8be3873877d4b92b4a27ba58289a34071752671262a3e3c202abcc3fa2aac64d8447b4b0fa1ba3c9047f1c20 + checksum: cd1a7353135f385909468ff0cf20bdd37e59f2ee49a13a966dedf921943e222082c583ade2b579ff6cd0d8faafcb5461f253e1bf2a9f48fec439211fdbe788f5 languageName: node linkType: hard @@ -488,6 +488,19 @@ __metadata: languageName: node linkType: hard +"@npmcli/agent@npm:^2.0.0": + version: 2.2.0 + resolution: "@npmcli/agent@npm:2.2.0" + dependencies: + agent-base: ^7.1.0 + http-proxy-agent: ^7.0.0 + https-proxy-agent: ^7.0.1 + lru-cache: ^10.0.1 + socks-proxy-agent: ^8.0.1 + checksum: 3b25312edbdfaa4089af28e2d423b6f19838b945e47765b0c8174c1395c79d43c3ad6d23cb364b43f59fd3acb02c93e3b493f72ddbe3dfea04c86843a7311fc4 + languageName: node + linkType: hard + "@npmcli/fs@npm:^3.1.0": version: 3.1.0 resolution: "@npmcli/fs@npm:3.1.0" @@ -505,8 +518,8 @@ __metadata: linkType: hard "@reduxjs/toolkit@npm:^1.9.3": - version: 1.9.6 - resolution: "@reduxjs/toolkit@npm:1.9.6" + version: 1.9.7 + resolution: "@reduxjs/toolkit@npm:1.9.7" dependencies: immer: ^9.0.21 redux: ^4.2.1 @@ -520,90 +533,90 @@ __metadata: optional: true react-redux: optional: true - checksum: 61d445f7e084c79f9601f61fcfc4eb65152b850b2a4330239d982297605bd870e63dc1e0211deb3822392cd3bc0c88ca0cdb236a9711a4311dfb199c607b6ac5 + checksum: ac25dec73a5d2df9fc7fbe98c14ccc73919e5ee1d6f251db0d2ec8f90273f92ef39c26716704bf56b5a40189f72d94b4526dc3a8c7ac3986f5daf44442bcc364 languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.1.5" +"@rollup/rollup-android-arm-eabi@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.5.0" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-android-arm64@npm:4.1.5" +"@rollup/rollup-android-arm64@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-android-arm64@npm:4.5.0" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-darwin-arm64@npm:4.1.5" +"@rollup/rollup-darwin-arm64@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.5.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-darwin-x64@npm:4.1.5" +"@rollup/rollup-darwin-x64@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.5.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.1.5" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.5.0" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.1.5" +"@rollup/rollup-linux-arm64-gnu@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.5.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.1.5" +"@rollup/rollup-linux-arm64-musl@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.5.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.1.5" +"@rollup/rollup-linux-x64-gnu@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.5.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.1.5" +"@rollup/rollup-linux-x64-musl@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.5.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.1.5" +"@rollup/rollup-win32-arm64-msvc@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.5.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.1.5" +"@rollup/rollup-win32-ia32-msvc@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.5.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.1.5": - version: 4.1.5 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.1.5" +"@rollup/rollup-win32-x64-msvc@npm:4.5.0": + version: 4.5.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.5.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -615,33 +628,19 @@ __metadata: languageName: node linkType: hard -"@tootallnate/once@npm:2": - version: 2.0.0 - resolution: "@tootallnate/once@npm:2.0.0" - checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 - languageName: node - linkType: hard - "@types/chai-subset@npm:^1.3.3": - version: 1.3.3 - resolution: "@types/chai-subset@npm:1.3.3" + version: 1.3.5 + resolution: "@types/chai-subset@npm:1.3.5" dependencies: "@types/chai": "*" - checksum: 4481da7345022995f5a105e6683744f7203d2c3d19cfe88d8e17274d045722948abf55e0adfd97709e0f043dade37a4d4e98cd4c660e2e8a14f23e6ecf79418f + checksum: 715c46d3e90f87482c2769389d560456bb257b225716ff44c275c231bdb62c8a30629f355f412bac0ecab07ebc036c1806d9ed9dde9792254f8ef4f07f76033b languageName: node linkType: hard -"@types/chai@npm:*": - version: 4.3.6 - resolution: "@types/chai@npm:4.3.6" - checksum: 32a6c18bf53fb3dbd89d1bfcadb1c6fd45cc0007c34e436393cc37a0a5a556f9e6a21d1e8dd71674c40cc36589d2f30bf4d9369d7787021e54d6e997b0d7300a - languageName: node - linkType: hard - -"@types/chai@npm:^4.3.5": - version: 4.3.9 - resolution: "@types/chai@npm:4.3.9" - checksum: 2300a2c7abd4cb590349927a759b3d0172211a69f363db06e585faf7874a47f125ef3b364cce4f6190e3668147587fc11164c791c9560cf9bce8478fb7019610 +"@types/chai@npm:*, @types/chai@npm:^4.3.5": + version: 4.3.10 + resolution: "@types/chai@npm:4.3.10" + checksum: cb9ebe31f5da2d72c4b9362ec4efb33497355372270163c0290f6b9c389934ff178dac933be6b2911a125f15972c0379603736ea83ad10bfca933b6aaf6c0c5b languageName: node linkType: hard @@ -656,26 +655,26 @@ __metadata: linkType: hard "@types/hoist-non-react-statics@npm:^3.3.1": - version: 3.3.2 - resolution: "@types/hoist-non-react-statics@npm:3.3.2" + version: 3.3.5 + resolution: "@types/hoist-non-react-statics@npm:3.3.5" dependencies: "@types/react": "*" hoist-non-react-statics: ^3.3.0 - checksum: fe5d4b751e13f56010811fd6c4e49e53e2ccbcbbdc54bb8d86a413fbd08c5a83311bca9ef75a1a88d3ba62806711b5dea3f323c0e0f932b3a283dcebc3240238 + checksum: b645b062a20cce6ab1245ada8274051d8e2e0b2ee5c6bd58215281d0ec6dae2f26631af4e2e7c8abe238cdcee73fcaededc429eef569e70908f82d0cc0ea31d7 languageName: node linkType: hard "@types/json-schema@npm:^7.0.9": - version: 7.0.13 - resolution: "@types/json-schema@npm:7.0.13" - checksum: 345df21a678fa72fb389f35f33de77833d09d4a142bb2bcb27c18690efa4cf70fc2876e43843cefb3fbdb9fcb12cd3e970a90936df30f53bbee899865ff605ab + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 languageName: node linkType: hard "@types/lodash@npm:^4.14.175": - version: 4.14.199 - resolution: "@types/lodash@npm:4.14.199" - checksum: e68d1fcbbfce953ed87b296a628573f62939227bcda0c934954e862b421e8a34c5e71cad6fea27b9980567909e6a4698f09025692958e36d64ea9ed99ec6fb2e + version: 4.14.201 + resolution: "@types/lodash@npm:4.14.201" + checksum: 484be655298e9b2dc2d218ea934071b2ea31e4a531c561dd220dbda65237e8d08c20dc2d457ac24f29be7fe167415bf7bb9360ea0d80bdb8b0f0ec8d8db92fae languageName: node linkType: hard @@ -687,44 +686,46 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 20.8.0 - resolution: "@types/node@npm:20.8.0" - checksum: ebad6342d54238a24bf980d7750117a5d67749c9b72cbb7a974a1e932c39034aa3a810d669e007e8a5071782a253aa069a187b614407a382403c9826e837c849 + version: 20.9.2 + resolution: "@types/node@npm:20.9.2" + dependencies: + undici-types: ~5.26.4 + checksum: 5bbb8fb2248fc5c5c4071d9809fb9af85997677c07124d65665202b53283a3b7bdff26fb844e9ee407e3847dfce6399c2b01e3329ea44a4b720647b1b987c678 languageName: node linkType: hard "@types/prop-types@npm:*": - version: 15.7.8 - resolution: "@types/prop-types@npm:15.7.8" - checksum: 61dfad79da8b1081c450bab83b77935df487ae1cdd4660ec7df6be8e74725c15fa45cf486ce057addc956ca4ae78300b97091e2a25061133d1b9a1440bc896ae + version: 15.7.10 + resolution: "@types/prop-types@npm:15.7.10" + checksum: 39ecc2d9e439ed16b32937a08d98b84ed4a70f53bcd52c8564c0cd7a36fe1004ca83a1fb94b13c1b7a5c048760f06445c3c6a91a6972c8eff652c0b50c9424b1 languageName: node linkType: hard "@types/react@npm:*": - version: 18.2.24 - resolution: "@types/react@npm:18.2.24" + version: 18.2.37 + resolution: "@types/react@npm:18.2.37" dependencies: "@types/prop-types": "*" "@types/scheduler": "*" csstype: ^3.0.2 - checksum: ea5d8204e71b1c9c6631f429a93f8e7be0614cdbdb464e92b3181bdccd8a7c45e30ded8b13da726684b6393f651317c36d54832e3d3cdea0da480a3f26268909 + checksum: 2d2599f1a09e4f678509161fea8baeaf76d21deee460f4f3ccc1ca431ebe85f896d7d0b906127de17e97ed57240cec61955eb97d0b5d9cbf4e97fd6620b1acdb languageName: node linkType: hard "@types/scheduler@npm:*": - version: 0.16.4 - resolution: "@types/scheduler@npm:0.16.4" - checksum: a57b0f10da1b021e6bd5eeef8a1917dd3b08a8715bd8029e2ded2096d8f091bb1bb1fef2d66e139588a983c4bfbad29b59e48011141725fa83c76e986e1257d7 + version: 0.16.6 + resolution: "@types/scheduler@npm:0.16.6" + checksum: 4cec89727584a50c66a07c322469a4d9e64f5b0117691f36afd4ceae75741c0038a6e107c05e515511d5358b5897becbe065b6e4560664cb1b16f6754915043d languageName: node linkType: hard "@types/shelljs@npm:^0.8.11": - version: 0.8.13 - resolution: "@types/shelljs@npm:0.8.13" + version: 0.8.15 + resolution: "@types/shelljs@npm:0.8.15" dependencies: "@types/glob": ~7.2.0 "@types/node": "*" - checksum: 86ac87f4688aeb578c3a85fad1c6b4a740f3951d4c9b1d5e5fd9240225589b2c7a10aacff5c857a5f12961e55ffef40698a46e41d7b27cbdf9cf4bcaf1349a7a + checksum: 94939421c6c83d3075e1c56bf940eb3c34567c6b2ac0b553ec81de7f4c7e7cdfc729117d821c22418d64c45fcd4f96a6ec7ae21ed0d7a80e3e9a008672dde35f languageName: node linkType: hard @@ -849,6 +850,13 @@ __metadata: languageName: node linkType: hard +"@ungap/structured-clone@npm:^1.2.0": + version: 1.2.0 + resolution: "@ungap/structured-clone@npm:1.2.0" + checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524 + languageName: node + linkType: hard + "@vitest/expect@npm:0.34.6": version: 0.34.6 resolution: "@vitest/expect@npm:0.34.6" @@ -902,10 +910,10 @@ __metadata: languageName: node linkType: hard -"abbrev@npm:^1.0.0": - version: 1.1.1 - resolution: "abbrev@npm:1.1.1" - checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 0e994ad2aa6575f94670d8a2149afe94465de9cedaaaac364e7fb43a40c3691c980ff74899f682f4ca58fa96b4cbd7421a015d3a6defe43a442117d7821a2f36 languageName: node linkType: hard @@ -919,36 +927,27 @@ __metadata: linkType: hard "acorn-walk@npm:^8.2.0": - version: 8.2.0 - resolution: "acorn-walk@npm:8.2.0" - checksum: 1715e76c01dd7b2d4ca472f9c58968516a4899378a63ad5b6c2d668bba8da21a71976c14ec5f5b75f887b6317c4ae0b897ab141c831d741dc76024d8745f1ad1 + version: 8.3.0 + resolution: "acorn-walk@npm:8.3.0" + checksum: 15ea56ab6529135be05e7d018f935ca80a572355dd3f6d3cd717e36df3346e0f635a93ae781b1c7942607693e2e5f3ef81af5c6fc697bbadcc377ebda7b7f5f6 languageName: node linkType: hard "acorn@npm:^8.10.0, acorn@npm:^8.9.0": - version: 8.10.0 - resolution: "acorn@npm:8.10.0" + version: 8.11.2 + resolution: "acorn@npm:8.11.2" bin: acorn: bin/acorn - checksum: 538ba38af0cc9e5ef983aee196c4b8b4d87c0c94532334fa7e065b2c8a1f85863467bb774231aae91613fcda5e68740c15d97b1967ae3394d20faddddd8af61d + checksum: 818450408684da89423e3daae24e4dc9b68692db8ab49ea4569c7c5abb7a3f23669438bf129cc81dfdada95e1c9b944ee1bfca2c57a05a4dc73834a612fbf6a7 languageName: node linkType: hard -"agent-base@npm:6, agent-base@npm:^6.0.2": - version: 6.0.2 - resolution: "agent-base@npm:6.0.2" - dependencies: - debug: 4 - checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d - languageName: node - linkType: hard - -"agentkeepalive@npm:^4.2.1": - version: 4.5.0 - resolution: "agentkeepalive@npm:4.5.0" +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0": + version: 7.1.0 + resolution: "agent-base@npm:7.1.0" dependencies: - humanize-ms: ^1.2.1 - checksum: 13278cd5b125e51eddd5079f04d6fe0914ac1b8b91c1f3db2c1822f99ac1a7457869068997784342fe455d59daaff22e14fb7b8c3da4e741896e7e31faf92481 + debug: ^4.3.4 + checksum: f7828f991470a0cc22cb579c86a18cbae83d8a3cbed39992ab34fc7217c4d126017f1c74d0ab66be87f71455318a8ea3e757d6a37881b8d0f2a2c6aa55e5418f languageName: node linkType: hard @@ -1028,23 +1027,6 @@ __metadata: languageName: node linkType: hard -"aproba@npm:^1.0.3 || ^2.0.0": - version: 2.0.0 - resolution: "aproba@npm:2.0.0" - checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 - languageName: node - linkType: hard - -"are-we-there-yet@npm:^3.0.0": - version: 3.0.1 - resolution: "are-we-there-yet@npm:3.0.1" - dependencies: - delegates: ^1.0.0 - readable-stream: ^3.6.0 - checksum: 52590c24860fa7173bedeb69a4c05fb573473e860197f618b9a28432ee4379049336727ae3a1f9c4cb083114601c1140cee578376164d0e651217a9843f9fe83 - languageName: node - linkType: hard - "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -1217,14 +1199,14 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^17.0.0": - version: 17.1.4 - resolution: "cacache@npm:17.1.4" +"cacache@npm:^18.0.0": + version: 18.0.0 + resolution: "cacache@npm:18.0.0" dependencies: "@npmcli/fs": ^3.1.0 fs-minipass: ^3.0.0 glob: ^10.2.2 - lru-cache: ^7.7.1 + lru-cache: ^10.0.1 minipass: ^7.0.3 minipass-collect: ^1.0.2 minipass-flush: ^1.0.5 @@ -1233,17 +1215,18 @@ __metadata: ssri: ^10.0.0 tar: ^6.1.11 unique-filename: ^3.0.0 - checksum: b7751df756656954a51201335addced8f63fc53266fa56392c9f5ae83c8d27debffb4458ac2d168a744a4517ec3f2163af05c20097f93d17bdc2dc8a385e14a6 + checksum: 2cd6bf15551abd4165acb3a4d1ef0593b3aa2fd6853ae16b5bb62199c2faecf27d36555a9545c0e07dd03347ec052e782923bdcece724a24611986aafb53e152 languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": - version: 1.0.2 - resolution: "call-bind@npm:1.0.2" +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.4, call-bind@npm:^1.0.5": + version: 1.0.5 + resolution: "call-bind@npm:1.0.5" dependencies: - function-bind: ^1.1.1 - get-intrinsic: ^1.0.2 - checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.1 + set-function-length: ^1.1.1 + checksum: 449e83ecbd4ba48e7eaac5af26fea3b50f8f6072202c2dd7c5a6e7a6308f2421abe5e13a3bbd55221087f76320c5e09f25a8fdad1bab2b77c68ae74d92234ea5 languageName: node linkType: hard @@ -1337,15 +1320,6 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.3": - version: 1.1.3 - resolution: "color-support@npm:1.1.3" - bin: - color-support: bin.js - checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b - languageName: node - linkType: hard - "commander@npm:^4.0.0": version: 4.1.1 resolution: "commander@npm:4.1.1" @@ -1360,13 +1334,6 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.1.0": - version: 1.1.0 - resolution: "console-control-strings@npm:1.1.0" - checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed - languageName: node - linkType: hard - "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -1385,7 +1352,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -1413,14 +1380,14 @@ __metadata: languageName: node linkType: hard -"define-data-property@npm:^1.0.1": - version: 1.1.0 - resolution: "define-data-property@npm:1.1.0" +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.1": + version: 1.1.1 + resolution: "define-data-property@npm:1.1.1" dependencies: get-intrinsic: ^1.2.1 gopd: ^1.0.1 has-property-descriptors: ^1.0.0 - checksum: 7ad4ee84cca8ad427a4831f5693526804b62ce9dfd4efac77214e95a4382aed930072251d4075dc8dc9fc949a353ed51f19f5285a84a788ba9216cc51472a093 + checksum: a29855ad3f0630ea82e3c5012c812efa6ca3078d5c2aa8df06b5f597c1cde6f7254692df41945851d903e05a1668607b6d34e778f402b9ff9ffb38111f1a3f0d languageName: node linkType: hard @@ -1435,13 +1402,6 @@ __metadata: languageName: node linkType: hard -"delegates@npm:^1.0.0": - version: 1.0.0 - resolution: "delegates@npm:1.0.0" - checksum: a51744d9b53c164ba9c0492471a1a2ffa0b6727451bdc89e31627fdf4adda9d51277cfcbfb20f0a6f08ccb3c436f341df3e92631a3440226d93a8971724771fd - languageName: node - linkType: hard - "diff-sequences@npm:^29.4.3": version: 29.6.3 resolution: "diff-sequences@npm:29.6.3" @@ -1521,24 +1481,24 @@ __metadata: linkType: hard "es-abstract@npm:^1.22.1": - version: 1.22.2 - resolution: "es-abstract@npm:1.22.2" + version: 1.22.3 + resolution: "es-abstract@npm:1.22.3" dependencies: array-buffer-byte-length: ^1.0.0 arraybuffer.prototype.slice: ^1.0.2 available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 + call-bind: ^1.0.5 es-set-tostringtag: ^2.0.1 es-to-primitive: ^1.2.1 function.prototype.name: ^1.1.6 - get-intrinsic: ^1.2.1 + get-intrinsic: ^1.2.2 get-symbol-description: ^1.0.0 globalthis: ^1.0.3 gopd: ^1.0.1 - has: ^1.0.3 has-property-descriptors: ^1.0.0 has-proto: ^1.0.1 has-symbols: ^1.0.3 + hasown: ^2.0.0 internal-slot: ^1.0.5 is-array-buffer: ^3.0.2 is-callable: ^1.2.7 @@ -1548,7 +1508,7 @@ __metadata: is-string: ^1.0.7 is-typed-array: ^1.1.12 is-weakref: ^1.0.2 - object-inspect: ^1.12.3 + object-inspect: ^1.13.1 object-keys: ^1.1.1 object.assign: ^4.1.4 regexp.prototype.flags: ^1.5.1 @@ -1562,8 +1522,8 @@ __metadata: typed-array-byte-offset: ^1.0.0 typed-array-length: ^1.0.4 unbox-primitive: ^1.0.2 - which-typed-array: ^1.1.11 - checksum: cc70e592d360d7d729859013dee7a610c6b27ed8630df0547c16b0d16d9fe6505a70ee14d1af08d970fdd132b3f88c9ca7815ce72c9011608abf8ab0e55fc515 + which-typed-array: ^1.1.13 + checksum: b1bdc962856836f6e72be10b58dc128282bdf33771c7a38ae90419d920fc3b36cc5d2b70a222ad8016e3fc322c367bf4e9e89fc2bc79b7e933c05b218e83d79a languageName: node linkType: hard @@ -1590,22 +1550,22 @@ __metadata: linkType: hard "es-set-tostringtag@npm:^2.0.1": - version: 2.0.1 - resolution: "es-set-tostringtag@npm:2.0.1" + version: 2.0.2 + resolution: "es-set-tostringtag@npm:2.0.2" dependencies: - get-intrinsic: ^1.1.3 - has: ^1.0.3 + get-intrinsic: ^1.2.2 has-tostringtag: ^1.0.0 - checksum: ec416a12948cefb4b2a5932e62093a7cf36ddc3efd58d6c58ca7ae7064475ace556434b869b0bbeb0c365f1032a8ccd577211101234b69837ad83ad204fff884 + hasown: ^2.0.0 + checksum: afcec3a4c9890ae14d7ec606204858441c801ff84f312538e1d1ccf1e5493c8b17bd672235df785f803756472cb4f2d49b87bde5237aef33411e74c22f194e07 languageName: node linkType: hard "es-shim-unscopables@npm:^1.0.0": - version: 1.0.0 - resolution: "es-shim-unscopables@npm:1.0.0" + version: 1.0.2 + resolution: "es-shim-unscopables@npm:1.0.2" dependencies: - has: ^1.0.3 - checksum: 83e95cadbb6ee44d3644dfad60dcad7929edbc42c85e66c3e99aefd68a3a5c5665f2686885cddb47dfeabfd77bd5ea5a7060f2092a955a729bbd8834f0d86fa1 + hasown: ^2.0.0 + checksum: 432bd527c62065da09ed1d37a3f8e623c423683285e6188108286f4a1e8e164a5bcbfbc0051557c7d14633cd2a41ce24c7048e6bbb66a985413fd32f1be72626 languageName: node linkType: hard @@ -1698,31 +1658,31 @@ __metadata: linkType: hard "esbuild@npm:^0.19.3": - version: 0.19.5 - resolution: "esbuild@npm:0.19.5" - dependencies: - "@esbuild/android-arm": 0.19.5 - "@esbuild/android-arm64": 0.19.5 - "@esbuild/android-x64": 0.19.5 - "@esbuild/darwin-arm64": 0.19.5 - "@esbuild/darwin-x64": 0.19.5 - "@esbuild/freebsd-arm64": 0.19.5 - "@esbuild/freebsd-x64": 0.19.5 - "@esbuild/linux-arm": 0.19.5 - "@esbuild/linux-arm64": 0.19.5 - "@esbuild/linux-ia32": 0.19.5 - "@esbuild/linux-loong64": 0.19.5 - "@esbuild/linux-mips64el": 0.19.5 - "@esbuild/linux-ppc64": 0.19.5 - "@esbuild/linux-riscv64": 0.19.5 - "@esbuild/linux-s390x": 0.19.5 - "@esbuild/linux-x64": 0.19.5 - "@esbuild/netbsd-x64": 0.19.5 - "@esbuild/openbsd-x64": 0.19.5 - "@esbuild/sunos-x64": 0.19.5 - "@esbuild/win32-arm64": 0.19.5 - "@esbuild/win32-ia32": 0.19.5 - "@esbuild/win32-x64": 0.19.5 + version: 0.19.6 + resolution: "esbuild@npm:0.19.6" + dependencies: + "@esbuild/android-arm": 0.19.6 + "@esbuild/android-arm64": 0.19.6 + "@esbuild/android-x64": 0.19.6 + "@esbuild/darwin-arm64": 0.19.6 + "@esbuild/darwin-x64": 0.19.6 + "@esbuild/freebsd-arm64": 0.19.6 + "@esbuild/freebsd-x64": 0.19.6 + "@esbuild/linux-arm": 0.19.6 + "@esbuild/linux-arm64": 0.19.6 + "@esbuild/linux-ia32": 0.19.6 + "@esbuild/linux-loong64": 0.19.6 + "@esbuild/linux-mips64el": 0.19.6 + "@esbuild/linux-ppc64": 0.19.6 + "@esbuild/linux-riscv64": 0.19.6 + "@esbuild/linux-s390x": 0.19.6 + "@esbuild/linux-x64": 0.19.6 + "@esbuild/netbsd-x64": 0.19.6 + "@esbuild/openbsd-x64": 0.19.6 + "@esbuild/sunos-x64": 0.19.6 + "@esbuild/win32-arm64": 0.19.6 + "@esbuild/win32-ia32": 0.19.6 + "@esbuild/win32-x64": 0.19.6 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -1770,7 +1730,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 5a0227cf6ffffa3076714d88230af1dfdd2fc363d91bd712a81fb91230c315a395e2c9b7588eee62986aeebf4999804b9b1b59eeab8e2457184eb0056bfe20c8 + checksum: b5f6e19c9f3e5302ffea4ad0ba39e17f7eed09f342f04d5561cfa491a69334095655ac2a9166c29a80da14af35cfcaaaf7751f8b2bad870d49ccdb8817921f37 languageName: node linkType: hard @@ -1862,16 +1822,17 @@ __metadata: linkType: hard "eslint@npm:^8.0.1": - version: 8.50.0 - resolution: "eslint@npm:8.50.0" + version: 8.54.0 + resolution: "eslint@npm:8.54.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.6.1 - "@eslint/eslintrc": ^2.1.2 - "@eslint/js": 8.50.0 - "@humanwhocodes/config-array": ^0.11.11 + "@eslint/eslintrc": ^2.1.3 + "@eslint/js": 8.54.0 + "@humanwhocodes/config-array": ^0.11.13 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 + "@ungap/structured-clone": ^1.2.0 ajv: ^6.12.4 chalk: ^4.0.0 cross-spawn: ^7.0.2 @@ -1904,7 +1865,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 9ebfe5615dc84700000d218e32ddfdcfc227ca600f65f18e5541ec34f8902a00356a9a8804d9468fd6c8637a5ef6a3897291dad91ba6579d5b32ffeae5e31768 + checksum: 7e876e9da2a18a017271cf3733d05a3dfbbe469272d75753408c6ea5b1646c71c6bb18cb91e10ca930144c32c1ce3701e222f1ae6784a3975a69f8f8aa68e49f languageName: node linkType: hard @@ -1990,15 +1951,15 @@ __metadata: linkType: hard "fast-glob@npm:^3.2.9": - version: 3.3.1 - resolution: "fast-glob@npm:3.3.1" + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" dependencies: "@nodelib/fs.stat": ^2.0.2 "@nodelib/fs.walk": ^1.2.3 glob-parent: ^5.1.2 merge2: ^1.3.0 micromatch: ^4.0.4 - checksum: b6f3add6403e02cf3a798bfbb1183d0f6da2afd368f27456010c0bc1f9640aea308243d4cb2c0ab142f618276e65ecb8be1661d7c62a7b4e5ba774b9ce5432e5 + checksum: 900e4979f4dbc3313840078419245621259f349950411ca2fa445a2f9a1a6d98c3b5e7e0660c5ccd563aa61abe133a21765c6c0dec8e57da1ba71d8000b05ec1 languageName: node linkType: hard @@ -2054,17 +2015,17 @@ __metadata: linkType: hard "flat-cache@npm:^3.0.4": - version: 3.1.0 - resolution: "flat-cache@npm:3.1.0" + version: 3.2.0 + resolution: "flat-cache@npm:3.2.0" dependencies: - flatted: ^3.2.7 + flatted: ^3.2.9 keyv: ^4.5.3 rimraf: ^3.0.2 - checksum: 99312601d5b90f44aef403f17f056dc09be7e437703740b166cdc9386d99e681f74e6b6e8bd7d010bda66904ea643c9527276b1b80308a2119741d94108a4d8f + checksum: e7e0f59801e288b54bee5cb9681e9ee21ee28ef309f886b312c9d08415b79fc0f24ac842f84356ce80f47d6a53de62197ce0e6e148dc42d5db005992e2a756ec languageName: node linkType: hard -"flatted@npm:^3.2.7": +"flatted@npm:^3.2.9": version: 3.2.9 resolution: "flatted@npm:3.2.9" checksum: f14167fbe26a9d20f6fca8d998e8f1f41df72c8e81f9f2c9d61ed2bea058248f5e1cbd05e7f88c0e5087a6a0b822a1e5e2b446e879f3cfbe0b07ba2d7f80b026 @@ -2134,10 +2095,10 @@ __metadata: languageName: node linkType: hard -"function-bind@npm:^1.1.1": - version: 1.1.1 - resolution: "function-bind@npm:1.1.1" - checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a +"function-bind@npm:^1.1.1, function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 2b0ff4ce708d99715ad14a6d1f894e2a83242e4a52ccfcefaee5e40050562e5f6dafc1adbb4ce2d4ab47279a45dc736ab91ea5042d843c3c092820dfe032efb1 languageName: node linkType: hard @@ -2167,38 +2128,22 @@ __metadata: languageName: node linkType: hard -"gauge@npm:^4.0.3": - version: 4.0.4 - resolution: "gauge@npm:4.0.4" - dependencies: - aproba: ^1.0.3 || ^2.0.0 - color-support: ^1.1.3 - console-control-strings: ^1.1.0 - has-unicode: ^2.0.1 - signal-exit: ^3.0.7 - string-width: ^4.2.3 - strip-ansi: ^6.0.1 - wide-align: ^1.1.5 - checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d - languageName: node - linkType: hard - -"get-func-name@npm:^2.0.0, get-func-name@npm:^2.0.2": +"get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2": version: 2.0.2 resolution: "get-func-name@npm:2.0.2" checksum: 3f62f4c23647de9d46e6f76d2b3eafe58933a9b3830c60669e4180d6c601ce1b4aa310ba8366143f55e52b139f992087a9f0647274e8745621fa2af7e0acf13b languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": - version: 1.2.1 - resolution: "get-intrinsic@npm:1.2.1" +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2": + version: 1.2.2 + resolution: "get-intrinsic@npm:1.2.2" dependencies: - function-bind: ^1.1.1 - has: ^1.0.3 + function-bind: ^1.1.2 has-proto: ^1.0.1 has-symbols: ^1.0.3 - checksum: 5b61d88552c24b0cf6fa2d1b3bc5459d7306f699de060d76442cce49a4721f52b8c560a33ab392cf5575b7810277d54ded9d4d39a1ea61855619ebc005aa7e5f + hasown: ^2.0.0 + checksum: 447ff0724df26829908dc033b62732359596fcf66027bc131ab37984afb33842d9cd458fd6cecadfe7eac22fd8a54b349799ed334cf2726025c921c7250e7417 languageName: node linkType: hard @@ -2251,7 +2196,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2": +"glob@npm:^10.2.2, glob@npm:^10.3.10": version: 10.3.10 resolution: "glob@npm:10.3.10" dependencies: @@ -2266,7 +2211,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.1.3, glob@npm:^7.1.4": +"glob@npm:^7.0.0, glob@npm:^7.1.3": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -2281,11 +2226,11 @@ __metadata: linkType: hard "globals@npm:^13.19.0": - version: 13.22.0 - resolution: "globals@npm:13.22.0" + version: 13.23.0 + resolution: "globals@npm:13.23.0" dependencies: type-fest: ^0.20.2 - checksum: 64af5a09565341432770444085f7aa98b54331c3b69732e0de411003921fa2dd060222ae7b50bec0b98f29c4d00b4f49bf434049ba9f7c36ca4ee1773f60458c + checksum: 194c97cf8d1ef6ba59417234c2386549c4103b6e5f24b1ff1952de61a4753e5d2069435ba629de711a6480b1b1d114a98e2ab27f85e966d5a10c319c3bbd3dc3 languageName: node linkType: hard @@ -2350,11 +2295,11 @@ __metadata: linkType: hard "has-property-descriptors@npm:^1.0.0": - version: 1.0.0 - resolution: "has-property-descriptors@npm:1.0.0" + version: 1.0.1 + resolution: "has-property-descriptors@npm:1.0.1" dependencies: - get-intrinsic: ^1.1.1 - checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb + get-intrinsic: ^1.2.2 + checksum: 2bcc6bf6ec6af375add4e4b4ef586e43674850a91ad4d46666d0b28ba8e1fd69e424c7677d24d60f69470ad0afaa2f3197f508b20b0bb7dd99a8ab77ffc4b7c4 languageName: node linkType: hard @@ -2381,19 +2326,12 @@ __metadata: languageName: node linkType: hard -"has-unicode@npm:^2.0.1": - version: 2.0.1 - resolution: "has-unicode@npm:2.0.1" - checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 - languageName: node - linkType: hard - -"has@npm:^1.0.3": - version: 1.0.3 - resolution: "has@npm:1.0.3" +"hasown@npm:^2.0.0": + version: 2.0.0 + resolution: "hasown@npm:2.0.0" dependencies: - function-bind: ^1.1.1 - checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 + function-bind: ^1.1.2 + checksum: 6151c75ca12554565098641c98a40f4cc86b85b0fd5b6fe92360967e4605a4f9610f7757260b4e8098dd1c2ce7f4b095f2006fe72a570e3b6d2d28de0298c176 languageName: node linkType: hard @@ -2413,24 +2351,23 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "http-proxy-agent@npm:5.0.0" +"http-proxy-agent@npm:^7.0.0": + version: 7.0.0 + resolution: "http-proxy-agent@npm:7.0.0" dependencies: - "@tootallnate/once": 2 - agent-base: 6 - debug: 4 - checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 + agent-base: ^7.1.0 + debug: ^4.3.4 + checksum: 48d4fac997917e15f45094852b63b62a46d0c8a4f0b9c6c23ca26d27b8df8d178bed88389e604745e748bd9a01f5023e25093722777f0593c3f052009ff438b6 languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" +"https-proxy-agent@npm:^7.0.1": + version: 7.0.2 + resolution: "https-proxy-agent@npm:7.0.2" dependencies: - agent-base: 6 + agent-base: ^7.0.2 debug: 4 - checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 + checksum: 088969a0dd476ea7a0ed0a2cf1283013682b08f874c3bc6696c83fa061d2c157d29ef0ad3eb70a2046010bb7665573b2388d10fdcb3e410a66995e5248444292 languageName: node linkType: hard @@ -2441,15 +2378,6 @@ __metadata: languageName: node linkType: hard -"humanize-ms@npm:^1.2.1": - version: 1.2.1 - resolution: "humanize-ms@npm:1.2.1" - dependencies: - ms: ^2.0.0 - checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 - languageName: node - linkType: hard - "iconv-lite@npm:^0.6.2": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" @@ -2460,9 +2388,9 @@ __metadata: linkType: hard "ignore@npm:^5.1.8, ignore@npm:^5.2.0": - version: 5.2.4 - resolution: "ignore@npm:5.2.4" - checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef + version: 5.3.0 + resolution: "ignore@npm:5.3.0" + checksum: 2736da6621f14ced652785cb05d86301a66d70248597537176612bd0c8630893564bd5f6421f8806b09e8472e75c591ef01672ab8059c07c6eb2c09cefe04bf9 languageName: node linkType: hard @@ -2507,7 +2435,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.3": +"inherits@npm:2": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -2515,13 +2443,13 @@ __metadata: linkType: hard "internal-slot@npm:^1.0.5": - version: 1.0.5 - resolution: "internal-slot@npm:1.0.5" + version: 1.0.6 + resolution: "internal-slot@npm:1.0.6" dependencies: - get-intrinsic: ^1.2.0 - has: ^1.0.3 + get-intrinsic: ^1.2.2 + hasown: ^2.0.0 side-channel: ^1.0.4 - checksum: 97e84046bf9e7574d0956bd98d7162313ce7057883b6db6c5c7b5e5f05688864b0978ba07610c726d15d66544ffe4b1050107d93f8a39ebc59b15d8b429b497a + checksum: 7872454888047553ce97a3fa1da7cc054a28ec5400a9c2e9f4dbe4fe7c1d041cb8e8301467614b80d4246d50377aad2fb58860b294ed74d6700cc346b6f89549 languageName: node linkType: hard @@ -2594,12 +2522,12 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0, is-core-module@npm:^2.9.0": - version: 2.13.0 - resolution: "is-core-module@npm:2.13.0" +"is-core-module@npm:^2.13.0": + version: 2.13.1 + resolution: "is-core-module@npm:2.13.1" dependencies: - has: ^1.0.3 - checksum: 053ab101fb390bfeb2333360fd131387bed54e476b26860dc7f5a700bbf34a0ec4454f7c8c4d43e8a0030957e4b3db6e16d35e1890ea6fb654c833095e040355 + hasown: ^2.0.0 + checksum: 256559ee8a9488af90e4bad16f5583c6d59e92f0742e9e8bb4331e758521ee86b810b93bae44f390766ffbc518a0488b18d9dab7da9a5ff997d499efc9403f7c languageName: node linkType: hard @@ -2797,6 +2725,13 @@ __metadata: languageName: node linkType: hard +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e + languageName: node + linkType: hard + "iterator.prototype@npm:^1.1.2": version: 1.1.2 resolution: "iterator.prototype@npm:1.1.2" @@ -2889,11 +2824,11 @@ __metadata: linkType: hard "keyv@npm:^4.5.3": - version: 4.5.3 - resolution: "keyv@npm:4.5.3" + version: 4.5.4 + resolution: "keyv@npm:4.5.4" dependencies: json-buffer: 3.0.1 - checksum: 3ffb4d5b72b6b4b4af443bbb75ca2526b23c750fccb5ac4c267c6116888b4b65681015c2833cb20d26cf3e6e32dac6b988c77f7f022e1a571b7d90f1442257da + checksum: 74a24395b1c34bd44ad5cb2b49140d087553e170625240b86755a6604cd65aa16efdbdeae5cdb17ba1284a0fbb25ad06263755dbc71b8d8b06f74232ce3cdd72 languageName: node linkType: hard @@ -2984,11 +2919,18 @@ __metadata: linkType: hard "loupe@npm:^2.3.6": - version: 2.3.6 - resolution: "loupe@npm:2.3.6" + version: 2.3.7 + resolution: "loupe@npm:2.3.7" dependencies: - get-func-name: ^2.0.0 - checksum: cc83f1b124a1df7384601d72d8d1f5fe95fd7a8185469fec48bb2e4027e45243949e7a013e8d91051a138451ff0552310c32aa9786e60b6a30d1e801bdc2163f + get-func-name: ^2.0.1 + checksum: 96c058ec7167598e238bb7fb9def2f9339215e97d6685d9c1e3e4bdb33d14600e11fe7a812cf0c003dfb73ca2df374f146280b2287cae9e8d989e9d7a69a203b + languageName: node + linkType: hard + +"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": + version: 10.0.3 + resolution: "lru-cache@npm:10.0.3" + checksum: e4b100c5a6b2ac778c0f63711499b5098686205c57907d8c04a413270d37089112d9bd0192dfa36940eb5d94b88c7db54fdb6fd23319c8f89903cfd4323ea06c languageName: node linkType: hard @@ -3001,20 +2943,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.7.1": - version: 7.18.3 - resolution: "lru-cache@npm:7.18.3" - checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 - languageName: node - linkType: hard - -"lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.0.1 - resolution: "lru-cache@npm:10.0.1" - checksum: 06f8d0e1ceabd76bb6f644a26dbb0b4c471b79c7b514c13c6856113879b3bf369eb7b497dad4ff2b7e2636db202412394865b33c332100876d838ad1372f0181 - languageName: node - linkType: hard - "magic-string@npm:^0.30.1": version: 0.30.5 resolution: "magic-string@npm:0.30.5" @@ -3024,26 +2952,22 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^11.0.3": - version: 11.1.1 - resolution: "make-fetch-happen@npm:11.1.1" +"make-fetch-happen@npm:^13.0.0": + version: 13.0.0 + resolution: "make-fetch-happen@npm:13.0.0" dependencies: - agentkeepalive: ^4.2.1 - cacache: ^17.0.0 + "@npmcli/agent": ^2.0.0 + cacache: ^18.0.0 http-cache-semantics: ^4.1.1 - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 is-lambda: ^1.0.1 - lru-cache: ^7.7.1 - minipass: ^5.0.0 + minipass: ^7.0.2 minipass-fetch: ^3.0.0 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 - socks-proxy-agent: ^7.0.0 ssri: ^10.0.0 - checksum: 7268bf274a0f6dcf0343829489a4506603ff34bd0649c12058753900b0eb29191dce5dba12680719a5d0a983d3e57810f594a12f3c18494e93a1fbc6348a4540 + checksum: 7c7a6d381ce919dd83af398b66459a10e2fe8f4504f340d1d090d3fa3d1b0c93750220e1d898114c64467223504bd258612ba83efbc16f31b075cd56de24b4af languageName: node linkType: hard @@ -3177,7 +3101,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.3": +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3": version: 7.0.4 resolution: "minipass@npm:7.0.4" checksum: 87585e258b9488caf2e7acea242fd7856bbe9a2c84a7807643513a338d66f368c7d518200ad7b70a508664d408aa000517647b2930c259a8b1f9f0984f344a21 @@ -3222,13 +3146,6 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.0.0": - version: 2.1.3 - resolution: "ms@npm:2.1.3" - checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d - languageName: node - linkType: hard - "mz@npm:^2.7.0": version: 2.7.0 resolution: "mz@npm:2.7.0" @@ -3241,11 +3158,11 @@ __metadata: linkType: hard "nanoid@npm:^3.3.6": - version: 3.3.6 - resolution: "nanoid@npm:3.3.6" + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" bin: nanoid: bin/nanoid.cjs - checksum: 7d0eda657002738aa5206107bd0580aead6c95c460ef1bdd0b1a87a9c7ae6277ac2e9b945306aaa5b32c6dcb7feaf462d0f552e7f8b5718abfc6ead5c94a71b3 + checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2 languageName: node linkType: hard @@ -3264,34 +3181,33 @@ __metadata: linkType: hard "node-gyp@npm:latest": - version: 9.4.0 - resolution: "node-gyp@npm:9.4.0" + version: 10.0.1 + resolution: "node-gyp@npm:10.0.1" dependencies: env-paths: ^2.2.0 exponential-backoff: ^3.1.1 - glob: ^7.1.4 + glob: ^10.3.10 graceful-fs: ^4.2.6 - make-fetch-happen: ^11.0.3 - nopt: ^6.0.0 - npmlog: ^6.0.0 - rimraf: ^3.0.2 + make-fetch-happen: ^13.0.0 + nopt: ^7.0.0 + proc-log: ^3.0.0 semver: ^7.3.5 tar: ^6.1.2 - which: ^2.0.2 + which: ^4.0.0 bin: node-gyp: bin/node-gyp.js - checksum: 78b404e2e0639d64e145845f7f5a3cb20c0520cdaf6dda2f6e025e9b644077202ea7de1232396ba5bde3fee84cdc79604feebe6ba3ec84d464c85d407bb5da99 + checksum: 60a74e66d364903ce02049966303a57f898521d139860ac82744a5fdd9f7b7b3b61f75f284f3bfe6e6add3b8f1871ce305a1d41f775c7482de837b50c792223f languageName: node linkType: hard -"nopt@npm:^6.0.0": - version: 6.0.0 - resolution: "nopt@npm:6.0.0" +"nopt@npm:^7.0.0": + version: 7.2.0 + resolution: "nopt@npm:7.2.0" dependencies: - abbrev: ^1.0.0 + abbrev: ^2.0.0 bin: nopt: bin/nopt.js - checksum: 82149371f8be0c4b9ec2f863cc6509a7fd0fa729929c009f3a58e4eb0c9e4cae9920e8f1f8eb46e7d032fec8fb01bede7f0f41a67eb3553b7b8e14fa53de1dac + checksum: a9c0f57fb8cb9cc82ae47192ca2b7ef00e199b9480eed202482c962d61b59a7fbe7541920b2a5839a97b42ee39e288c0aed770e38057a608d7f579389dfde410 languageName: node linkType: hard @@ -3311,18 +3227,6 @@ __metadata: languageName: node linkType: hard -"npmlog@npm:^6.0.0": - version: 6.0.2 - resolution: "npmlog@npm:6.0.2" - dependencies: - are-we-there-yet: ^3.0.0 - console-control-strings: ^1.1.0 - gauge: ^4.0.3 - set-blocking: ^2.0.0 - checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a - languageName: node - linkType: hard - "object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -3330,10 +3234,10 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": - version: 1.12.3 - resolution: "object-inspect@npm:1.12.3" - checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db +"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0": + version: 1.13.1 + resolution: "object-inspect@npm:1.13.1" + checksum: 7d9fa9221de3311dcb5c7c307ee5dc011cdd31dc43624b7c184b3840514e118e05ef0002be5388304c416c0eb592feb46e983db12577fc47e47d5752fbbfb61f languageName: node linkType: hard @@ -3623,6 +3527,13 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^3.0.0": + version: 3.0.0 + resolution: "proc-log@npm:3.0.0" + checksum: 02b64e1b3919e63df06f836b98d3af002b5cd92655cab18b5746e37374bfb73e03b84fe305454614b34c25b485cc687a9eebdccf0242cda8fda2475dd2c97e02 + languageName: node + linkType: hard + "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -3645,9 +3556,9 @@ __metadata: linkType: hard "punycode@npm:^2.1.0": - version: 2.3.0 - resolution: "punycode@npm:2.3.0" - checksum: 39f760e09a2a3bbfe8f5287cf733ecdad69d6af2fe6f97ca95f24b8921858b91e9ea3c9eeec6e08cede96181b3bb33f95c6ffd8c77e63986508aa2e8159fa200 + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: bb0a0ceedca4c3c57a9b981b90601579058903c62be23c5e8e843d2c2d4148a3ecf029d5133486fb0e1822b098ba8bba09e89d6b21742d02fa26bda6441a6fb2 languageName: node linkType: hard @@ -3725,17 +3636,6 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.6.0": - version: 3.6.2 - resolution: "readable-stream@npm:3.6.2" - dependencies: - inherits: ^2.0.3 - string_decoder: ^1.1.1 - util-deprecate: ^1.0.1 - checksum: bdcbe6c22e846b6af075e32cf8f4751c2576238c5043169a1c221c92ee2878458a816a4ea33f4c67623c0b6827c8a400409bfb3cf0bf3381392d0b1dfb52ac8d - languageName: node - linkType: hard - "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -3868,54 +3768,54 @@ __metadata: linkType: hard "resolve@npm:^1.1.6": - version: 1.22.6 - resolution: "resolve@npm:1.22.6" + version: 1.22.8 + resolution: "resolve@npm:1.22.8" dependencies: is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: d13bf66d4e2ee30d291491f16f2fa44edd4e0cefb85d53249dd6f93e70b2b8c20ec62f01b18662e3cd40e50a7528f18c4087a99490048992a3bb954cf3201a5b + checksum: f8a26958aa572c9b064562750b52131a37c29d072478ea32e129063e2da7f83e31f7f11e7087a18225a8561cfe8d2f0df9dbea7c9d331a897571c0a2527dbb4c languageName: node linkType: hard "resolve@npm:^2.0.0-next.4": - version: 2.0.0-next.4 - resolution: "resolve@npm:2.0.0-next.4" + version: 2.0.0-next.5 + resolution: "resolve@npm:2.0.0-next.5" dependencies: - is-core-module: ^2.9.0 + is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: c438ac9a650f2030fd074219d7f12ceb983b475da2d89ad3d6dd05fbf6b7a0a8cd37d4d10b43cb1f632bc19f22246ab7f36ebda54d84a29bfb2910a0680906d3 + checksum: a73ac69a1c4bd34c56b213d91f5b17ce390688fdb4a1a96ed3025cc7e08e7bfb90b3a06fcce461780cb0b589c958afcb0080ab802c71c01a7ecc8c64feafc89f languageName: node linkType: hard "resolve@patch:resolve@^1.1.6#~builtin": - version: 1.22.6 - resolution: "resolve@patch:resolve@npm%3A1.22.6#~builtin::version=1.22.6&hash=07638b" + version: 1.22.8 + resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=07638b" dependencies: is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: 9d3b3c67aefd12cecbe5f10ca4d1f51ea190891096497c43f301b086883b426466918c3a64f1bbf1788fabb52b579d58809614006c5d0b49186702b3b8fb746a + checksum: 5479b7d431cacd5185f8db64bfcb7286ae5e31eb299f4c4f404ad8aa6098b77599563ac4257cb2c37a42f59dfc06a1bec2bcf283bb448f319e37f0feb9a09847 languageName: node linkType: hard "resolve@patch:resolve@^2.0.0-next.4#~builtin": - version: 2.0.0-next.4 - resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin::version=2.0.0-next.4&hash=07638b" + version: 2.0.0-next.5 + resolution: "resolve@patch:resolve@npm%3A2.0.0-next.5#~builtin::version=2.0.0-next.5&hash=07638b" dependencies: - is-core-module: ^2.9.0 + is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: 4bf9f4f8a458607af90518ff73c67a4bc1a38b5a23fef2bb0ccbd45e8be89820a1639b637b0ba377eb2be9eedfb1739a84cde24fe4cd670c8207d8fea922b011 + checksum: 064d09c1808d0c51b3d90b5d27e198e6d0c5dad0eb57065fd40803d6a20553e5398b07f76739d69cbabc12547058bec6b32106ea66622375fb0d7e8fca6a846c languageName: node linkType: hard @@ -3958,22 +3858,22 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^4.1.4": - version: 4.1.5 - resolution: "rollup@npm:4.1.5" - dependencies: - "@rollup/rollup-android-arm-eabi": 4.1.5 - "@rollup/rollup-android-arm64": 4.1.5 - "@rollup/rollup-darwin-arm64": 4.1.5 - "@rollup/rollup-darwin-x64": 4.1.5 - "@rollup/rollup-linux-arm-gnueabihf": 4.1.5 - "@rollup/rollup-linux-arm64-gnu": 4.1.5 - "@rollup/rollup-linux-arm64-musl": 4.1.5 - "@rollup/rollup-linux-x64-gnu": 4.1.5 - "@rollup/rollup-linux-x64-musl": 4.1.5 - "@rollup/rollup-win32-arm64-msvc": 4.1.5 - "@rollup/rollup-win32-ia32-msvc": 4.1.5 - "@rollup/rollup-win32-x64-msvc": 4.1.5 +"rollup@npm:^4.2.0": + version: 4.5.0 + resolution: "rollup@npm:4.5.0" + dependencies: + "@rollup/rollup-android-arm-eabi": 4.5.0 + "@rollup/rollup-android-arm64": 4.5.0 + "@rollup/rollup-darwin-arm64": 4.5.0 + "@rollup/rollup-darwin-x64": 4.5.0 + "@rollup/rollup-linux-arm-gnueabihf": 4.5.0 + "@rollup/rollup-linux-arm64-gnu": 4.5.0 + "@rollup/rollup-linux-arm64-musl": 4.5.0 + "@rollup/rollup-linux-x64-gnu": 4.5.0 + "@rollup/rollup-linux-x64-musl": 4.5.0 + "@rollup/rollup-win32-arm64-msvc": 4.5.0 + "@rollup/rollup-win32-ia32-msvc": 4.5.0 + "@rollup/rollup-win32-x64-msvc": 4.5.0 fsevents: ~2.3.2 dependenciesMeta: "@rollup/rollup-android-arm-eabi": @@ -4004,7 +3904,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: f323c5f61e49892e565c46f0114908ba58cc6139a2c1940181f3c82f8ba1bd608fabc833f35b679113b156dc1960ad5aa8bdf7ef987a9116f8db767293c52d95 + checksum: 942f08783bf45e623d177c3b58701e463952b9388822d4f8aadf4e3b465141837ad7f8c85737f7709c2dd0063782c9ce528f71dd33ca6dc3a2d112991d6cc097 languageName: node linkType: hard @@ -4029,13 +3929,6 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:~5.2.0": - version: 5.2.1 - resolution: "safe-buffer@npm:5.2.1" - checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 - languageName: node - linkType: hard - "safe-regex-test@npm:^1.0.0": version: 1.0.0 resolution: "safe-regex-test@npm:1.0.0" @@ -4083,10 +3976,15 @@ __metadata: languageName: node linkType: hard -"set-blocking@npm:^2.0.0": - version: 2.0.0 - resolution: "set-blocking@npm:2.0.0" - checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 +"set-function-length@npm:^1.1.1": + version: 1.1.1 + resolution: "set-function-length@npm:1.1.1" + dependencies: + define-data-property: ^1.1.1 + get-intrinsic: ^1.2.1 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.0 + checksum: c131d7569cd7e110cafdfbfbb0557249b538477624dfac4fc18c376d879672fa52563b74029ca01f8f4583a8acb35bb1e873d573a24edb80d978a7ee607c6e06 languageName: node linkType: hard @@ -4148,7 +4046,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.3": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -4176,18 +4074,18 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^7.0.0": - version: 7.0.0 - resolution: "socks-proxy-agent@npm:7.0.0" +"socks-proxy-agent@npm:^8.0.1": + version: 8.0.2 + resolution: "socks-proxy-agent@npm:8.0.2" dependencies: - agent-base: ^6.0.2 - debug: ^4.3.3 - socks: ^2.6.2 - checksum: 720554370154cbc979e2e9ce6a6ec6ced205d02757d8f5d93fe95adae454fc187a5cbfc6b022afab850a5ce9b4c7d73e0f98e381879cf45f66317a4895953846 + agent-base: ^7.0.2 + debug: ^4.3.4 + socks: ^2.7.1 + checksum: 4fb165df08f1f380881dcd887b3cdfdc1aba3797c76c1e9f51d29048be6e494c5b06d68e7aea2e23df4572428f27a3ec22b3d7c75c570c5346507433899a4b6d languageName: node linkType: hard -"socks@npm:^2.6.2": +"socks@npm:^2.7.1": version: 2.7.1 resolution: "socks@npm:2.7.1" dependencies: @@ -4230,13 +4128,13 @@ __metadata: linkType: hard "std-env@npm:^3.3.3": - version: 3.4.3 - resolution: "std-env@npm:3.4.3" - checksum: bef186fb2baddda31911234b1e58fa18f181eb6930616aaec3b54f6d5db65f2da5daaa5f3b326b98445a7d50ca81d6fe8809ab4ebab85ecbe4a802f1b40921bf + version: 3.5.0 + resolution: "std-env@npm:3.5.0" + checksum: 8eba87eab2d6933e0575f13a65a359952a2e3e8c4d24eb55beac5500fe0403b3482c7b59a5de8d035ae13d390c76dd6c677772f9d2a89ea7cf39ae267b71bdd3 languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -4308,15 +4206,6 @@ __metadata: languageName: node linkType: hard -"string_decoder@npm:^1.1.1": - version: 1.3.0 - resolution: "string_decoder@npm:1.3.0" - dependencies: - safe-buffer: ~5.2.0 - checksum: 8417646695a66e73aefc4420eb3b84cc9ffd89572861fe004e6aeb13c7bc00e2f616247505d2dbbef24247c372f70268f594af7126f43548565c68c117bdeb56 - languageName: node - linkType: hard - "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -4631,9 +4520,9 @@ __metadata: linkType: hard "ufo@npm:^1.3.0": - version: 1.3.1 - resolution: "ufo@npm:1.3.1" - checksum: 2db2f9d24e3f572ddb9b2f4415eda679fd366cbb9eec4c56996651323737f17528b4aab2bb45c5f2effff2304f9b0c46e0981aee3e48f38ac51106a8993dff31 + version: 1.3.2 + resolution: "ufo@npm:1.3.2" + checksum: f1180bb715ff4dd46152fd4dec41c731e84d7b9eaf1432548a0210b2f7e0cd29de125ac88e582c6a079d8ae5bc9ab04ef2bdbafe125086480b10c1006b81bfce languageName: node linkType: hard @@ -4649,6 +4538,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -4685,13 +4581,6 @@ __metadata: languageName: node linkType: hard -"util-deprecate@npm:^1.0.1": - version: 1.0.2 - resolution: "util-deprecate@npm:1.0.2" - checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 - languageName: node - linkType: hard - "vite-node@npm:0.34.6": version: 0.34.6 resolution: "vite-node@npm:0.34.6" @@ -4709,13 +4598,13 @@ __metadata: linkType: hard "vite@npm:^3.0.0 || ^4.0.0 || ^5.0.0-0, vite@npm:^3.1.0 || ^4.0.0 || ^5.0.0-0": - version: 5.0.0-beta.13 - resolution: "vite@npm:5.0.0-beta.13" + version: 5.0.0 + resolution: "vite@npm:5.0.0" dependencies: esbuild: ^0.19.3 fsevents: ~2.3.3 postcss: ^8.4.31 - rollup: ^4.1.4 + rollup: ^4.2.0 peerDependencies: "@types/node": ^18.0.0 || >=20.0.0 less: "*" @@ -4744,7 +4633,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 92b940f126a984659e5b7200b4728023cd8dfcee1d7b1b8b6da92129a8b3d4374b33db99b2e81cddaa4e98a9f2ea7287a7824b39053d2ed295681bbbdf913211 + checksum: 1f953b062593b072f0e718384e1ff3307b548235ff8c4016fcaa85c09568eb0ba8cd8cfd80e99d940d3bea296b4661b1d0384fe5cb9a996d3e935feb69259755 languageName: node linkType: hard @@ -4871,20 +4760,20 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.9": - version: 1.1.11 - resolution: "which-typed-array@npm:1.1.11" +"which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.9": + version: 1.1.13 + resolution: "which-typed-array@npm:1.1.13" dependencies: available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 + call-bind: ^1.0.4 for-each: ^0.3.3 gopd: ^1.0.1 has-tostringtag: ^1.0.0 - checksum: 711ffc8ef891ca6597b19539075ec3e08bb9b4c2ca1f78887e3c07a977ab91ac1421940505a197758fb5939aa9524976d0a5bbcac34d07ed6faa75cedbb17206 + checksum: 3828a0d5d72c800e369d447e54c7620742a4cc0c9baf1b5e8c17e9b6ff90d8d861a3a6dd4800f1953dbf80e5e5cec954a289e5b4a223e3bee4aeb1f8c5f33309 languageName: node linkType: hard -"which@npm:^2.0.1, which@npm:^2.0.2": +"which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" dependencies: @@ -4895,6 +4784,17 @@ __metadata: languageName: node linkType: hard +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" + dependencies: + isexe: ^3.1.1 + bin: + node-which: bin/which.js + checksum: f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651 + languageName: node + linkType: hard + "why-is-node-running@npm:^2.2.2": version: 2.2.2 resolution: "why-is-node-running@npm:2.2.2" @@ -4907,15 +4807,6 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.5": - version: 1.1.5 - resolution: "wide-align@npm:1.1.5" - dependencies: - string-width: ^1.0.2 || 2 || 3 || 4 - checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 - languageName: node - linkType: hard - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0"