@mmstack/primitives 20.5.7 → 20.5.9

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/index.d.ts CHANGED
@@ -228,6 +228,55 @@ declare function mutable<T>(initial: T, opt?: CreateSignalOptions<T>): MutableSi
228
228
  */
229
229
  declare function isMutable<T = any>(value: WritableSignal<T>): value is MutableSignal<T>;
230
230
 
231
+ /** @internal Narrows `'array'` so it is only assignable when `T` is an array type. */
232
+ type VivifyArray<T> = T extends any[] ? 'array' : never;
233
+ /** @internal Narrows `'object'` so it is only assignable when `T` is an object type. */
234
+ type VivifyObject<T> = T extends object ? 'object' : never;
235
+ /**
236
+ * Controls **autovivification** — whether, and as what shape, a writable `derived` (or `store`)
237
+ * creates a missing container when the source value is `null`/`undefined` at the moment of a
238
+ * write. Without it, writing through a nullish value is a no-op; with it, a deep write such as
239
+ * `derived(user, 'name', { vivify: 'object' }).set('Ada')` materializes the missing object
240
+ * instead of silently dropping the write.
241
+ *
242
+ * A **present** value is always preserved — updated in place for a `MutableSignal` source,
243
+ * copied for an immutable one. Vivification only ever *creates*, it never *replaces*.
244
+ *
245
+ * Variants:
246
+ * - `false` — **default.** Off; a write through a nullish source does nothing.
247
+ * - `true` / `'auto'` — infer the shape from the key: an array (`[]`) for a numeric / index key,
248
+ * a plain object (`{}`) otherwise.
249
+ * - `'object'` — always create a plain object (`{}`). Only assignable when `T` is an object.
250
+ * - `'array'` — always create an array (`[]`). Only assignable when `T` is an array.
251
+ * - `() => T` — a factory producing the container to create. Called only on a nullish source,
252
+ * once per vivification (a fresh instance each time), so a present value is never clobbered.
253
+ * Useful for seeding defaults, e.g. `() => ({ items: [], total: 0 })`.
254
+ *
255
+ * @typeParam T - The type of the container that may be created (the source/parent value).
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * const user = signal<{ name: string } | null>(null);
260
+ *
261
+ * derived(user, 'name').set('Ada'); // off: dropped, user() === null
262
+ * derived(user, 'name', { vivify: 'object' }).set('Ada'); // user() === { name: 'Ada' }
263
+ * ```
264
+ */
265
+ type Vivify<T = any> = 'auto' | boolean | (() => T) | VivifyArray<T> | VivifyObject<T>;
266
+ /**
267
+ * Options mix-in that adds an optional {@link Vivify} setting to the `options` argument of the
268
+ * `derived` / `store` key & index overloads.
269
+ *
270
+ * @typeParam T - The type of the container that may be vivified (the source/parent value).
271
+ */
272
+ type WithVivify<T> = {
273
+ /**
274
+ * Whether, and as what shape, to create a missing container when the source value is
275
+ * `null`/`undefined` at write time. Defaults to `false` (no vivification). See {@link Vivify}.
276
+ */
277
+ vivify?: Vivify<T>;
278
+ };
279
+
231
280
  /**
232
281
  * Options for creating a derived signal using the full `derived` function signature.
233
282
  * @typeParam T - The type of the source signal's value (parent).
@@ -290,7 +339,10 @@ declare function derived<T, U>(source: WritableSignal<T>, opt: CreateDerivedOpti
290
339
  * @typeParam TKey The key of the property to derive.
291
340
  * @param source The source `WritableSignal` (holding an object).
292
341
  * @param key The key of the property to derive.
293
- * @param options Optional signal options for the derived signal.
342
+ * @param options Optional signal options for the derived signal. Also accepts a
343
+ * {@link Vivify} `vivify` flag (off by default) that, when set, creates the missing
344
+ * container instead of dropping a write made through a `null`/`undefined` source —
345
+ * e.g. `derived(user, 'name', { vivify: 'object' })`. See {@link WithVivify}.
294
346
  * @returns A `DerivedSignal` instance.
295
347
  *
296
348
  * @example
@@ -306,7 +358,7 @@ declare function derived<T, U>(source: WritableSignal<T>, opt: CreateDerivedOpti
306
358
  * console.log(user().name); // Outputs: Jane
307
359
  * ```
308
360
  */
