@mmstack/primitives 19.0.3 → 19.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { untracked, signal, computed } from '@angular/core';
1
+ import { untracked, signal, computed, isSignal, linkedSignal } from '@angular/core';
2
2
 
3
3
  /**
4
4
  * Converts a read-only `Signal` into a `WritableSignal` by providing custom `set` and, optionally, `update` functions.
@@ -132,6 +132,35 @@ function isDerivation(sig) {
132
132
  return 'from' in sig;
133
133
  }
134
134
 
135
+ function createReconciler(source, opt) {
136
+ const map = opt?.map ?? ((source, index) => source[index]);
137
+ return (length, prev) => {
138
+ if (!prev)
139
+ return Array.from({ length }, (_, i) => computed(() => map(source(), i), opt));
140
+ if (length === prev.source)
141
+ return prev.value;
142
+ if (length < prev.source) {
143
+ return prev.value.slice(0, length);
144
+ }
145
+ else {
146
+ const next = [...prev.value];
147
+ for (let i = prev.source; i < length; i++) {
148
+ next.push(computed(() => map(source(), i), opt));
149
+ }
150
+ return next;
151
+ }
152
+ };
153
+ }
154
+ function mapArray(source, opt) {
155
+ const data = isSignal(source) ? source : computed(source);
156
+ const length = computed(() => data().length);
157
+ const reconciler = createReconciler(data, opt);
158
+ return linkedSignal({
159
+ source: () => length(),
160
+ computation: (len, prev) => reconciler(len, prev),
161
+ });
162
+ }
163
+
135
164
  const { is } = Object;
136
165
  function mutable(initial, opt) {
137
166
  const baseEqual = opt?.equal ?? is;
@@ -187,5 +216,5 @@ function isMutable(value) {
187
216
  * Generated bundle index. Do not edit.
188
217
  */
189
218
 
