@tstdl/base 0.91.3 → 0.91.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.
Files changed (32) hide show
  1. package/data-structures/circular-buffer.d.ts +17 -17
  2. package/data-structures/circular-buffer.js +16 -16
  3. package/data-structures/collection.d.ts +16 -7
  4. package/data-structures/collection.js +29 -25
  5. package/package.json +5 -5
  6. package/signals/api.d.ts +6 -0
  7. package/signals/api.js +3 -1
  8. package/signals/computed-with-dependencies.d.ts +1 -1
  9. package/signals/effect-with-dependencies.d.ts +1 -1
  10. package/signals/implementation/computed.js +3 -1
  11. package/signals/implementation/configure.js +4 -2
  12. package/signals/implementation/effect.d.ts +22 -0
  13. package/signals/implementation/effect.js +0 -7
  14. package/signals/implementation/equality.js +7 -1
  15. package/signals/implementation/graph.js +15 -9
  16. package/signals/implementation/index.d.ts +1 -0
  17. package/signals/implementation/index.js +1 -0
  18. package/signals/implementation/signal.d.ts +1 -36
  19. package/signals/implementation/signal.js +2 -39
  20. package/signals/implementation/to-observable.d.ts +16 -1
  21. package/signals/implementation/to-observable.js +2 -2
  22. package/signals/implementation/to-signal.d.ts +28 -8
  23. package/signals/implementation/to-signal.js +10 -7
  24. package/signals/implementation/watch.d.ts +1 -2
  25. package/signals/implementation/watch.js +8 -2
  26. package/signals/implementation/writable-signal.d.ts +48 -0
  27. package/signals/implementation/writable-signal.js +33 -0
  28. package/signals/operators/derive-async.d.ts +5 -5
  29. package/signals/to-lazy-signal.js +4 -12
  30. package/utils/function/memoize.d.ts +2 -2
  31. package/utils/function/memoize.js +2 -2
  32. package/utils/reactive-value-to-signal.d.ts +5 -5
@@ -1,5 +1,5 @@
1
- import type { CancellationSignal } from '../cancellation/token.js';
2
- import type { Observable } from 'rxjs';
1
+ import { type Observable } from 'rxjs';
2
+ import { type CancellationSignal } from '../cancellation/token.js';
3
3
  import { Collection } from './collection.js';
4
4
  export declare class CircularBuffer<T> extends Collection<T, CircularBuffer<T>> {
5
5
  private readonly maxBufferSizeSubject;
@@ -7,31 +7,31 @@ export declare class CircularBuffer<T> extends Collection<T, CircularBuffer<T>>
7
7
  private backingArray;
8
8
  private writeIndex;
9
9
  private readIndex;
10
- /** emits overwritten items */
10
+ /** Emits overwritten items */
11
11
  readonly overflow$: Observable<T>;
12
- /** emits count of free slots in the buffer */
12
+ /** Emits count of free slots in the buffer */
13
13
  readonly freeSlots$: Observable<number>;
14
- /** emits when the buffer is full */
14
+ /** Emits when the buffer is full */
15
15
  readonly onFull$: Observable<void>;
16
- /** emits when the buffer has free slots */
16
+ /** Emits when the buffer has free slots */
17
17
  readonly onFreeSlots$: Observable<void>;
18
- /** emits whether the buffer is full */
18
+ /** Emits whether the buffer is full */
19
19
  readonly isFull$: Observable<boolean>;
20
- /** emits whether the buffer has free slots */
20
+ /** Emits whether the buffer has free slots */
21
21
  readonly hasFreeSlots$: Observable<boolean>;
22
- /** resolves when the buffer is full */
22
+ /** Resolves when the buffer is full */
23
23
  get $onFull(): Promise<void>;
24
- /** resolves when the buffer has items */
24
+ /** Resolves when the buffer has items */
25
25
  get $onFreeSlots(): Promise<void>;
26
- /** size of buffer */
26
+ /** Size of buffer */
27
27
  get bufferSize(): number;
28
- /** size of buffer */
28
+ /** Size of buffer */
29
29
  get maxBufferSize(): number;
30
- /** count of free slots in buffer */
30
+ /** Count of free slots in buffer */
31
31
  get freeSlots(): number;
32
- /** whether the buffer is full */
32
+ /** Whether the buffer is full */
33
33
  get isFull(): boolean;
34
- /** whether the buffer has free slots */
34
+ /** Whether the buffer has free slots */
35
35
  get hasFreeSlots(): boolean;
36
36
  constructor(maxBufferSize?: number);
37
37
  includes(item: T): boolean;
@@ -41,10 +41,10 @@ export declare class CircularBuffer<T> extends Collection<T, CircularBuffer<T>>
41
41
  tryRemove(): T | undefined;
42
42
  clone(newMaxBufferSize?: number | undefined): CircularBuffer<T>;
43
43
  items(): IterableIterator<T>;
44
- /** yields all items from the buffer and removes them */
44
+ /** Yields all items from the buffer and removes them */
45
45
  consume(): IterableIterator<T>;
46
46
  /**
47
- * yields all items from the buffer, removes them and waits fore more
47
+ * Yields all items from the buffer, removes them and waits fore more
48
48
  * @param cancellationSignal token to cancel iteration
49
49
  * @param yieldOutstandingItems whether to yield all outstanding items or exit immdiately when {@link cancellationSignal} is set
50
50
  * @returns
@@ -1,6 +1,6 @@
1
+ import { BehaviorSubject, Subject, distinctUntilChanged, filter, first, firstValueFrom, from, map, race } from 'rxjs';
1
2
  import { CancellationToken } from '../cancellation/token.js';
2
3
  import { isArray, isDefined, isUndefined } from '../utils/type-guards.js';
3
- import { BehaviorSubject, Subject, distinctUntilChanged, filter, first, firstValueFrom, from, map, race } from 'rxjs';
4
4
  import { Collection } from './collection.js';
5
5
  export class CircularBuffer extends Collection {
6
6
  maxBufferSizeSubject;
@@ -8,46 +8,46 @@ export class CircularBuffer extends Collection {
8
8
  backingArray;
9
9
  writeIndex;
10
10
  readIndex;
11
- /** emits overwritten items */
11
+ /** Emits overwritten items */
12
12
  overflow$;
13
- /** emits count of free slots in the buffer */
13
+ /** Emits count of free slots in the buffer */
14
14
  freeSlots$;
15
- /** emits when the buffer is full */
15
+ /** Emits when the buffer is full */
16
16
  onFull$;
17
- /** emits when the buffer has free slots */
17
+ /** Emits when the buffer has free slots */
18
18
  onFreeSlots$;
19
- /** emits whether the buffer is full */
19
+ /** Emits whether the buffer is full */
20
20
  isFull$;
21
- /** emits whether the buffer has free slots */
21
+ /** Emits whether the buffer has free slots */
22
22
  hasFreeSlots$;
23
- /** resolves when the buffer is full */
23
+ /** Resolves when the buffer is full */
24
24
  get $onFull() {
25
25
  return firstValueFrom(this.onFull$);
26
26
  }
27
- /** resolves when the buffer has items */
27
+ /** Resolves when the buffer has items */
28
28
  get $onFreeSlots() {
29
29
  return firstValueFrom(this.onFreeSlots$);
30
30
  }
31
- /** size of buffer */
31
+ /** Size of buffer */
32
32
  get bufferSize() {
33
33
  return this.backingArray.length;
34
34
  }
35
- /** size of buffer */
35
+ /** Size of buffer */
36
36
  get maxBufferSize() {
37
37
  return this.maxBufferSizeSubject.value ?? Infinity;
38
38
  }
39
- /** count of free slots in buffer */
39
+ /** Count of free slots in buffer */
40
40
  get freeSlots() {
41
41
  if (isUndefined(this.maxBufferSize)) {
42
42
  return Infinity;
43
43
  }
44
44
  return this.bufferSize - this.size;
45
45
  }
46
- /** whether the buffer is full */
46
+ /** Whether the buffer is full */
47
47
  get isFull() {
48
48
  return this.freeSlots == 0;
49
49
  }
50
- /** whether the buffer has free slots */
50
+ /** Whether the buffer has free slots */
51
51
  get hasFreeSlots() {
52
52
  return this.freeSlots > 0;
53
53
  }
@@ -134,14 +134,14 @@ export class CircularBuffer extends Collection {
134
134
  subscription.unsubscribe();
135
135
  }
136
136
  }