309
- declare function derived<T extends object, TKey extends keyof T>(source: MutableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]>): DerivedSignal<T, T[TKey]> & MutableSignal<T[TKey]>;
361
+ declare function derived<T extends object, TKey extends keyof T>(source: MutableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]> & WithVivify<T>): DerivedSignal<T, T[TKey]> & MutableSignal<T[TKey]>;
310
362
  /**
311
363
  * Creates a `DerivedSignal` that derives a property from an object held by the source signal.
312
364
  * This overload is a convenient shorthand for accessing object properties.
@@ -315,7 +367,10 @@ declare function derived<T extends object, TKey extends keyof T>(source: Mutable
315
367
  * @typeParam TKey The key of the property to derive.
316
368
  * @param source The source `WritableSignal` (holding an object).
317
369
  * @param key The key of the property to derive.
318
- * @param options Optional signal options for the derived signal.
370
+ * @param options Optional signal options for the derived signal. Also accepts a
371
+ * {@link Vivify} `vivify` flag (off by default) that, when set, creates the missing
372
+ * container instead of dropping a write made through a `null`/`undefined` source —
373
+ * e.g. `derived(user, 'name', { vivify: 'object' })`. See {@link WithVivify}.
319
374
  * @returns A `DerivedSignal` instance.
320
375
  *
321
376
  * @example
@@ -331,7 +386,7 @@ declare function derived<T extends object, TKey extends keyof T>(source: Mutable
331
386
  * console.log(user().name); // Outputs: Jane
332
387
  * ```
333
388
  */
334
- declare function derived<T extends object, TKey extends keyof T>(source: WritableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]>): DerivedSignal<T, T[TKey]>;
389
+ declare function derived<T extends object, TKey extends keyof T>(source: WritableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]> & WithVivify<T>): DerivedSignal<T, T[TKey]>;
335
390
  /**
336
391
  * Creates a `DerivedSignal` that derives its value from another `MutableSignal`.
337
392
  * Use mutuable signals with caution, but very useful for deeply nested structures.
@@ -354,7 +409,7 @@ declare function derived<T extends object, TKey extends keyof T>(source: Writabl
354
409
  * console.log(user().name); // Outputs: Jane
355
410
  * ```
356
411
  */
357
- declare function derived<T, U>(source: MutableSignal<T>, optOrKey: CreateDerivedOptions<T, U> | keyof T, opt?: CreateSignalOptions<U>): DerivedSignal<T, U> & MutableSignal<U>;
412
+ declare function derived<T, U>(source: MutableSignal<T>, optOrKey: CreateDerivedOptions<T, U> | keyof T, opt?: CreateSignalOptions<U> & WithVivify<T>): DerivedSignal<T, U> & MutableSignal<U>;
358
413
  /**
359
414
  * Creates a `DerivedSignal` from an array, deriving an element by its index.
360
415
  * This overload is a convenient shorthand for accessing array elements.
@@ -362,7 +417,10 @@ declare function derived<T, U>(source: MutableSignal<T>, optOrKey: CreateDerived
362
417
  * @typeParam T The type of the source signal's value (must be an array).
363
418
  * @param source The source `WritableSignal` (holding an array).
364
419
  * @param index The index of the element to derive.
365
- * @param options Optional signal options for the derived signal.
420
+ * @param options Optional signal options for the derived signal. Also accepts a
421
+ * {@link Vivify} `vivify` flag (off by default) that, when set, creates the missing
422
+ * container instead of dropping a write made through a `null`/`undefined` source —
423
+ * e.g. `derived(user, 'name', { vivify: 'object' })`. See {@link WithVivify}.
366
424
  * @returns A `DerivedSignal` instance.
367
425
  *
368
426
  * @example
@@ -378,7 +436,7 @@ declare function derived<T, U>(source: MutableSignal<T>, optOrKey: CreateDerived
378
436
  * console.log(numbers()); // Outputs: [1, 5, 3]
379
437
  * ```
380
438
  */
