import * as _ngrx_signals from '@ngrx/signals'; import { SignalStoreFeatureResult, SignalStoreFeature, Prettify, StateSignals, WritableStateSource, DeepSignal, EmptyFeatureResult } from '@ngrx/signals'; import { EntityState, EntityProps, NamedEntityState, NamedEntityProps, SelectEntityId } from '@ngrx/signals/entities'; import * as _angular_core from '@angular/core'; import { Signal, WritableSignal, Injector, EffectRef, OnDestroy, Provider } from '@angular/core'; import { Observable, Subscription } from 'rxjs'; import { CollectionViewer } from '@angular/cdk/collections'; import { Params } from '@angular/router'; import * as _ngrx_traits_signals from '@ngrx-traits/signals'; type CallStatus$1 = 'init' | 'loading' | 'loaded' | { error: unknown; }; type CallStatusState = { callStatus: CallStatus$1; }; type CallStatusComputed = { isLoading: Signal; } & { isLoaded: Signal; } & { error: Signal; }; type CallStatusMethods = { setLoading: () => void; } & { setLoaded: () => void; } & { setError: (error?: Error) => void; }; type NamedCallStatusState = { [K in Prop as `${K}CallStatus`]: CallStatus$1; }; type NamedCallStatusComputed = { [K in Prop as `is${Capitalize}Loading`]: Signal; } & { [K in Prop as `is${Capitalize}Loaded`]: Signal; } & { [K in Prop as `${K}Error`]: Signal; }; type NamedCallStatusMethods = { [K in Prop as `set${Capitalize}Loading`]: () => void; } & { [K in Prop as `set${Capitalize}Loaded`]: () => void; } & { [K in Prop as `set${Capitalize}Error`]: (error?: Error) => void; }; /** * Types in this file replace the giant structural mismatch typescript reports * when a store feature is used without the features it depends on, with a * readable message like: * * Property 'Missing store feature: withEntitiesLoadingCall requires * withCallStatus({ collection: 'product' }) to be present in the store before * it' is missing in type ... * * They check if the required feature keys are already in the Input, if they * are, the real requirement is returned, so the store is type checked as * before, if they are not, a feature result with a single method named after * the error message is returned, that method can never exist in the store, so * typescript reports it as missing and shows the message. */ /** * A feature result that can never be satisfied, its only member is named after * the error message, so typescript shows the message to the user. */ type MissingFeatureError = { state: {}; props: {}; methods: { [K in `Missing store feature: ${Message}`]: () => void; } & { __missingStoreFeature: () => void; }; }; /** * True when Input['state'] is the SignalStoreFeatureResult constraint instead of * a real store state, which happens when the feature is used inside another * generic feature, e.g. inside withFeatureFactory, there the store is only known * at the point the outer feature is added, so there is nothing to check yet. */ type IsOpaqueState = object extends State ? string extends State ? false : true : false; /** * Returns the Requirement if the store already has the state Key generated by * the required feature, or an error message naming the missing feature. */ type RequireFeature = IsOpaqueState extends true ? Requirement : [Key] extends [keyof Input['state']] ? Requirement : MissingFeatureError; /** * How the required feature config looks in the error message, e.g. * withEntities({ entity, collection: 'product' }) */ type EntitiesConfig = Collection extends '' ? '{ entity }' : `{ entity, collection: '${Collection}' }`; type CollectionConfig = Collection extends '' ? '' : `{ collection: '${Collection}' }`; /** * The state, props and methods withEntities generates for a collection. */ type EntitiesRequirement = Collection extends '' ? { state: EntityState; props: EntityProps; methods: {}; } : { state: NamedEntityState; props: NamedEntityProps; methods: {}; }; /** * The state, props and methods withCallStatus generates for a collection. */ type EntitiesCallStatusRequirement = Collection extends '' ? { state: CallStatusState; props: CallStatusComputed; methods: CallStatusMethods; } : { state: NamedCallStatusState<`${Collection}Entities`>; props: NamedCallStatusComputed<`${Collection}Entities`, Error>; methods: NamedCallStatusMethods<`${Collection}Entities`, Error>; }; /** * Requires withEntities for the given collection to be present in the store, * or fails with a readable error message. */ type RequireEntities> = RequireFeature}) to be present in the store before it`>; /** * Requires withCallStatus for the given collection to be present in the store, * or fails with a readable error message. */ type RequireEntitiesCallStatus> = RequireFeature}) to be present in the store before it`>; /** * Requires one of the withEntities*Filter features for the given collection to * be present in the store, or fails with a readable error message. */ type RequireEntitiesFilter = RequireFeature}) to be present in the store before it`>; /** * Requires one of the withEntities*Sort features for the given collection to be * present in the store, or fails with a readable error message. */ type RequireEntitiesSort = RequireFeature}) to be present in the store before it`>; /** * Requires withEntitiesSingleSelection for the given collection to be present * in the store, or fails with a readable error message. */ type RequireEntitiesSingleSelection = RequireFeature}) to be present in the store before it`>; /** * Requires withEntitiesMultiSelection for the given collection to be present in * the store, or fails with a readable error message. */ type RequireEntitiesMultiSelection = RequireFeature}) to be present in the store before it`>; type FeatureConfigFactory, FactoryConfig extends Record = Config> = Config | ((store: StoreSource) => FactoryConfig); type StoreSource = Prettify & Input['props'] & Input['methods'] & WritableStateSource>; declare function getFeatureConfig>(config: FeatureConfigFactory, store: StoreSource): Config; type ExtractStoreFeatureOutput SignalStoreFeature> = ReturnType extends SignalStoreFeature ? In & Out : never; /** * Generates necessary state, computed and methods for call progress status to the store * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.prop - The name of the property for which this represents the call status * @param configFactory.initialValue - The initial value of the call status * @param configFactory.collection - The name of the collection for which this represents the call status is an alias to prop param * @param configFactory.errorType - The type of the error * they do the same thing * * prop or collection is required * @example * const store = signalStore( * withCallStatus({ collection: 'user', }) * // other valid configurations * // withCallStatus() * // withCallStatus({ collection: 'user', initialValue: 'loading' , errorType: type()}) * ) * * // generates the following signals * store.userEntitiesCallStatus // 'init' | 'loading' | 'loaded' | { error: unknown } * // generates the following computed signals * store.isUserEntitiesLoading // boolean * store.isUserEntitiesLoaded // boolean * store.userEntitiesError // unknown | null * // generates the following methods * store.setUserEntitiesLoading // () => void * store.setUserEntitiesLoaded // () => void * store.setUserEntitiesError // (error?: unknown) => void */ declare function withCallStatus(configFactory: FeatureConfigFactory): SignalStoreFeature; props: NamedCallStatusComputed<`${Prop}Entities`, Error>; methods: NamedCallStatusMethods<`${Prop}Entities`, Error>; }>; declare function withCallStatus(configFactory: FeatureConfigFactory): SignalStoreFeature; props: NamedCallStatusComputed; methods: NamedCallStatusMethods; }>; declare function withCallStatus(configFactory?: FeatureConfigFactory): SignalStoreFeature; methods: CallStatusMethods; }>; type ObservableCall$1 = (() => Observable) | ((arg: Param) => Observable); type PromiseCall$1 = (() => Promise) | ((arg: Param) => Promise); type Call$1 = ObservableCall$1 | PromiseCall$1; type CallConfig = { /** * The main function to be called. */ call: Call$1; /** * The name of the property where the result of the call will be stored. */ resultProp: PropName; /** * Specifies how to map emissions of the call, using one of the following: * - 'switchMap': Cancels the previous call when a new one starts. * - 'concatMap': Queues calls and executes them sequentially. * - 'exhaustMap': Ignores new calls until the current one completes. * Default is exhaustMap */ mapPipe?: 'switchMap' | 'concatMap' | 'exhaustMap'; /** * default is true, if false disables automatically storing the result of the * function, and removes the generated types. */ storeResult?: boolean; /** * A default value for the result before the call is executed */ defaultResult?: NoInfer; /** * Callback function invoked on successful completion of the call. * Receives the result of the call and the parameter used. */ onSuccess?: (result: NoInfer, param: NoInfer, previousResult: NoInfer | undefined) => void; /** * A function to transform an error from the call into a custom `Error` type. * Receives the error and the parameter used. */ mapError?: (error: unknown, param: NoInfer) => Error; /** * Callback function invoked if the call encounters an error. * Receives the mapped error and the parameter used. */ onError?: (error: Error, param: NoInfer) => void; /** * A function with condition that determines whether the call should be skipped. * The function accepts the call parameter and must return a boolean | Observable. */ skipWhen?: (param: NoInfer, previousResult: NoInfer | undefined) => boolean | Promise | Observable; /** * Reactively execute the call with the provided params. * Supports the following: * - A direct parameter value. Which execute the call once on init. * - A function or `Observable` emitting the parameter of the call or undefined. * - A function returning the parameter or undefined. * * **Warning**: By default, when withCall is a function, signal * or observable that when returns a falsy value it will skip the call. * To override this behavior, define a skipWhen with your own rule or skipWhen: () => false * to always execute on any value. */ callWith?: Param extends undefined ? Observable | (() => boolean) | boolean : NoInfer | null | undefined | Observable> | (() => NoInfer | null | undefined); }; type ExtractCallResultPropName = T extends CallConfig ? T['storeResult'] extends false ? never : T['resultProp'] extends '' ? `${K & string}Result` : T['resultProp'] & string : `${K & string}Result`; type ExtractCallResultType = T extends Call$1 ? R | undefined : T extends CallConfig ? D extends undefined ? R | undefined : D : never; type ExtractErrorType = T extends CallConfig ? E : unknown; type NamedCallsStatusComputed> = { [K in keyof Calls as K extends `_${infer J}` ? `_is${Capitalize}Loading` : `is${Capitalize}Loading`]: Signal; } & { [K in keyof Calls as K extends `_${infer J}` ? `_is${Capitalize}Loaded` : `is${Capitalize}Loaded`]: Signal; } & { [K in keyof Calls as `${K & string}Error`]: Calls[K] extends CallConfig ? Signal : Signal; }; type RxMethodRef = { destroy: () => void; }; type EntitiesFilterState = { entitiesFilter: Filter; }; type EntitiesFilterComputed = { isEntitiesFilterChanged: Signal; }; type NamedEntitiesFilterState = { [K in Collection as `${K}EntitiesFilter`]: Filter; }; type NamedEntitiesFilterComputed = { [K in Collection as `is${Capitalize}EntitiesFilterChanged`]: Signal; }; type FilterOptions = Filter | { filter: Filter; debounce?: number; patch?: false | undefined; forceLoad?: boolean; } | { filter: Partial; debounce?: number; patch: true; forceLoad?: boolean; }; type EntitiesFilterMethods = { filterEntities: { (options?: (() => FilterOptions) | Observable>): RxMethodRef; (options?: FilterOptions): Promise<{ value: Signal; ok: true; } | { error: Signal; ok: false; }>; }; resetEntitiesFilter: (options?: { newDefaultFilter?: Filter; debounce?: number; forceLoad?: boolean; skipLoadingCall?: boolean; }) => void; }; type NamedEntitiesFilterMethods = { [K in Collection as `filter${Capitalize}Entities`]: { (options?: (() => FilterOptions) | Observable>): RxMethodRef; (options?: FilterOptions): Promise<{ value: Signal; ok: true; } | { error: Signal; ok: false; }>; }; } & { [K in Collection as `reset${Capitalize}EntitiesFilter`]: (options?: { newDefaultFilter?: Filter; debounce?: number; forceLoad?: boolean; skipLoadingCall?: boolean; }) => void; }; /** * Generates necessary state, computed and methods for locally filtering entities in the store, * the generated filter[Collection]Entities method will filter the entities based on the filter function * and is debounced by default. * * Requires withEntities to be used. * * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.filterFn - The function that will be used to filter the entities * @param configFactory.defaultFilter - The default filter to be used * @param configFactory.defaultDebounce - The default debounce time to be used, if not set it will default to 300ms * @param configFactory.entity - The entity type to be used * @param configFactory.collection - The optional collection name to be used * @param configFactory.selectId - The function to use to select the id of the entity * * @example * const entity = type(); * const collection = 'product'; * const store = signalStore( * { providedIn: 'root' }, * // requires withEntities to be used * withEntities({ entity, collection }), * * withEntitiesLocalFilter({ * entity, * collection, * defaultFilter: { search: '' }, * filterFn: (entity, filter) => * !filter?.search || // if there is no search term return all entities * entity?.name.toLowerCase().includes(filter?.search.toLowerCase()), * }), * ); * * // generates the following signals * store.productEntitiesFilter // { search: string } * // generates the following methods * store.filterProductEntities // (options: { filter: { search: string }, debounce?: number, patch?: boolean, forceLoad?: boolean }) => void * store.resetProductEntitiesFilter // (options?: { newDefaultFilter?: { search: string } }) => void — resets to defaultFilter or to newDefaultFilter if provided, updating the default for future resets */ declare function withEntitiesLocalFilter, Collection extends string = ''>(configFactory: FeatureConfigFactory boolean; defaultFilter: Filter; defaultDebounce?: number; entity: Entity; collection?: Collection; selectId?: SelectEntityId; }>): SignalStoreFeature, Collection extends '' ? { state: EntitiesFilterState; props: EntitiesFilterComputed; methods: EntitiesFilterMethods; } : { state: NamedEntitiesFilterState; props: NamedEntitiesFilterComputed; methods: NamedEntitiesFilterMethods; }>; type EntitiesRemoteFilterMethods = { filterEntities: { (options?: Observable & { skipLoadingCall?: boolean; }> | (() => FilterOptions)): RxMethodRef; (options?: FilterOptions & { skipLoadingCall?: boolean; }): Promise<{ value: Signal; ok: true; } | { error: Signal; ok: false; }>; }; resetEntitiesFilter: (options?: { newDefaultFilter?: Filter; debounce?: number; forceLoad?: boolean; skipLoadingCall?: boolean; }) => void; }; type NamedEntitiesRemoteFilterMethods = { [K in Collection as `filter${Capitalize}Entities`]: { (options?: Observable & { skipLoadingCall?: boolean; }> | (() => FilterOptions)): RxMethodRef; (options?: FilterOptions & { skipLoadingCall?: boolean; }): Promise<{ value: Signal; ok: true; } | { error: Signal; ok: false; }>; }; } & { [K in Collection as `reset${Capitalize}EntitiesFilter`]: (options?: { newDefaultFilter?: Filter; debounce?: number; forceLoad?: boolean; skipLoadingCall?: boolean; }) => void; }; /** * Generates necessary state, computed and methods for remotely filtering entities in the store, * the generated filter[Collection]Entities method will filter the entities by calling set[Collection]Loading() * and you should either create an effect that listens to [Collection]Loading can call the api with the [Collection]Filter params * or use withEntitiesLoadingCall to call the api with the [Collection]Filter params. * filter[Collection]Entities is debounced by default, you can change the debounce by using the debounce option filter[Collection]Entities or changing the defaultDebounce prop in the config. * * In case you dont want filter[Collection]Entities to call set[Collection]Loading() (which triggers a fetchEntities), you can pass skipLoadingCall: true to filter[Collection]Entities. * Useful in cases where you want to further change the state before manually calling set[Collection]Loading() to trigger a fetch of entities. * * Requires withEntities and withCallStatus to be present before this function. * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.defaultFilter - The default filter to be used * @param configFactory.defaultDebounce - The default debounce time to be used, if not set it will default to 300ms * @param configFactory.entity - The entity type to be used * @param configFactory.collection - The optional collection name to be used * * @example * const entity = type(); * const collection = 'product'; * export const store = signalStore( * // requires withEntities and withCallStatus to be used * withEntities({ entity, collection }), * withCallStatus({ collection, initialValue: 'loading' }), * * withEntitiesRemoteFilter({ * entity, * collection, * defaultFilter: { search: '' }, * }), * // after you can use withEntitiesLoadingCall to connect the filter to * // the api call, or do it manually as shown after * withEntitiesLoadingCall({ * collection, * fetchEntities: ({ productEntitiesFilter }) => { * return inject(ProductService) * .getProducts({ * search: productEntitiesFilter().search, * }) * }, * }), * // withEntitiesLoadingCall is the same as doing the following: * // withHooks(({ isProductEntitiesLoading, productEntitiesFilter, setProductEntitiesError, ...state }) => ({ * // onInit: async () => { * // effect(() => { * // if (isProductEntitiesLoading()) { * // inject(ProductService) * // .getProducts({ * // search: productEntitiesFilter().search, * // }) * // .pipe( * // takeUntilDestroyed(), * // tap((res) => * // patchState( * // state, * // setAllEntities(res.resultList, { collection: 'product' }), * // ), * // ), * // catchError((error) => { * // setProductEntitiesError(error); * // return EMPTY; * // }), * // ) * // .subscribe(); * // } * // }); * // }, * })), * // generates the following signals * store.productEntitiesFilter // { search: string } * // generates the following methods * store.filterProductEntities // (options: { filter: { search: string }, debounce?: number, patch?: boolean, forceLoad?: boolean, skipLoadingCall?:boolean }) => void * store.resetProductEntitiesFilter // (options?: { newDefaultFilter?: { search: string } }) => void — resets to defaultFilter or to newDefaultFilter if provided, updating the default for future resets */ declare function withEntitiesRemoteFilter, Collection extends string = ''>(configFactory: FeatureConfigFactory): SignalStoreFeature & RequireEntitiesCallStatus, Collection extends '' ? { state: EntitiesFilterState; props: EntitiesFilterComputed; methods: EntitiesRemoteFilterMethods; } : { state: NamedEntitiesFilterState; props: NamedEntitiesFilterComputed; methods: NamedEntitiesRemoteFilterMethods; }>; /** * Generates necessary state and methods to do remote and local filtering of entities in the store, * the generated filter[Collection]Entities method will filter the entities by calling set[Collection]Loading() if the isRemoteFilter returns true * and if false will call the filterFn to filter the entities locally. * * For the remote case you should either create an effect that listens to [Collection]Loading can call the api with the [Collection]Filter params * or use withEntitiesLoadingCall to call the api with the [Collection]Filter params. filter[Collection]Entities * is debounced by default, you can change the debounce by using the debounce option filter[Collection]Entities or changing the defaultDebounce prop in the config. * * In case you dont want filter[Collection]Entities to call set[Collection]Loading() (which triggers a fetchEntities), you can pass skipLoadingCall: true to filter[Collection]Entities. * Useful in cases where you want to further change the state before manually calling set[Collection]Loading() to trigger a fetch of entities. * * Requires withEntities and withCallStatus to be present before this function. * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.defaultFilter - The default filter to be used * @param configFactory.defaultDebounce - The default debounce time to be used, if not set it will default to 300ms * @param configFactory.filterFn - The function to filter the entities * @param configFactory.isRemoteFilter - The function to determine if the filter is remote or local * @param configFactory.entity - The entity type to be used * @param configFactory.collection - The optional collection name to be used * @param configFactory.selectId - The optional function to select the id of the entity * * @example * const entity = type(); * const collection = 'product'; * export const store = signalStore( * { providedIn: 'root' }, * // requires withEntities and withCallStatus to be used * withEntities({ entity, collection }), * withCallStatus({ collection, initialValue: 'loading' }), * withEntitiesHybridFilter({ * entity, * collection, * defaultFilter: { search: '' , category: ''}, * filterFn: (entity, filter) => * (!filter.search || entity.name.toLowerCase().includes(filter.search.toLowerCase())) * // in this case the filter will call setProductEntitiesLoading() if the category changes, othewise * // it will filter the entities locally using filterFn * isRemoteFilter: (previous, current) => { * return previous.category !== current.category; * } * }), * // after you can use withEntitiesLoadingCall to connect the filter to * // the api call, or do it manually as shown after * withEntitiesLoadingCall({ * collection, * fetchEntities: ({ productEntitiesFilter }) => { * return inject(ProductService) * .getProducts({ * category: productEntitiesFilter().category, * }) * }, * }), * // withEntitiesLoadingCall is the same as doing the following: * // withHooks(({ productEntitiesCallStatus, setProductEntitiesError, ...state }) => ({ * // onInit: async () => { * // effect(() => { * // if (isProductEntitiesLoading()) { * // inject(ProductService) * // .getProducts({ * // category: productEntitiesFilter().category, * // }) * // .pipe( * // takeUntilDestroyed(), * // tap((res) => * // patchState( * // state, * // setAllEntities(res.resultList, { collection: 'product' }), * // ), * // ), * // catchError((error) => { * // setProductEntitiesError(error); * // return EMPTY; * // }), * // ) * // .subscribe(); * // } * // }); * // }, * })), * // generates the following signals * store.productEntitiesFilter // { search: string , category: string } * // generates the following methods * store.filterProductEntities // (options: { filter: { search: string, category: string }, debounce?: number, patch?: boolean, forceLoad?: boolean, skipLoadingCall?:boolean }) => void * store.resetProductEntitiesFilter // (options?: { newDefaultFilter?: { search: string, category: string } }) => void — resets to defaultFilter or to newDefaultFilter if provided, updating the default for future resets */ declare function withEntitiesHybridFilter, Collection extends string = ''>(configFactory: FeatureConfigFactory; filterFn: (entity: NoInfer, filter: NoInfer) => boolean; isRemoteFilter: (previous: NoInfer, current: NoInfer) => boolean; }>): SignalStoreFeature & RequireEntitiesCallStatus, Collection extends '' ? { state: EntitiesFilterState; props: EntitiesFilterComputed; methods: EntitiesRemoteFilterMethods; } : { state: NamedEntitiesFilterState; props: NamedEntitiesFilterComputed; methods: NamedEntitiesRemoteFilterMethods; }>; /** * Shallow, order-sensitive array equality: same length and every element * equal by `Object.is`. Use it when the source produces a fresh array on * every read (e.g. a `computed` mapping ids), so syncs are only triggered by * real changes. */ declare function equalArray(a: unknown, b: unknown): boolean; /** * Order-insensitive array equality, with set semantics: same elements * regardless of position. The size check guards against duplicates making * unequal arrays match, but the same values with different duplicate counts * (e.g. [x,x,y] vs [x,y,y]) still compare equal — on purpose, this is meant * for sources that hold a set, like an entity selection. */ declare function equalSet(a: unknown, b: unknown): boolean; /** * Structural equality by `JSON.stringify` of both values, for objects and * arrays alike. Key order matters (`{a,b}` and `{b,a}` are not equal) and * values JSON can not represent are lost, so prefer `'array'` for arrays of * primitives; this is the catch-all for nested objects. */ declare function equalStringify(a: unknown, b: unknown): boolean; /** * Equality by a single property, for values identified by an id: compares * `a[key]` and `b[key]` with `Object.is`. On arrays it compares element by * element by that same key, in order — an array of entities is equal when it * holds the same ids in the same positions, whatever else changed in them * (this is what the 'array.' name resolves to). * * Non-object values (including a null on one side only) fall back to * `Object.is`, so an optional value is handled. */ declare function equalByKey(key: string): (a: unknown, b: unknown) => boolean; /** * Order-insensitive version of `equalByKey` for arrays: the same key values * regardless of position, with the set semantics of `equalSet` (this is what * the 'set.' name resolves to). */ declare function equalSetBy(key: string): (a: unknown, b: unknown) => boolean; /** * The default comparison, chosen from the values at hand rather than fixed up * front: `Object.is` for primitives, element by element for arrays, and * structural for plain objects. * * A link is a two-way binding, so reference equality is the wrong default for * anything but a primitive: a `readFrom` that rebuilds an object out of the * state it reads (picking a few props off a bigger one) produces a fresh * reference every run, is never equal to its own previous value, and every * write it makes re-triggers the read — the link never settles. Comparing by * content is what stops that, so it is what happens unless a comparison is * asked for by name. Pass `'reference'` to get `Object.is` back. * * Arrays are compared with `equalArray` and never serialized, so a list stays * cheap however long it is — an array whose elements are rebuilt on every * read is the one shape this does not settle on its own, and wants an * explicit `equal` ('stringify', or 'array.' to compare by id). * * Structural comparison is JSON-based, with the caveats of `equalStringify`, * and only applies to plain objects: a `Date`, `Map`, `Set` or class instance * falls back to `Object.is`, since JSON flattens those to a shape that would * call two different values equal and silently drop an update. * * That check is on the value itself, not on what it contains — one of those * nested inside a plain object is still flattened, and two different ones * compare equal. Non-serializable values in store state are discouraged for * other reasons too; a link over one wants an `equal` of its own. */ declare function equalAuto(a: unknown, b: unknown): boolean; /** Properties of `T`, when it is an object that is not an array. */ type PropertyName = T extends readonly any[] ? never : T extends object ? keyof NonNullable & string : never; /** * Properties of the elements of `T`, prefixed with the comparison to run over * the array: 'array.id' compares element by element in order, 'set.id' * compares the ids regardless of order. */ type ElementPropertyName = T extends readonly (infer E)[] ? E extends object ? `array.${keyof NonNullable & string}` | `set.${keyof NonNullable & string}` : never : never; /** * Premade equality names accepted wherever a custom `equal` can be given: * - 'reference': `Object.is`, the escape hatch from the content-based default * - 'array': shallow, order-sensitive element comparison * - 'set': order-insensitive element comparison, for selections * - 'stringify': structural comparison via JSON.stringify * - a property name of the value, e.g. 'id': comparison by that property * - 'array.' / 'set.' on an array of objects: comparison of the * elements by that property, in order or as a set * * The array ones are only offered when the value is an array, the property * names when it is an object, the prefixed ones when it is an array of them. * * `unknown extends T` short-circuits all of that while `T` is still * unresolved, which is the case on the `computation` form of `withLink`: the * value type comes from a context-sensitive callback, typed only on a later * inference pass, while `equal` is a plain string checked on the first one. * Narrowing against the empty `T` of that first pass pins it to `unknown` for * good — the value type collapses and every name is rejected. Accepting any * name there costs nothing: `T` is known by the time the argument is checked, * so a wrong name or one that does not fit the value is still an error. */ type EqualName = unknown extends T ? string : 'stringify' | 'reference' | (T extends readonly any[] ? 'array' | 'set' : never) | PropertyName | ElementPropertyName; /** A custom equality function, or the name of a premade one. */ type EqualOption = ((a: T, b: T) => boolean) | EqualName; /** * Resolves an `equal` option to a function, defaulting to `equalAuto`, which * compares by content rather than by reference — pass `'reference'` for * `Object.is`. * * A name that is not one of the premade comparisons is a property name, * optionally prefixed with 'array.' or 'set.', so those four win over a * property called 'array', 'set', 'stringify' or 'reference' — pass * `equalByKey('stringify')` for one of those. */ declare function resolveEqual(equal?: EqualOption): (a: T, b: T) => boolean; type LinkCommonOptions = { /** * Gates which writes reach the store. When provided, the returned signal * becomes a `linkedSignal` buffer over the source: writes are kept locally * and only pushed to the store when `updateStoreWhen(value)` returns true. * * It runs inside an effect, so it is reactive: a value held back while it * returned false is pushed as soon as its dependencies make it true * (e.g. a form becoming valid). * * Requires an injection context, since an effect is created. */ updateStoreWhen?: (value: T) => boolean; }; /** * Options of the generated `link()` method. * * `syncWith` is mutually exclusive with `readFrom`/`writeTo`: use `syncWith` * when the external signal already matches the store type, or the * `readFrom` + `writeTo` pair when each direction needs its own mapping. */ type LinkOptions = (LinkCommonOptions & { /** * External signal kept in sync with the store both ways: writing it * updates the store, and store changes are written back to it. * Requires a WritableSignal (e.g. `model()`). */ syncWith: WritableSignal; readFrom?: never; writeTo?: never; /** * Where the value that wins on link comes from. * - 'external' (default): the signal's current value is pushed to the store. * - 'store': the store value is written to the signal. */ initialValueFrom?: 'store' | 'external'; }) | (LinkCommonOptions & { /** * External signal the store only reads from: its value is pushed to the * store, and store changes are never written back. Accepts any signal, * including a writable one whose writes you drive yourself * (e.g. a `model()` only written on a button click), or a `computed` * that maps an external model to the store type. * * Also accepts a function that receives the previous linked value (the * store value, or the buffer when `updateStoreWhen` is used) so a partial * external signal can be merged into it, e.g. * `(prev) => ({ ...prev, search: this.search() })`. The signals it * reads are tracked; the previous value is not, so store changes alone * do not re-run it. */ readFrom?: Signal | ((prev: T) => T); /** * Where store changes are pushed: a WritableSignal that is set, or a * function called with the new value — use it to map back to an * external model's type, or to emit an output. Only changes after link * are pushed, the value at link time is not. * * Combine with `readFrom` for a two-way sync with a mapping in each * direction. Cannot be combined with `syncWith`. */ writeTo?: WritableSignal | ((value: T) => void); syncWith?: never; initialValueFrom?: never; }); /** The `link()` method generated by `withLink`. */ type LinkMethod = (options?: LinkOptions) => WritableSignal; /** * The `_set()` method generated by `withLink`, the same write path the * linked signal uses (the `set` callback, or `patchState` by default), * exposed as a store method. The `_` prefix makes it private to the store, so * other features and methods can write through it, but consumers cannot. * * Like the setters of `withStateSetter`, it is a `signalMethod`: it accepts a * plain value, a signal/reactive fn (keeping the store in sync with it), or an * updater `(current) => next` receiving the current value, for partial updates. */ type LinkSetter = ((input: T | (() => T) | ((current: T) => T), config?: { injector?: Injector; }) => EffectRef) & EffectRef; /** * Methods generated besides `link()`: the private setter, unless * `noSetter: true` was passed. */ type LinkSetterMethods = NoSetter extends true ? {} : { [P in `_set${Capitalize}`]: LinkSetter; }; type LinkSourceOptions = { set?: (value: Input['state'][NoInfer], store: StoreSource) => void; /** * Equality used to suppress redundant syncs in both directions. * * Defaults to comparing by content, chosen from the value at hand: * `Object.is` for primitives, element by element for arrays, and * structurally (JSON.stringify) for plain objects. Reference equality is * the wrong default for a two-way link — a source that rebuilds an object * on every read is never equal to its own previous value, so every write * re-triggers the read and the link never settles. * * The structural comparison is JSON-based, so a `Date`, `Map`, `Set` or * class instance nested in the value is flattened and two different ones * can compare equal, dropping the update. Keeping non-serializable values * in store state is discouraged anyway; if you do, pass an `equal` that * knows how to compare them, or 'reference'. * * Override with a function, or the name of a premade one — 'reference' * (`Object.is`, to opt out of the content comparison), 'array' (shallow, * order sensitive), 'set' (order insensitive, both only offered for * arrays), 'stringify' (JSON.stringify, works on objects and arrays, and * the one to reach for when an array holds objects rebuilt on every read), * a property of the value to compare by, e.g. 'id', or 'array.id' / * 'set.id' to compare the elements of an array by one of their properties. */ equal?: EqualOption]>; /** * Skip generating the private `_set()` method, when the store has no * use for it — e.g. it already exposes its own method for the same write. */ noSetter?: NoSetter; computation?: never; }; type LinkComputedOptions = { computation: (store: StoreSource) => T; set: (value: T, store: StoreSource) => void; /** * Equality used to suppress redundant syncs in both directions. * * Defaults to comparing by content, chosen from the value at hand: * `Object.is` for primitives, element by element for arrays, and * structurally (JSON.stringify) for plain objects. Reference equality is * the wrong default for a two-way link — a source that rebuilds an object * on every read is never equal to its own previous value, so every write * re-triggers the read and the link never settles. * * The structural comparison is JSON-based, so a `Date`, `Map`, `Set` or * class instance nested in the value is flattened and two different ones * can compare equal, dropping the update. Keeping non-serializable values * in store state is discouraged anyway; if you do, pass an `equal` that * knows how to compare them, or 'reference'. * * Override with a function, or the name of a premade one — 'reference' * (`Object.is`, to opt out of the content comparison), 'array' (shallow, * order sensitive), 'set' (order insensitive, both only offered for * arrays), 'stringify' (JSON.stringify, works on objects and arrays, and * the one to reach for when an array holds objects rebuilt on every read), * a property of the value to compare by, e.g. 'id', or 'array.id' / * 'set.id' to compare the elements of an array by one of their properties. */ equal?: EqualOption>; /** * Skip generating the private `_set()` method, when the store has no * use for it — e.g. it already exposes its own method for the same write. */ noSetter?: NoSetter; }; /** * Generates a `link()` method that connects store state to component * signals (inputs, models, signal forms), plus a `_set()` method — the * same write path, private to the store, for other features and methods to * write through (see `LinkSetter`), unless `noSetter: true` is passed. * * The first argument names the generated method and doubles as the state key * to link to, unless `computation` is provided in the options — then it is * just a name and the value is derived from the store. * * The method always returns a WritableSignal that is a live view of the store: * reading it reads the source, writing it calls `set` (or `patchState` by * default). It takes an options object that can also connect an external * signal, in one of three ways: * - `syncWith`: two-way, requires a WritableSignal (e.g. `model()`). * - `readFrom`: one-way external → store, accepts any signal — including a * writable one you only write yourself (e.g. a `model()` set by a button) — * or a function receiving the previous value, to merge a partial signal in. * - `writeTo`: one-way store → external, a WritableSignal that is set or a * function called with each committed change (e.g. an `output` emit). * * `readFrom` and `writeTo` combine into a two-way sync with a mapping in each * direction (e.g. a `model()` whose type differs from the store's); `syncWith` * is mutually exclusive with both, and `initialValueFrom` only applies to it. * * Both sync directions are guarded by `equal`, so writes only happen when the * value actually changed — this prevents echo loops when `set` transforms the * value. It defaults to comparing by content, chosen from the value at hand: * `Object.is` for primitives, element by element for arrays, and structurally * for plain objects. Reference equality is the wrong default for a two-way * link, since a source that rebuilds an object on every read is never equal * to its own previous value and the link never settles. * * Besides a function, `equal` accepts the name of a premade comparison: * `'reference'` (`Object.is`, to opt out of the content comparison), * `'array'` (shallow, order sensitive), `'set'` (order insensitive — both * only offered when the value is an array), `'stringify'` (JSON.stringify, * for objects and arrays), a property of the value to compare by, or * `'array.'` / `'set.'` to compare the elements of an array by * one of their properties. * * `syncWith`, `readFrom`, `writeTo` and `updateStoreWhen` each require an injection context * (field initializer or constructor), because effects are created to keep * things in sync. The plain no-arg form has no such requirement. * * @param name - State key to link to, or a custom name when `computation` is used * @param options.computation - Derive the linked value from the store * @param options.set - How writes reach the store; defaults to * `patchState(store, { [name]: value })`, required with `computation` * @param options.equal - Equality guard for both sync directions, a function, * a premade name ('array', 'set', 'stringify'), a property to compare by * ('id'), or the same per element of an array ('array.id', 'set.id') * @param options.noSetter - Skip generating the private `_set()` method * * @example * // State key - generates linkFilter(), writes patch state * const Store = signalStore( * withState({ filter: { search: '' } }), * withLink('filter'), * ); * // in a component: * // filterForm = form(this.store.linkFilter()); * * @example * // State key with a custom write * withLink('productEntitiesFilter', { * set: (value, store) => store.filterProductEntities({ filter: value }), * }); * * @example * // Custom name with computation + set, synced two-way with a model() input * withLink('selectedGenreIds', { * computation: (store) => store.genreIdsSelected(), * set: (value, store) => * store.selectGenreEntities({ ids: value, clearSelectionBeforeSelect: true }), * // premade equality; a selection is a set, so order does not matter * equal: 'set', * }); * // in a component: * // value = model([]); * // valueField = form(this.store.linkSelectedGenreIds({ syncWith: this.value })); * * @example * // Premade equality for an object state that is rebuilt on every read * withLink('filter', { equal: 'stringify' }); * * @example * // Compare by a property, autocompleted from the linked value's type: the * // selected product only counts as changed when its id does * withLink('selectedProduct', { equal: 'id' }); * * @example * // Same, per element of an array: 'array.id' keeps the order significant, * // 'set.id' does not * withLink('products', { equal: 'array.id' }); * * @example * // One way: the store reads the model, writes to it stay local until the * // button pushes them * // draft = model(''); * // linked = this.store.linkFilter({ readFrom: this.draft }); * * @example * // Merge a partial signal into the previous value: only search is external, * // other filter keys are left as they are * // search = input(''); // store state is { search: string; category: string } * // linked = this.store.linkFilter({ * // readFrom: (prev) => ({ ...prev, search: this.search() }), * // }); * * @example * // Two-way with a model of a different type: map in with a computed, * // map back out with a function * // search = model(''); // store state is { search: string } * // linked = this.store.linkFilter({ * // readFrom: computed(() => ({ search: this.search() })), * // writeTo: (value) => this.search.set(value.search), * // }); * * @example * // Emit committed changes as an output * // filterChange = output<{ search: string }>(); * // linked = this.store.linkFilter({ * // writeTo: (value) => this.filterChange.emit(value), * // }); * * @example * // The private setter, for use inside the store * const Store = signalStore( * withState({ filter: { search: '', category: '' } }), * withLink('filter'), * withMethods((store) => ({ * // value, updater for a partial change, or a signal to stay in sync with * search: (search: string) => store._setFilter((f) => ({ ...f, search })), * })), * ); * * @example * // Buffered writes: only valid form data reaches the store * // filterForm = form(this.formData, (value) => required(value.search)); * // formData = this.store.linkFilter({ * // // annotated because filterForm is declared below * // updateStoreWhen: (): boolean => this.filterForm().valid(), * // }); */ declare function withLink(source: K, options?: LinkSourceOptions): SignalStoreFeature}`]: LinkMethod; } & LinkSetterMethods; }>; declare function withLink(name: N, options: LinkComputedOptions): SignalStoreFeature}`]: LinkMethod; } & LinkSetterMethods; }>; type ExtractFilter = Collection extends '' ? State extends { entitiesFilter: infer F; } ? F : Record : State extends { [K in `${Collection}EntitiesFilter`]: infer F; } ? F : Record; /** * Generates a `link[Collection]EntitiesFilter()` method that connects the * entities filter to component signals (inputs, models, signal forms). * * Prebuilt version of `withLink` for `withEntitiesLocalFilter` / * `withEntitiesRemoteFilter` / `withEntitiesHybridFilter`: writes route through * `filter[Collection]Entities` (so filtering and its debounce still happen). * Echo loops are prevented because the filter features patch the filter value * by reference, so the link method's default `Object.is` guard converges, and * `filter[Collection]Entities` itself drops structurally-equal filters. * * Requires one of the withEntities*Filter features to be used before it. * * @param config - The configuration object for the feature * @param config.entity - The entity type to be used * @param config.collection - The optional collection name to be used * @param config.debounce - Debounce passed to filter[Collection]Entities on * each sync; defaults to 0 to respect signal semantics, and so user can use the signalForm field debounce * @param config.forceLoad - forceLoad passed to filter[Collection]Entities * * @example * const entity = type(); * const store = signalStore( * withEntities({ entity }), * withEntitiesLocalFilter({ * entity, * defaultFilter: { search: '' }, * filterFn: (entity, filter) => * !filter?.search || entity?.name.toLowerCase().includes(filter?.search.toLowerCase()), * }), * withLinkEntitiesFilter({ entity }), * ); * // in a component: * // filterForm = form(this.store.linkEntitiesFilter()); */ declare function withLinkEntitiesFilter(config?: { entity?: Entity; collection?: Collection; debounce?: number; forceLoad?: boolean; }): SignalStoreFeature>; props: {}; methods: {}; } : { state: NamedEntitiesFilterState>; props: {}; methods: {}; }>, { state: {}; props: {}; methods: { [P in Collection extends '' ? 'entitiesFilter' : `${Collection}EntitiesFilter` as `link${Capitalize}`]: LinkMethod>; }; }>; type EntitiesPaginationLocalState = { entitiesPagination: { currentPage: number; pageSize: number; }; }; type NamedEntitiesPaginationLocalState = { [K in Collection as `${K}EntitiesPagination`]: { currentPage: number; pageSize: number; }; }; type EntitiesPaginationLocalComputed = { entitiesCurrentPage: DeepSignal<{ entities: Entity[]; pageIndex: number; total: number | undefined; pageSize: number; pagesCount: number | undefined; hasPrevious: boolean; hasNext: boolean; }>; }; type NamedEntitiesPaginationLocalComputed = { [K in Collection as `${K}EntitiesCurrentPage`]: DeepSignal<{ entities: Entity[]; pageIndex: number; total: number | undefined; pageSize: number; pagesCount: number | undefined; hasPrevious: boolean; hasNext: boolean; }>; }; type EntitiesPaginationLocalMethods = { loadEntitiesPage: (options: { pageIndex: number; pageSize?: number; }) => void; }; type NamedEntitiesPaginationLocalMethods = { [K in Collection as `load${Capitalize}EntitiesPage`]: (options: { pageIndex: number; pageSize?: number; }) => void; }; type SetEntitiesResult = { setEntitiesPagedResult: (result: ResultParam) => void; }; type NamedSetEntitiesResult = { [K in Collection as `set${Capitalize}EntitiesPagedResult`]: (result: ResultParam) => void; }; /** * Generates necessary state, computed and methods for local pagination of entities in the store. * * Requires withEntities to be present in the store. * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.pageSize - The number of entities to show per page * @param configFactory.currentPage - The current page to show * @param configFactory.entity - The entity type * @param configFactory.collection - The name of the collection * * @example * const entity = type(); * const collection = "product"; * export const ProductsLocalStore = signalStore( * { providedIn: 'root' }, * // required withEntities * withEntities({ entity, collection }), * withEntitiesLocalPagination({ * entity, * collection, * pageSize: 5, * }), * * // generates the following signals * store.productEntitiesPagination // { currentPage: 0, pageSize: 5 } * // generates the following computed signals * store.productEntitiesCurrentPage // { entities: Product[], pageIndex: 0, total: 10, pageSize: 5, pagesCount: 2, hasPrevious: false, hasNext: true } * // generates the following methods * store.loadProductEntitiesPage // ({ pageIndex: number }) => void */ declare function withEntitiesLocalPagination(configFactory: FeatureConfigFactory): SignalStoreFeature, Collection extends '' ? { state: EntitiesPaginationLocalState; props: EntitiesPaginationLocalComputed; methods: EntitiesPaginationLocalMethods; } : { state: NamedEntitiesPaginationLocalState; props: NamedEntitiesPaginationLocalComputed; methods: NamedEntitiesPaginationLocalMethods; }>; type PaginationState = { currentPage: number; requestPage: number; pageSize: number; total: number; pagesToCache: number; cache: { start: number; end: number; }; }; type EntitiesPaginationRemoteState = { entitiesPagination: PaginationState; }; type NamedEntitiesPaginationRemoteState = { [K in Collection as `${K}EntitiesPagination`]: PaginationState; }; type EntitiesPaginationRemoteComputed = { entitiesCurrentPage: DeepSignal<{ entities: Entity[]; pageIndex: number; total: number | undefined; pageSize: number; pagesCount: number | undefined; hasPrevious: boolean; hasNext: boolean; isLoading: boolean; }>; entitiesPagedRequest: DeepSignal<{ startIndex: number; size: number; page: number; }>; }; type NamedEntitiesPaginationRemoteComputed = { [K in Collection as `${K}EntitiesPagedRequest`]: DeepSignal<{ startIndex: number; size: number; page: number; }>; } & { [K in Collection as `${K}EntitiesCurrentPage`]: DeepSignal<{ entities: Entity[]; pageIndex: number; total: number | undefined; pageSize: number; pagesCount: number | undefined; hasPrevious: boolean; hasNext: boolean; isLoading: boolean; }>; }; type EntitiesPaginationRemoteMethods = { loadEntitiesPage: (options: { pageIndex: number; pageSize?: number; skipLoadingCall?: boolean; forceLoad?: boolean; }) => void; } & SetEntitiesResult<{ entities: Entity[]; total: number; }>; type NamedEntitiesPaginationRemoteMethods = { [K in Collection as `load${Capitalize}EntitiesPage`]: (options: { pageIndex: number; pageSize?: number; skipLoadingCall?: boolean; forceLoad?: boolean; }) => void; } & NamedSetEntitiesResult; /** * Generates necessary state, computed and methods for remote pagination of entities in the store. * Call load[Collection]Page to change the page, it will try to load the new page from cache if it's not present, * it will call set[Collection]Loading(), and you should either create an effect that listens to [Collection]Loading * and call the api with the [Collection]PagedRequest params and use set[Collection]Result to set the result * and changing the status errors manually, * or use withEntitiesLoadingCall to call the api with the [Collection]PagedRequest params which handles setting * the result and errors automatically. * * In case you dont want load[Collection]Page to call set[Collection]Loading() (which triggers a fetchEntities), you can pass skipLoadingCall: true to load[Collection]Page. * Useful in cases where you want to further change the state before manually calling set[Collection]Loading() to trigger a fetch of entities. * * This will keep at least the provided (pagesToCache) pages in memory, so previous pages could be removed from the cache. * If you need to keep all previous pages in memory, use withEntitiesRemoteScrollPagination instead. * * Requires withEntities and withCallStatus to be present in the store. * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.pageSize - The number of entities to show per page * @param configFactory.currentPage - The current page to show * @param configFactory.pagesToCache - The number of pages to cache * @param configFactory.entity - The entity type * @param configFactory.collection - The name of the collection * @param configFactory.selectId - The function to use to select the id of the entity * * @example * const entity = type(); * const collection = "product"; * export const store = signalStore( * { providedIn: 'root' }, * // required withEntities and withCallStatus * withEntities({ entity, collection }), * withCallStatus({ prop: collection, initialValue: 'loading' }), * * withEntitiesRemotePagination({ * entity, * collection, * pageSize: 5, * pagesToCache: 2, * }) * // after you can use withEntitiesLoadingCall to connect the filter to * // the api call, or do it manually as shown after * withEntitiesLoadingCall({ * collection, * fetchEntities: ({ productPagedRequest }) => { * return inject(ProductService) * .getProducts({ * take: productPagedRequest().size, * skip: productPagedRequest().startIndex, * }).pipe( * map((d) => ({ * entities: d.resultList, * total: d.total, * })), * ) * }, * }), * // withEntitiesLoadingCall is the same as doing the following: * // withHooks(({ productsLoading, setProductEntitiesError, setProductEntitiesPagedResult, ...state }) => ({ * // onInit: async () => { * // effect(() => { * // if (isProductEntitiesLoading()) { * // inject(ProductService) * // .getProducts({ * // take: productPagedRequest().size, * // skip: productPagedRequest().startIndex, * // }) * // .pipe( * // takeUntilDestroyed(), * // tap((res) => * // patchState( * // state, * // setProductEntitiesPagedResult({ entities: res.resultList, total: res.total } ), * // ), * // ), * // catchError((error) => { * // setProductEntitiesError(error); * // return EMPTY; * // }), * // ) * // .subscribe(); * // } * // }); * // }, * })), * // generates the following signals * store.productEntitiesPagination // { currentPage: number, requestPage: number, pageSize: 5, total: number, pagesToCache: number, cache: { start: number, end: number } } used internally * // generates the following computed signals * store.productEntitiesCurrentPage // { entities: Product[], pageIndex: number, total: number, pageSize: 5, pagesCount: number, hasPrevious: boolean, hasNext: boolean, isLoading: boolean } * store.productPagedRequest // { startIndex: number, size: number, page: number } * // generates the following methods * store.loadProductEntitiesPage({ pageIndex: number, forceLoad?: boolean, skipLoadingCall?:boolean }) // loads the page and sets the requestPage to the pageIndex * store.setProductEntitiesPagedResult(entities: Product[], total: number) // appends the entities to the cache of entities and total */ declare function withEntitiesRemotePagination(configFactory: FeatureConfigFactory; }>): SignalStoreFeature & RequireEntitiesCallStatus, Collection extends '' ? { state: EntitiesPaginationRemoteState; props: EntitiesPaginationRemoteComputed; methods: EntitiesPaginationRemoteMethods; } : { state: NamedEntitiesPaginationRemoteState; props: NamedEntitiesPaginationRemoteComputed; methods: NamedEntitiesPaginationRemoteMethods; }>; type ScrollPaginationState = { hasMore: boolean; pageSize: number; pagesToCache: number; currentPage: number; requestPage: number; }; type EntitiesScrollPaginationState = { pagination: ScrollPaginationState; }; type NamedEntitiesScrollPaginationState = { [K in Collection as `${K}EntitiesPagination`]: ScrollPaginationState; }; type EntitiesScrollPaginationComputed = { entitiesCurrentPage: DeepSignal<{ entities: Entity[]; pageIndex: number; pageSize: number; hasPrevious: boolean; hasNext: boolean; isLoading: boolean; }>; entitiesPagedRequest: DeepSignal<{ startIndex: number; size: number; }>; }; type NamedEntitiesScrollPaginationComputed = { [K in Collection as `${K}EntitiesPagedRequest`]: DeepSignal<{ startIndex: number; size: number; }>; } & { [K in Collection as `${K}EntitiesCurrentPage`]: DeepSignal<{ entities: Entity[]; pageIndex: number; pageSize: number; hasPrevious: boolean; hasNext: boolean; isLoading: boolean; }>; }; type EntitiesScrollPaginationMethods = SetEntitiesResult<{ entities: Entity[]; total: number; } | { entities: Entity[]; hasMore: boolean; } | { entities: Entity[]; }> & { loadMoreEntities: () => void; loadEntitiesNextPage: () => void; loadEntitiesPreviousPage: () => void; loadEntitiesFirstPage: () => void; }; type NamedEntitiesScrollPaginationMethods = NamedSetEntitiesResult & { [K in Collection as `loadMore${Capitalize}Entities`]: () => void; } & { [K in Collection as `load${Capitalize}EntitiesNextPage`]: () => void; } & { [K in Collection as `load${Capitalize}EntitiesPreviousPage`]: () => void; } & { [K in Collection as `load${Capitalize}EntitiesFirstPage`]: () => void; }; /** * Generates necessary state, computed and methods for remote infinite scroll pagination of entities in the store. * This is ideal for implementing infinite scroll where the entities cache keeps growing, or for a paginated list that only * allows going to the next and previous page because you dont know the total number of entities * probably because the data is top big and partitioned in multiple nodes. * * When the page changes, it will try to load the current page from cache if it's not present, * it will call set[Collection]Loading(), and you should either create an effect that listens to is[Collection]Loading * and call the api with the [Collection]PagedRequest params and use set[Collection]Result to set the result * and changing the status errors manually * or use withEntitiesLoadingCall to call the api with the [Collection]PagedRequest params which handles setting * the result and errors automatically. Requires withEntities and withCallStatus to be used. * * The generated set[Collection]Result method will append the entities to the cache of entities, * it requires either just set of requested entities set[Collection]Result({ entities }) in which case it will assume there is no more result if you set less entities * than the requested buffer size, or you can provide an extra param to the entities, total set[Collection]Result({ entities, total }) so it calculates if there is more * or a hasMore param set[Collection]Result({entities, hasMore}) that you can set to false to indicate the end of the entities. * * Requires withEntities and withCallStatus to be present in the store. * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.pageSize - The number of entities to show per page * @param configFactory.pagesToCache - The number of pages to cache * @param configFactory.entity - The entity type * @param configFactory.collection - The name of the collection * * @example * const entity = type(); * const collection = 'product'; * export const store = signalStore( * { providedIn: 'root' }, * // required withEntities and withCallStatus * withEntities({ entity, collection }), * withCallStatus({ collection, initialValue: 'loading' }), * * withEntitiesRemoteScrollPagination({ * entity, * collection, * pageSize: 5, * pagesToCache: 2, * }) * // after you can use withEntitiesLoadingCall to connect the filter to * // the api call, or do it manually as shown after * withEntitiesLoadingCall({ * collection, * fetchEntities: ({ productPagedRequest }) => { * return inject(ProductService) * .getProducts({ * take: productPagedRequest().size, * skip: productPagedRequest().startIndex, * }).pipe( * map((d) => ({ * entities: d.resultList, * total: d.total, * })), * ) * }, * }), * // withEntitiesLoadingCall is the same as doing the following: * // withHooks(({ productEntitiesCallStatus, setProductEntitiesError, setProductPagedResult, ...state }) => ({ * // onInit: async () => { * // effect(() => { * // if (isProductEntitiesLoading()) { * // inject(ProductService) * // .getProducts({ * // take: productPagedRequest().size, * // skip: productPagedRequest().startIndex, * // }) * // .pipe( * // takeUntilDestroyed(), * // tap((res) => * // // total is not required, you can use hasMore or none see docs * // setProductPagedResult({ entities: res.resultList, total: res.total } ) * // ), * // catchError((error) => { * // setProductEntitiesError(error); * // return EMPTY; * // }), * // ) * // .subscribe(); * // } * // }); * // }, * })), * * // in your component add * store = inject(ProductsRemoteStore); * dataSource = getInfiniteScrollDataSource(store, { collection: 'product' }) // pass this to your cdkVirtualFor see examples section * // generates the following signals * store.productEntitiesPagination // { currentPage: number, pageSize: number, pagesToCache: number, hasMore: boolean } used internally * // generates the following computed signals * store.productEntitiesCurrentPage // { entities: Entity[], pageIndex: number, total: number, pageSize: number, hasPrevious: boolean, hasNext: boolean, isLoading: boolean } * store.productEntitiesPagedRequest // { startIndex: number, size: number } * // generates the following methods * store.loadProductEntitiesNextPage() // loads next page * store.loadProductEntitiesPreviousPage() // loads previous page * store.loadProductEntitiesFirstPage() // loads first page * store.loadMoreProductEntities() // loads more entities (used for infinite scroll datasource) * store.setProductEntitiesPagedResult(entities: Product[], total: number) // appends the entities to the cache of entities and total */ declare function withEntitiesRemoteScrollPagination(configFactory: FeatureConfigFactory; }>): SignalStoreFeature & RequireEntitiesCallStatus, Collection extends '' ? { state: EntitiesScrollPaginationState; props: EntitiesScrollPaginationComputed; methods: EntitiesScrollPaginationMethods; } : { state: NamedEntitiesScrollPaginationState; props: NamedEntitiesScrollPaginationComputed; methods: NamedEntitiesScrollPaginationMethods; }>; declare function getInfiniteScrollDataSource(options: { store: EntityProps & EntitiesScrollPaginationMethods; debounceLoadMoreTime?: number; } | { collection: Collection; entity: Entity; store: NamedEntityProps & NamedEntitiesScrollPaginationMethods; debounceLoadMoreTime?: number; }): { subscription?: Subscription; entitiesList: Observable; connect(collectionViewer: CollectionViewer): Observable; disconnect(): void; }; type SortDirection = 'asc' | 'desc' | ''; type Sort = { /** The id of the column being sorted. */ field: keyof Entity | (string & {}); /** The sort direction. */ direction: SortDirection; }; type CdkSort = { /** The id of the column being sorted. */ active: keyof Entity | (string & {}); /** The sort direction. */ direction: SortDirection; }; type EntitiesSortState = { entitiesSort: Sort; }; type NamedEntitiesSortState = { [K in Collection as `${K}EntitiesSort`]: Sort; }; type EntitiesSortMethods = { sortEntities: (options?: Sort | CdkSort | { sort: Sort | CdkSort; } | Observable | CdkSort | { sort: Sort | CdkSort; }> | (() => Sort | CdkSort | { sort: Sort | CdkSort; })) => void; }; type NamedEntitiesSortMethods = { [K in Collection as `sort${Capitalize}Entities`]: (options?: Sort | CdkSort | { sort: Sort | CdkSort; } | Observable | CdkSort | { sort: Sort | CdkSort; }> | (() => Sort | CdkSort | { sort: Sort | CdkSort; })) => void; }; type FilterQueryMapper = { /** * @param defaultFilter the filter the store was created with, to fall back on * for fields the query params do not carry * @param currentFilter the filter the store holds right now, to keep the * parts of it the query params say nothing about * @returns the filter to apply, or undefined to leave the current one alone */ queryParamsToFilter: (query: T, defaultFilter: Filter, currentFilter: Filter) => Filter | undefined; filterToQueryParams: (filter: Filter) => T | undefined | null; /** * Merge the restored filter into the current one instead of replacing it, * for mappers that only map some of the filter fields. Defaults to false, * which replaces the whole filter. */ patch?: boolean; }; type QueryMapper = Record, Input extends SignalStoreFeatureResult = SignalStoreFeatureResult> = { /** * @param firstLoad true only for the first query params emission restored * into this store instance. Mappers should only read it, the caller resets * it once all mappers have run. */ queryParamsToState: (query: T, store: Store, firstLoad: boolean) => void; stateToQueryParams: (store: Store) => Signal | undefined | null; }; /** * The kind of value a state prop holds, it decides how the prop is written to * and read back from the query params. */ type QueryParamType = 'string' | 'number' | 'boolean' | 'date' | 'date-time' | 'time' | 'string-array' | 'number-array' | 'json'; /** * The type that can be declared for a state prop holding T, so only the valid * ones are suggested and a wrong one fails on the prop itself. Null and * undefined are stripped so optional props can be synced too. * * Each kind of value gets exactly one type, except dates which choose how much * of the date to keep in the url, and objects which either travel as 'json' or * are described field by field with a nested props object of the same shape. * 'json' is not offered as an alternative for the primitives because it writes * the same url while validating less on the way back, so '?flag=123' would put * a number in a boolean prop, and not for dates because JSON.parse gives back a * string, not a Date. An array of strings or of numbers can travel as a comma * separated list instead of json, anything else in an array only takes 'json'. */ type QueryParamTypeFor = 0 extends 1 & T ? // an any typed prop matches every branch below, so let it pick any type QueryParamType | Record : [NonNullable] extends [Date] ? 'date' | 'date-time' | 'time' : [NonNullable] extends [boolean] ? 'boolean' : [NonNullable] extends [string] ? 'string' : [NonNullable] extends [number] ? 'number' : [NonNullable] extends [readonly string[]] ? 'json' | 'string-array' : [NonNullable] extends [readonly number[]] ? 'json' | 'number-array' : [NonNullable] extends [readonly any[]] ? 'json' : [NonNullable] extends [object] ? 'json' | QueryParamTypesFor> : 'json'; /** * The props object shape for a given store state, every state prop is optional * and can only be declared as one of the types that fits its value, or as a * nested props object when it holds one. */ type QueryParamTypesFor = { [K in keyof State]?: QueryParamTypeFor; }; /** * The query param names a props object produces, a prop declared with a nested * props object becomes one param per leaf, named with the path to it joined by * dots. */ type QueryParamNames = { [K in keyof Props & string]: Props[K] extends QueryParamType ? K : `${K}.${QueryParamNames}`; }[keyof Props & string]; /** * Fails on the props that are not in the state, at every level. The names are * autocompleted from QueryParamTypesFor but not checked against it, because the * state is still being inferred while the props object is read, which is what * turns the excess property check off. Mapping every key to the type it is * allowed to have, or to a message when the state does not have it, checks them * once the state is known. */ type OnlyStateProps = { [K in keyof Props]: K extends keyof State ? Props[K] extends QueryParamType ? QueryParamTypeFor : OnlyStateProps> : `'${K & string}' is not a prop of the state`; }; /** * Creates a QueryMapper that syncs state props to query params with the same * name, using the declared type of each prop to serialize and deserialize it. * * The prop names come from the state of the store the * withSyncToRouteQueryParams feature is added to, so they autocomplete, and * each prop only accepts the types that fit its value, so declaring * `page: 'string'` for a numeric prop, or a prop the state does not have, is a * compile error, at every level of a nested props object. * * Only 'json' props go through JSON.stringify, the rest are written in their * plain form so urls stay readable. Date props pick how much of the date to * keep, 'date' writes 2026-08-11, 'date-time' writes an iso timestamp and * 'time' writes 09:30, all in local time except 'date-time'. An array of * strings or of numbers can be written as a comma separated list with * 'string-array' or 'number-array', so ?tags=shoes,boots. The comma is the * separator, so a string carrying one comes back split in two, use 'json' for * those, in dev mode a warning is logged when it happens. * * A prop holding an object can be declared as 'json' to travel as a single * param, or described field by field with a nested props object, which gives * it one param per field named with the path to it, so `filter.color=red`. The * fields left undeclared keep the value they have in the store, they are * neither written to the url nor cleared when it is read back. * * A prop that is undefined or null is removed from the url, a param missing * from the url is left untouched in the store, and a param that does not match * its declared type (a hand edited url) is skipped instead of writing a wrong * value into the state. * * @param props - the state props to sync, mapped to their type * * @example * const Store = signalStore( * withState({ * search: '', * page: 0, * showSold: false, * day: new Date(), * startsAt: new Date(), * tags: ['shoes'], * filter: { color: 'red', size: 10 }, * }), * withSyncToRouteQueryParams({ * mappers: [ * getQueryMapperForState({ * search: 'string', * page: 'number', * showSold: 'boolean', * day: 'date', * startsAt: 'date-time', * tags: 'string-array', * filter: 'json', * }), * ], * }), * ); * // ?search=shoes&page=2&showSold=true&day=2026-08-11 * // &startsAt=2026-08-11T09:30:00.000Z&tags=shoes,boots * // &filter=%7B%22color%22%3A%22red%22%2C%22size%22%3A10%7D * * @example * // the same filter prop described field by field instead, which keeps the * // url readable and brings back a real Date rather than the string * // JSON.parse would give * const Store = signalStore( * withState({ * filter: { color: 'red', size: 10, from: new Date() }, * }), * withSyncToRouteQueryParams({ * mappers: [ * getQueryMapperForState({ * filter: { color: 'string', from: 'date' }, * }), * ], * }), * ); * // ?filter.color=red&filter.from=2026-08-11 * // size is not declared, so it stays at whatever the store holds */ declare function getQueryMapperForState & OnlyStateProps>(props: Props): QueryMapper, string>>, Record, Input>; /** * Creates a FilterQueryMapper for the filterMapper option of * withEntitiesSyncToRouteQueryParams and getQueryMapperForEntitiesFilter, using * the same types as getQueryMapperForState. It spreads the filter over one * query param per field instead of the default single json blob, so the url * reads as ?search=shoes&maxPrice=100 instead of ?filter=%7B%22search%22... * * The filter type cannot be inferred from the store, so it has to be given, * which is what makes the field names autocomplete and their types check. * * A field holding an object can be described field by field too, with a nested * props object, which gives it one param per field named with the path to it, * so `range.from=2026-08-11`. * * A field that is undefined or null is removed from the url. On the way back * the mapper patches the filter instead of replacing it, so the fields it does * not declare keep their value, the ones at the top level and the ones inside * a declared object alike, while every declared field is restored, falling * back to the value the field has in the store defaultFilter when its param is * missing or does not match its type, or to the one the filter already holds * when defaultFilter says nothing about it. A bare url therefore restores the * declared fields to their defaults, which is also what makes the back button * work after clearing a filter. * * The flip side of leaning on defaultFilter is that a field cannot travel as * cleared unless its default already is, restoring a url without its param * brings the default back rather than an empty value. * * @param props - the filter fields to sync, mapped to their type * * @example * type ProductFilter = { search: string; maxPrice: number; from: Date }; * * withEntitiesSyncToRouteQueryParams({ * entity, * collection, * filterMapper: getFilterQueryMapper({ * search: 'string', * maxPrice: 'number', * from: 'date', * }), * }); * // ?product-search=shoes&product-maxPrice=100&product-from=2026-08-11 * * @example * type ProductFilter = { * search: string; * range: { from: Date; to: Date }; * }; * * withEntitiesSyncToRouteQueryParams({ * entity, * collection, * filterMapper: getFilterQueryMapper({ * search: 'string', * range: { from: 'date', to: 'date' }, * }), * }); * // ?product-search=shoes&product-range.from=2026-08-11 * // &product-range.to=2026-08-31 */ declare function getFilterQueryMapper, const Props extends QueryParamTypesFor = QueryParamTypesFor>(props: Props): FilterQueryMapper; /** * @internal * @ignore * Gets a sorted copy of the data array based on the state of the Sort. * @param data The array of data that should be sorted. * @param sort The connected MatSort that holds the current sort state. */ declare function sortData(data: T[], sort: Sort): T[]; /** * Generates necessary state, computed and methods for sorting locally entities in the store. * * Requires withEntities to be present before this function * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.defaultSort - The default sort to be applied to the entities * @param configFactory.entity - The type entity to be used * @param configFactory.collection - The name of the collection for which will be sorted * @param configFactory.selectId - The function to use to select the id of the entity * @param configFactory.sortFunction - Optional custom function use to sort the entities * * @example * const entity = type(); * const collection = "product"; * export const store = signalStore( * { providedIn: 'root' }, * withEntities({ entity, collection }), * withEntitiesLocalSort({ * entity, * collection, * defaultSort: { field: 'name', direction: 'asc' }, * }), * ); * // generates the following signals * store.productEntitiesSort - the current sort applied to the products * // generates the following methods * store.sortProductEntities({ sort: { field: 'name', direction: 'asc' } }) - sorts the products entities */ declare function withEntitiesLocalSort(configFactory: FeatureConfigFactory>; entity: Entity; collection?: Collection; selectId?: SelectEntityId; sortFunction?: (entities: Entity[], sort: Sort) => Entity[]; }>): SignalStoreFeature, Collection extends '' ? { state: EntitiesSortState; props: {}; methods: EntitiesSortMethods; } : { state: NamedEntitiesSortState; props: {}; methods: NamedEntitiesSortMethods; }>; type EntitiesRemoteSortMethods = { sortEntities: (options?: Sort | CdkSort | { sort: Sort | CdkSort; skipLoadingCall?: boolean; } | Observable | CdkSort | { sort: Sort | CdkSort; skipLoadingCall?: boolean; }> | (() => Sort | CdkSort | { sort: Sort | CdkSort; skipLoadingCall?: boolean; })) => void; }; type NamedEntitiesRemoteSortMethods = { [K in Collection as `sort${Capitalize}Entities`]: (options?: Sort | CdkSort | { sort: Sort | CdkSort; skipLoadingCall?: boolean; } | Observable | CdkSort | { sort: Sort | CdkSort; skipLoadingCall?: boolean; }> | (() => Sort | CdkSort | { sort: Sort | CdkSort; skipLoadingCall?: boolean; })) => void; }; /** * Generates state, signals, and methods to sort entities remotely. When the sort method sort[Collection]Entities is called it will store the sort * and call set[Collection]Loading, and you should either create an effect that listens to [Collection]Loading * and call the api with the [Collection]Sort params and use wither setAllEntities if is not paginated or set[Collection]Result if is paginated * with the sorted result that come from the backend, plus changing the status and set errors is needed. * or use withEntitiesLoadingCall to call the api with the [Collection]Sort params which handles setting * the result and errors automatically. * * In case you dont want sort[Collection]Entities to call set[Collection]Loading() (which triggers a fetchEntities), you can pass skipLoadingCall: true to sort[Collection]Entities. * Useful in cases where you want to further change the state before manually calling set[Collection]Loading() to trigger a fetch of entities. * * Requires withEntities and withCallStatus to be present before this function. * * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.defaultSort - The default sort to use when the store is initialized * @param configFactory.entity - The entity type * @param configFactory.collection - The collection name * * @example * const entity = type(); * const collection = 'product'; * export const store = signalStore( * { providedIn: 'root' }, * // required withEntities and withCallStatus * withEntities({ entity, collection }), * withCallStatus({ collection, initialValue: 'loading' }), * * withEntitiesRemoteSort({ * entity, * collection, * defaultSort: { field: 'name', direction: 'asc' }, * }), * // after you can use withEntitiesLoadingCall to connect the filter to * // the api call, or do it manually as shown after * withEntitiesLoadingCall({ * collection, * fetchEntities: ({ productEntitiesSort }) => { * return inject(ProductService) * .getProducts({ * sortColumn: productEntitiesSort().field, * sortAscending: productEntitiesSort().direction === 'asc', * }) * }, * }), * // withEntitiesLoadingCall is the same as doing the following: * // withHooks(({ productEntitiesSort, isProductEntitiesLoading, setProductEntitiesError, ...state }) => ({ * // onInit: async () => { * // effect(() => { * // if (isProductEntitiesLoading()) { * // inject(ProductService) * // .getProducts({ * // sortColumn: productEntitiesSort().field, * // sortAscending: productEntitiesSort().direction === 'asc', * // }) * // .pipe( * // takeUntilDestroyed(), * // tap((res) => * // patchState( * // state, * // setAllEntities(res.resultList, { collection: 'product' }), * // ), * // ), * // catchError((error) => { * // setProductEntitiesError(error); * // return EMPTY; * // }), * // ) * // .subscribe(); * // } * // }); * // }, * })), * * // generate the following signals * store.productEntitiesSort // the current sort * // and the following methods * store.sortProductEntities // (options: { sort: Sort; , skipLoadingCall?:boolean}) => void; */ declare function withEntitiesRemoteSort(configFactory: FeatureConfigFactory; collection?: Collection; }>): SignalStoreFeature & RequireEntitiesCallStatus, Collection extends '' ? { state: EntitiesSortState; props: {}; methods: EntitiesRemoteSortMethods; } : { state: NamedEntitiesSortState; props: {}; methods: NamedEntitiesRemoteSortMethods; }>; /** * Generates a `link[Collection]EntitiesSort()` method that connects the * entities sort to component signals (inputs, models, signal forms). * * Prebuilt version of `withLink` for `withEntitiesLocalSort` / * `withEntitiesRemoteSort`: writes route through `sort[Collection]Entities` * (so the entities are re-sorted), and syncs are guarded with a structural * equality on the sort, preventing echo loops. * * Requires withEntitiesLocalSort or withEntitiesRemoteSort to be used before it. * * @param config - The configuration object for the feature * @param config.entity - The entity type to be used * @param config.collection - The optional collection name to be used * * @example * const entity = type(); * const store = signalStore( * withEntities({ entity }), * withEntitiesLocalSort({ * entity, * defaultSort: { field: 'name', direction: 'asc' }, * }), * withLinkEntitiesSort({ entity }), * ); * // in a component: * // sort = model>({ field: 'name', direction: 'asc' }); * // linked = this.store.linkEntitiesSort({ syncWith: this.sort }); */ declare function withLinkEntitiesSort(config?: { entity?: Entity; collection?: Collection; }): SignalStoreFeature; props: {}; methods: {}; } : { state: NamedEntitiesSortState; props: {}; methods: {}; }>, { state: {}; props: {}; methods: { [P in Collection extends '' ? 'entitiesSort' : `${Collection}EntitiesSort` as `link${Capitalize}`]: LinkMethod>; }; }>; type EntitiesSingleSelectionState = { idSelected: string | number | undefined; }; type NamedEntitiesSingleSelectionState = { [K in Collection as `${K}IdSelected`]: string | number | undefined; }; type EntitiesSingleSelectionComputed = { entitySelected: Signal; }; type NamedEntitiesSingleSelectionComputed = { [K in Collection as `${K}EntitySelected`]: Signal; }; type EntitySelectOptions$1 = { id: string | number; } | undefined; type EntitiesSingleSelectionMethods = { selectEntity: (options: EntitySelectOptions$1 | Observable | (() => EntitySelectOptions$1)) => void; deselectEntity: () => void; toggleSelectEntity: (options: EntitySelectOptions$1 | Observable | (() => EntitySelectOptions$1)) => void; }; type NamedEntitiesSingleSelectionMethods = { [K in Collection as `select${Capitalize}Entity`]: (options: EntitySelectOptions$1 | Observable | (() => EntitySelectOptions$1)) => void; } & { [K in Collection as `deselect${Capitalize}Entity`]: () => void; } & { [K in Collection as `toggleSelect${Capitalize}Entity`]: (options: EntitySelectOptions$1 | Observable | (() => EntitySelectOptions$1)) => void; }; /** * Generates state, computed and methods for single selection of entities. * * Requires withEntities to be present before this function. * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.collection - The collection name * @param configFactory.entity - The entity type * @param configFactory.clearOnFilter - Clear the selected entity when the filter changes (default: true) * @param configFactory.clearOnRemoteSort - Clear the selected entity when the remote sort changes (default: true) * @example * const entity = type(); * const collection = "product"; * export const store = signalStore( * { providedIn: 'root' }, * // Required withEntities and withCallStatus * withEntities({ entity, collection }), * withCallStatus({ prop: collection, initialValue: 'loading' }), * * withEntitiesSingleSelection({ * entity, * collection, * }), * ); * * // generates the following signals * store.productIdSelected // string | number | undefined * // generates the following computed signals * store.productEntitySelected // Entity | undefined * // generates the following methods * store.selectProductEntity // (config: { id: string | number }) => void * store.deselectProductEntity // (config: { id: string | number }) => void * store.toggleProductEntity // (config: { id: string | number }) => void */ declare function withEntitiesSingleSelection(configFactory: FeatureConfigFactory): SignalStoreFeature, Collection extends '' ? { state: EntitiesSingleSelectionState; props: EntitiesSingleSelectionComputed; methods: EntitiesSingleSelectionMethods; } : { state: NamedEntitiesSingleSelectionState; props: NamedEntitiesSingleSelectionComputed; methods: NamedEntitiesSingleSelectionMethods; }>; type EntitiesMultiSelectionState = { idsSelectedMap: Record; }; type NamedEntitiesMultiSelectionState = { [K in Collection as `${K}IdsSelectedMap`]: Record; }; type EntitiesMultiSelectionComputed = { entitiesSelected: Signal; idsSelected: Signal<(string | number)[]>; isAllEntitiesSelected: Signal<'all' | 'none' | 'some'>; }; type NamedEntitiesMultiSelectionComputed = { [K in Collection as `${K}EntitiesSelected`]: Signal; } & { [K in Collection as `${K}IdsSelected`]: Signal<(string | number)[]>; } & { [K in Collection as `isAll${Capitalize}EntitiesSelected`]: Signal<'all' | 'none' | 'some'>; }; type EntitySelectOptions = { id: string | number; } | { ids: (string | number)[]; }; type EntitiesMultiSelectionMethods = { selectEntities: (options: (EntitySelectOptions & { clearSelectionBeforeSelect?: boolean; }) | Observable | (() => EntitySelectOptions & { clearSelectionBeforeSelect?: boolean; })) => void; deselectEntities: (options: EntitySelectOptions) => void; toggleSelectEntities: (options: EntitySelectOptions) => void; toggleSelectAllEntities: () => void; clearEntitiesSelection: () => void; }; type NamedEntitiesMultiSelectionMethods = { [K in Collection as `select${Capitalize}Entities`]: (options: (EntitySelectOptions & { clearSelectionBeforeSelect?: boolean; }) | Observable | (() => EntitySelectOptions & { clearSelectionBeforeSelect?: boolean; })) => void; } & { [K in Collection as `deselect${Capitalize}Entities`]: (options: EntitySelectOptions) => void; } & { [K in Collection as `toggleSelect${Capitalize}Entities`]: (options: EntitySelectOptions) => void; } & { [K in Collection as `toggleSelectAll${Capitalize}Entities`]: () => void; } & { [K in Collection as `clear${Capitalize}EntitiesSelection`]: () => void; }; /** * Generates state, signals and methods for multi selection of entities. * Warning: isAll[Collection]Selected and toggleSelectAll[Collection] wont work * correctly in using remote pagination, because they cant select all the data. * * Requires withEntities to be used before this feature. * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.entity - the entity type * @param configFactory.collection - the collection name * @param configFactory.clearOnFilter - Clear the selected entity when the filter changes (default: true) * @param configFactory.clearOnRemoteSort - Clear the selected entity when the remote sort changes (default: true) * * @example * const entity = type(); * const collection = "product"; * export const store = signalStore( * { providedIn: 'root' }, * withEntities({ entity, collection }), * withEntitiesMultiSelection({ entity, collection }), * ); * * // generates the following signals * store.productIdsSelectedMap // Record; * // generates the following computed signals * store.productEntitiesSelected // Entity[]; * store.isAllProductEntitiesSelected // 'all' | 'none' | 'some'; * // generates the following methods * store.selectProducts // (config: { id: string | number } | { ids: (string | number)[] }) => void; * store.deselectProducts // (config: { id: string | number } | { ids: (string | number)[] }) => void; * store.toggleSelectProducts // (config: { id: string | number } | { ids: (string | number)[] }) => void; * store.toggleSelectAllProducts // () => void; */ declare function withEntitiesMultiSelection(configFactory: FeatureConfigFactory; clearOnFilter?: boolean; clearOnRemoteSort?: boolean; defaultSelectedIds?: (string | number)[]; }>): SignalStoreFeature, Collection extends '' ? { state: EntitiesMultiSelectionState; props: EntitiesMultiSelectionComputed; methods: EntitiesMultiSelectionMethods; } : { state: NamedEntitiesMultiSelectionState; props: NamedEntitiesMultiSelectionComputed; methods: NamedEntitiesMultiSelectionMethods; }>; /** * Generates a `link[Collection]IdSelected()` method that connects the selected * entity id to component signals (inputs, models, signal forms). * * Prebuilt version of `withLink` for `withEntitiesSingleSelection`: writes * route through `select[Collection]Entity` / `deselect[Collection]Entity` * (undefined deselects). * * Requires withEntitiesSingleSelection to be used before it. * * @param config - The configuration object for the feature * @param config.entity - The entity type to be used * @param config.collection - The optional collection name to be used * * @example * const entity = type(); * const store = signalStore( * withEntities({ entity }), * withEntitiesSingleSelection({ entity }), * withLinkEntitiesSingleSelection({ entity }), * ); * // in a component: * // selectedId = model(undefined); * // linked = this.store.linkIdSelected({ syncWith: this.selectedId }); */ declare function withLinkEntitiesSingleSelection(config?: { entity?: Entity; collection?: Collection; }): SignalStoreFeature; props: {}; methods: {}; }>, { state: {}; props: {}; methods: { [P in Collection extends '' ? 'idSelected' : `${Collection}IdSelected` as `link${Capitalize}`]: LinkMethod; }; }>; /** * Generates a `link[Collection]IdsSelected()` method that connects the * selected entity ids to component signals (inputs, models, signal forms). * * Prebuilt version of `withLink` for `withEntitiesMultiSelection`: reads the * `[collection]IdsSelected` computed, writes route through * `select[Collection]Entities` with `clearSelectionBeforeSelect` (an empty * array clears the selection), and syncs are guarded with an order-insensitive * ids equality — the selection map does not preserve the order of the ids it * was given, so an order-sensitive compare would cause echo loops. * * Requires withEntitiesMultiSelection to be used before it. * * @param config - The configuration object for the feature * @param config.entity - The entity type to be used * @param config.collection - The optional collection name to be used * * @example * const entity = type(); * const store = signalStore( * withEntities({ entity }), * withEntitiesMultiSelection({ entity }), * withLinkEntitiesMultiSelection({ entity }), * ); * // in a component: * // value = model<(string | number)[]>([]); * // valueField = form(this.store.linkIdsSelected({ syncWith: this.value })); */ declare function withLinkEntitiesMultiSelection(config?: { entity?: Entity; collection?: Collection; }): SignalStoreFeature; props: {}; methods: {}; }>, { state: {}; props: {}; methods: { [P in Collection extends '' ? 'idsSelected' : `${Collection}IdsSelected` as `link${Capitalize}`]: LinkMethod<(string | number)[]>; }; }>; /** * The key an entities pagination feature generates to store a page result. */ type SetEntitiesPagedResultKey = Collection extends '' ? 'setEntitiesPagedResult' : `set${Capitalize}EntitiesPagedResult`; /** * The result fetchEntities returns, when the store has an entities pagination * feature it is whatever its set[Collection]EntitiesPagedResult accepts, which * varies per pagination feature, e.g. { entities, total } for * withEntitiesRemotePagination, otherwise the plain entities result. */ type FetchEntitiesResult = Input['methods'] extends NamedSetEntitiesResult ? ResultParam : Entity[] | { entities: Entity[]; }; /** * Adds an unreachable member to the result type, its key explains why the * result has that shape, so a wrong result reports * * Type 'Observable' is not assignable to type * 'Observable>' * * instead of only naming the properties that are missing. */ type PagedFetchEntitiesResult = ResultParam | { [K in Explanation]: never; }; /** * Same as FetchEntitiesResult, but explaining where the shape comes from when * the store has an entities pagination feature. Only for the value fetchEntities * returns, results handed back to the user, like the onSuccess param, use * FetchEntitiesResult so they don't have to narrow the explanation away. */ type ExpectedFetchEntitiesResult = Input['methods'] extends NamedSetEntitiesResult ? PagedFetchEntitiesResult} accepts`> : Entity[] | { entities: Entity[]; }; /** * Generates a onInit hook that fetches entities from a remote source * when the [Collection]Loading is true, by calling the fetchEntities function * and if successful, it will call set[Collection]Loaded and also set the entities * to the store using the setAllEntities method or the setEntitiesPagedResult method * if it exists (comes from withEntitiesRemotePagination), * if an error occurs it will set the error to the store using set[Collection]Error with the error. * * Requires withEntities and withCallStatus to be present in the store. * * @param config - Configuration object or factory function that returns the configuration object * @param config.fetchEntities - A function that fetches the entities from a remote source, the return type can be an array of entities or an object with entities and total * @param config.collection - The collection name * @param config.onSuccess - A function that is called when the fetchEntities is successful * @param config.mapError - A function to transform the error before setting it to the store, requires withCallStatus errorType to be set * @param config.onError - A function that is called when the fetchEntities fails * @param config.selectId - The function to use to select the id of the entity * @param config.storeResult - Whether to automatically store the fetched entities in the store (default: true). When false, entities are not stored, but setLoaded and onSuccess are still called, useful when you want to handle storing in onSuccess yourself * * * @example * export const ProductsRemoteStore = signalStore( * { providedIn: 'root' }, * // requires at least withEntities and withCallStatus * withEntities({ entity, collection }), * withCallStatus({ prop: collection, initialValue: 'loading' }), * // other features * withEntitiesRemoteFilter({ * entity, * collection, * defaultFilter: { name: '' }, * }), * withEntitiesRemotePagination({ * entity, * collection, * pageSize: 5, * pagesToCache: 2, * }), * withEntitiesRemoteSort({ * entity, * collection, * defaultSort: { field: 'name', direction: 'asc' }, * }), * // now we add the withEntitiesLoadingCall, in this case any time the filter, * // pagination or sort changes they call set[Collection]Loading() which then * // triggers the onInit effect that checks if [Collection]Loading(), if true * // then calls fetchEntities function * withEntitiesLoadingCall({ * collection, * fetchEntities: ({ productEntitiesFilter, productEntitiesPagedRequest, productEntitiesSort }) => { * return inject(ProductService) * .getProducts({ * search: productEntitiesFilter().name, * take: productEntitiesPagedRequest().size, * skip: productEntitiesPagedRequest().startIndex, * sortColumn: productEntitiesSort().field, * sortAscending: productEntitiesSort().direction === 'asc', * }) * .pipe( * map((d) => ({ * entities: d.resultList, * total: d.total, * })), * ); * }, * }), */ declare function withEntitiesLoadingCall(config: FeatureConfigFactory) => Observable> | Promise>; mapPipe?: 'switchMap' | 'concatMap' | 'exhaustMap'; onSuccess?: (result: FetchEntitiesResult) => void; mapError?: (error: unknown) => Error; onError?: (error: Error) => void; entity?: Entity; selectId?: SelectEntityId; storeResult?: boolean; }>): SignalStoreFeature & RequireEntitiesCallStatus>, EmptyFeatureResult>; /** * Log the state of the store on every change, optionally filter the signals to log * the filter prop can receive an array with the names of the props to filter, or you can provide a function * which receives the store as an argument and should return the object to log, if any of the props in the object is a signal * it will log the value of the signal, the returned value can also be a non object value like a nested signal value. * If showDiff is true it will log the diff of the state on every change. * * @param name - The name of the store to log * @param filter - optional filter function to filter the store signals or an array of keys to filter * @param showDiff - optional flag to log the diff of the state on every change * * @example * * const Store = signalStore( * withState(() => ({ prop1: 1, prop2: 2 })), * withComputed(({ prop1, prop2 }) => ({ * prop3: computed(() => prop1() + prop2()), * })), * withLogger({ * name: 'Store', * // by default it will log all state and computed signals * // or you can filter with an array of keys * // filter: ['prop1', 'prop2'], * // or you can filter with a function * // filter: ({ prop1, prop2 }) => ({ prop1, prop2 }), * // the function can also return nested signals or their values * // filter: ({ myObject }) => ({ x: myObject.x }), * // filter: ({ myObject }) => myObject.x(), * // showDiff: true, * }), * ); */ declare function withLogger({ name, filter, showDiff, }: { name: string; filter?: ((store: StateSignals & Input['props']) => any) | readonly (keyof (StateSignals & Input['props']))[]; showDiff?: boolean; }): SignalStoreFeature; /** * Generates necessary state, computed and methods to track the progress of the * call and store the result of the call. The generated methods are rxMethods with * the same name as the original call, which accepts either the original parameters * or a Signal or Observable of the same type as the original parameters. * The original call can only have zero or one parameter, use an object with multiple * props as first param if you need more. * If the name start with an underscore, the call will be private and all generated methods * will also start with an underscore, making it only accessible inside the store. * @param {callsFactory} callsFactory - a factory function that receives the store and returns an object of type {Record} with the calls to be made * * @example * withCalls(({ productsSelectedEntity }) => ({ * loadProductDetail: callConfig({ * call: ({ id }: { id: string }) => * inject(ProductService).getProductDetail(id), * resultProp: 'productDetail', * // storeResult: false, // will omit storing the result, and remove the result prop from the store * mapPipe: 'switchMap', // default is 'exhaustMap' * onSuccess: (result, callParam) => { * // do something with the result * }, * mapError: (error, callParam) => { * return // transform the error before storing it * }, * onError: (error, callParam) => { * // do something with the error * }, * skipWhen: (callParam) => { * // if return true, the call will be skip, if false, the call will execute as usual * return // boolean | Promise | Observable * }, * callWith: () => * // reactively call with the selected product id, if undefined is return, the call is skip by default * productEntitySelected() * ? { id: productEntitySelected()!.id } * : undefined, * }), * checkout: () => * inject(OrderService).checkout({ * productId: productsSelectedEntity()!.id, * quantity: 1, * }), * })), * * // generates the following signals * store.loadProductDetailCallStatus // 'init' | 'loading' | 'loaded' | { error: unknown } * store.productDetail // the result of the call * store.checkoutCallStatus // 'init' | 'loading' | 'loaded' | { error: unknown } * store.checkoutResult // the result of the call * // generates the following computed signals * store.isLoadProductDetailLoading // boolean * store.isLoadProductDetailLoaded // boolean * store.loadProductDetailError // string | null * store.isCheckoutLoading // boolean * store.isCheckoutLoaded // boolean * store.checkoutError // unknown | null * // generates the following methods * store.loadProductDetail // ({id: string} | Signal<{id: string}> | Observable<{id: string}>) => void * store.checkout // () => Promise<{value, ok: true} | {error, ok: false}> * * @warning The default mapPipe is {@link https://www.learnrxjs.io/learn-rxjs/operators/transformation/exhaustmap exhaustMap}. If your call returns an observable that does not complete after the first value is emitted, any changes to the input params will be ignored. Either specify {@link https://www.learnrxjs.io/learn-rxjs/operators/transformation/switchmap switchMap} as mapPipe, or use {@link https://www.learnrxjs.io/learn-rxjs/operators/filtering/take take(1)} or {@link https://www.learnrxjs.io/learn-rxjs/operators/filtering/first first()} as part of your call. */ declare function withCalls>(callsFactory: (store: StoreSource) => Calls): SignalStoreFeature & { [K in keyof Calls as ExtractCallResultPropName]: ExtractCallResultType; }; props: NamedCallsStatusComputed; methods: { [K in keyof Calls]: Calls[K] extends (...args: infer P) => any ? P extends [] ? () => Promise<{ value: Signal>; ok: true; } | { error: Signal>; ok: false; }> : { (param: P[0]): Promise<{ value: Signal>; ok: true; } | { error: Signal>; ok: false; }>; (param: Observable | (() => P[0])): RxMethodRef; } : Calls[K] extends CallConfig ? Parameters extends undefined[] ? () => Promise<{ value: Signal>; ok: true; } | { error: Signal>; ok: false; }> : { (param: Parameters[0]): Promise<{ value: Signal>; ok: true; } | { error: Signal>; ok: false; }>; (param: Observable[0]> | (() => Parameters[0])): RxMethodRef; } : never; }; }>; type NamedCallStatusMapState = { [K in Prop as `${K}CallStatus`]: Record; }; type NamedCallStatusMapComputed = { [K in Prop as `isAny${Capitalize}Loading`]: Signal; } & { [K in Prop as `areAll${Capitalize}Loaded`]: Signal; } & { [K in Prop as `${K}Errors`]: Signal; }; type NamedCallStatusMapMethods = { [K in Prop as `is${Capitalize}Loading`]: (id: string) => boolean; } & { [K in Prop as `is${Capitalize}Loaded`]: (id: string) => boolean; } & { [K in Prop as `${K}Error`]: (id: string) => Error | undefined; } & { [K in Prop as `set${Capitalize}Loading`]: (id: string) => void; } & { [K in Prop as `set${Capitalize}Loaded`]: (id: string) => void; } & { [K in Prop as `set${Capitalize}Error`]: (id: string, error: Error) => void; }; /** * Generates necessary state, computed and methods for call progress status but map by a key, allowing to implement * calls of the same type that run on parallel each with its own status. * @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object * @param configFactory.prop - The name of the property for which this represents the call status * @param configFactory.initialValue - The initial value of the call status * @param configFactory.collection - The name of the collection for which this represents the call status is an alias to prop param * @param configFactory.errorType - The type of the error * they do the same thing * * prop or collection is required * @example * export const Store = signalStore( * { providedIn: 'root' }, * withEntities(orderEntity), * withCallStatusMap({ prop: 'loadDetails' }), * withMethods((store) => ({ * loadProducts: rxMethod<{ orderId: string }>( * pipe( * switchMap((params) => { * store.setLoadDetailsLoading(params.orderId); * return inject(OrderService) * .getOrderDetail(params.orderId) * .pipe( * tap((res) => * patchState( * store, * updateEntity( * { * id: params.orderId, * changes: { items: res.items }, * }, * orderEntity, * ), * ), * ), * catchError((error) => { * store.setLoadDetailsError(params.orderId, error); * return EMPTY; * }), * ); * }), * ), * ), * })), * ); * * // generates the following signals * store.loadDetailsCallStatus // '{[key:string]: init' | 'loading' | 'loaded' | { error: unknown }} * // generates the following computed signals * store.isAnyLoadDetailsLoading() // boolean * store.areAllLoadDetailsLoaded // boolean * store.loadDetailsErrors() // Errors[] | undefined * // generates the following methods * store.isLoadDetailsLoading(key: string) // boolean * store.isLoadDetailsLoaded(key: string) // boolean * store.loadDetailsError(key: string) // unknown | null * store.setLoadDetailsLoading(key: string) // () => void * store.setLoadDetailsLoaded(key: string) // () => void * store.setLoadDetailsError(key: string) // (error?: unknown) => void */ declare function withCallStatusMap(configFactory: FeatureConfigFactory; errorType?: Error; prop: Prop; }>): SignalStoreFeature; props: NamedCallStatusMapComputed; methods: NamedCallStatusMapMethods; }>; /** * Call configuration object for withCalls * @param config - the call configuration * @param config.call - required, the function that will be called * @param config.mapPipe - optional, default exhaustMap the pipe operator that will be used to map the call result * @param config.storeResult - optional, default true, if false, the result will not be stored in the store * @param config.resultProp - optional, default callName + 'Result', the name of the prop where the result will be stored * @param config.onSuccess - optional, a function that will be called when the call is successful * @param config.mapError - optional, a function that will be called to transform the error before storing it * @param config.onError - optional, a function that will be called when the call fails * @param config.skipWhen - optional, a function that will be called to determine if the call should be skipped * @param config.callWith - optional, reactively execute the call with the provided params return by a function or observable * @param config.defaultResult - optional, A default value for the result before the call is executed */ declare function callConfig = CallConfig, DefaultResult extends Result | undefined = undefined>(config: Omit, 'resultProp' | 'storeResult' | 'defaultResult'> & { resultProp?: PropName; defaultResult: NoInfer; }): C; /** * Call configuration object for withCalls * @param config - the call configuration * @param config.call - required, the function that will be called * @param config.mapPipe - optional, default exhaustMap the pipe operator that will be used to map the call result * @param config.storeResult - optional, default true, if false, the result will not be stored in the store * @param config.resultProp - optional, default callName + 'Result', the name of the prop where the result will be stored * @param config.onSuccess - optional, a function that will be called when the call is successful * @param config.mapError - optional, a function that will be called to transform the error before storing it * @param config.onError - optional, a function that will be called when the call fails * @param config.skipWhen - optional, a function that will be called to determine if the call should be skipped * @param config.callWith - optional, reactively execute the call with the provided params return by a function or observable * @param config.defaultResult - optional, A default value for the result before the call is executed */ declare function callConfig = CallConfig, DefaultResult extends Result | undefined = undefined>(config: Omit, 'resultProp' | 'storeResult' | 'defaultResult'> & { resultProp?: PropName; }): C; /** * Call configuration object for withCalls * @param config - the call configuration * @param config.call - required, the function that will be called * @param config.mapPipe - optional, default exhaustMap the pipe operator that will be used to map the call result * @param config.storeResult - optional, default true, if false, the result will not be stored in the store * @param config.resultProp - optional, default callName + 'Result', the name of the prop where the result will be stored * @param config.onSuccess - optional, a function that will be called when the call is successful * @param config.mapError - optional, a function that will be called to transform the error before storing it * @param config.onError - optional, a function that will be called when the call fails * @param config.skipWhen - optional, a function that will be called to determine if the call should be skipped * @param config.callWith - optional, reactively execute the call with the provided params return by a function or observable * @param config.defaultResult - optional, A default value for the result before the call is executed */ declare function callConfig, 'resultProp' | 'storeResult' | 'defaultResult'> & { storeResult: false; } = Omit, 'resultProp' | 'storeResult' | 'defaultResult'> & { storeResult: false; }>(config: Omit, 'resultProp' | 'storeResult' | 'defaultResult'> & { storeResult: false; }): C & { resultProp: ''; defaultResult: undefined; }; /** * Call configuration object for withCalls * @Deprecated renamed to callConfig() * @param config - the call configuration * @param config.call - required, the function that will be called * @param config.mapPipe - optional, default exhaustMap the pipe operator that will be used to map the call result * @param config.storeResult - optional, default true, if false, the result will not be stored in the store * @param config.resultProp - optional, default callName + 'Result', the name of the prop where the result will be stored * @param config.onSuccess - optional, a function that will be called when the call is successful * @param config.mapError - optional, a function that will be called to transform the error before storing it * @param config.onError - optional, a function that will be called when the call fails * @param config.skipWhen - optional, a function that will be called to determine if the call should be skipped * @param config.callWith - optional, reactively execute the call with the provided params return by a function or observable * @param config.defaultResult - optional, A default value for the result before the call is executed */ declare function typedCallConfig = CallConfig, DefaultResult extends Result | undefined = undefined>(config: Omit, 'resultProp' | 'storeResult' | 'defaultResult'> & { resultProp?: PropName; defaultResult: NoInfer; }): C; /** * Call configuration object for withCalls * @Deprecated renamed to callConfig() * @param config - the call configuration * @param config.call - required, the function that will be called * @param config.mapPipe - optional, default exhaustMap the pipe operator that will be used to map the call result * @param config.storeResult - optional, default true, if false, the result will not be stored in the store * @param config.resultProp - optional, default callName + 'Result', the name of the prop where the result will be stored * @param config.onSuccess - optional, a function that will be called when the call is successful * @param config.mapError - optional, a function that will be called to transform the error before storing it * @param config.onError - optional, a function that will be called when the call fails * @param config.skipWhen - optional, a function that will be called to determine if the call should be skipped * @param config.callWith - optional, reactively execute the call with the provided params return by a function or observable * @param config.defaultResult - optional, A default value for the result before the call is executed */ declare function typedCallConfig = CallConfig, DefaultResult extends Result | undefined = undefined>(config: Omit, 'resultProp' | 'storeResult' | 'defaultResult'> & { resultProp?: PropName; }): C; /** * Call configuration object for withCalls * @Deprecated renamed to callConfig() * @param config - the call configuration * @param config.call - required, the function that will be called * @param config.mapPipe - optional, default exhaustMap the pipe operator that will be used to map the call result * @param config.storeResult - optional, default true, if false, the result will not be stored in the store * @param config.resultProp - optional, default callName + 'Result', the name of the prop where the result will be stored * @param config.onSuccess - optional, a function that will be called when the call is successful * @param config.mapError - optional, a function that will be called to transform the error before storing it * @param config.onError - optional, a function that will be called when the call fails * @param config.skipWhen - optional, a function that will be called to determine if the call should be skipped * @param config.callWith - optional, reactively execute the call with the provided params return by a function or observable * @param config.defaultResult - optional, A default value for the result before the call is executed */ declare function typedCallConfig, 'resultProp' | 'storeResult' | 'defaultResult'> & { storeResult: false; } = Omit, 'resultProp' | 'storeResult' | 'defaultResult'> & { storeResult: false; }>(config: Omit, 'resultProp' | 'storeResult' | 'defaultResult'> & { storeResult: false; }): C & { resultProp: ''; defaultResult: undefined; }; type StorageValueMapper = Record> = (store: Store) => { storageValueToState: (value: T) => void; stateToStorageValue: () => T | undefined | null; }; /** * Sync the state of the store to the web storage * @param key - the key to use in the web storage * @param type - 'session' or 'local' storage * @param saveStateChangesAfterMs - save the state to the storage after this many milliseconds, 0 to disable * @param restoreOnInit - restore the state from the storage on init * @param filterState - filter the state before saving to the storage (mutually exclusive with valueMapper) * @param valueMapper - custom transformation between store state and storage value (mutually exclusive with filterState) * @param onRestore - callback after the state is restored from the storage * @param expires - storage will not be loaded if is older than this many milliseconds * * @example * // Example 1: Using filterState to save specific state properties * const store = signalStore( * withEntities({ entity, collection }), * withCallStatus({ prop: collection, initialValue: 'loading' }), * * withSyncToWebStorage({ * key: 'my-key', * type: 'session', * restoreOnInit: true, * saveStateChangesAfterMs: 300, * // optionally, filter the state before saving to the storage * filterState: ({ orderItemsEntityMap, orderItemsIds }) => ({ * orderItemsEntityMap, * orderItemsIds, * }), * }), * ); * * @example * // Example 2: Using valueMapper for custom transformation * const store = signalStore( * withState({ * userProfile: { * userName: '', * email: '', * preferences: { theme: 'light', notifications: true }, * tempData: null, * } * }), * * withSyncToWebStorage({ * key: 'user-form', * type: 'local', * restoreOnInit: true, * saveStateChangesAfterMs: 500, * // Custom mapper to store only userName and email * valueMapper: (store) => ({ * stateToStorageValue: () => ({ * userName: store.userProfile().userName, * email: store.userProfile().email, * }), * storageValueToState: (savedData) => { * patchState(store, { * userProfile: { * ...store.userProfile(), * userName: savedData.userName, * email: savedData.email, * } * }); * }, * }), * }), * ); * * // generates the following methods * store.saveToStorage(); * store.loadFromStorage(); * store.clearFromStore(); * */ declare function withSyncToWebStorage({ key, type: storageType, saveStateChangesAfterMs, restoreOnInit, onRestore, expires, ...rest }: { key: string; type: 'session' | 'local'; restoreOnInit?: boolean; saveStateChangesAfterMs?: number; expires?: number; onRestore?: (store: StoreSource) => void; } & ({ filterState: (state: Input['state']) => Partial; } | { valueMapper: StorageValueMapper>; } | {})): SignalStoreFeature void; loadFromStorage: () => void; clearFromStore: () => void; }; }>; type TransferValueMapper = Record> = (store: Store) => { transferValueToState: (value: T) => void; stateToTransferValue: () => T | undefined | null; }; /** * Sync the state of the store using Angular's TransferState API for SSR * @param key - the key to use in the TransferState * @param filterState - filter the state before saving to TransferState (mutually exclusive with valueMapper) * @param valueMapper - custom transformation between store state and transfer value (mutually exclusive with filterState) * @param onRestore - callback after the state is restored from TransferState * * @example * // Example 1: Using filterState to transfer specific state properties * const store = signalStore( * withEntities({ entity, collection }), * withCallStatus({ prop: collection, initialValue: 'loading' }), * * withServerStateTransfer({ * key: 'my-state', * // optionally, filter the state before transferring * filterState: ({ orderItemsEntityMap, orderItemsIds }) => ({ * orderItemsEntityMap, * orderItemsIds, * }), * }), * ); * * @example * // Example 2: Using valueMapper for custom transformation * const store = signalStore( * withState({ * userProfile: { * userName: '', * email: '', * preferences: { theme: 'light', notifications: true }, * tempData: null, * } * }), * * withServerStateTransfer({ * key: 'user-profile', * // Custom mapper to transfer only userName and email * valueMapper: (store) => ({ * stateToTransferValue: () => ({ * userName: store.userProfile().userName, * email: store.userProfile().email, * }), * transferValueToState: (savedData) => { * patchState(store, { * userProfile: { * ...store.userProfile(), * userName: savedData.userName, * email: savedData.email, * } * }); * }, * }), * }), * ); * */ declare function withServerStateTransfer({ key, onRestore, ...rest }: { key: string; onRestore?: (store: StoreSource) => void; } & ({ filterState: (state: Input['state']) => Partial; } | { valueMapper: TransferValueMapper>; } | {})): SignalStoreFeature; /** * Syncs entities filter, pagination, sort and single selection to route query params for local or remote entities store features. If a collection is provided, it will be used as a prefix (if non is provided) for the query params. * The prefix can be disabled by setting it to false, or changed by providing a string. The filterMapper can be used to customize how the filter object is map to a query params object, * when is not provided the filter will use JSON.stringify to serialize the filter object. * * Requires withEntities and withCallStatus to be present in the store. * * @param config.collection The collection name to use as a prefix for the query params. If not provided, the collection name will be used. * @param config.filterMapper A function to map the filter object to a query params object. * @param config.prefix The prefix to use for the query params. If set to false, the prefix will be disabled. * @param config.onQueryParamsLoaded A function to be called when the query params are loaded into the store, (only gets called once). * @param config.defaultDebounce The default debounce time to use sync the store changes back to the route query params. * @param config.skipLoadingCall When true, restoring state from query params will update the store state but will not trigger a backend call to fetch entities. Default is false. * * @example * export const ProductsRemoteStore = signalStore( * { providedIn: 'root' }, * // requires at least withEntities and withCallStatus * withEntities({ entity, collection }), * withCallStatus({ collection, initialValue: 'loading' }), * withEntitiesRemoteFilter({ * entity, * collection, * }), * withEntitiesRemotePagination({ * entity, * collection, * }), * withEntitiesRemoteSort({ * entity, * collection, * defaultSort: { field: 'name', direction: 'asc' }, * }), * withEntitiesLoadingCall({ * collection, * fetchEntities: ({ productEntitiesFilter, productEntitiesPagedRequest, productEntitiesSort }) => { * return inject(ProductService) * .getProducts({ * search: productEntitiesFilter().name, * take: productEntitiesPagedRequest().size, * skip: productEntitiesPagedRequest().startIndex, * sortColumn: productEntitiesSort().field, * sortAscending: productEntitiesSort().direction === 'asc', * }) * .pipe( * map((d) => ({ * entities: d.resultList, * total: d.total, * })), * ); * }, * }), * // syncs the entities filter, pagination, sort and single selection to the route query params * withEntitiesSyncToRouteQueryParams({ * entity, * collection, * }) *); * * @example * // this feature only covers the entities params, any other state that should * // survive a reload is synced by adding withSyncToRouteQueryParams with a * // mapper generated by getQueryMapperForState, the two live side by side * export const ProductsRemoteStore = signalStore( * { providedIn: 'root' }, * withEntities({ entity, collection }), * withCallStatus({ collection, initialValue: 'loading' }), * withEntitiesRemoteFilter({ entity, collection }), * withEntitiesRemotePagination({ entity, collection }), * withEntitiesLoadingCall({ collection, fetchEntities: () => ... }), * withState({ * view: 'grid' as 'grid' | 'list', * showSold: false, * day: new Date(), * }), * // 👇 product-filter, product-page, product-pageSize, product-selectedId * withEntitiesSyncToRouteQueryParams({ * entity, * collection, * }), * // 👇 view, showSold and day, the prop names and their types are checked * // against the state above * withSyncToRouteQueryParams({ * mappers: [ * getQueryMapperForState({ * view: 'string', * showSold: 'boolean', * day: 'date', * }), * ], * }), *); * // ?product-filter=%7B%22search%22%3A%22tv%22%7D&product-page=2 * // &view=grid&showSold=true&day=2026-08-11 * * @example * // the filter is serialized with JSON.stringify by default, which makes it * // the longest param in the url. getFilterQueryMapper builds a filterMapper * // that spreads it over one param per field instead, the filter type has to * // be given since it cannot be inferred from the store * type ProductFilter = { search: string; maxPrice: number; from: Date }; * * export const ProductsRemoteStore = signalStore( * { providedIn: 'root' }, * withEntities({ entity, collection }), * withCallStatus({ collection, initialValue: 'loading' }), * withEntitiesRemoteFilter({ * entity, * collection, * defaultFilter: { search: '', maxPrice: 0, from: new Date() }, * }), * withEntitiesLoadingCall({ collection, fetchEntities: () => ... }), * withEntitiesSyncToRouteQueryParams({ * entity, * collection, * filterMapper: getFilterQueryMapper({ * search: 'string', * maxPrice: 'number', * from: 'date', * }), * }), *); * // ?product-search=tv&product-maxPrice=100&product-from=2026-08-11 * // instead of ?product-filter=%7B%22search%22%3A%22tv%22%2C%22maxPrice%22... */ declare function withEntitiesSyncToRouteQueryParams(config: { entity: Entity; collection?: Collection; filterMapper?: FilterQueryMapper; prefix?: string | false; onQueryParamsLoaded?: (store: StoreSource) => void; defaultDebounce?: number; restoreOnInit?: boolean; skipLoadingCall?: boolean; syncFilter?: boolean; syncPagination?: boolean; syncSort?: boolean; syncSingleSelection?: boolean; syncMultiSelection?: boolean; }): SignalStoreFeature & RequireEntitiesCallStatus, { state: {}; props: {}; methods: { loadFromQueryParams: () => void; }; }>; declare function getQueryMapperWithPrefix(config: { prefix: string; mapper: QueryMapper; }): QueryMapper; /** * Syncs the route query params with the store and back. On init it will load * the query params once and set them in the store using the mapper.queryParamsToState, after that * and change on the store will be reflected in the query params using the mapper.stateToQueryParams * @param config.mappers - The mappers to sync the query params with the store * @param config.defaultDebounce - The debounce time to wait before updating the query params from the store * * @example * const Store = signalStore( * withState({ * test: 'test', * foo: 'foo', * bar: false, * }), * withSyncToRouteQueryParams({ * mappers: [ * { * queryParamsToState: (query, store) => { * // set the query params in the store (only called once on init) * patchState(store, { * test: query.test, * foo: query.foo, * bar: query.bar === 'true', * }); * }, * stateToQueryParams: (store) => * // return the query params to be set in the route * computed(() => ({ * test: store.test(), * foo: store.foo(), * bar: store.bar().toString(), * })), * }, * ], * defaultDebounce: debounce, * }), * ); */ declare function withSyncToRouteQueryParams, Mappers extends ReadonlyArray, Input>>>(config: { mappers: Mappers; defaultDebounce?: number; restoreOnInit?: boolean; onQueryParamsStored?: (store: StoreSource) => void; }): SignalStoreFeature void; }; }>; /** * Store feature that provides access to route params, query params, and route data. * Creates a computed signal for each property returned by the mapParams function. * * @param mapParams Function receiving `{ params, queryParams, data }` and returning an object. * * @example * // Combined params, queryParams and data * const Store = signalStore( * withRoute(({ params, queryParams, data }) => ({ * id: params['id'] as string, * tab: queryParams['tab'] as string, * title: data?.['title'] as string, * })), * withHooks(({ id, tab, title }) => ({ * onInit: () => console.log(`Product ID: ${id()}, tab: ${tab()}, title:${title()}` ), * })), * ); */ declare function withRoute>(mapParams: (options: { params: Params; queryParams: Params; data?: { [key: string | symbol]: any; }; }) => T): _ngrx_signals.SignalStoreFeature<_ngrx_signals.EmptyFeatureResult, { state: {}; props: { [P in keyof { [K in keyof T]: Signal; }]: { [K in keyof T]: Signal; }[P] extends Signal ? { [K in keyof T]: Signal; }[P] : { [K in keyof T]: Signal; }[P] extends () => infer V ? Signal : never; }; methods: {}; }>; /** * @deprecated use withRoute instead * This store feature provides access to the route params. The mapParams receives the route params object, use it to transform it * to an object, this will create a computed for each prop return by the mapParams function * @param mapParams A function to transform the params before they are stored. * * @example * * // example route /products/:id/ * const ProductDetailStore = signalStore( * withRouteParams(({ id }) => ({ id })), * withCalls(() => ({ * loadProductDetail: (id: string) => * inject(ProductService).getProductDetail(id), * })), * withHooks(({ loadProductDetail, id }) => ({ * onInit: () => { * loadProductDetail(id()); * }, * })), * ); */ declare function withRouteParams>(mapParams: (params: Params, data?: any) => T): _ngrx_signals.SignalStoreFeature<_ngrx_signals.EmptyFeatureResult, { state: {}; props: { [P in keyof { [K in keyof T]: Signal; }]: { [K in keyof T]: Signal; }[P] extends Signal ? { [K in keyof T]: Signal; }[P] : { [K in keyof T]: Signal; }[P] extends () => infer V ? Signal : never; }; methods: {}; }>; type RecordSignals = { [K in keyof T]: Signal; }; /** * Binds component inputs to the store, so that the store is updated with the latest values of the inputs. * @param inputs * * @example * * const Store = signalStore( * withInputBindings({ * pageIndex: 0, * length: 0, * pageSize: 10, * pageSizeOptions: [5, 10, 20], * }), * //... other features that use foo and bar * ); * // generates the signals * store.pageIndex(); // 0 * store.length(); // 0 * store.pageSize(); // 10 * store.pageSizeOptions(); // [5, 10, 20] * * generates the method * store.bindInputs({ pageIndex: Signal, length: Signal, pageSize: Signal, pageSizeOptions: Signal }); * * // use in a component * class PaginatorComponent { * readonly pageIndex = input.required; * readonly length = input.required; * readonly pageSize = input.required; * readonly pageSizeOptions = input.required; * readonly store = inject(Store); * constructor() { * this.store.bindInputs({ * pageIndex: this.pageIndex, * length: this.length, * pageSize: this.pageSize, * pageSizeOptions: this.pageSizeOptions, * }); * // if the inputs have the same name and type as the store, * // you can use bindInputs like * // this.store.bindInputs(this); * } * } * */ declare function withInputBindings = RecordSignals>(inputs: Object): _ngrx_signals.SignalStoreFeature<_ngrx_signals.EmptyFeatureResult, { state: { _inputsInitialized: boolean; } & Object extends infer T ? { [K in keyof T]: T[K]; } : never; props: {}; methods: { bindInputs: (inputs: Inputs) => { destroy: () => void; }; }; }>; /** * This store feature allows access to the store's state, methods, computed signals, to store features that don't have a config factory that * can access the store. This can be useful for creating store features that need to access the store's state, methods, computed signals, etc. or to wrap store * features that don't have a config factory that can access the store. * @param featureFactory * * @example * // Use case 1: allow a custome store feature that receives a plain config object to access the store's state, methods, computed signals. * function withCustomFeature(config: { fooValue: string }) { * ...// create a custom store feature * } * const Store = signalStore( * withState({ foo: 'foo' }), * // 👇use previous state to configure custom feature * withFeatureFactory(({ foo }) => withCustomFeature({ fooValue: foo() })), * // you can also use a signalStoreFeature inside withFeatureFactory * withFeatureFactory(({ foo }) => * signalStoreFeature( * withState({ foo2: foo() }), * // ... other store features, * ), * ), * ); * * // Use case 2: use withFeatureFactory inside a custom feature to create a store whose config can be a factory that receives the store * function withCustomFeature2( * configFactory: FeatureConfigFactory, * ): SignalStoreFeature< * Input, * { * state: { foo: string }; * computed: { bar: Signal }; * methods: { baz: () => number }; * } * > { * return withFeatureFactory((store: StoreSource) => { * const config = getFeatureConfig(configFactory, store); * return signalStoreFeature( * withState<{ foo: string }>({ foo: config.fooValue }), * withComputed(({ foo }) => ({ bar: computed(() => foo() + 1) })), * withMethods(({ foo, bar }) => ({ * baz: () => foo() + bar() + 2, * })), * ); * }) as any; * } * * // now withCustomFeature2 can be used like : * const Store = signalStore( * // withCustomFeature2({ fooValue: 'foo' }), // usual way with a plain object * withState({ fooValue: 'foo' }), * // or with a factory that receives the store * withCustomFeature2(({ fooValue }) => ({ fooValue: fooValue() })), * ); */ declare function withFeatureFactory>(featureFactory: (store: StoreSource) => Feature): SignalStoreFeature ? In : never), Feature extends SignalStoreFeature ? Out : never>; type CallStatus = { loading: Signal; error: Signal; }; /** * Adds methods to the store to track the status of all calls in the store * @example * export const ProductsLocalStore = signalStore( * withAllCallStatus(), // <-- add this line * withEntities({ entity, collection }), * withCallStatus({ collection, initialValue: 'loading' }), * withEntitiesLoadingCall({ * collection, * fetchEntities: () => { * return inject(ProductService) * .getProducts() * .pipe(map((d) => d.resultList)); * }, * }), * withCalls(() => ({ * loadProductDetail: callConfig({ * call: ({ id }: { id: string }) => * inject(ProductService).getProductDetail(id), * resultProp: 'productDetail', * }), * checkout: () => inject(OrderService).checkout(), * })), * ); * // generates the following methods * store.isAnyCallLoading() // Signal * store.callsErrors // () => Signals */ declare function withAllCallStatus(): _ngrx_signals.SignalStoreFeature<_ngrx_signals.EmptyFeatureResult, { state: { _allCallStatus: CallStatus[]; }; props: { isAnyCallLoading: _angular_core.Signal; callsErrors: _angular_core.Signal; }; methods: { _registerCallStatus: (callStatus: CallStatus) => void; }; }>; declare function registerCallState(store: Record, callStatus: CallStatus): void; type ObservableCall = (arg: Param) => Observable; type PromiseCall = (arg: Param) => Promise; type Call = ObservableCall | PromiseCall; type EntityCall) = string | number | Entity | ({ entity: Entity; } & Record), Result extends Partial | undefined = Partial | undefined> = Call; type EntityCallConfig = { /** * The main function to be called. */ call: Call; /** * function that returns the entity id in the params * @param param */ paramsSelectId?: (param: NoInfer) => string; /** * default is true, if false disables automatically storing the result of the * function, to allow you do your own implementation using onSuccess. */ storeResult?: boolean; /** * Callback function invoked on successful completion of the call. * Receives the result of the call and the parameter used. */ onSuccess?: (result: NoInfer, param: NoInfer, previousResult: NoInfer | undefined) => void; /** * A function to transform an error from the call into a custom `Error` type. * Receives the error and the parameter used. */ mapError?: (error: unknown, param: NoInfer) => Error; /** * Callback function invoked if the call encounters an error. * Receives the mapped error and the parameter used. */ onError?: (error: Error, param: NoInfer) => void; /** * A function with condition that determines whether the call should be skipped. * The function accepts the call parameter and must return a boolean | Observable. */ skipWhen?: Call, boolean> | (() => boolean) | ((param: NoInfer, previousResult: NoInfer | undefined) => boolean); /** * Reactively execute the call with the provided params. * Supports the following: * - A direct parameter value. Which execute the call once on init. * - A function or `Observable` emitting the parameter of the call or undefined. * - A function returning the parameter or undefined. * * **Warning**: By default, when withCall is a function, signal * or observable that when returns a falsy value it will skip the call. * To override this behavior, define a skipWhen with your own rule or skipWhen: () => false * to always execute on any value. */ callWith?: Param extends undefined ? Observable | (() => boolean) | boolean : NoInfer | null | undefined | Observable> | (() => NoInfer | null | undefined); }; type ExtractEntityCallErrorType | EntityCallConfig> = T extends EntityCallConfig ? E : unknown; type NamedEntitiesCallsStatusComputed | EntityCallConfig>> = { [K in keyof Calls as K extends `_${infer J}` ? `_isAny${Capitalize}Loading` : `isAny${Capitalize}Loading`]: Signal; } & { [K in keyof Calls as K extends `_${infer J}` ? `_areAll${Capitalize}Loaded` : `areAll${Capitalize}Loaded`]: Signal; } & { [K in keyof Calls as `${K & string}Errors`]: Calls[K] extends EntityCallConfig ? Signal : Signal; }; type NamedEntitiesCallsStatusMethods | EntityCallConfig>> = { [K in keyof Calls as K extends `_${infer J}` ? `_is${Capitalize}Loading` : `is${Capitalize}Loading`]: (entityOrId: Entity | string | number) => boolean; } & { [K in keyof Calls as K extends `_${infer J}` ? `_is${Capitalize}Loaded` : `is${Capitalize}Loaded`]: (entityOrId: Entity | string | number) => boolean; } & { [K in keyof Calls as `${K & string}Error`]: Calls[K] extends EntityCallConfig ? (entityOrId: Entity | string | number) => Error | undefined : (entityOrId: Entity | string | number) => unknown | undefined; }; /** * Generates necessary state, computed and methods to track the progress of * calls related to an entity and merges the result back to entities list. The generated methods are rxMethods with * the same name as the original call, which accepts either the original parameters * or a Signal or Observable of the same type as the original parameters. * The original call can only have zero or one parameter, use an object with multiple * props as first param if you need more. * *Important* The calls must have a parameter of type Entity {entity: Entity, ...extra params} or use entityCallConfig * and the paramsSelectId to return with param prop represents the entityId . * The call can be skipped based on the result of the previous call, to skip a call return undefined or false. * @param config.entity - The entity type to be used * @param config.collection - The optional collection name to be used * @param config.selectId - The function to use to select the id of the entity * @param config.callsFactory - a factory function that receives the store and returns an object of type {Record} with the calls to be made * * @example * const orderEntity = entityConfig({ * entity: type(), * collection: 'order', * }); * export const OrderStore = signalStore( * withEntities(orderEntity), * withEntitiesCalls({ * ...orderEntity, * calls: (store, orderService = inject(OrderService)) => ({ * loadOrderDetail: (entity) => orderService.getOrderDetail(entity.id), * // alternative way to define the call * // loadOrderDetail: entityCallConfig({ * // call: (entity: OrderSummary) => orderService.getOrderDetail(entity.id), * // // skip the call if result is already loaded * // skipWhen: (param, previousResult) => !!previousResult?.items, * // }), * changeOrderStatus: (option: { * entity: OrderSummary; * status: OrderSummary['status']; * }) => orderService.changeStatus(option.entity.id, option.status), * deleteOrder: (entity: OrderSummary) => { * return orderService.delete(entity.id).pipe( * map((deleted) => { * deleted ? undefined : entity; // returning undefined will remove the entity from the store * }), * ); * }, * }), * }), * ); * * // generates the following signals * store.loadOrderDetailCallStatus // a map { [id: string]:'init' | 'loading' | 'loaded' | { error: unknown }} * // similar for changeOrderStatus and deleteOrder * * // the calls updates the entities so results, which can be accessed with the usual entities list computed signals * // generates the following computed signals * store.isAnyLoadOrderDetailLoading: Signal * store.isAnyLoadOrderDetailLoaded: Signal * store.loadOrderDetailErrors: Signal * // same for changeOrderStatus and deleteOrder * // generates the following methods * store.isLoadOrderDetailLoading(id: string) => boolean * store.isLoadOrderDetailLoaded(id: string) => boolean * store.loadOrderDetailError(id: string) => string | null * store.loadOrderDetail // ({id: string}) => Promise<{value, ok: true} | {error, ok: false}> * // passing a Signal or Observable instead returns an RxMethodRef * // same for changeOrderStatus and deleteOrder * */ declare function withEntitiesCalls> | EntityCallConfig>>, Collection extends string = ''>(config: { entity: Entity; collection?: Collection; selectId?: SelectEntityId>; calls: (store: StoreSource) => Calls; }): SignalStoreFeature, { state: NamedCallStatusMapState; props: NamedEntitiesCallsStatusComputed; methods: NamedEntitiesCallsStatusMethods & { [K in keyof Calls]: Calls[K] extends (...args: infer P) => any ? { (param: P[0]): Promise<{ value: Signal; ok: true; } | { error: Signal>; ok: false; }>; (param: Observable | (() => P[0])): RxMethodRef; } : Calls[K] extends EntityCallConfig ? Parameters extends undefined[] ? never : { (...param: Parameters): Promise<{ value: Signal; ok: true; } | { error: Signal>; ok: false; }>; (param: Observable[0]> | (() => Parameters[0])): RxMethodRef; } : never; }; }>; /** * Call configuration object for withEntitiesCalls * @param config - the call configuration * @param config.call - required, the function that will be called * @param config.mapResult - required, a function to transform the result of the call to the entity * @param config.entityId - required, a function that returns the entity id in the params * @param config.onSuccess - optional, a function that will be called when the call is successful * @param config.mapError - optional, a function that will be called to transform the error before storing it * @param config.onError - optional, a function that will be called when the call fails * @param config.skipWhen - optional, a function that will be called to determine if the call should be skipped * @param config.callWith - optional, reactively execute the call with the provided params return by a function or observable * @param config.defaultResult - optional, A default value for the result before the call is executed */ declare function entityCallConfig | undefined, Error = unknown, C extends EntityCallConfig = EntityCallConfig>(config: Omit, 'storeResult' | 'paramsSelectId'> & { paramsSelectId: (param: NoInfer) => string; }): EntityCallConfig; declare function entityCallConfig = EntityCallConfig>(config: Omit, 'storeResult' | 'paramsSelectId'> & { paramsSelectId: (param: NoInfer) => string; storeResult: false; }): EntityCallConfig; declare function entityCallConfig), Result = Partial | undefined, Error = unknown, C extends EntityCallConfig = EntityCallConfig>(config: Omit, 'storeResult' | 'paramsSelectId'>): EntityCallConfig; type CacheKey = string | (string | object)[]; interface CacheData { value: any; date: number; expires?: number; invalid: boolean; hitCount: number; } interface CacheKeys { keys?: Map; data?: CacheData; } type CacheState = CacheKeys; type CacheValue = string | number | boolean | bigint | object | null | undefined; declare class CacheStore implements OnDestroy { cacheState: CacheState; skipAllCache: boolean; private intervalCancelKey; private isBrowser; constructor({ clearExpiredEvery }: { clearExpiredEvery: number; }); get({ key }: { key: CacheKey; }): _ngrx_traits_signals.CacheData | undefined; set({ key, value: valueOrFn, expires, maxCacheSize, }: { key: CacheKey; value: ((previousValue?: T) => T) | T; expires?: number; maxCacheSize?: number; }): void; invalidate({ key }: { key: CacheKey; }): void; delete({ key }: { key: CacheKey; }): void; setSkipCacheForAllCalls(skipAllCache: boolean): void; clear(): void; ngOnDestroy(): void; } declare function setGlobalCache(cache: CacheStore): void; declare function getGlobalCache(): CacheStore; /** * Return the cached results of the key if available, otherwise return the value of source and cache it * @param options */ declare function cacheCall({ key, call, expires, maxCacheSize, skip, cacheStore, }: { key: CacheKey; call: () => Promise; expires?: number; maxCacheSize?: number; skip?: boolean; cacheStore?: CacheStore; }): Promise; /** * Return the cached results of the key if available, otherwise return the value of source and cache it * @param options */ declare function cacheRxCall({ key, call, expires, maxCacheSize, skip, cacheStore, }: { key: CacheKey; call: Observable; expires?: number; maxCacheSize?: number; skip?: boolean; cacheStore?: CacheStore; }): Observable; declare function provideCacheStore(cacheStore: CacheStore): Provider[]; /** * Setter generated by `withStateSetter` for a state prop of type T. * Accepts a plain value, a signal/reactive fn (keeps state in sync via * `signalMethod`), or an updater `(current) => next` for partial updates. */ type StateSetter = ((input: T | (() => T) | ((current: T) => T), config?: { injector?: Injector; }) => EffectRef) & EffectRef; /** Prop name without its leading underscore, if it has one. */ type Unprefixed