137
- /** yields all items from the buffer and removes them */
137
+ /** Yields all items from the buffer and removes them */
138
138
  *consume() {
139
139
  while (this.size > 0) {
140
140
  yield this.tryRemove();
141
141
  }
142
142
  }
143
143
  /**
144
- * yields all items from the buffer, removes them and waits fore more
144
+ * Yields all items from the buffer, removes them and waits fore more
145
145
  * @param cancellationSignal token to cancel iteration
146
146
  * @param yieldOutstandingItems whether to yield all outstanding items or exit immdiately when {@link cancellationSignal} is set
147
147
  * @returns
@@ -1,24 +1,33 @@
1
- import type { ToJson } from '../interfaces.js';
2
1
  import { type Observable } from 'rxjs';
2
+ import type { ToJson } from '../interfaces.js';
3
+ import { type Signal } from '../signals/index.js';
3
4
  export declare abstract class Collection<T, TThis extends Collection<T, TThis> = Collection<T, any>> implements Iterable<T>, ToJson {
4
5
  private readonly sizeSubject;
5
6
  private readonly changeSubject;
6
7
  private readonly clearSubject;
7
- /** Emits collection on subscribe and change */
8
- readonly observe$: Observable<TThis>;
9
8
  /** Emits size of collection */
10
9
  readonly size$: Observable<number>;
10
+ /** Size of collection */
11
+ readonly $size: Signal<number>;
11
12
  /** Emits collection on change */
12
13
  readonly change$: Observable<TThis>;
14
+ /** Emits collection on subscribe and change */
15
+ readonly observe$: Observable<TThis>;
16
+ /** Notifies on change */
17
+ readonly $observe: Signal<TThis>;
13
18
  readonly clear$: Observable<TThis>;
14
- /** Emits when the collection is empty */
15
- readonly onEmpty$: Observable<void>;
16
- /** Emits when the collection has items */
17
- readonly onItems$: Observable<void>;
18
19
  /** Emits whether the collection is empty */
19
20
  readonly isEmpty$: Observable<boolean>;
21
+ /** Whether the collection is empty */
22
+ readonly $isEmpty: Signal<boolean>;
20
23
  /** Emits whether the collection has items */
21
24
  readonly hasItems$: Observable<boolean>;
25
+ /** Whether the collection has items */
26
+ readonly $hasItems: Signal<boolean>;
27
+ /** Emits when the collection is empty */
28
+ readonly onEmpty$: Observable<undefined>;
29
+ /** Emits when the collection has items */
30
+ readonly onItems$: Observable<undefined>;
22
31
  /** Resolves when the collection is empty */
23
32
  get $onEmpty(): Promise<void>;
24
33
  /** Resolves when the collection has items */
@@ -1,24 +1,34 @@
1
1
  import { BehaviorSubject, Subject, distinctUntilChanged, filter, firstValueFrom, map, startWith } from 'rxjs';