381
- declare function derived<T extends any[]>(source: WritableSignal<T>, index: number, opt?: CreateSignalOptions<T[number]>): DerivedSignal<T, T[number]>;
439
+ declare function derived<T extends any[]>(source: WritableSignal<T>, index: number, opt?: CreateSignalOptions<T[number]> & WithVivify<T>): DerivedSignal<T, T[number]>;
382
440
  /**
383
441
  * Creates a "fake" `DerivedSignal` from a simple value. This is useful for creating
384
442
  * `FormControlSignal` instances that are not directly derived from another signal.
@@ -1938,7 +1996,26 @@ type ResolvableTarget = EventTargetLike | Signal<EventTargetLike | null>;
1938
1996
  declare function signalFromEvent<TEvent extends Event>(target: ResolvableTarget, eventName: string, initial: TEvent | null, opt?: SignalFromEventOptions): Signal<TEvent | null>;
1939
1997
  declare function signalFromEvent<TEvent extends Event, U>(target: ResolvableTarget, eventName: string, initial: U, project: (event: TEvent) => U, opt?: SignalFromEventOptions): Signal<U>;
1940
1998
 
1941
- type BaseType = string | number | boolean | symbol | undefined | null | Function | Date | RegExp;
1999
+ /**
2000
+ * Runtime marker + compile-time brand for an opaque value. A `const`-declared `Symbol`
2001
+ * has a `unique symbol` type, so the same symbol serves as both the property key written
2002
+ * by {@link opaque} and the type-level brand carried by {@link Opaque}.
2003
+ */
2004
+ declare const OPAQUE: unique symbol;
2005
+ /**
2006
+ * An object marked via {@link opaque} — the store treats it as an indivisible leaf
2007
+ * (like a `Date`), returning it whole instead of deep-proxying its keys.
2008
+ */
2009
+ type Opaque<T> = T & {
2010
+ readonly [OPAQUE]: true;
2011
+ };
2012
+ /** @internal Strips the opaque brand from the value a leaf signal carries. */
2013
+ type Unwrap<T> = T extends {
2014
+ readonly [OPAQUE]: true;
2015
+ } ? Omit<T, typeof OPAQUE> : T;
2016
+ type BaseType = string | number | boolean | symbol | undefined | null | Function | Date | RegExp | {
2017
+ readonly [OPAQUE]: true;
2018
+ };
1942
2019
  type Key = string | number;
1943
2020
  type AnyRecord = Record<Key, any>;