= P extends `_${infer R}` ? R : P; /** * Name of the setter generated by `withStateSetter` for state prop P: always * the public `set

`, a leading underscore in the prop name is dropped * (`_filter` -> `setFilter`). */ type SetterKey

= `set${Capitalize>}`; /** * Name of the setter generated by `withStatePrivateSetter` for state prop P: * always the private `_set

`, regardless of whether P itself starts with an * underscore (`filter` and `_filter` both -> `_setFilter`). */ type PrivateSetterKey

= `_set${Capitalize>}`; /** * Generates a `set()` method for each provided state prop that patches * the state with the given value. The generated methods are `signalMethod`s, * so they accept either a plain value or a signal/computed; when given a * signal, the state is kept in sync with it. They also accept an updater * function `(current) => next` receiving the current prop value, for partial * updates. * * The generated setters are always public: a leading underscore in the prop * name is dropped (`_filter` -> `setFilter`). Use `withStatePrivateSetter` to * generate setters only accessible inside the store. * * Note: updaters are told apart from reactive fns by arity — an updater must * declare its parameter (`(current) => ...`), a zero-arg fn is treated as a * reactive computation. * * @param keys - names of state props to generate setters for * * @example * const Store = signalStore( * withState({ * a: { b: '' }, * c: 1, * d: 12, * }), * withStateSetter('a', 'd'), * ); * // generates the methods * store.setA({ b: 'hello' }); // patchState(store, { a: { b: 'hello' } }) * store.setD(42); // patchState(store, { d: 42 }) * // updater fn receives the current prop value, for partial updates: * store.setA((a) => ({ ...a, b: 'partial' })); * // signalMethod also accepts signals, keeping state in sync: * store.setD(this.dInput); // dInput: Signal */ declare function withStateSetter(...keys: K[]): SignalStoreFeature]: StateSetter; }; }>; /** * Like `withStateSetter`, but the generated setters are private: for each * provided state prop it generates a `_set()` method, which starts with * an underscore, making it only accessible inside the store. Useful to allow a * prop to be publicly readable while only writable from within the store. * * The generated methods behave exactly like the ones from `withStateSetter`: * they are `signalMethod`s that accept a plain value, a signal/computed (keeps * state in sync), or an updater function `(current) => next` for partial * updates. A leading underscore in the prop name is dropped, so `filter` and * `_filter` both generate `_setFilter`. * * @param keys - names of state props to generate private setters for * * @example * const Store = signalStore( * withState({ * filter: '', * d: 12, * }), * withStatePrivateSetter('filter'), * withMethods((store) => ({ * clearFilter: () => store._setFilter(''), // usable inside the store * })), * ); * // outside the store, filter is readable but _setFilter is not accessible * store.filter(); */ declare function withStatePrivateSetter(...keys: K[]): SignalStoreFeature]: StateSetter; }; }>; export { CacheStore, cacheCall, cacheRxCall, callConfig, entityCallConfig, equalArray, equalAuto, equalByKey, equalSet, equalSetBy, equalStringify, getFeatureConfig, getFilterQueryMapper, getGlobalCache, getInfiniteScrollDataSource, getQueryMapperForState, getQueryMapperWithPrefix, provideCacheStore, registerCallState, resolveEqual, setGlobalCache, sortData, typedCallConfig, withAllCallStatus, withCallStatus, withCallStatusMap, withCalls, withEntitiesCalls, withEntitiesHybridFilter, withEntitiesLoadingCall, withEntitiesLocalFilter, withEntitiesLocalPagination, withEntitiesLocalSort, withEntitiesMultiSelection, withEntitiesRemoteFilter, withEntitiesRemotePagination, withEntitiesRemoteScrollPagination, withEntitiesRemoteSort, withEntitiesSingleSelection, withEntitiesSyncToRouteQueryParams, withFeatureFactory, withInputBindings, withLink, withLinkEntitiesFilter, withLinkEntitiesMultiSelection, withLinkEntitiesSingleSelection, withLinkEntitiesSort, withLogger, withRoute, withRouteParams, withServerStateTransfer, withStatePrivateSetter, withStateSetter, withSyncToRouteQueryParams, withSyncToWebStorage }; export type { CacheData, CacheKey, CacheKeys, CacheState, CacheValue, Call$1 as Call, CallConfig, CallStatus$1 as CallStatus, CallStatusComputed, CallStatusMethods, CallStatusState, CdkSort, EntitiesCallStatusRequirement, EntitiesFilterComputed, EntitiesFilterMethods, EntitiesFilterState, EntitiesMultiSelectionComputed, EntitiesMultiSelectionMethods, EntitiesMultiSelectionState, EntitiesPaginationLocalComputed, EntitiesPaginationLocalMethods, EntitiesPaginationLocalState, EntitiesPaginationRemoteComputed, EntitiesPaginationRemoteMethods, EntitiesPaginationRemoteState, EntitiesRequirement, EntitiesScrollPaginationComputed, EntitiesScrollPaginationMethods, EntitiesScrollPaginationState, EntitiesSingleSelectionComputed, EntitiesSingleSelectionMethods, EntitiesSingleSelectionState, EntitiesSortMethods, EntitiesSortState, EntityCall, EntityCallConfig, EqualName, EqualOption, ExtractCallResultPropName, ExtractCallResultType, ExtractEntityCallErrorType, ExtractErrorType, ExtractStoreFeatureOutput, FeatureConfigFactory, FilterOptions, LinkComputedOptions, LinkMethod, LinkOptions, LinkSetter, LinkSourceOptions, MissingFeatureError, NamedCallStatusComputed, NamedCallStatusMapComputed, NamedCallStatusMapMethods, NamedCallStatusMapState, NamedCallStatusMethods, NamedCallStatusState, NamedCallsStatusComputed, NamedEntitiesCallsStatusComputed, NamedEntitiesCallsStatusMethods, NamedEntitiesFilterComputed, NamedEntitiesFilterMethods, NamedEntitiesFilterState, NamedEntitiesMultiSelectionComputed, NamedEntitiesMultiSelectionMethods, NamedEntitiesMultiSelectionState, NamedEntitiesPaginationLocalComputed, NamedEntitiesPaginationLocalMethods, NamedEntitiesPaginationLocalState, NamedEntitiesPaginationRemoteComputed, NamedEntitiesPaginationRemoteMethods, NamedEntitiesPaginationRemoteState, NamedEntitiesScrollPaginationComputed, NamedEntitiesScrollPaginationMethods, NamedEntitiesScrollPaginationState, NamedEntitiesSingleSelectionComputed, NamedEntitiesSingleSelectionMethods, NamedEntitiesSingleSelectionState, NamedEntitiesSortMethods, NamedEntitiesSortState, NamedSetEntitiesResult, ObservableCall$1 as ObservableCall, PaginationState, PrivateSetterKey, PromiseCall$1 as PromiseCall, QueryMapper, QueryParamType, RequireEntities, RequireEntitiesCallStatus, RequireEntitiesFilter, RequireEntitiesMultiSelection, RequireEntitiesSingleSelection, RequireEntitiesSort, RequireFeature, RxMethodRef, ScrollPaginationState, SetEntitiesResult, SetterKey, Sort, SortDirection, StateSetter, StoreSource, TransferValueMapper };