2
+ import { getCurrentSignalsInjector, toLazySignal, untracked } from '../signals/index.js';
3
+ import { lazyProperty } from '../utils/object/lazy-property.js';
2
4
  export class Collection {
3
- sizeSubject;
4
- changeSubject;
5
- clearSubject;
6
- /** Emits collection on subscribe and change */
7
- observe$;
5
+ sizeSubject = new BehaviorSubject(0);
6
+ changeSubject = new Subject();
7
+ clearSubject = new Subject();
8
8
  /** Emits size of collection */
9
- size$;
9
+ size$ = this.sizeSubject.asObservable();
10
+ /** Size of collection */
11
+ $size;
10
12
  /** Emits collection on change */
11
- change$;
13
+ change$ = this.changeSubject.asObservable();
14
+ /** Emits collection on subscribe and change */
15
+ observe$ = this.change$.pipe(startWith(this)); // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
16
+ /** Notifies on change */
17
+ $observe;
12
18
  /* Emits collection on clear */
13
- clear$;
14
- /** Emits when the collection is empty */
15
- onEmpty$;
16
- /** Emits when the collection has items */
17
- onItems$;
19
+ clear$ = this.clearSubject.asObservable();
18
20
  /** Emits whether the collection is empty */
19
- isEmpty$;
21
+ isEmpty$ = this.size$.pipe(map(() => this.isEmpty), distinctUntilChanged());
22
+ /** Whether the collection is empty */
23
+ $isEmpty;
20
24
  /** Emits whether the collection has items */
21
- hasItems$;
25
+ hasItems$ = this.size$.pipe(map(() => this.hasItems), distinctUntilChanged());
26
+ /** Whether the collection has items */
27
+ $hasItems;
28
+ /** Emits when the collection is empty */
29
+ onEmpty$ = this.isEmpty$.pipe(filter((isEmpty) => isEmpty), map(() => undefined));
30
+ /** Emits when the collection has items */
31
+ onItems$ = this.hasItems$.pipe(filter((hasItems) => hasItems), map(() => undefined));
22
32
  /** Resolves when the collection is empty */
23
33
  get $onEmpty() {
24
34
  return firstValueFrom(this.onEmpty$);
@@ -40,17 +50,11 @@ export class Collection {
40
50
  return this.size > 0;
41
51
  }
42
52
  constructor() {
43
- this.sizeSubject = new BehaviorSubject(0);
44
- this.changeSubject = new Subject();
45
- this.clearSubject = new Subject();
46
- this.size$ = this.sizeSubject.asObservable();
47
- this.change$ = this.changeSubject.asObservable();
48
- this.clear$ = this.clearSubject.asObservable();
49
- this.observe$ = this.change$.pipe(startWith(this));
50
- this.isEmpty$ = this.size$.pipe(map(() => this.isEmpty), distinctUntilChanged());
51
- this.hasItems$ = this.size$.pipe(map(() => this.hasItems), distinctUntilChanged());
52
- this.onEmpty$ = this.isEmpty$.pipe(filter((isEmpty) => isEmpty), map(() => undefined));
53
- this.onItems$ = this.hasItems$.pipe(filter((hasItems) => hasItems), map(() => undefined));
53
+ const injector = getCurrentSignalsInjector();
54
+ lazyProperty(this, '$size', () => untracked(() => toLazySignal(this.size$, { injector, requireSync: true })));
55
+ lazyProperty(this, '$observe', () => untracked(() => toLazySignal(this.observe$, { injector, requireSync: true, equal: () => false })));
56
+ lazyProperty(this, '$isEmpty', () => untracked(() => toLazySignal(this.isEmpty$, { injector, requireSync: true })));
57
+ lazyProperty(this, '$hasItems', () => untracked(() => toLazySignal(this.hasItems$, { injector, requireSync: true })));
54
58
  }
55
59
  [Symbol.iterator]() {
56
60
  return this.items();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.91.3",
3
+ "version": "0.91.6",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -108,10 +108,10 @@
108
108
  },
109
109
  "dependencies": {
110
110
  "disposablestack": "1.1",
111
- "luxon": "^3.4",
111
+ "luxon": "^3.5",
112
112
  "reflect-metadata": "^0.2",
113
113
  "rxjs": "^7.8",
114
- "type-fest": "4.23"
114
+ "type-fest": "4.24"
115
115
  },
116
116
  "devDependencies": {
117
117
  "@mxssfd/typedoc-theme": "1.1",
@@ -142,7 +142,7 @@
142
142
  "@zxcvbn-ts/language-de": "^3.0",
143
143
  "@zxcvbn-ts/language-en": "^3.0",
144
144
  "chroma-js": "^2.6",
145
- "drizzle-orm": "^0.32",
145
+ "drizzle-orm": "^0.33",
146
146
  "handlebars": "^4.7",
147
147
  "koa": "^2.15",
148
148
  "minio": "^8.0",
@@ -150,7 +150,7 @@
150
150
  "mongodb": "^6.8",
151
151
  "nodemailer": "^6.9",
152
152
  "pg": "^8.12",
153
- "playwright": "^1.45",
153
+ "playwright": "^1.46",
154
154
  "preact": "^10.23",
155
155
  "preact-render-to-string": "^6.5",
156
156
  "undici": "^6.19",
package/signals/api.d.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import type { Tagged } from 'type-fest';
1
2
  import type * as Types from './implementation/index.js';
2
3
  export type { CreateComputedOptions, CreateEffectOptions, CreateSignalOptions, EffectCleanupRegisterFn, EffectRef, Signal, ToSignalOptions, WritableSignal } from './implementation/index.js';
4
+ export type Injector = Tagged<symbol, 'injector'>;
3
5
  export type SignalsConfiguration = {
4
6
  signal: typeof Types.signal;
5
7
  computed: typeof Types.computed;
@@ -8,6 +10,8 @@ export type SignalsConfiguration = {
8
10
  isSignal: typeof Types.isSignal;
9
11
  toSignal: typeof Types.toSignal;
10
12
  toObservable: typeof Types.toObservable;
13
+ getCurrentSignalsInjector: () => Injector;
14
+ runInSignalsInjectionContext: <ReturnT>(injector: Injector, fn: () => ReturnT) => ReturnT;
11
15
  };
12
16
  export declare let signal: typeof Types.signal;
13
17
  export declare let computed: typeof Types.computed;
@@ -16,4 +20,6 @@ export declare let untracked: typeof Types.untracked;
16
20
  export declare let isSignal: typeof Types.isSignal;
17
21
  export declare let toSignal: typeof Types.toSignal;
18
22
  export declare let toObservable: typeof Types.toObservable;
23
+ export declare let getCurrentSignalsInjector: SignalsConfiguration['getCurrentSignalsInjector'];
24
+ export declare let runInSignalsInjectionContext: SignalsConfiguration['runInSignalsInjectionContext'];
19
25
  export declare function configureSignals(configuration: SignalsConfiguration): void;
package/signals/api.js CHANGED
@@ -6,9 +6,11 @@ export let untracked = throwSignalsNotConfigured;
6
6
  export let isSignal = throwSignalsNotConfigured;
7
7
  export let toSignal = throwSignalsNotConfigured;
8
8
  export let toObservable = throwSignalsNotConfigured;
9
+ export let getCurrentSignalsInjector = throwSignalsNotConfigured;
10
+ export let runInSignalsInjectionContext = throwSignalsNotConfigured;
9
11
  /* eslint-enable import/no-mutable-exports */
10
12
  export function configureSignals(configuration) {
11
- ({ signal, computed, effect, untracked, isSignal, toSignal, toObservable } = configuration);
13
+ ({ signal, computed, effect, untracked, isSignal, toSignal, toObservable, runInSignalsInjectionContext, getCurrentSignalsInjector } = configuration);
12
14
  }
13
15
  function throwSignalsNotConfigured() {
14
16
  throw new Error('Signals are not configured. Use configureDefaultSignalsImplementation() for default implementation or configureSignals() for custom implementation.');
@@ -1,2 +1,2 @@
1
- import type { CreateComputedOptions, Signal } from './api.js';
1
+ import { type CreateComputedOptions, type Signal } from './api.js';
2
2
  export declare function computedWithDependencies<T>(computation: () => T, dependencies: Signal<any>[], options?: CreateComputedOptions<T>): Signal<T>;
@@ -1,2 +1,2 @@
1
- import type { CreateEffectOptions, EffectCleanupRegisterFn, EffectRef, Signal } from './api.js';
1
+ import { type CreateEffectOptions, type EffectCleanupRegisterFn, type EffectRef, type Signal } from './api.js';
2
2
  export declare function effectWithDependencies(effectFn: (onCleanup: EffectCleanupRegisterFn) => void, dependencies: Signal<any>[], options?: CreateEffectOptions): EffectRef;
@@ -67,7 +67,9 @@ const COMPUTED_NODE = {
67
67
  finally {
68
68
  consumerAfterComputation(node, prevConsumer);
69
69
  }
70
- if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
70
+ if (oldValue !== UNSET &&
71
+ oldValue !== ERRORED &&
72
+ newValue !== ERRORED &&
71
73
  node.equal(oldValue, newValue)) {
72
74
  // No change to `valueVersion` - old and new values are
73
75
  // semantically equivalent.
@@ -2,10 +2,10 @@ import { configureSignals } from '../api.js';
2
2
  import { isSignal } from './api.js';
3
3
  import { computed } from './computed.js';
4
4
  import { effect } from './effect.js';
5
- import { signal } from './signal.js';
6
5
  import { toObservable } from './to-observable.js';
7
6
  import { toSignal } from './to-signal.js';
8
7
  import { untracked } from './untracked.js';
8
+ import { signal } from './writable-signal.js';
9
9
  export function configureDefaultSignalsImplementation() {
10
10
  configureSignals({
11
11
  signal,
@@ -14,6 +14,8 @@ export function configureDefaultSignalsImplementation() {
14
14
  untracked,
15
15
  isSignal,
16
16
  toSignal,
17
- toObservable
17
+ toObservable,
18
+ getCurrentSignalsInjector: () => null,
19
+ runInSignalsInjectionContext: (_, fn) => fn()
18
20
  });
19
21
  }
@@ -1,3 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+ import type { Injector } from '../api.js';
1
9
  /**
2
10
  * An effect can, optionally, register a cleanup function. If registered, the cleanup is executed
3
11
  * before the next effect run. The cleanup function makes it possible to "cancel" any work that the
@@ -45,6 +53,20 @@ export interface EffectRef {
45
53
  * Options passed to the `effect` function.
46
54
  */
47
55
  export interface CreateEffectOptions {
56
+ /**
57
+ * The `Injector` in which to create the effect.
58
+ *
59
+ * If this is not provided, the current [injection context](guide/di/dependency-injection-context)
60
+ * will be used instead (via `inject`).
61
+ */
62
+ injector?: Injector;
63
+ /**
64
+ * Whether the `effect` should require manual cleanup.
65
+ *
66
+ * If this is `false` (the default) the effect will automatically register itself to be cleaned up
67
+ * with the current `DestroyRef`.
68
+ */
69
+ manualCleanup?: boolean;
48
70
  /**
49
71
  * Whether the `effect` should allow writing to signals.
50
72
  *
@@ -1,11 +1,4 @@
1
1
  /* eslint-disable */
2
- /**
3
- * @license
4
- * Copyright Google LLC All Rights Reserved.
5
- *
6
- * Use of this source code is governed by an MIT-style license that can be
7
- * found in the LICENSE file at https://angular.io/license
8
- */
9
2
  import { assertNotInReactiveContext } from './asserts.js';
10
3
  import { createWatch } from './watch.js';
11
4
  /**
@@ -1,4 +1,10 @@
1
- /* eslint-disable */
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
2
8
  /**
3
9
  * The default equality function used for `signal` and `computed`, which uses referential equality.
4
10
  */
@@ -85,8 +85,9 @@ export function producerAccessed(node) {
85
85
  activeConsumer.producerNode[idx] = node;
86
86
  // If the active consumer is live, then add it as a live consumer. If not, then use 0 as a
87
87
  // placeholder value.
88
- activeConsumer.producerIndexOfThis[idx] =
89
- consumerIsLive(activeConsumer) ? producerAddLiveConsumer(node, activeConsumer, idx) : 0;
88
+ activeConsumer.producerIndexOfThis[idx] = consumerIsLive(activeConsumer)
89
+ ? producerAddLiveConsumer(node, activeConsumer, idx)
90
+ : 0;
90
91
  }
91
92
  activeConsumer.producerLastReadVersion[idx] = node.version;
92
93
  }
@@ -176,7 +177,9 @@ export function consumerBeforeComputation(node) {
176
177
  */
177
178
  export function consumerAfterComputation(node, prevConsumer) {
178
179
  setActiveConsumer(prevConsumer);
179
- if (!node || node.producerNode === undefined || node.producerIndexOfThis === undefined ||
180
+ if (!node ||
181
+ node.producerNode === undefined ||
182
+ node.producerIndexOfThis === undefined ||
180
183
  node.producerLastReadVersion === undefined) {
181
184
  return;
182
185
  }
@@ -234,8 +237,10 @@ export function consumerDestroy(node) {
234
237
  }
235
238
  }
236
239
  // Truncate all the arrays to drop all connection from this node to the graph.
237
- node.producerNode.length = node.producerLastReadVersion.length = node.producerIndexOfThis.length =
238
- 0;
240
+ node.producerNode.length =
241
+ node.producerLastReadVersion.length =
242
+ node.producerIndexOfThis.length =
243
+ 0;
239
244
  if (node.liveConsumerNode) {
240
245
  node.liveConsumerNode.length = node.liveConsumerIndexOfThis.length = 0;
241
246
  }
@@ -248,8 +253,7 @@ export function consumerDestroy(node) {
248
253
  */
249
254
  function producerAddLiveConsumer(node, consumer, indexOfThis) {
250
255
  assertProducerNode(node);
251
- assertConsumerNode(node);
252
- if (node.liveConsumerNode.length === 0) {
256
+ if (node.liveConsumerNode.length === 0 && isConsumerNode(node)) {
253
257
  // When going from 0 to 1 live consumers, we become a live consumer to our producers.
254
258
  for (let i = 0; i < node.producerNode.length; i++) {
255
259
  node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i);
@@ -263,11 +267,10 @@ function producerAddLiveConsumer(node, consumer, indexOfThis) {
263
267
  */
264
268
  function producerRemoveLiveConsumerAtIndex(node, idx) {
265
269
  assertProducerNode(node);
266
- assertConsumerNode(node);
267
270
  if (idx >= node.liveConsumerNode.length) {
268
271
  throw new Error(`Assertion error: active consumer index ${idx} is out of bounds of ${node.liveConsumerNode.length} consumers)`);
269
272
  }
270
- if (node.liveConsumerNode.length === 1) {
273
+ if (node.liveConsumerNode.length === 1 && isConsumerNode(node)) {
271
274
  // When removing the last live consumer, we will no longer be live. We need to remove
272
275
  // ourselves from our producers' tracking (which may cause consumer-producers to lose
273
276
  // liveness as well).
@@ -304,3 +307,6 @@ function assertProducerNode(node) {
304
307
  node.liveConsumerNode ??= [];
305
308
  node.liveConsumerIndexOfThis ??= [];
306
309
  }
310
+ function isConsumerNode(node) {
311
+ return node.producerNode !== undefined;
312
+ }
@@ -10,3 +10,4 @@ export * from './to-observable.js';
10
10
  export * from './to-signal.js';
11
11
  export * from './untracked.js';
12
12
  export * from './watch.js';
13
+ export * from './writable-signal.js';
@@ -10,3 +10,4 @@ export * from './to-observable.js';
10
10
  export * from './to-signal.js';
11
11
  export * from './untracked.js';
12
12
  export * from './watch.js';
13
+ export * from './writable-signal.js';
@@ -5,14 +5,12 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
- import type { Signal } from './api.js';
9
8
  import type { ValueEqualityFn } from './equality.js';
10
9
  import type { ReactiveNode } from './graph.js';
11
10
  import { SIGNAL } from './graph.js';
12
11
  export interface SignalNode<T> extends ReactiveNode {
13
12
  value: T;
14
13
  equal: ValueEqualityFn<T>;
15
- readonly [SIGNAL]: SignalNode<T>;
16
14
  }
17
15
  export type SignalBaseGetter<T> = (() => T) & {
18
16
  readonly [SIGNAL]: unknown;
@@ -28,37 +26,4 @@ export declare function setPostSignalSetFn(fn: (() => void) | null): (() => void
28
26
  export declare function signalGetFn<T>(this: SignalNode<T>): T;
29
27
  export declare function signalSetFn<T>(node: SignalNode<T>, newValue: T): void;
30
28
  export declare function signalUpdateFn<T>(node: SignalNode<T>, updater: (value: T) => T): void;
31
- export declare function signalMutateFn<T>(node: SignalNode<T>, mutator: (value: T) => void): void;
32
- /**
33
- * A `Signal` with a value that can be mutated via a setter interface.
34
- */
35
- export interface WritableSignal<T> extends Signal<T> {
36
- /**
37
- * Directly set the signal to a new value, and notify any dependents.
38
- */
39
- set(value: T): void;
40
- /**
41
- * Update the value of the signal based on its current value, and
42
- * notify any dependents.
43
- */
44
- update(updateFn: (value: T) => T): void;
45
- /**
46
- * Returns a readonly version of this signal. Readonly signals can be accessed to read their value
47
- * but can't be changed using set or update methods. The readonly signals do _not_ have
48
- * any built-in mechanism that would prevent deep-mutation of their value.
49
- */
50
- asReadonly(): Signal<T>;
51
- }
52
- /**
53
- * Options passed to the `signal` creation function.
54
- */
55
- export interface CreateSignalOptions<T> {
56
- /**
57
- * A comparison function which defines equality for signal values.
58
- */
59
- equal?: ValueEqualityFn<T>;
60
- }
61
- /**
62
- * Create a `Signal` that can be set or updated directly.
63
- */
64
- export declare function signal<T>(initialValue: T, options?: CreateSignalOptions<T>): WritableSignal<T>;
29
+ export declare const SIGNAL_NODE: SignalNode<unknown>;
@@ -35,13 +35,7 @@ export function signalSetFn(node, newValue) {
35
35
  if (!producerUpdatesAllowed()) {
36
36
  throwInvalidWriteToSignalError();
37
37
  }
38
- const value = node.value;
39
- if (Object.is(value, newValue)) {
40
- if (!node.equal(value, newValue)) {
41
- console.warn('Signal value equality implementations should always return `true` for values that are the same according to `Object.is` but returned `false` instead.');
42
- }
43
- }
44
- else if (!node.equal(value, newValue)) {
38
+ if (!node.equal(node.value, newValue)) {
45
39
  node.value = newValue;
46
40
  signalValueChanged(node);
47
41
  }
@@ -52,15 +46,7 @@ export function signalUpdateFn(node, updater) {
52
46
  }
53
47
  signalSetFn(node, updater(node.value));
54
48
  }
55
- export function signalMutateFn(node, mutator) {
56
- if (!producerUpdatesAllowed()) {
57
- throwInvalidWriteToSignalError();
58
- }
59
- // Mutate bypasses equality checks as it's by definition changing the value.
60
- mutator(node.value);
61
- signalValueChanged(node);
62
- }
63
- const SIGNAL_NODE = {
49
+ export const SIGNAL_NODE = {
64
50
  ...REACTIVE_NODE,
65
51
  equal: defaultEquals,
66
52
  value: undefined,
@@ -71,26 +57,3 @@ function signalValueChanged(node) {
71
57
  producerNotifyConsumers(node);
72
58
  postSignalSetFn?.();
73
59
  }
74
- /**
75
- * Create a `Signal` that can be set or updated directly.
76
- */
77
- export function signal(initialValue, options) {
78
- const signalFn = createSignal(initialValue);
79
- const node = signalFn[SIGNAL];
80
- if (options?.equal) {
81
- node.equal = options.equal;
82
- }
83
- signalFn.set = (newValue) => signalSetFn(node, newValue);
84
- signalFn.update = (updateFn) => signalUpdateFn(node, updateFn);
85
- signalFn.asReadonly = signalAsReadonlyFn.bind(signalFn);
86
- return signalFn;
87
- }
88
- function signalAsReadonlyFn() {
89
- const node = this[SIGNAL];
90
- if (node.readonlyFn === undefined) {
91
- const readonlyFn = () => this();
92
- readonlyFn[SIGNAL] = node;
93
- node.readonlyFn = readonlyFn;
94
- }
95
- return node.readonlyFn;
96
- }
@@ -1,8 +1,23 @@
1
1
  import { Observable } from 'rxjs';
2
+ import type { Injector } from '../api.js';
2
3
  import type { Signal } from './api.js';
4
+ /**
5
+ * Options for `toObservable`.
6
+ *
7
+ * @developerPreview
8
+ */
9
+ export interface ToObservableOptions {
10
+ /**
11
+ * The `Injector` to use when creating the underlying `effect` which watches the signal.
12
+ *
13
+ * If this isn't specified, the current [injection context](guide/di/dependency-injection-context)
14
+ * will be used.
15
+ */
16
+ injector?: Injector;
17
+ }
3
18
  /**
4
19
  * Exposes the value of an `Signal` as an RxJS `Observable`.
5
20
  *
6
21
  * The signal's value will be propagated into the `Observable`'s subscribers using an `effect`.
7
22
  */
8
- export declare function toObservable<T>(source: Signal<T>): Observable<T>;
23
+ export declare function toObservable<T>(source: Signal<T>, options?: ToObservableOptions): Observable<T>;
@@ -6,7 +6,7 @@ import { untracked } from './untracked.js';
6
6
  *
7
7
  * The signal's value will be propagated into the `Observable`'s subscribers using an `effect`.
8
8
  */
9
- export function toObservable(source) {
9
+ export function toObservable(source, options) {
10
10
  return new Observable((subscriber) => {
11
11
  const effectRef = effect(() => {
12
12
  try {
@@ -16,7 +16,7 @@ export function toObservable(source) {
16
16
  catch (error) {
17
17
  untracked(() => subscriber.error(error));
18
18
  }
19
- }, { allowSignalWrites: true });
19
+ }, { allowSignalWrites: true, injector: options?.injector });
20
20
  return () => effectRef.destroy();
21
21
  });
22
22
  }
@@ -6,11 +6,10 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import type { Observable, Subscribable } from 'rxjs';
9
+ import { Injector } from '../api.js';
9
10
  import type { Signal } from './api.js';
10
- /**
11
- * Options for `toSignal`.
12
- */
13
- export interface ToSignalOptions {
11
+ import { ValueEqualityFn } from './equality.js';
12
+ export interface ToSignalOptions<T> {
14
13
  /**
15
14
  * Initial value for the signal produced by `toSignal`.
16
15
  *
@@ -26,6 +25,21 @@ export interface ToSignalOptions {
26
25
  * not met.
27
26
  */
28
27
  requireSync?: boolean;
28
+ /**
29
+ * `Injector` which will provide the `DestroyRef` used to clean up the Observable subscription.
30
+ *
31
+ * If this is not provided, a `DestroyRef` will be retrieved from the current [injection
32
+ * context](guide/di/dependency-injection-context), unless manual cleanup is requested.
33
+ */
34
+ injector?: Injector;
35
+ /**
36
+ * Whether the subscription should be automatically cleaned up (via `DestroyRef`) when
37
+ * `toSignal`'s creation context is destroyed.
38
+ *
39
+ * If manual cleanup is enabled, then `DestroyRef` is not used, and the subscription will persist
40
+ * until the `Observable` itself completes.
41
+ */
42
+ manualCleanup?: boolean;
29
43
  /**
30
44
  * Whether `toSignal` should throw errors from the Observable error channel back to RxJS, where
31
45
  * they'll be processed as uncaught exceptions.
@@ -35,21 +49,27 @@ export interface ToSignalOptions {
35
49
  * the behavior of the `async` pipe.
36
50
  */
37
51
  rejectErrors?: boolean;
52
+ /**
53
+ * A comparison function which defines equality for values emitted by the observable.
54
+ *
55
+ * Equality comparisons are executed against the initial value if one is provided.
56
+ */
57
+ equal?: ValueEqualityFn<T>;
38
58
  }
39
59
  export declare function toSignal<T>(source: Observable<T> | Subscribable<T>): Signal<T | undefined>;
40
- export declare function toSignal<T>(source: Observable<T> | Subscribable<T>, options: ToSignalOptions & {
60
+ export declare function toSignal<T>(source: Observable<T> | Subscribable<T>, options: NoInfer<ToSignalOptions<T | undefined>> & {
41
61
  initialValue?: undefined;
42
62
  requireSync?: false;
43
63
  }): Signal<T | undefined>;
44
- export declare function toSignal<T>(source: Observable<T> | Subscribable<T>, options: ToSignalOptions & {
64
+ export declare function toSignal<T>(source: Observable<T> | Subscribable<T>, options: NoInfer<ToSignalOptions<T | null>> & {
45
65
  initialValue?: null;
46
66
  requireSync?: false;
47
67
  }): Signal<T | null>;
48
- export declare function toSignal<T>(source: Observable<T> | Subscribable<T>, options: ToSignalOptions & {
68
+ export declare function toSignal<T>(source: Observable<T> | Subscribable<T>, options: NoInfer<ToSignalOptions<T>> & {
49
69
  initialValue?: undefined;
50
70
  requireSync: true;
51
71
  }): Signal<T>;
52
- export declare function toSignal<T, const U extends T>(source: Observable<T> | Subscribable<T>, options: ToSignalOptions & {
72
+ export declare function toSignal<T, const U extends T>(source: Observable<T> | Subscribable<T>, options: NoInfer<ToSignalOptions<T | U>> & {
53
73
  initialValue: U;
54
74
  requireSync?: false;
55
75
  }): Signal<T | U>;
@@ -2,7 +2,7 @@
2
2
  import { registerFinalization } from '../../memory/finalization.js';
3
3
  import { assertNotInReactiveContext } from './asserts.js';
4
4
  import { computed } from './computed.js';
5
- import { signal } from './signal.js';
5
+ import { signal } from './writable-signal.js';
6
6
  /**
7
7
  * Get the current value of an `Observable` as a reactive `Signal`.
8
8
  *
@@ -18,16 +18,17 @@ import { signal } from './signal.js';
18
18
  export function toSignal(source, options) {
19
19
  assertNotInReactiveContext(toSignal, 'Invoking `toSignal` causes new subscriptions every time. ' +
20
20
  'Consider moving `toSignal` outside of the reactive context and read the signal value where needed.');
21
+ const equal = makeToSignalEqual(options?.equal);
21
22
  // Note: T is the Observable value type, and U is the initial value type. They don't have to be
22
23
  // the same - the returned signal gives values of type `T`.
23
24
  let state;
24
25
  if (options?.requireSync) {
25
26
  // Initially the signal is in a `NoValue` state.
26
- state = signal({ kind: StateKind.NoValue });
27
+ state = signal({ kind: StateKind.NoValue }, { equal });
27
28
  }
28
29
  else {
29
30
  // If an initial value was passed, use it. Otherwise, use `undefined` as the initial value.
30
- state = signal({ kind: StateKind.Value, value: options?.initialValue });
31
+ state = signal({ kind: StateKind.Value, value: options?.initialValue }, { equal });
31
32
  }
32
33
  // Note: This code cannot run inside a reactive context (see assertion above). If we'd support
33
34
  // this, we would subscribe to the observable outside of the current reactive context, avoiding
@@ -36,8 +37,8 @@ export function toSignal(source, options) {
36
37
  // subscription. Additional context (related to async pipe):
37
38
  // https://github.com/angular/angular/pull/50522.
38
39
  const sub = source.subscribe({
39
- next: value => state.set({ kind: StateKind.Value, value }),
40
- error: error => {
40
+ next: (value) => state.set({ kind: StateKind.Value, value }),
41
+ error: (error) => {
41
42
  if (options?.rejectErrors) {
42
43
  // Kick the error back to RxJS. It will be caught and rethrown in a macrotask, which causes
43
44
  // the error to end up as an uncaught exception.
@@ -62,13 +63,15 @@ export function toSignal(source, options) {
62
63
  throw current.error;
63
64
  case StateKind.NoValue:
64
65
  // This shouldn't really happen because the error is thrown on creation.
65
- // TODO(alxhub): use a RuntimeError when we finalize the error semantics
66
66
  throw new Error('`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.');
67
67
  }
68
- });
68
+ }, { equal: options?.equal });
69
69
  registerFinalization(result, () => sub.unsubscribe());
70
70
  return result;
71
71
  }
72
+ function makeToSignalEqual(userEquality = Object.is) {
73
+ return (a, b) => a.kind === StateKind.Value && b.kind === StateKind.Value && userEquality(a.value, b.value);
74
+ }
72
75
  var StateKind;
73
76
  (function (StateKind) {
74
77
  StateKind[StateKind["NoValue"] = 0] = "NoValue";
@@ -5,8 +5,7 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
- import type { ReactiveNode } from './graph.js';
9
- import { SIGNAL } from './graph.js';
8
+ import { type ReactiveNode, SIGNAL } from './graph.js';
10
9
  /**
11
10
  * A cleanup function that can be optionally registered from the watch logic. If registered, the
12
11
  * cleanup logic runs before the next watch execution.
@@ -1,5 +1,11 @@
1
- /* eslint-disable */
2
- import { consumerAfterComputation, consumerBeforeComputation, consumerDestroy, consumerMarkDirty, consumerPollProducersForChange, isInNotificationPhase, REACTIVE_NODE, SIGNAL } from './graph.js';
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+ import { consumerAfterComputation, consumerBeforeComputation, consumerDestroy, consumerMarkDirty, consumerPollProducersForChange, isInNotificationPhase, REACTIVE_NODE, SIGNAL, } from './graph.js';
3
9
  export function createWatch(fn, schedule, allowSignalWrites) {
4
10
  const node = Object.create(WATCH_NODE);
5
11
  if (allowSignalWrites) {
@@ -0,0 +1,48 @@
1
+ import { Signal } from './api.js';
2
+ import { ValueEqualityFn } from './equality.js';
3
+ import { SignalGetter } from './signal.js';
4
+ /**
5
+ * @license
6
+ * Copyright Google LLC All Rights Reserved.
7
+ *
8
+ * Use of this source code is governed by an MIT-style license that can be
9
+ * found in the LICENSE file at https://angular.io/license
10
+ */
11
+ /**
12
+ * A `Signal` with a value that can be mutated via a setter interface.
13
+ */
14
+ export interface WritableSignal<T> extends Signal<T> {
15
+ /**
16
+ * Directly set the signal to a new value, and notify any dependents.
17
+ */
18
+ set(value: T): void;
19
+ /**
20
+ * Update the value of the signal based on its current value, and
21
+ * notify any dependents.
22
+ */
23
+ update(updateFn: (value: T) => T): void;
24
+ /**
25
+ * Returns a readonly version of this signal. Readonly signals can be accessed to read their value
26
+ * but can't be changed using set or update methods. The readonly signals do _not_ have
27
+ * any built-in mechanism that would prevent deep-mutation of their value.
28
+ */
29
+ asReadonly(): Signal<T>;
30
+ }
31
+ /**
32
+ * Options passed to the `signal` creation function.
33
+ */
34
+ export interface CreateSignalOptions<T> {
35
+ /**
36
+ * A comparison function which defines equality for signal values.
37
+ */
38
+ equal?: ValueEqualityFn<T>;
39
+ }
40
+ /**
41
+ * Create a `Signal` that can be set or updated directly.
42
+ */
43
+ export declare function signal<T>(initialValue: T, options?: CreateSignalOptions<T>): WritableSignal<T>;
44
+ export declare function signalAsReadonlyFn<T>(this: SignalGetter<T>): Signal<T>;
45
+ /**
46
+ * Checks if the given `value` is a writeable signal.
47
+ */
48
+ export declare function isWritableSignal(value: unknown): value is WritableSignal<unknown>;
@@ -0,0 +1,33 @@
1
+ /* eslint-disable */
2
+ import { SIGNAL } from '../symbol.js';
3
+ import { isSignal } from './api.js';
4
+ import { createSignal, signalSetFn, signalUpdateFn } from './signal.js';
5
+ /**
6
+ * Create a `Signal` that can be set or updated directly.
7
+ */
8
+ export function signal(initialValue, options) {
9
+ const signalFn = createSignal(initialValue);
10
+ const node = signalFn[SIGNAL];
11
+ if (options?.equal) {
12
+ node.equal = options.equal;
13
+ }
14
+ signalFn.set = (newValue) => signalSetFn(node, newValue);
15
+ signalFn.update = (updateFn) => signalUpdateFn(node, updateFn);
16
+ signalFn.asReadonly = signalAsReadonlyFn.bind(signalFn);
17
+ return signalFn;
18
+ }
19
+ export function signalAsReadonlyFn() {
20
+ const node = this[SIGNAL];
21
+ if (node.readonlyFn === undefined) {
22
+ const readonlyFn = () => this();
23
+ readonlyFn[SIGNAL] = node;
24
+ node.readonlyFn = readonlyFn;
25
+ }
26
+ return node.readonlyFn;
27
+ }
28
+ /**
29
+ * Checks if the given `value` is a writeable signal.
30
+ */
31
+ export function isWritableSignal(value) {
32
+ return isSignal(value) && typeof value.set === 'function';
33
+ }
@@ -1,6 +1,6 @@
1
1
  import { concatAll, exhaustAll, mergeAll, switchAll, type Observable } from 'rxjs';
2
2
  import { type Signal, type ToSignalOptions } from '../api.js';
3
- export type DeriveAsyncOptions = ToSignalOptions & {
3
+ export type DeriveAsyncOptions<T> = ToSignalOptions<T> & {
4
4
  behavior?: DeriveAsyncBehavior;
5
5
  };
6
6
  declare const operatorMap: {
@@ -12,19 +12,19 @@ declare const operatorMap: {
12
12
  export type DeriveAsyncBehavior = keyof typeof operatorMap;
13
13
  type DeriveAsyncSourceParameter<T> = () => (T | Promise<T> | Observable<T>);
14
14
  export declare function deriveAsync<T>(source: DeriveAsyncSourceParameter<T>): Signal<T | undefined>;
15
- export declare function deriveAsync<T>(source: DeriveAsyncSourceParameter<T>, options: DeriveAsyncOptions & {
15
+ export declare function deriveAsync<T>(source: DeriveAsyncSourceParameter<T>, options: DeriveAsyncOptions<T> & {
16
16
  initialValue?: undefined;
17
17
  requireSync?: false;
18
18
  }): Signal<T | undefined>;
19
- export declare function deriveAsync<T>(source: DeriveAsyncSourceParameter<T>, options: DeriveAsyncOptions & {
19
+ export declare function deriveAsync<T>(source: DeriveAsyncSourceParameter<T>, options: DeriveAsyncOptions<T> & {
20
20
  initialValue?: null;
21
21
  requireSync?: false;
22
22
  }): Signal<T | null>;
23
- export declare function deriveAsync<T>(source: DeriveAsyncSourceParameter<T>, options: DeriveAsyncOptions & {
23
+ export declare function deriveAsync<T>(source: DeriveAsyncSourceParameter<T>, options: DeriveAsyncOptions<T> & {
24
24
  initialValue?: undefined;
25
25
  requireSync: true;
26
26
  }): Signal<T>;
27
- export declare function deriveAsync<T, const U extends T>(source: DeriveAsyncSourceParameter<T>, options: DeriveAsyncOptions & {
27
+ export declare function deriveAsync<T, const U extends T>(source: DeriveAsyncSourceParameter<T>, options: DeriveAsyncOptions<T> & {
28
28
  initialValue: U;
29
29
  requireSync?: false;
30
30
  }): Signal<T | U>;
@@ -1,19 +1,11 @@
1
- import { Subject, switchMap } from 'rxjs';
2
- import { computed, toSignal } from './api.js';
3
- const LAZY = Symbol('LAZY');
1
+ import { computed, getCurrentSignalsInjector, runInSignalsInjectionContext, toSignal, untracked } from './api.js';
4
2
  export const toLazySignal = function toLazySignal(source, options) {
5
- const subscribe$ = new Subject();
6
- const lazySource = subscribe$.pipe(switchMap(() => source));
7
- const signal = toSignal(lazySource, { initialValue: LAZY, ...options }); // eslint-disable-line @typescript-eslint/no-unsafe-argument
3
+ const injector = getCurrentSignalsInjector();
8
4
  let computation = () => {
9
- subscribe$.next();
10
- subscribe$.complete();
5
+ const signal = untracked(() => runInSignalsInjectionContext(injector, () => toSignal(source, { ...options }))); // eslint-disable-line @typescript-eslint/no-unsafe-argument
11
6
  const value = signal();
12
7
  computation = signal;
13
- if (value == LAZY) {
14
- throw new Error('`toLazySignal()` called with `requireSync` but `Observable` did not emit synchronously.');
15
- }
16
8
  return value;
17
9
  };
18
- return computed(() => computation());
10
+ return computed(() => computation(), { equal: options?.equal });
19
11
  };
@@ -4,12 +4,12 @@ export type MemoizeOptions = {
4
4
  weak?: boolean;
5
5
  };
6
6
  /**
7
- * memoizes a function with an arbitrary number of parameters. If you only need a single parameter, {@link memoizeSingle} is faster
7
+ * Memoizes a function with an arbitrary number of parameters. If you only need a single parameter, {@link memoizeSingle} is faster
8
8
  * @param fn function memoize
9
9
  * @returns memoized function
10
10
  */ export declare function memoize<Fn extends (...parameters: any[]) => any>(fn: Fn, options?: MemoizeOptions): Fn;
11
11
  /**
12
- * memoizes a function with a single parameter. Faster than {@link memoize}
12
+ * Memoizes a function with a single parameter. Faster than {@link memoize}
13
13
  * @param fn function memoize
14
14
  * @returns memoized function
15
15
  */
@@ -1,7 +1,7 @@
1
1
  import { IterableWeakMap } from '../../data-structures/iterable-weak-map.js';
2
2
  import { MultiKeyMap } from '../../data-structures/multi-key-map.js';
3
3
  /**
4
- * memoizes a function with an arbitrary number of parameters. If you only need a single parameter, {@link memoizeSingle} is faster
4
+ * Memoizes a function with an arbitrary number of parameters. If you only need a single parameter, {@link memoizeSingle} is faster
5
5
  * @param fn function memoize
6
6
  * @returns memoized function
7
7
  */ export function memoize(fn, options = {}) {
@@ -19,7 +19,7 @@ import { MultiKeyMap } from '../../data-structures/multi-key-map.js';
19
19
  }[name];
20
20
  }
21
21
  /**
22
- * memoizes a function with a single parameter. Faster than {@link memoize}
22
+ * Memoizes a function with a single parameter. Faster than {@link memoize}
23
23
  * @param fn function memoize
24
24
  * @returns memoized function
25
25
  */
@@ -1,20 +1,20 @@
1
1
  import { type Signal, type ToSignalOptions } from '../signals/api.js';
2
2
  import type { ReactiveValue } from '../types.js';
3
- export type ReactiveValueToSignalOptions = ToSignalOptions;
3
+ export type ReactiveValueToSignalOptions<T> = ToSignalOptions<T>;
4
4
  export declare function reactiveValueToSignal<T>(source: ReactiveValue<T>): Signal<T | undefined>;
5
- export declare function reactiveValueToSignal<T>(source: ReactiveValue<T>, options: ReactiveValueToSignalOptions & {
5
+ export declare function reactiveValueToSignal<T>(source: ReactiveValue<T>, options: ReactiveValueToSignalOptions<T | undefined> & {
6
6
  initialValue?: undefined;
7
7
  requireSync?: false;
8
8
  }): Signal<T | undefined>;
9
- export declare function reactiveValueToSignal<T>(source: ReactiveValue<T>, options: ReactiveValueToSignalOptions & {
9
+ export declare function reactiveValueToSignal<T>(source: ReactiveValue<T>, options: ReactiveValueToSignalOptions<T | null> & {
10
10
  initialValue?: null;
11
11
  requireSync?: false;
12
12
  }): Signal<T | null>;
13
- export declare function reactiveValueToSignal<T>(source: ReactiveValue<T>, options: ReactiveValueToSignalOptions & {
13
+ export declare function reactiveValueToSignal<T>(source: ReactiveValue<T>, options: ReactiveValueToSignalOptions<T> & {
14
14
  initialValue?: undefined;
15
15
  requireSync: true;
16
16
  }): Signal<T>;
17
- export declare function reactiveValueToSignal<T, const I>(source: ReactiveValue<T>, options: ReactiveValueToSignalOptions & {
17
+ export declare function reactiveValueToSignal<T, const I>(source: ReactiveValue<T>, options: ReactiveValueToSignalOptions<T | I> & {
18
18
  initialValue: I;
19
19
  requireSync?: false;
20
20
  }): Signal<T | I>;