1944
2021
  /**
@@ -1963,28 +2040,75 @@ type MutableArrayStore<T extends any[]> = MutableSignal<T> & {
1963
2040
  readonly length: Signal<number>;
1964
2041
  [Symbol.iterator](): Iterator<MutableSignalStore<T[number]>>;
1965
2042
  };
1966
- type SignalStore<T> = Signal<T> & (NonNullable<T> extends BaseType ? unknown : NonNullable<T> extends Array<any> ? SignalArrayStore<NonNullable<T>> : Readonly<{
2043
+ /**
2044
+ * @internal Resolves to `true` only for `any`. In a conditional type, `any` distributes across
2045
+ * *both* branches (`unknown | object`), and `unknown | X` collapses to `unknown` — which would
2046
+ * erase a store's property access and `extend`. Guarding on this routes an `any`-typed store to
2047
+ * the full object shape instead.
2048
+ */
2049
+ type IsAny<T> = 0 extends 1 & T ? true : false;
2050
+ /**
2051
+ * @internal Flattens an intersection (`A & B & C`) into a single object literal so editor
2052
+ * tooltips show the resolved members instead of the raw intersection chain. Display-only —
2053
+ * structurally identical to its input.
2054
+ */
2055
+ type Simplify<T> = {
2056
+ [K in keyof T]: T[K];
2057
+ } & {};
2058
+ /** @internal The object shape of a readonly store: a child store per key, plus `extend`. */
2059
+ type SignalStoreObject<T> = Simplify<Readonly<{
1967
2060
  [K in keyof Required<T>]: SignalStore<NonNullable<T>[K]>;
1968
- }>);
1969
- type WritableSignalStore<T> = WritableSignal<T> & {
1970
- readonly asReadonlyStore: () => SignalStore<T>;
1971
- } & (NonNullable<T> extends BaseType ? unknown : NonNullable<T> extends Array<any> ? WritableArrayStore<NonNullable<T>> : Readonly<{
2061
+ }> & {
2062
+ readonly extend: {
2063
+ <L extends AnyRecord>(source: Signal<L>): SignalStore<Simplify<Omit<NonNullable<T>, keyof L> & L>>;
2064
+ <L extends AnyRecord>(props: L): SignalStore<Simplify<Omit<NonNullable<T>, keyof L> & L>>;
2065
+ };
2066
+ }>;
2067
+ /** @internal The object shape of a writable store. */
2068
+ type WritableSignalStoreObject<T> = Simplify<Readonly<{
1972
2069
  [K in keyof Required<T>]: WritableSignalStore<NonNullable<T>[K]>;
1973
- }>);
1974
- type MutableSignalStore<T> = MutableSignal<T> & {
1975
- readonly asReadonlyStore: () => SignalStore<T>;
1976
- } & (NonNullable<T> extends BaseType ? unknown : NonNullable<T> extends Array<any> ? MutableArrayStore<NonNullable<T>> : Readonly<{
2070
+ }> & {
2071
+ readonly extend: {
2072
+ <L extends AnyRecord>(source: WritableSignal<L>): WritableSignalStore<Simplify<Omit<NonNullable<T>, keyof L> & L>>;
2073
+ <L extends AnyRecord>(props: L): WritableSignalStore<Simplify<Omit<NonNullable<T>, keyof L> & L>>;
2074
+ };
2075
+ }>;
2076
+ /** @internal The object shape of a mutable store. */
2077
+ type MutableSignalStoreObject<T> = Simplify<Readonly<{
1977
2078
  [K in keyof Required<T>]: MutableSignalStore<NonNullable<T>[K]>;
1978
- }>);
1979
- declare function toStore<T extends AnyRecord>(source: MutableSignal<T>, injector?: Injector): MutableSignalStore<T>;
1980
- declare function toStore<T extends AnyRecord>(source: WritableSignal<T>, injector?: Injector): WritableSignalStore<T>;
1981
- declare function toStore<T extends AnyRecord>(source: Signal<T>, injector?: Injector): SignalStore<T>;
2079
+ }> & {
2080
+ readonly extend: {
2081
+ <L extends AnyRecord>(source: MutableSignal<L>): MutableSignalStore<Simplify<Omit<NonNullable<T>, keyof L> & L>>;
2082
+ <L extends AnyRecord>(props: L): MutableSignalStore<Simplify<Omit<NonNullable<T>, keyof L> & L>>;
2083
+ };
2084
+ }>;
2085
+ type SignalStore<T> = Signal<Unwrap<T>> & (IsAny<T> extends true ? SignalStoreObject<T> : NonNullable<T> extends BaseType ? unknown : NonNullable<T> extends Array<any> ? SignalArrayStore<NonNullable<T>> : SignalStoreObject<T>);
2086
+ type WritableSignalStore<T> = WritableSignal<Unwrap<T>> & {
2087
+ readonly asReadonlyStore: () => SignalStore<T>;
2088
+ } & (IsAny<T> extends true ? WritableSignalStoreObject<T> : NonNullable<T> extends BaseType ? unknown : NonNullable<T> extends Array<any> ? WritableArrayStore<NonNullable<T>> : WritableSignalStoreObject<T>);
2089
+ type MutableSignalStore<T> = MutableSignal<Unwrap<T>> & {
2090
+ readonly asReadonlyStore: () => SignalStore<T>;
2091
+ } & (IsAny<T> extends true ? MutableSignalStoreObject<T> : NonNullable<T> extends BaseType ? unknown : NonNullable<T> extends Array<any> ? MutableArrayStore<NonNullable<T>> : MutableSignalStoreObject<T>);
2092
+ declare function toStore<T extends AnyRecord>(source: MutableSignal<T>, injector?: Injector, vivify?: Vivify): MutableSignalStore<T>;
2093
+ declare function toStore<T extends AnyRecord>(source: WritableSignal<T>, injector?: Injector, vivify?: Vivify): WritableSignalStore<T>;
2094
+ declare function toStore<T extends AnyRecord>(source: Signal<T>, injector?: Injector, vivify?: Vivify): SignalStore<T>;
1982
2095
  /**
1983
2096
  * Creates a WritableSignalStore from a value.
1984
2097
  * @see {@link toStore}
1985
2098
  */