190
- export { debounced, derived, isDerivation, isMutable, mutable, toFakeDerivation, toFakeSignalDerivation, toWritable };
219
+ export { debounced, derived, isDerivation, isMutable, mapArray, mutable, toFakeDerivation, toFakeSignalDerivation, toWritable };
191
220
  //# sourceMappingURL=mmstack-primitives.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mmstack-primitives.mjs","sources":["../../../../packages/primitives/src/lib/to-writable.ts","../../../../packages/primitives/src/lib/debounced.ts","../../../../packages/primitives/src/lib/derived.ts","../../../../packages/primitives/src/lib/mutable.ts","../../../../packages/primitives/src/mmstack-primitives.ts"],"sourcesContent":["import { Signal, untracked, WritableSignal } from '@angular/core';\n\n/**\n * Converts a read-only `Signal` into a `WritableSignal` by providing custom `set` and, optionally, `update` functions.\n * This can be useful for creating controlled write access to a signal that is otherwise read-only.\n *\n * @typeParam T - The type of value held by the signal.\n *\n * @param signal - The read-only `Signal` to be made writable.\n * @param set - A function that will be used to set the signal's value. This function *must* handle\n * the actual update mechanism (e.g., updating a backing store, emitting an event, etc.).\n * @param update - (Optional) A function that will be used to update the signal's value based on its\n * previous value. If not provided, a default `update` implementation is used that\n * calls the provided `set` function with the result of the updater function. The\n * default implementation uses `untracked` to avoid creating unnecessary dependencies\n * within the updater function.\n *\n * @returns A `WritableSignal` that uses the provided `set` and `update` functions. The `asReadonly`\n * method of the returned signal will still return the original read-only signal.\n *\n * @example\n * // Basic usage: Making a read-only signal writable with a custom set function.\n * const originalValue = signal({a: 0});\n * const readOnlySignal = computed(() => originalValue().a);\n * const writableSignal = toWritable(readOnlySignal, (newValue) => {\n * originalValue.update((prev) => { ...prev, a: newValue });\n * });\n *\n * writableSignal.set(5); // sets value of originalValue.a to 5 & triggers all signals\n */\nexport function toWritable<T>(\n signal: Signal<T>,\n set: (value: T) => void,\n update?: (updater: (value: T) => T) => void,\n): WritableSignal<T> {\n const internal = signal as WritableSignal<T>;\n internal.asReadonly = () => signal;\n internal.set = set;\n internal.update = update ?? ((updater) => set(updater(untracked(internal))));\n\n return internal;\n}\n","import {\n computed,\n type CreateSignalOptions,\n signal,\n untracked,\n type WritableSignal,\n} from '@angular/core';\nimport { toWritable } from './to-writable';\n\n/**\n * A `DebouncedSignal` is a special type of `WritableSignal` that delays updates\n * to its value. This is useful for scenarios where you want to avoid\n * frequent updates, such as responding to user input in a search field.\n * It keeps a reference to the original `WritableSignal` via the `original` property.\n *\n * @typeParam T - The type of value held by the signal.\n */\nexport type DebouncedSignal<T> = WritableSignal<T> & {\n /**\n * A reference to the original, un-debounced `WritableSignal`. This allows\n * you to access the immediate value (without the debounce delay) if needed,\n * and also ensures that any direct modifications to the original signal\n * are reflected in the debounced signal after the debounce period.\n */\n original: WritableSignal<T>;\n};\n\n/**\n * Options for creating a debounced signal.\n *\n * @typeParam T - The type of value held by the signal.\n */\nexport type CreateDebouncedOptions<T> = CreateSignalOptions<T> & {\n /**\n * The debounce delay in milliseconds. Defaults to 300.\n */\n ms?: number;\n};\n\n/**\n * Creates a debounced signal. The signal's value will only be propagated to\n * subscribers after a specified delay (debounce time) has passed since the last\n * time it was set or updated. Crucially, updates to the *original* signal\n * are also debounced.\n *\n * @see {@link DebouncedSignal}\n * @see {@link CreateDebouncedOptions}\n *\n * @example\n * ```typescript\n * // Create a debounced signal with an initial value and a custom delay.\n * const searchTerm = debounced('initial value', { ms: 500 });\n *\n * // Update the debounced signal. The actual update will be delayed by 500ms.\n * searchTerm.set('new value');\n *\n * // Access the original, un-debounced signal.\n * console.log(searchTerm.original()); // Outputs 'new value' (immediately)\n * // ... after 500ms ...\n * console.log(searchTerm()); // Outputs 'new value' (debounced)\n *\n * // Directly update the *original* signal.\n * searchTerm.original.set('direct update');\n * console.log(searchTerm.original()); // Outputs 'direct update' (immediately)\n * console.log(searchTerm()); // Outputs 'new value' (still debounced from the previous set)\n * // ... after 500ms ...\n * console.log(searchTerm()); // Outputs 'direct update' (now reflects the original signal)\n *\n * // Create a debounced signal with undefined initial value and default delay\n * const anotherSignal = debounced();\n * ```\n * @typeParam T - The type of the signal's value.\n * @param initial The initial value of the signal. Optional; defaults to `undefined`.\n * @param opt Configuration options for the signal, including the debounce delay (`ms`).\n * @returns A `DebouncedSignal` instance.\n */\nexport function debounced<T>(): DebouncedSignal<T | undefined>;\n/**\n * Creates a debounced signal with a defined initial value.\n *\n * @typeParam T - The type of the signal's value.\n * @param initial The initial value of the signal.\n * @param opt Configuration options for the signal, including the debounce delay (`ms`).\n * @returns A `DebouncedSignal` instance.\n */\nexport function debounced<T>(\n initial: T,\n opt?: CreateDebouncedOptions<T>,\n): DebouncedSignal<T>;\nexport function debounced<T>(\n value?: T,\n opt?: CreateDebouncedOptions<T | undefined>,\n): DebouncedSignal<T | undefined> {\n const sig = signal<T | undefined>(value, opt) as DebouncedSignal<\n T | undefined\n >;\n const ms = opt?.ms ?? 300;\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n\n const originalSet = sig.set;\n const originalUpdate = sig.update;\n\n const trigger = signal(false);\n\n // Set on the original signal, then trigger the debounced update\n const set = (value: T | undefined) => {\n originalSet(value); // Update the *original* signal immediately\n\n if (timeout) clearTimeout(timeout);\n timeout = setTimeout(() => {\n trigger.update((cur) => !cur); // Trigger the computed signal\n }, ms);\n };\n\n // Update on the original signal, then trigger the debounced update\n const update = (fn: (prev: T | undefined) => T | undefined) => {\n originalUpdate(fn); // Update the *original* signal immediately\n\n if (timeout) clearTimeout(timeout);\n timeout = setTimeout(() => {\n trigger.update((cur) => !cur); // Trigger the computed signal\n }, ms);\n };\n\n // Create a computed signal that depends on the trigger.\n // This computed signal is what provides the debounced behavior.\n const writable = toWritable(\n computed(() => {\n trigger();\n return untracked(sig);\n }),\n set,\n update,\n ) as DebouncedSignal<T | undefined>;\n\n writable.original = sig;\n\n return writable;\n}\n","import {\n computed,\n CreateSignalOptions,\n signal,\n untracked,\n type WritableSignal,\n} from '@angular/core';\nimport type { UnknownObject } from '@mmstack/object';\nimport { toWritable } from './to-writable';\n\n/**\n * Options for creating a derived signal using the full `derived` function signature.\n * @typeParam T - The type of the source signal's value (parent).\n * @typeParam U - The type of the derived signal's value (child).\n */\ntype CreateDerivedOptions<T, U> = CreateSignalOptions<U> & {\n /**\n * A function that extracts the derived value (`U`) from the source signal's value (`T`).\n */\n from: (v: T) => U;\n /**\n * A function that updates the source signal's value (`T`) when the derived signal's value (`U`) changes.\n * This establishes the two-way binding.\n */\n onChange: (newValue: U) => void;\n};\n\n/**\n * A `WritableSignal` that derives its value from another `WritableSignal` (the \"source\" signal).\n * It provides two-way binding: changes to the source signal update the derived signal, and\n * changes to the derived signal update the source signal.\n *\n * @typeParam T - The type of the source signal's value (parent).\n * @typeParam U - The type of the derived signal's value (child).\n */\nexport type DerivedSignal<T, U> = WritableSignal<U> & {\n /**\n * The function used to derive the derived signal's value from the source signal's value.\n * This is primarily for internal use and introspection.\n */\n from: (v: T) => U;\n};\n\n/**\n * Creates a `DerivedSignal` that derives its value from another `WritableSignal` (the source signal).\n * This overload provides the most flexibility, allowing you to specify custom `from` and `onChange` functions.\n *\n * @typeParam T - The type of the source signal's value.\n * @typeParam U - The type of the derived signal's value.\n * @param source - The source `WritableSignal`.\n * @param options - An object containing the `from` and `onChange` functions, and optional signal options.\n * @returns A `DerivedSignal` instance.\n *\n * @example\n * const user = signal({ name: 'John', age: 30 });\n * const name = derived(user, {\n * from: (u) => u.name,\n * onChange: (newName) => user.update((u) => ({ ...u, name: newName })),\n * });\n */\nexport function derived<T, U>(\n source: WritableSignal<T>,\n opt: CreateDerivedOptions<T, U>,\n): DerivedSignal<T, U>;\n\n/**\n * Creates a `DerivedSignal` that derives a property from an object held by the source signal.\n * This overload simplifies creating derived signals for object properties.\n *\n * @typeParam T - The type of the source signal's value (must be an object).\n * @typeParam TKey - The key of the property to derive.\n * @param source - The source `WritableSignal` (holding an object).\n * @param key - The key of the property to derive.\n * @param options - Optional signal options for the derived signal.\n * @returns A `DerivedSignal` instance.\n *\n * @example\n * const user = signal({ name: 'John', age: 30 });\n * const name = derived(user, 'name');\n */\nexport function derived<T extends UnknownObject, TKey extends keyof T>(\n source: WritableSignal<T>,\n key: TKey,\n opt?: CreateSignalOptions<T[TKey]>,\n): DerivedSignal<T, T[TKey]>;\n\n/**\n * Creates a `DerivedSignal` from an array, and derives an element by index.\n *\n * @typeParam T - The type of the source signal's value (must be an array).\n * @param source - The source `WritableSignal` (holding an array).\n * @param index - The index of the element to derive.\n * @param options - Optional signal options for the derived signal.\n * @returns A `DerivedSignal` instance.\n *\n * @example\n * const numbers = signal([1, 2, 3]);\n * const secondNumber = derived(numbers, 1); // secondNumber() === 2\n * secondNumber.set(5); // numbers() === [1, 5, 3]\n */\nexport function derived<T extends any[]>(\n source: WritableSignal<T>,\n index: number,\n opt?: CreateSignalOptions<T[number]>,\n): DerivedSignal<T, T[number]>;\n\nexport function derived<T, U>(\n source: WritableSignal<T>,\n optOrKey: CreateDerivedOptions<T, U> | keyof T,\n opt?: CreateSignalOptions<U>,\n): DerivedSignal<T, U> {\n const isArray =\n Array.isArray(untracked(source)) && typeof optOrKey === 'number';\n\n const from =\n typeof optOrKey === 'object' ? optOrKey.from : (v: T) => v[optOrKey] as U;\n const onChange =\n typeof optOrKey === 'object'\n ? optOrKey.onChange\n : isArray\n ? (next: U) => {\n source.update(\n (cur) =>\n (cur as unknown as any[]).map((v, i) =>\n i === optOrKey ? next : v,\n ) as T,\n );\n }\n : (next: U) => {\n source.update((cur) => ({ ...cur, [optOrKey]: next }));\n };\n\n const rest = typeof optOrKey === 'object' ? optOrKey : opt;\n\n const sig = toWritable<U>(\n computed(() => from(source()), rest),\n (newVal) => onChange(newVal),\n ) as DerivedSignal<T, U>;\n\n sig.from = from;\n\n return sig;\n}\n\n/**\n * Creates a \"fake\" `DerivedSignal` from a simple value. This is useful for creating\n * `FormControlSignal` instances that are not directly derived from another signal.\n * The returned signal's `from` function will always return the initial value.\n *\n * @typeParam T - This type parameter is not used in the implementation but is kept for type compatibility with `DerivedSignal`.\n * @typeParam U - The type of the signal's value.\n * @param initial - The initial value of the signal.\n * @returns A `DerivedSignal` instance.\n * @internal\n */\nexport function toFakeDerivation<T, U>(initial: U): DerivedSignal<T, U> {\n const sig = signal(initial) as DerivedSignal<T, U>;\n sig.from = () => initial;\n\n return sig;\n}\n\n/**\n * Creates a \"fake\" `DerivedSignal` from an existing `WritableSignal`. This is useful\n * for treating a regular `WritableSignal` as a `DerivedSignal` without changing its behavior.\n * The returned signal's `from` function returns the current value of signal, using `untracked`.\n *\n * @typeParam T - This type parameter is not used in the implementation but is kept for type compatibility with `DerivedSignal`.\n * @typeParam U - The type of the signal's value.\n * @param initial - The existing `WritableSignal`.\n * @returns A `DerivedSignal` instance.\n * @internal\n */\nexport function toFakeSignalDerivation<T, U>(\n initial: WritableSignal<U>,\n): DerivedSignal<T, U> {\n const sig = initial as DerivedSignal<T, U>;\n sig.from = () => untracked(initial);\n return sig;\n}\n\n/**\n * Type guard function to check if a given `WritableSignal` is a `DerivedSignal`.\n *\n * @typeParam T - The type of the source signal's value (optional, defaults to `any`).\n * @typeParam U - The type of the derived signal's value (optional, defaults to `any`).\n * @param sig - The `WritableSignal` to check.\n * @returns `true` if the signal is a `DerivedSignal`, `false` otherwise.\n */\nexport function isDerivation<T, U>(\n sig: WritableSignal<U>,\n): sig is DerivedSignal<T, U> {\n return 'from' in sig;\n}\n","import {\r\n signal,\r\n type CreateSignalOptions,\r\n type ValueEqualityFn,\r\n type WritableSignal,\r\n} from '@angular/core';\r\n\r\nconst { is } = Object;\r\n\r\n/**\r\n * A `MutableSignal` is a special type of `WritableSignal` that allows for in-place mutation of its value.\r\n * In addition to the standard `set` and `update` methods, it provides a `mutate` method. This is useful\r\n * for performance optimization when dealing with complex objects or arrays, as it avoids unnecessary\r\n * object copying.\r\n *\r\n * @typeParam T - The type of value held by the signal.\r\n */\r\nexport type MutableSignal<T> = WritableSignal<T> & {\r\n /**\r\n * Mutates the signal's value in-place. This is similar to `update`, but it's optimized for\r\n * scenarios where you want to modify the existing object directly rather than creating a new one.\r\n *\r\n * @param updater - A function that takes the current value as input and modifies it directly.\r\n *\r\n * @example\r\n * const myArray = mutable([1, 2, 3]);\r\n * myArray.mutate((arr) => {\r\n * arr.push(4);\r\n * return arr;\r\n * }); // myArray() now returns [1, 2, 3, 4]\r\n */\r\n mutate: WritableSignal<T>['update'];\r\n\r\n /**\r\n * Mutates the signal's value in-place, similar to `mutate`, but with a void-returning value in updater\r\n * function. This further emphasizes that the mutation is happening inline, improving readability\r\n * in some cases.\r\n * @param updater - Function to change to the current value\r\n * @example\r\n * const myObject = mutable({ a: 1, b: 2 });\r\n * myObject.inline((obj) => (obj.a = 3)); // myObject() now returns { a: 3, b: 2 }\r\n */\r\n inline: (updater: Parameters<WritableSignal<T>['update']>[0]) => void;\r\n};\r\n\r\n/**\r\n * Creates a `MutableSignal`. This function overloads the standard `signal` function to provide\r\n * the additional `mutate` and `inline` methods.\r\n *\r\n * @typeParam T - The type of value held by the signal.\r\n *\r\n * @param initial - The initial value of the signal. If no initial value is provided, it defaults to `undefined`.\r\n * @param options - Optional. An object containing signal options, including a custom equality function (`equal`).\r\n *\r\n * @returns A `MutableSignal` instance.\r\n *\r\n * @example\r\n * // Create a mutable signal with an initial value:\r\n * const mySignal = mutable({ count: 0 }) // MutableSignal<{ count: number }>;\r\n *\r\n * // Create a mutable signal with no initial value (starts as undefined):\r\n * const = mutable<number>(); // MutableSignal<number | undefined>\r\n *\r\n * // Create a mutable signal with a custom equality function:\r\n * const myCustomSignal = mutable({ a: 1 }, { equal: (a, b) => a.a === b.a });\r\n */\r\nexport function mutable<T>(): MutableSignal<T | undefined>;\r\nexport function mutable<T>(initial: T): MutableSignal<T>;\r\nexport function mutable<T>(\r\n initial: T,\r\n opt?: CreateSignalOptions<T>,\r\n): MutableSignal<T>;\r\n\r\nexport function mutable<T>(\r\n initial?: T,\r\n opt?: CreateSignalOptions<T>,\r\n): MutableSignal<T> {\r\n const baseEqual = opt?.equal ?? is;\r\n let trigger = false;\r\n\r\n const equal: ValueEqualityFn<T | undefined> = (a, b) => {\r\n if (trigger) return false;\r\n return baseEqual(a, b);\r\n };\r\n\r\n const sig = signal<T | undefined>(initial, {\r\n ...opt,\r\n equal,\r\n }) as MutableSignal<T>;\r\n\r\n const internalUpdate = sig.update;\r\n\r\n sig.mutate = (updater) => {\r\n trigger = true;\r\n internalUpdate(updater);\r\n trigger = false;\r\n };\r\n\r\n sig.inline = (updater) => {\r\n sig.mutate((prev) => {\r\n updater(prev);\r\n return prev;\r\n });\r\n };\r\n\r\n return sig;\r\n}\r\n\r\n/**\r\n * Type guard function to check if a given `WritableSignal` is a `MutableSignal`. This is useful\r\n * for situations where you need to conditionally use the `mutate` or `inline` methods.\r\n *\r\n * @typeParam T - The type of the signal's value (optional, defaults to `any`).\r\n * @param value - The `WritableSignal` to check.\r\n * @returns `true` if the signal is a `MutableSignal`, `false` otherwise.\r\n *\r\n * @example\r\n * const mySignal = signal(0);\r\n * const myMutableSignal = mutable(0);\r\n *\r\n * if (isMutable(mySignal)) {\r\n * mySignal.mutate(x => x + 1); // This would cause a type error, as mySignal is not a MutableSignal.\r\n * }\r\n *\r\n * if (isMutable(myMutableSignal)) {\r\n * myMutableSignal.mutate(x => x + 1); // This is safe.\r\n * }\r\n */\r\nexport function isMutable<T = any>(\r\n value: WritableSignal<T>,\r\n): value is MutableSignal<T> {\r\n return 'mutate' in value && typeof value.mutate === 'function';\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mmstack-primitives.mjs","sources":["../../../../packages/primitives/src/lib/to-writable.ts","../../../../packages/primitives/src/lib/debounced.ts","../../../../packages/primitives/src/lib/derived.ts","../../../../packages/primitives/src/lib/map-array.ts","../../../../packages/primitives/src/lib/mutable.ts","../../../../packages/primitives/src/mmstack-primitives.ts"],"sourcesContent":["import { Signal, untracked, WritableSignal } from '@angular/core';\n\n/**\n * Converts a read-only `Signal` into a `WritableSignal` by providing custom `set` and, optionally, `update` functions.\n * This can be useful for creating controlled write access to a signal that is otherwise read-only.\n *\n * @typeParam T - The type of value held by the signal.\n *\n * @param signal - The read-only `Signal` to be made writable.\n * @param set - A function that will be used to set the signal's value. This function *must* handle\n * the actual update mechanism (e.g., updating a backing store, emitting an event, etc.).\n * @param update - (Optional) A function that will be used to update the signal's value based on its\n * previous value. If not provided, a default `update` implementation is used that\n * calls the provided `set` function with the result of the updater function. The\n * default implementation uses `untracked` to avoid creating unnecessary dependencies\n * within the updater function.\n *\n * @returns A `WritableSignal` that uses the provided `set` and `update` functions. The `asReadonly`\n * method of the returned signal will still return the original read-only signal.\n *\n * @example\n * // Basic usage: Making a read-only signal writable with a custom set function.\n * const originalValue = signal({a: 0});\n * const readOnlySignal = computed(() => originalValue().a);\n * const writableSignal = toWritable(readOnlySignal, (newValue) => {\n * originalValue.update((prev) => { ...prev, a: newValue });\n * });\n *\n * writableSignal.set(5); // sets value of originalValue.a to 5 & triggers all signals\n */\nexport function toWritable<T>(\n signal: Signal<T>,\n set: (value: T) => void,\n update?: (updater: (value: T) => T) => void,\n): WritableSignal<T> {\n const internal = signal as WritableSignal<T>;\n internal.asReadonly = () => signal;\n internal.set = set;\n internal.update = update ?? ((updater) => set(updater(untracked(internal))));\n\n return internal;\n}\n","import {\n computed,\n type CreateSignalOptions,\n signal,\n untracked,\n type WritableSignal,\n} from '@angular/core';\nimport { toWritable } from './to-writable';\n\n/**\n * A `DebouncedSignal` is a special type of `WritableSignal` that delays updates\n * to its value. This is useful for scenarios where you want to avoid\n * frequent updates, such as responding to user input in a search field.\n * It keeps a reference to the original `WritableSignal` via the `original` property.\n *\n * @typeParam T - The type of value held by the signal.\n */\nexport type DebouncedSignal<T> = WritableSignal<T> & {\n /**\n * A reference to the original, un-debounced `WritableSignal`. This allows\n * you to access the immediate value (without the debounce delay) if needed,\n * and also ensures that any direct modifications to the original signal\n * are reflected in the debounced signal after the debounce period.\n */\n original: WritableSignal<T>;\n};\n\n/**\n * Options for creating a debounced signal.\n *\n * @typeParam T - The type of value held by the signal.\n */\nexport type CreateDebouncedOptions<T> = CreateSignalOptions<T> & {\n /**\n * The debounce delay in milliseconds. Defaults to 300.\n */\n ms?: number;\n};\n\n/**\n * Creates a debounced signal. The signal's value will only be propagated to\n * subscribers after a specified delay (debounce time) has passed since the last\n * time it was set or updated. Crucially, updates to the *original* signal\n * are also debounced.\n *\n * @see {@link DebouncedSignal}\n * @see {@link CreateDebouncedOptions}\n *\n * @example\n * ```typescript\n * // Create a debounced signal with an initial value and a custom delay.\n * const searchTerm = debounced('initial value', { ms: 500 });\n *\n * // Update the debounced signal. The actual update will be delayed by 500ms.\n * searchTerm.set('new value');\n *\n * // Access the original, un-debounced signal.\n * console.log(searchTerm.original()); // Outputs 'new value' (immediately)\n * // ... after 500ms ...\n * console.log(searchTerm()); // Outputs 'new value' (debounced)\n *\n * // Directly update the *original* signal.\n * searchTerm.original.set('direct update');\n * console.log(searchTerm.original()); // Outputs 'direct update' (immediately)\n * console.log(searchTerm()); // Outputs 'new value' (still debounced from the previous set)\n * // ... after 500ms ...\n * console.log(searchTerm()); // Outputs 'direct update' (now reflects the original signal)\n *\n * // Create a debounced signal with undefined initial value and default delay\n * const anotherSignal = debounced();\n * ```\n * @typeParam T - The type of the signal's value.\n * @param initial The initial value of the signal. Optional; defaults to `undefined`.\n * @param opt Configuration options for the signal, including the debounce delay (`ms`).\n * @returns A `DebouncedSignal` instance.\n */\nexport function debounced<T>(): DebouncedSignal<T | undefined>;\n/**\n * Creates a debounced signal with a defined initial value.\n *\n * @typeParam T - The type of the signal's value.\n * @param initial The initial value of the signal.\n * @param opt Configuration options for the signal, including the debounce delay (`ms`).\n * @returns A `DebouncedSignal` instance.\n */\nexport function debounced<T>(\n initial: T,\n opt?: CreateDebouncedOptions<T>,\n): DebouncedSignal<T>;\nexport function debounced<T>(\n value?: T,\n opt?: CreateDebouncedOptions<T | undefined>,\n): DebouncedSignal<T | undefined> {\n const sig = signal<T | undefined>(value, opt) as DebouncedSignal<\n T | undefined\n >;\n const ms = opt?.ms ?? 300;\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n\n const originalSet = sig.set;\n const originalUpdate = sig.update;\n\n const trigger = signal(false);\n\n // Set on the original signal, then trigger the debounced update\n const set = (value: T | undefined) => {\n originalSet(value); // Update the *original* signal immediately\n\n if (timeout) clearTimeout(timeout);\n timeout = setTimeout(() => {\n trigger.update((cur) => !cur); // Trigger the computed signal\n }, ms);\n };\n\n // Update on the original signal, then trigger the debounced update\n const update = (fn: (prev: T | undefined) => T | undefined) => {\n originalUpdate(fn); // Update the *original* signal immediately\n\n if (timeout) clearTimeout(timeout);\n timeout = setTimeout(() => {\n trigger.update((cur) => !cur); // Trigger the computed signal\n }, ms);\n };\n\n // Create a computed signal that depends on the trigger.\n // This computed signal is what provides the debounced behavior.\n const writable = toWritable(\n computed(() => {\n trigger();\n return untracked(sig);\n }),\n set,\n update,\n ) as DebouncedSignal<T | undefined>;\n\n writable.original = sig;\n\n return writable;\n}\n","import {\n computed,\n CreateSignalOptions,\n signal,\n untracked,\n type WritableSignal,\n} from '@angular/core';\nimport type { UnknownObject } from '@mmstack/object';\nimport { toWritable } from './to-writable';\n\n/**\n * Options for creating a derived signal using the full `derived` function signature.\n * @typeParam T - The type of the source signal's value (parent).\n * @typeParam U - The type of the derived signal's value (child).\n */\ntype CreateDerivedOptions<T, U> = CreateSignalOptions<U> & {\n /**\n * A function that extracts the derived value (`U`) from the source signal's value (`T`).\n */\n from: (v: T) => U;\n /**\n * A function that updates the source signal's value (`T`) when the derived signal's value (`U`) changes.\n * This establishes the two-way binding.\n */\n onChange: (newValue: U) => void;\n};\n\n/**\n * A `WritableSignal` that derives its value from another `WritableSignal` (the \"source\" signal).\n * It provides two-way binding: changes to the source signal update the derived signal, and\n * changes to the derived signal update the source signal.\n *\n * @typeParam T - The type of the source signal's value (parent).\n * @typeParam U - The type of the derived signal's value (child).\n */\nexport type DerivedSignal<T, U> = WritableSignal<U> & {\n /**\n * The function used to derive the derived signal's value from the source signal's value.\n * This is primarily for internal use and introspection.\n */\n from: (v: T) => U;\n};\n\n/**\n * Creates a `DerivedSignal` that derives its value from another `WritableSignal` (the source signal).\n * This overload provides the most flexibility, allowing you to specify custom `from` and `onChange` functions.\n *\n * @typeParam T - The type of the source signal's value.\n * @typeParam U - The type of the derived signal's value.\n * @param source - The source `WritableSignal`.\n * @param options - An object containing the `from` and `onChange` functions, and optional signal options.\n * @returns A `DerivedSignal` instance.\n *\n * @example\n * const user = signal({ name: 'John', age: 30 });\n * const name = derived(user, {\n * from: (u) => u.name,\n * onChange: (newName) => user.update((u) => ({ ...u, name: newName })),\n * });\n */\nexport function derived<T, U>(\n source: WritableSignal<T>,\n opt: CreateDerivedOptions<T, U>,\n): DerivedSignal<T, U>;\n\n/**\n * Creates a `DerivedSignal` that derives a property from an object held by the source signal.\n * This overload simplifies creating derived signals for object properties.\n *\n * @typeParam T - The type of the source signal's value (must be an object).\n * @typeParam TKey - The key of the property to derive.\n * @param source - The source `WritableSignal` (holding an object).\n * @param key - The key of the property to derive.\n * @param options - Optional signal options for the derived signal.\n * @returns A `DerivedSignal` instance.\n *\n * @example\n * const user = signal({ name: 'John', age: 30 });\n * const name = derived(user, 'name');\n */\nexport function derived<T extends UnknownObject, TKey extends keyof T>(\n source: WritableSignal<T>,\n key: TKey,\n opt?: CreateSignalOptions<T[TKey]>,\n): DerivedSignal<T, T[TKey]>;\n\n/**\n * Creates a `DerivedSignal` from an array, and derives an element by index.\n *\n * @typeParam T - The type of the source signal's value (must be an array).\n * @param source - The source `WritableSignal` (holding an array).\n * @param index - The index of the element to derive.\n * @param options - Optional signal options for the derived signal.\n * @returns A `DerivedSignal` instance.\n *\n * @example\n * const numbers = signal([1, 2, 3]);\n * const secondNumber = derived(numbers, 1); // secondNumber() === 2\n * secondNumber.set(5); // numbers() === [1, 5, 3]\n */\nexport function derived<T extends any[]>(\n source: WritableSignal<T>,\n index: number,\n opt?: CreateSignalOptions<T[number]>,\n): DerivedSignal<T, T[number]>;\n\nexport function derived<T, U>(\n source: WritableSignal<T>,\n optOrKey: CreateDerivedOptions<T, U> | keyof T,\n opt?: CreateSignalOptions<U>,\n): DerivedSignal<T, U> {\n const isArray =\n Array.isArray(untracked(source)) && typeof optOrKey === 'number';\n\n const from =\n typeof optOrKey === 'object' ? optOrKey.from : (v: T) => v[optOrKey] as U;\n const onChange =\n typeof optOrKey === 'object'\n ? optOrKey.onChange\n : isArray\n ? (next: U) => {\n source.update(\n (cur) =>\n (cur as unknown as any[]).map((v, i) =>\n i === optOrKey ? next : v,\n ) as T,\n );\n }\n : (next: U) => {\n source.update((cur) => ({ ...cur, [optOrKey]: next }));\n };\n\n const rest = typeof optOrKey === 'object' ? optOrKey : opt;\n\n const sig = toWritable<U>(\n computed(() => from(source()), rest),\n (newVal) => onChange(newVal),\n ) as DerivedSignal<T, U>;\n\n sig.from = from;\n\n return sig;\n}\n\n/**\n * Creates a \"fake\" `DerivedSignal` from a simple value. This is useful for creating\n * `FormControlSignal` instances that are not directly derived from another signal.\n * The returned signal's `from` function will always return the initial value.\n *\n * @typeParam T - This type parameter is not used in the implementation but is kept for type compatibility with `DerivedSignal`.\n * @typeParam U - The type of the signal's value.\n * @param initial - The initial value of the signal.\n * @returns A `DerivedSignal` instance.\n * @internal\n */\nexport function toFakeDerivation<T, U>(initial: U): DerivedSignal<T, U> {\n const sig = signal(initial) as DerivedSignal<T, U>;\n sig.from = () => initial;\n\n return sig;\n}\n\n/**\n * Creates a \"fake\" `DerivedSignal` from an existing `WritableSignal`. This is useful\n * for treating a regular `WritableSignal` as a `DerivedSignal` without changing its behavior.\n * The returned signal's `from` function returns the current value of signal, using `untracked`.\n *\n * @typeParam T - This type parameter is not used in the implementation but is kept for type compatibility with `DerivedSignal`.\n * @typeParam U - The type of the signal's value.\n * @param initial - The existing `WritableSignal`.\n * @returns A `DerivedSignal` instance.\n * @internal\n */\nexport function toFakeSignalDerivation<T, U>(\n initial: WritableSignal<U>,\n): DerivedSignal<T, U> {\n const sig = initial as DerivedSignal<T, U>;\n sig.from = () => untracked(initial);\n return sig;\n}\n\n/**\n * Type guard function to check if a given `WritableSignal` is a `DerivedSignal`.\n *\n * @typeParam T - The type of the source signal's value (optional, defaults to `any`).\n * @typeParam U - The type of the derived signal's value (optional, defaults to `any`).\n * @param sig - The `WritableSignal` to check.\n * @returns `true` if the signal is a `DerivedSignal`, `false` otherwise.\n */\nexport function isDerivation<T, U>(\n sig: WritableSignal<U>,\n): sig is DerivedSignal<T, U> {\n return 'from' in sig;\n}\n","import {\n computed,\n type CreateSignalOptions,\n isSignal,\n linkedSignal,\n type Signal,\n} from '@angular/core';\n\n/**\n * Options for the mapArray function.\n * @template T The type of elements in the array.\n * @extends CreateSignalOptions<T> Inherits options for creating individual signals.\n */\nexport type MapArrayOptions<T> = CreateSignalOptions<T> & {\n /**\n * An optional function to transform each element from the source array.\n * If not provided, the original element is used.\n * @param source The current value of the source array signal.\n * @param index The index of the element being mapped.\n * @returns The transformed element for the corresponding signal.\n */\n map?: (source: T[], index: number) => T;\n};\n\nfunction createReconciler<T>(source: Signal<T[]>, opt?: MapArrayOptions<T>) {\n const map = opt?.map ?? ((source, index) => source[index]);\n\n return (\n length: number,\n prev?: {\n value: Signal<T>[];\n source: number;\n },\n ): Signal<T>[] => {\n if (!prev)\n return Array.from({ length }, (_, i) =>\n computed(() => map(source(), i), opt),\n );\n\n if (length === prev.source) return prev.value;\n\n if (length < prev.source) {\n return prev.value.slice(0, length);\n } else {\n const next = [...prev.value];\n for (let i = prev.source; i < length; i++) {\n next.push(computed(() => map(source(), i), opt));\n }\n\n return next;\n }\n };\n}\n\n/**\n * Creates a reactive array of signals from a source array signal (or a function returning one),\n * applying an optional mapping function to each element.\n *\n * This is useful for scenarios like rendering lists where each item\n * needs its own reactive state derived from the source array. It efficiently\n * handles changes in the source array's length by reusing existing signals\n * for elements that remain, adding signals for new elements, and removing signals\n * for deleted elements.\n *\n * @template T The type of elements in the source array.\n * @param source A function that returns the source array (or readonly array).\n * This function will be tracked for changes.\n * @param opt Optional configuration including a `map` function to transform elements\n * and options (`CreateSignalOptions`) for the created signals.\n * @returns A signal (`Signal<Signal<T | undefined>[]>`) where the outer signal updates\n * when the array length changes, and the inner array contains signals\n * representing each element (potentially mapped).\n */\nexport function mapArray<T>(\n source: () => T[],\n opt?: MapArrayOptions<T | undefined>,\n): Signal<Signal<T | undefined>[]>;\n\n/**\n * Creates a reactive array of signals from a source readonly array (or a function returning one),\n * applying an optional mapping function to each element.\n *\n * This is useful for scenarios like rendering lists where each item\n * needs its own reactive state derived from the source array. It efficiently\n * handles changes in the source array's length by reusing existing signals\n * for elements that remain, adding signals for new elements, and removing signals\n * for deleted elements.\n *\n * @template T The type of elements in the source array.\n * @param source A function that returns the source readonly array.\n * This function will be tracked for changes.\n * @param opt Optional configuration including a `map` function to transform elements\n * and options (`CreateSignalOptions`) for the created signals.\n * @returns A signal (`Signal<Signal<T>[]>`) where the outer signal updates\n * when the array length changes, and the inner array contains signals\n * representing each element (potentially mapped).\n */\nexport function mapArray<T>(\n source: () => readonly T[],\n opt?: MapArrayOptions<T>,\n): Signal<Signal<T>[]>;\n\nexport function mapArray<T>(\n source: (() => T[]) | (() => readonly T[]),\n opt?: MapArrayOptions<T>,\n): Signal<Signal<T | undefined>[]> {\n const data = isSignal(source) ? source : computed(source);\n const length = computed(() => data().length);\n\n const reconciler = createReconciler<T>(data as Signal<T[]>, opt);\n\n return linkedSignal<number, Signal<T>[]>({\n source: () => length(),\n computation: (len, prev) => reconciler(len, prev),\n });\n}\n","import {\r\n signal,\r\n type CreateSignalOptions,\r\n type ValueEqualityFn,\r\n type WritableSignal,\r\n} from '@angular/core';\r\n\r\nconst { is } = Object;\r\n\r\n/**\r\n * A `MutableSignal` is a special type of `WritableSignal` that allows for in-place mutation of its value.\r\n * In addition to the standard `set` and `update` methods, it provides a `mutate` method. This is useful\r\n * for performance optimization when dealing with complex objects or arrays, as it avoids unnecessary\r\n * object copying.\r\n *\r\n * @typeParam T - The type of value held by the signal.\r\n */\r\nexport type MutableSignal<T> = WritableSignal<T> & {\r\n /**\r\n * Mutates the signal's value in-place. This is similar to `update`, but it's optimized for\r\n * scenarios where you want to modify the existing object directly rather than creating a new one.\r\n *\r\n * @param updater - A function that takes the current value as input and modifies it directly.\r\n *\r\n * @example\r\n * const myArray = mutable([1, 2, 3]);\r\n * myArray.mutate((arr) => {\r\n * arr.push(4);\r\n * return arr;\r\n * }); // myArray() now returns [1, 2, 3, 4]\r\n */\r\n mutate: WritableSignal<T>['update'];\r\n\r\n /**\r\n * Mutates the signal's value in-place, similar to `mutate`, but with a void-returning value in updater\r\n * function. This further emphasizes that the mutation is happening inline, improving readability\r\n * in some cases.\r\n * @param updater - Function to change to the current value\r\n * @example\r\n * const myObject = mutable({ a: 1, b: 2 });\r\n * myObject.inline((obj) => (obj.a = 3)); // myObject() now returns { a: 3, b: 2 }\r\n */\r\n inline: (updater: Parameters<WritableSignal<T>['update']>[0]) => void;\r\n};\r\n\r\n/**\r\n * Creates a `MutableSignal`. This function overloads the standard `signal` function to provide\r\n * the additional `mutate` and `inline` methods.\r\n *\r\n * @typeParam T - The type of value held by the signal.\r\n *\r\n * @param initial - The initial value of the signal. If no initial value is provided, it defaults to `undefined`.\r\n * @param options - Optional. An object containing signal options, including a custom equality function (`equal`).\r\n *\r\n * @returns A `MutableSignal` instance.\r\n *\r\n * @example\r\n * // Create a mutable signal with an initial value:\r\n * const mySignal = mutable({ count: 0 }) // MutableSignal<{ count: number }>;\r\n *\r\n * // Create a mutable signal with no initial value (starts as undefined):\r\n * const = mutable<number>(); // MutableSignal<number | undefined>\r\n *\r\n * // Create a mutable signal with a custom equality function:\r\n * const myCustomSignal = mutable({ a: 1 }, { equal: (a, b) => a.a === b.a });\r\n */\r\nexport function mutable<T>(): MutableSignal<T | undefined>;\r\nexport function mutable<T>(initial: T): MutableSignal<T>;\r\nexport function mutable<T>(\r\n initial: T,\r\n opt?: CreateSignalOptions<T>,\r\n): MutableSignal<T>;\r\n\r\nexport function mutable<T>(\r\n initial?: T,\r\n opt?: CreateSignalOptions<T>,\r\n): MutableSignal<T> {\r\n const baseEqual = opt?.equal ?? is;\r\n let trigger = false;\r\n\r\n const equal: ValueEqualityFn<T | undefined> = (a, b) => {\r\n if (trigger) return false;\r\n return baseEqual(a, b);\r\n };\r\n\r\n const sig = signal<T | undefined>(initial, {\r\n ...opt,\r\n equal,\r\n }) as MutableSignal<T>;\r\n\r\n const internalUpdate = sig.update;\r\n\r\n sig.mutate = (updater) => {\r\n trigger = true;\r\n internalUpdate(updater);\r\n trigger = false;\r\n };\r\n\r\n sig.inline = (updater) => {\r\n sig.mutate((prev) => {\r\n updater(prev);\r\n return prev;\r\n });\r\n };\r\n\r\n return sig;\r\n}\r\n\r\n/**\r\n * Type guard function to check if a given `WritableSignal` is a `MutableSignal`. This is useful\r\n * for situations where you need to conditionally use the `mutate` or `inline` methods.\r\n *\r\n * @typeParam T - The type of the signal's value (optional, defaults to `any`).\r\n * @param value - The `WritableSignal` to check.\r\n * @returns `true` if the signal is a `MutableSignal`, `false` otherwise.\r\n *\r\n * @example\r\n * const mySignal = signal(0);\r\n * const myMutableSignal = mutable(0);\r\n *\r\n * if (isMutable(mySignal)) {\r\n * mySignal.mutate(x => x + 1); // This would cause a type error, as mySignal is not a MutableSignal.\r\n * }\r\n *\r\n * if (isMutable(myMutableSignal)) {\r\n * myMutableSignal.mutate(x => x + 1); // This is safe.\r\n * }\r\n */\r\nexport function isMutable<T = any>(\r\n value: WritableSignal<T>,\r\n): value is MutableSignal<T> {\r\n return 'mutate' in value && typeof value.mutate === 'function';\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './lib/debounced';
