@mmstack/primitives 21.0.5 → 21.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/primitives",
3
- "version": "21.0.5",
3
+ "version": "21.0.6",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "signals",
@@ -271,31 +271,32 @@ declare function derived<T, U>(source: WritableSignal<T>, opt: CreateDerivedOpti
271
271
  * console.log(user().name); // Outputs: Jane
272
272
  * ```
273
273
  */
274
- declare function derived<T extends UnknownObject, TKey extends keyof T>(source: WritableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]>): DerivedSignal<T, T[TKey]>;
274
+ declare function derived<T extends UnknownObject, TKey extends keyof T>(source: MutableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]>): DerivedSignal<T, T[TKey]> & MutableSignal<T[TKey]>;
275
275
  /**
276
- * Creates a `DerivedSignal` from an array, deriving an element by its index.
277
- * This overload is a convenient shorthand for accessing array elements.
276
+ * Creates a `DerivedSignal` that derives a property from an object held by the source signal.
277
+ * This overload is a convenient shorthand for accessing object properties.
278
278
  *
279
- * @typeParam T The type of the source signal's value (must be an array).
280
- * @param source The source `WritableSignal` (holding an array).
281
- * @param index The index of the element to derive.
279
+ * @typeParam T The type of the source signal's value (must be an object).
280
+ * @typeParam TKey The key of the property to derive.
281
+ * @param source The source `WritableSignal` (holding an object).
282
+ * @param key The key of the property to derive.
282
283
  * @param options Optional signal options for the derived signal.
283
284
  * @returns A `DerivedSignal` instance.
284
285
  *
285
286
  * @example
286
287
  * ```ts
287
- * const numbers = signal([1, 2, 3]);
288
- * const secondNumber = derived(numbers, 1);
288
+ * const user = signal({ name: 'John', age: 30 });
289
+ * const name = derived(user, 'name');
289
290
  *
290
- * console.log(secondNumber()); // Outputs: 2
291
+ * console.log(name()); // Outputs: John
291
292
  *
292
293
  * // Update the derived signal, which also updates the source
293
- * secondNumber.set(5);
294
+ * name.set('Jane');
294
295
  *
295
- * console.log(numbers()); // Outputs: [1, 5, 3]
296
+ * console.log(user().name); // Outputs: Jane
296
297
  * ```
297
298
  */
298
- declare function derived<T extends any[]>(source: WritableSignal<T>, index: number, opt?: CreateSignalOptions<T[number]>): DerivedSignal<T, T[number]>;
299
+ declare function derived<T extends UnknownObject, TKey extends keyof T>(source: WritableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]>): DerivedSignal<T, T[TKey]>;
299
300
  /**
300
301
  * Creates a `DerivedSignal` that derives its value from another `MutableSignal`.
301
302
  * Use mutuable signals with caution, but very useful for deeply nested structures.
@@ -319,6 +320,30 @@ declare function derived<T extends any[]>(source: WritableSignal<T>, index: numb
319
320
  * ```
320
321
  */
321
322
  declare function derived<T, U>(source: MutableSignal<T>, optOrKey: CreateDerivedOptions<T, U> | keyof T, opt?: CreateSignalOptions<U>): DerivedSignal<T, U> & MutableSignal<U>;
323
+ /**
324
+ * Creates a `DerivedSignal` from an array, deriving an element by its index.
325
+ * This overload is a convenient shorthand for accessing array elements.
326
+ *
327
+ * @typeParam T The type of the source signal's value (must be an array).
328
+ * @param source The source `WritableSignal` (holding an array).
329
+ * @param index The index of the element to derive.
330
+ * @param options Optional signal options for the derived signal.
331
+ * @returns A `DerivedSignal` instance.
332
+ *
333
+ * @example
334
+ * ```ts
335
+ * const numbers = signal([1, 2, 3]);
336
+ * const secondNumber = derived(numbers, 1);
337
+ *
338
+ * console.log(secondNumber()); // Outputs: 2
339
+ *
340
+ * // Update the derived signal, which also updates the source
341
+ * secondNumber.set(5);
342
+ *
343
+ * console.log(numbers()); // Outputs: [1, 5, 3]
344
+ * ```
345
+ */
346
+ declare function derived<T extends any[]>(source: WritableSignal<T>, index: number, opt?: CreateSignalOptions<T[number]>): DerivedSignal<T, T[number]>;
322
347
  /**
323
348
  * Creates a "fake" `DerivedSignal` from a simple value. This is useful for creating
324
349
  * `FormControlSignal` instances that are not directly derived from another signal.
@@ -407,30 +432,45 @@ declare function indexArray<T, U>(source: Signal<T[]> | (() => T[]), map: (value
407
432
  declare const mapArray: typeof indexArray;
408
433
 
409
434
  /**
410
- * Reactively maps items from a source array to a new array using a key function to maintain stability.
435
+ * Reactively maps items from a source array to a new array by value (identity).
411
436
  *
412
- * This function preserves the `mapped` signals for items even if they move within the array,
413
- * as long as their key remains the same. This is equivalent to SolidJS's `mapArray` or Angular's `@for (item of items; track item.id)`.
437
+ * similar to `Array.prototype.map`, but:
438
+ * 1. The `mapFn` receives the `index` as a Signal.
439
+ * 2. If an item in the `source` array moves to a new position, the *result* of the map function is reused and moved.
440
+ * The `index` signal is updated to the new index.
441
+ * 3. The `mapFn` is only run for *new* items.
414
442
  *
415
- * @template T The type of items in the source array.
416
- * @template U The type of items in the resulting mapped array.
417
- * @template K The type of the key.
443
+ * This is useful for building efficient lists where DOM nodes or heavy instances should be reused
444
+ * when the list is reordered.
418
445
  *
419
446
  * @param source A `Signal<T[]>` or a function returning `T[]`.
420
- * @param keyFn A function to extract a unique key for each item.
421
- * @param map The mapping function. It receives a stable signal for the item and a signal for its index.
422
- * @param options Optional configuration, including `CreateSignalOptions` and an `onDestroy` callback.
447
+ * @param mapFn The mapping function. Receives the item and its index as a Signal.
448
+ * @param options Optional configuration:
449
+ * - `onDestroy`: A callback invoked when a mapped item is removed from the array.
423
450
  * @returns A `Signal<U[]>` containing the mapped array.
424
451
  */
425
- declare function keyArray<T, U>(source: MutableSignal<T[]>, keyFn: (item: T) => string | number, map: (value: MutableSignal<T>, index: Signal<number>) => U, options?: CreateSignalOptions<T> & {
452
+ declare function keyArray<T, U, K>(source: Signal<T[]> | (() => T[]), mapFn: (v: T, i: Signal<number>) => U, options?: {
426
453
  onDestroy?: (value: U) => void;
454
+ /**
455
+ * Optional function to use a custom key for item comparison.
456
+ * Use this if you want to reuse mapped items based on a property (like an ID)
457
+ * even if the item reference changes.
458
+ */
459
+ key?: (item: T) => K;
427
460
  }): Signal<U[]>;
428
- declare function keyArray<T, U>(source: WritableSignal<T[]>, keyFn: (item: T) => string | number, map: (value: WritableSignal<T>, index: Signal<number>) => U, options?: CreateSignalOptions<T> & {
461
+
462
+ type MappedObject<T extends Record<string, any>, U> = {
463
+ [K in keyof T]: U;
464
+ };
465
+ declare function mapObject<T extends Record<string, any>, U>(source: MutableSignal<T>, mapFn: <K extends keyof T>(key: K, value: MutableSignal<T[K]>) => U, options?: {
429
466
  onDestroy?: (value: U) => void;
430
- }): Signal<U[]>;
431
- declare function keyArray<T, U>(source: Signal<T[]> | (() => T[]), keyFn: (item: T) => string | number, map: (value: Signal<T>, index: Signal<number>) => U, options?: CreateSignalOptions<T> & {
467
+ }): Signal<MappedObject<T, U>>;
468
+ declare function mapObject<T extends Record<string, any>, U>(source: WritableSignal<T>, mapFn: <K extends keyof T>(key: K, value: WritableSignal<T[K]>) => U, options?: {
432
469
  onDestroy?: (value: U) => void;
433
- }): Signal<U[]>;
470
+ }): Signal<MappedObject<T, U>>;
471
+ declare function mapObject<T extends Record<string, any>, U>(source: (() => T) | Signal<T>, mapFn: <K extends keyof T>(key: K, value: Signal<T[K]>) => U, options?: {
472
+ onDestroy?: (value: U) => void;
473
+ }): Signal<MappedObject<T, U>>;
434
474
 
435
475
  type Frame = {
436
476
  injector: Injector;
@@ -475,38 +515,38 @@ type Frame = {
475
515
  * @example
476
516
  * ```ts
477
517
  * const users = signal([
478
- { id: 1, name: 'Alice' },
479
- { id: 2, name: 'Bob' }
480
- ]);
481
-
482
- // The fine-grained mapped list
483
- const mappedUsers = mapArray(
484
- users,
485
- (userSignal, index) => {
486
- // 1. Create a fine-grained SIDE EFFECT for *this item*
487
- // This effect's lifetime is now tied to this specific item. created once on init of this index.
488
- const effectRef = nestedEffect(() => {
489
- // This only runs if *this* userSignal changes,
490
- // not if the whole list changes.
491
- console.log(`User ${index} updated:`, userSignal().name);
492
- });
493
-
494
- // 2. Return the data AND the cleanup logic
495
- return {
496
- // The mapped data
497
- label: computed(() => `User: ${userSignal().name}`),
498
-
499
- // The cleanup function
500
- destroyEffect: () => effectRef.destroy()
501
- };
502
- },
503
- {
504
- // 3. Tell mapArray HOW to clean up when an item is removed, this needs to be manual as it's not a nestedEffect itself
505
- onDestroy: (mappedItem) => {
506
- mappedItem.destroyEffect();
507
- }
508
- }
509
- );
518
+ * { id: 1, name: 'Alice' },
519
+ * { id: 2, name: 'Bob' }
520
+ * ]);
521
+ *
522
+ * // The fine-grained mapped list
523
+ * const mappedUsers = mapArray(
524
+ * users,
525
+ * (userSignal, index) => {
526
+ * // 1. Create a fine-grained SIDE EFFECT for *this item*
527
+ * // This effect's lifetime is now tied to this specific item. created once on init of this index.
528
+ * const effectRef = nestedEffect(() => {
529
+ * // This only runs if *this* userSignal changes,
530
+ * // not if the whole list changes.
531
+ * console.log(`User ${index} updated:`, userSignal().name);
532
+ * });
533
+ *
534
+ * // 2. Return the data AND the cleanup logic
535
+ * return {
536
+ * // The mapped data
537
+ * label: computed(() => `User: ${userSignal().name}`),
538
+ *
539
+ * // The cleanup function
540
+ * destroyEffect: () => effectRef.destroy()
541
+ * };
542
+ * },
543
+ * {
544
+ * // 3. Tell mapArray HOW to clean up when an item is removed, this needs to be manual as it's not a nestedEffect itself
545
+ * onDestroy: (mappedItem) => {
546
+ * mappedItem.destroyEffect();
547
+ * }
548
+ * }
549
+ * );
510
550
  * ```
511
551
  */
512
552
  declare function nestedEffect(effectFn: (registerCleanup: EffectCleanupRegisterFn) => void, options?: CreateEffectOptions & {
@@ -1257,25 +1297,21 @@ type Sensors<TKey extends keyof SensorTypedOptions> = {
1257
1297
  declare function sensors<const TType extends keyof SensorTypedOptions>(track: TType[], opt?: SensorsOptions<TType>): Sensors<TType>;
1258
1298
 
1259
1299
  type AnyRecord = Record<PropertyKey, any>;
1260
- type NonRecord = Iterable<any> | WeakSet<any> | WeakMap<any, any> | Promise<any> | Date | Error | RegExp | ArrayBuffer | DataView | Function | Array<any>;
1261
- type IsRecord<T> = T extends object ? T extends NonRecord ? false : true : false;
1262
- type IsUnknownRecord<T> = keyof T extends never ? true : string extends keyof T ? true : symbol extends keyof T ? true : number extends keyof T ? true : false;
1263
- type IsKnownRecord<T> = IsRecord<T> extends true ? IsUnknownRecord<T> extends true ? false : true : false;
1264
- type SignalStore<T> = Signal<T> & (IsKnownRecord<T> extends true ? Readonly<{
1265
- [K in keyof T]: IsKnownRecord<T[K]> extends true ? SignalStore<T[K]> : Signal<T[K]>;
1266
- }> : unknown);
1267
- type WritableSignalStore<T> = WritableSignal<T> & (IsKnownRecord<T> extends true ? Readonly<{
1268
- [K in keyof T]: IsKnownRecord<T[K]> extends true ? WritableSignalStore<T[K]> : WritableSignal<T[K]>;
1269
- }> : unknown);
1270
- type MutableSignalStore<T> = MutableSignal<T> & (IsKnownRecord<T> extends true ? Readonly<{
1271
- [K in keyof T]: IsKnownRecord<T[K]> extends true ? MutableSignalStore<T[K]> : MutableSignal<T[K]>;
1272
- }> : unknown);
1273
- type inferValid<T extends AnyRecord> = IsKnownRecord<T> extends false ? never : T;
1274
- declare function toStore<T extends AnyRecord>(source: MutableSignal<inferValid<T>>, injector?: Injector): MutableSignalStore<T>;
1275
- declare function toStore<T extends AnyRecord>(source: WritableSignal<inferValid<T>>, injector?: Injector): WritableSignalStore<T>;
1276
- declare function toStore<T extends AnyRecord>(source: Signal<inferValid<T>>, injector?: Injector): SignalStore<T>;
1277
- declare function store<T extends AnyRecord>(value: inferValid<T>, opt?: CreateSignalOptions<T>): WritableSignalStore<T>;
1278
- declare function mutableStore<T extends AnyRecord>(value: inferValid<T>, opt?: CreateSignalOptions<T>): MutableSignalStore<T>;
1300
+ type BaseType = string | number | boolean | symbol | undefined | null | Function | Date | RegExp | any[];
1301
+ type SignalStore<T> = Signal<T> & (T extends BaseType ? unknown : Readonly<{
1302
+ [K in keyof T]: SignalStore<T[K]>;
1303
+ }>);
1304
+ type WritableSignalStore<T> = WritableSignal<T> & (T extends BaseType ? unknown : Readonly<{
1305
+ [K in keyof T]: WritableSignalStore<T[K]>;
1306
+ }>);
1307
+ type MutableSignalStore<T> = MutableSignal<T> & (T extends BaseType ? unknown : Readonly<{
1308
+ [K in keyof T]: MutableSignalStore<T[K]>;
1309
+ }>);
1310
+ declare function toStore<T extends AnyRecord>(source: MutableSignal<T>, injector?: Injector): MutableSignalStore<T>;
1311
+ declare function toStore<T extends AnyRecord>(source: WritableSignal<T>, injector?: Injector): WritableSignalStore<T>;
1312
+ declare function toStore<T extends AnyRecord>(source: Signal<T>, injector?: Injector): SignalStore<T>;
1313
+ declare function store<T extends AnyRecord>(value: T, opt?: CreateSignalOptions<T>): WritableSignalStore<T>;
1314
+ declare function mutableStore<T extends AnyRecord>(value: T, opt?: CreateSignalOptions<T>): MutableSignalStore<T>;
1279
1315
 
1280
1316
  /**
1281
1317
  * Interface for storage mechanisms compatible with the `stored` signal.
@@ -1714,5 +1750,5 @@ type CreateHistoryOptions<T> = Omit<CreateSignalOptions<T[]>, 'equal'> & {
1714
1750
  */
1715
1751
  declare function withHistory<T>(source: WritableSignal<T>, opt?: CreateHistoryOptions<T>): SignalWithHistory<T>;
1716
1752
 
1717
- export { combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, indexArray, isDerivation, isMutable, keyArray, map, mapArray, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, pageVisibility, pipeable, piped, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, sensors, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
1718
- export type { CreateDebouncedOptions, CreateHistoryOptions, CreateStoredOptions, CreateThrottledOptions, DebouncedSignal, DerivedSignal, ElementSize, ElementSizeOptions, ElementSizeSignal, ElementVisibilityOptions, ElementVisibilitySignal, IsKnownRecord, IsRecord, IsUnknownRecord, MousePositionOptions, MousePositionSignal, MutableSignal, MutableSignalStore, NetworkStatusSignal, PipeableSignal, ScrollPosition, ScrollPositionOptions, ScrollPositionSignal, SignalStore, SignalWithHistory, StoredSignal, ThrottledSignal, UntilOptions, WindowSize, WindowSizeOptions, WindowSizeSignal, WritableSignalStore };
1753
+ export { combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, indexArray, isDerivation, isMutable, keyArray, map, mapArray, mapObject, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, pageVisibility, pipeable, piped, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, sensors, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
1754
+ export type { CreateDebouncedOptions, CreateHistoryOptions, CreateStoredOptions, CreateThrottledOptions, DebouncedSignal, DerivedSignal, ElementSize, ElementSizeOptions, ElementSizeSignal, ElementVisibilityOptions, ElementVisibilitySignal, MousePositionOptions, MousePositionSignal, MutableSignal, MutableSignalStore, NetworkStatusSignal, PipeableSignal, ScrollPosition, ScrollPositionOptions, ScrollPositionSignal, SignalStore, SignalWithHistory, StoredSignal, ThrottledSignal, UntilOptions, WindowSize, WindowSizeOptions, WindowSizeSignal, WritableSignalStore };