1986
2099
  declare function store<T extends AnyRecord>(value: T, opt?: CreateSignalOptions<T> & {
1987
2100
  injector?: Injector;
2101
+ /**
2102
+ * Opt-in autovivification: when writing through a `null`/`undefined` path, create the
2103
+ * missing intermediate containers instead of dropping the write. Off by default.
2104
+ *
2105
+ * Levels whose current value is a known object/array re-vivify as that same shape — the
2106
+ * knowledge is captured when the path is first accessed and cached, so it holds even after
2107
+ * the value is later nulled. This option governs only genuinely-unknown (currently
2108
+ * `null`/`undefined`) levels: `'auto'` (an array for index keys, an object otherwise), an
2109
+ * explicit `'object'`/`'array'`, or a `() => container` factory. See {@link Vivify}.
2110
+ */
2111
+ vivify?: Vivify;
1988
2112
  }): WritableSignalStore<T>;
1989
2113
  /**
1990
2114
  * Creates a MutableSignalStore from a value.
@@ -1992,7 +2116,30 @@ declare function store<T extends AnyRecord>(value: T, opt?: CreateSignalOptions<
1992
2116
  */
1993
2117
  declare function mutableStore<T extends AnyRecord>(value: T, opt?: CreateSignalOptions<T> & {
1994
2118
  injector?: Injector;
2119
+ /**
2120
+ * Opt-in autovivification: when writing through a `null`/`undefined` path, create the
2121
+ * missing intermediate containers instead of dropping the write. Off by default.
2122
+ *
2123
+ * Levels whose current value is a known object/array re-vivify as that same shape — the
2124
+ * knowledge is captured when the path is first accessed and cached, so it holds even after
2125
+ * the value is later nulled. This option governs only genuinely-unknown (currently
2126
+ * `null`/`undefined`) levels: `'auto'` (an array for index keys, an object otherwise), an
2127
+ * explicit `'object'`/`'array'`, or a `() => container` factory. See {@link Vivify}.
2128
+ */
2129
+ vivify?: Vivify;
1995
2130
  }): MutableSignalStore<T>;