2
2
  export * from './lib/derived';
3
+ export * from './lib/map-array';
3
4
  export * from './lib/mutable';
4
5
  export * from './lib/to-writable';
@@ -0,0 +1,56 @@
1
+ import { type CreateSignalOptions, type Signal } from '@angular/core';
2
+ /**
3
+ * Options for the mapArray function.
4
+ * @template T The type of elements in the array.
5
+ * @extends CreateSignalOptions<T> Inherits options for creating individual signals.
6
+ */
7
+ export type MapArrayOptions<T> = CreateSignalOptions<T> & {
8
+ /**
9
+ * An optional function to transform each element from the source array.
10
+ * If not provided, the original element is used.
11
+ * @param source The current value of the source array signal.
12
+ * @param index The index of the element being mapped.
13
+ * @returns The transformed element for the corresponding signal.
14
+ */
15
+ map?: (source: T[], index: number) => T;
16
+ };
17
+ /**
18
+ * Creates a reactive array of signals from a source array signal (or a function returning one),
19
+ * applying an optional mapping function to each element.
20
+ *
21
+ * This is useful for scenarios like rendering lists where each item
22
+ * needs its own reactive state derived from the source array. It efficiently
23
+ * handles changes in the source array's length by reusing existing signals
24
+ * for elements that remain, adding signals for new elements, and removing signals
25
+ * for deleted elements.
26
+ *
27
+ * @template T The type of elements in the source array.
28
+ * @param source A function that returns the source array (or readonly array).
29
+ * This function will be tracked for changes.
30
+ * @param opt Optional configuration including a `map` function to transform elements
31
+ * and options (`CreateSignalOptions`) for the created signals.
32
+ * @returns A signal (`Signal<Signal<T | undefined>[]>`) where the outer signal updates
33
+ * when the array length changes, and the inner array contains signals
34
+ * representing each element (potentially mapped).
35
+ */
36
+ export declare function mapArray<T>(source: () => T[], opt?: MapArrayOptions<T | undefined>): Signal<Signal<T | undefined>[]>;
37
+ /**
38
+ * Creates a reactive array of signals from a source readonly array (or a function returning one),
39
+ * applying an optional mapping function to each element.
40
+ *
41
+ * This is useful for scenarios like rendering lists where each item
42
+ * needs its own reactive state derived from the source array. It efficiently
43
+ * handles changes in the source array's length by reusing existing signals
44
+ * for elements that remain, adding signals for new elements, and removing signals
45
+ * for deleted elements.
46
+ *
47
+ * @template T The type of elements in the source array.
48
+ * @param source A function that returns the source readonly array.
49
+ * This function will be tracked for changes.
50
+ * @param opt Optional configuration including a `map` function to transform elements
51
+ * and options (`CreateSignalOptions`) for the created signals.
52
+ * @returns A signal (`Signal<Signal<T>[]>`) where the outer signal updates
53
+ * when the array length changes, and the inner array contains signals
54
+ * representing each element (potentially mapped).
55
+ */
56
+ export declare function mapArray<T>(source: () => readonly T[], opt?: MapArrayOptions<T>): Signal<Signal<T>[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/primitives",
3
- "version": "19.0.3",
3
+ "version": "19.0.4",
4
4
  "peerDependencies": {
5
5
  "@angular/core": "~19.2.3",
6
6
  "@mmstack/object": "~19.0.0"