2131
+ /**
2132
+ * Marks a plain object as opaque so {@link store} treats it as an indivisible leaf
2133
+ * (returned whole, never deep-proxied) — the same way it treats a `Date` or `RegExp`.
2134
+ * The marker is a non-enumerable symbol, so it never appears in spreads or iteration.
2135
+ * Idempotent. Call before freezing (`defineProperty` fails on a frozen object).
2136
+ *
2137
+ * @example
2138
+ * const s = store({ config: opaque({ theme: 'dark', nested: { a: 1 } }) });
2139
+ * s.config(); // the whole object, not a child store
2140
+ * s.config.set(opaque({ theme: 'light', nested: { a: 2 } }));
2141
+ */
2142
+ declare function opaque<T extends object>(value: T): Opaque<T>;
1996
2143
 
1997
2144
  /**
1998
2145
  * Interface for storage mechanisms compatible with the `stored` signal.
@@ -2421,5 +2568,5 @@ type CreateHistoryOptions<T> = Omit<CreateSignalOptions<T[]>, 'equal'> & {
2421
2568
  */
2422
2569
  declare function withHistory<T>(sourceOrValue: WritableSignal<T> | T, opt?: CreateHistoryOptions<T>): SignalWithHistory<T>;
2423
2570
 
2424
- export { batteryStatus, chunked, clipboard, combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, filterWith, focusWithin, geolocation, idle, indexArray, isDerivation, isMutable, isStore, keyArray, map, mapArray, mapObject, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, orientation, pageVisibility, pairwise, pipeable, piped, pooled, pooledArray, pooledMap, pooledSet, prefersDarkMode, prefersReducedMotion, scan, scrollPosition, select, sensor, sensors, signalFromEvent, startWith, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
2425
- export type { BatteryStatus, ClipboardSignal, Computation, CreateChunkedOptions, CreateDebouncedOptions, CreateHistoryOptions, CreatePooledOptions, CreateProvidedPooledOptions, CreateStoredOptions, CreateThrottledOptions, DebouncedSignal, DerivedSignal, ElementSize, ElementSizeOptions, ElementSizeSignal, ElementVisibilityOptions, ElementVisibilitySignal, GeolocationOptions, GeolocationSignal, IdleOptions, IdleSignal, MousePositionOptions, MousePositionSignal, MutableSignal, MutableSignalStore, NetworkStatusSignal, PipeableSignal, ScreenOrientation, ScrollPosition, ScrollPositionOptions, ScrollPositionSignal, SignalFromEventOptions, SignalStore, SignalWithHistory, StoredSignal, ThrottledSignal, UntilOptions, WindowSize, WindowSizeOptions, WindowSizeSignal, WritableSignalStore };
2571
+ export { batteryStatus, chunked, clipboard, combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, filterWith, focusWithin, geolocation, idle, indexArray, isDerivation, isMutable, isStore, keyArray, map, mapArray, mapObject, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, opaque, orientation, pageVisibility, pairwise, pipeable, piped, pooled, pooledArray, pooledMap, pooledSet, prefersDarkMode, prefersReducedMotion, scan, scrollPosition, select, sensor, sensors, signalFromEvent, startWith, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
2572
+ export type { BatteryStatus, ClipboardSignal, Computation, CreateChunkedOptions, CreateDebouncedOptions, CreateHistoryOptions, CreatePooledOptions, CreateProvidedPooledOptions, CreateStoredOptions, CreateThrottledOptions, DebouncedSignal, DerivedSignal, ElementSize, ElementSizeOptions, ElementSizeSignal, ElementVisibilityOptions, ElementVisibilitySignal, GeolocationOptions, GeolocationSignal, IdleOptions, IdleSignal, MousePositionOptions, MousePositionSignal, MutableSignal, MutableSignalStore, NetworkStatusSignal, Opaque, PipeableSignal, ScreenOrientation, ScrollPosition, ScrollPositionOptions, ScrollPositionSignal, SignalFromEventOptions, SignalStore, SignalWithHistory, StoredSignal, ThrottledSignal, UntilOptions, Vivify, WindowSize, WindowSizeOptions, WindowSizeSignal, WithVivify, WritableSignalStore };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/primitives",
3
- "version": "20.5.7",
3
+ "version": "20.5.9",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "signals",