signalium 0.3.2 → 0.3.3

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 (41) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/CHANGELOG.md +6 -0
  3. package/dist/cjs/hooks.d.ts +1 -6
  4. package/dist/cjs/hooks.d.ts.map +1 -1
  5. package/dist/cjs/hooks.js +7 -25
  6. package/dist/cjs/hooks.js.map +1 -1
  7. package/dist/cjs/index.d.ts +2 -2
  8. package/dist/cjs/index.d.ts.map +1 -1
  9. package/dist/cjs/index.js +2 -1
  10. package/dist/cjs/index.js.map +1 -1
  11. package/dist/cjs/signals.d.ts +4 -4
  12. package/dist/cjs/signals.d.ts.map +1 -1
  13. package/dist/cjs/signals.js +38 -21
  14. package/dist/cjs/signals.js.map +1 -1
  15. package/dist/cjs/types.d.ts +5 -1
  16. package/dist/cjs/types.d.ts.map +1 -1
  17. package/dist/esm/hooks.d.ts +1 -6
  18. package/dist/esm/hooks.d.ts.map +1 -1
  19. package/dist/esm/hooks.js +7 -25
  20. package/dist/esm/hooks.js.map +1 -1
  21. package/dist/esm/index.d.ts +2 -2
  22. package/dist/esm/index.d.ts.map +1 -1
  23. package/dist/esm/index.js +1 -1
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/signals.d.ts +4 -4
  26. package/dist/esm/signals.d.ts.map +1 -1
  27. package/dist/esm/signals.js +38 -21
  28. package/dist/esm/signals.js.map +1 -1
  29. package/dist/esm/types.d.ts +5 -1
  30. package/dist/esm/types.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/__tests__/hooks/async-task.test.ts +1 -1
  33. package/src/__tests__/signals/subscription.test.ts +6 -6
  34. package/src/__tests__/signals/watcher.test.ts +2 -2
  35. package/src/__tests__/utils/instrumented-hooks.ts +1 -2
  36. package/src/__tests__/utils/instrumented-signals.ts +23 -13
  37. package/src/__tests__/utils/permute.ts +3 -3
  38. package/src/hooks.ts +24 -63
  39. package/src/index.ts +2 -0
  40. package/src/signals.ts +48 -34
  41. package/src/types.ts +9 -4
package/src/hooks.ts CHANGED
@@ -19,6 +19,8 @@ import {
19
19
  SignalSubscription,
20
20
  Watcher,
21
21
  WriteableSignal,
22
+ SignalSubscribe,
23
+ SubscriptionState,
22
24
  } from './types.js';
23
25
  import { getObjectId, getUnknownSignalFnName, hashValue } from './utils.js';
24
26
  import { getFrameworkScope, useSignalValue } from './config.js';
@@ -172,40 +174,33 @@ export class SignalScope {
172
174
  let signal = this.getSignal(key, computedMask);
173
175
 
174
176
  if (signal === undefined) {
177
+ const optsWithMeta = { ...opts, id: key, desc: fnName, params };
175
178
  let initialized = false;
176
179
 
177
180
  if (makeSignal === createSubscriptionSignal) {
178
- signal = makeSignal(
179
- (get, set) => {
180
- const sub = this.run(fn, [{ get, set }, ...args], key, signal!, initialized) as
181
- | SignalSubscription
182
- | undefined;
183
-
184
- if (sub?.update) {
185
- const originalUpdate = sub.update;
186
-
187
- sub.update = () => {
188
- return this.run(originalUpdate, [], key, signal!, initialized);
189
- };
190
- }
191
-
192
- initialized = true;
193
-
194
- return sub;
195
- },
196
- { ...opts, id: key, desc: fnName, params },
197
- );
181
+ signal = makeSignal(state => {
182
+ const sub = this.run(fn, [state, ...args], key, signal!, initialized);
183
+
184
+ if (typeof sub === 'object' && sub !== null && sub?.update) {
185
+ const originalUpdate = sub.update;
186
+
187
+ sub.update = () => {
188
+ return this.run(originalUpdate, [], key, signal!, initialized);
189
+ };
190
+ }
191
+
192
+ initialized = true;
193
+
194
+ return sub;
195
+ }, optsWithMeta);
198
196
  } else {
199
- signal = makeSignal(
200
- (...runArgs) => {
201
- const result = this.run(fn, [...args, ...runArgs], key, signal!, initialized);
197
+ signal = makeSignal((...runArgs) => {
198
+ const result = this.run(fn, [...args, ...runArgs], key, signal!, initialized);
202
199
 
203
- initialized = true;
200
+ initialized = true;
204
201
 
205
- return result;
206
- },
207
- { ...opts, id: key, desc: fnName, params },
208
- );
202
+ return result;
203
+ }, optsWithMeta);
209
204
  }
210
205
  }
211
206
 
@@ -341,44 +336,10 @@ export function asyncComputed<T, Args extends unknown[]>(
341
336
  };
342
337
  }
343
338
 
344
- export interface SubscriptionState<T> {
345
- get: () => T;
346
- set: (value: T) => void;
347
- }
348
-
349
- export type SignalSubscribe<T, Args extends unknown[]> = (
350
- state: SubscriptionState<T>,
351
- ...args: Args
352
- ) => SignalSubscription | (() => unknown) | undefined | void;
353
-
354
339
  export function subscription<T, Args extends unknown[]>(
355
340
  fn: SignalSubscribe<T, Args>,
356
341
  opts?: Partial<SignalOptionsWithInit<T, Args>>,
357
342
  ): (...args: Args) => T {
358
- const wrapper = (state: SubscriptionState<T>, ...args: Args) => {
359
- let result = fn(state, ...args);
360
-
361
- if (typeof result === 'function') {
362
- return {
363
- update() {
364
- (result as () => void)();
365
- result = fn(state, ...args);
366
- },
367
-
368
- unsubscribe() {
369
- (result as () => void)();
370
- },
371
- };
372
- }
373
-
374
- return result;
375
- };
376
-
377
- Object.defineProperty(wrapper, 'name', {
378
- value: fn.name,
379
- writable: false,
380
- });
381
-
382
343
  return (...args) => {
383
344
  const params = getParamsKey(args, opts);
384
345
  const key = getComputedKey(fn, params);
@@ -387,7 +348,7 @@ export function subscription<T, Args extends unknown[]>(
387
348
  const scope = getCurrentScope();
388
349
  return scope.get(
389
350
  createSubscriptionSignal,
390
- wrapper,
351
+ fn,
391
352
  key,
392
353
  params,
393
354
  args,
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export type {
13
13
  AsyncReady,
14
14
  AsyncResult,
15
15
  Watcher,
16
+ SubscriptionState,
16
17
  } from './types.js';
17
18
 
18
19
  export {
@@ -32,6 +33,7 @@ export {
32
33
  withContext,
33
34
  computed,
34
35
  asyncComputed,
36
+ asyncTask,
35
37
  subscription,
36
38
  watcher,
37
39
  SignalScope,
package/src/signals.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  SignalOptionsWithInit,
21
21
  SignalSubscribe,
22
22
  SignalSubscription,
23
+ SubscriptionState,
23
24
  Watcher,
24
25
  WatcherListenerOptions,
25
26
  } from './types.js';
@@ -36,7 +37,7 @@ const enum SignalType {
36
37
  Watcher,
37
38
  }
38
39
 
39
- const SUBSCRIPTIONS = new WeakMap<ComputedSignal<any>, SignalSubscription | undefined | void>();
40
+ const SUBSCRIPTIONS = new WeakMap<ComputedSignal<any>, SignalSubscription | (() => unknown) | undefined | void>();
40
41
  const ACTIVE_ASYNCS = new WeakMap<ComputedSignal<any>, Promise<unknown>>();
41
42
 
42
43
  const enum SignalState {
@@ -90,14 +91,14 @@ export class ComputedSignal<T> {
90
91
  _computedCount: number = 0;
91
92
  _connectedCount: number = 0;
92
93
  _currentValue: T | AsyncResult<T> | undefined;
93
- _compute: SignalCompute<T> | SignalAsyncCompute<T> | SignalSubscribe<T>;
94
+ _compute: SignalCompute<T> | SignalAsyncCompute<T> | SignalSubscribe<T, []>;
94
95
 
95
96
  _opts: InternalSignalOptions<T>;
96
97
  _ref: WeakRef<ComputedSignal<T>> = new WeakRef(this);
97
98
 
98
99
  constructor(
99
100
  type: SignalType,
100
- compute: SignalCompute<T> | SignalAsyncCompute<T> | SignalSubscribe<T>,
101
+ compute: SignalCompute<T> | SignalAsyncCompute<T> | SignalSubscribe<T, []>,
101
102
  opts: InternalSignalOptions<T>,
102
103
  initValue?: T,
103
104
  ) {
@@ -389,37 +390,18 @@ export class ComputedSignal<T> {
389
390
 
390
391
  case SignalType.Subscription: {
391
392
  if (shouldConnect) {
392
- const subscription = (this._compute as SignalSubscribe<T>)(
393
- () => this._currentValue as T,
394
- value => {
395
- const version = this._version;
396
-
397
- if (version !== 0 && this._opts.equals(value, this._currentValue as T)) {
398
- return;
399
- }
400
-
401
- TRACER?.emit({
402
- type: TracerEventType.StartUpdate,
403
- id: this._opts.id,
404
- });
405
-
406
- this._currentValue = value;
407
- this._version = version + 1;
408
- this._dirtyConsumers();
409
-
410
- TRACER?.emit({
411
- type: TracerEventType.EndUpdate,
412
- id: this._opts.id,
413
- value: this._currentValue,
414
- preserveChildren: true,
415
- });
416
- },
417
- );
393
+ const subscription = (this._compute as SignalSubscribe<T, []>)(createSubscriptionState(this));
418
394
  SUBSCRIPTIONS.set(this, subscription);
419
395
  } else {
420
396
  const subscription = SUBSCRIPTIONS.get(this);
421
397
 
422
- subscription?.update?.();
398
+ if (typeof subscription === 'function') {
399
+ subscription();
400
+ const nextSubscription = (this._compute as SignalSubscribe<T, []>)(createSubscriptionState(this));
401
+ SUBSCRIPTIONS.set(this, nextSubscription);
402
+ } else if (subscription !== undefined) {
403
+ subscription.update?.();
404
+ }
423
405
  }
424
406
 
425
407
  break;
@@ -552,7 +534,10 @@ export class ComputedSignal<T> {
552
534
  if (this._type === SignalType.Subscription) {
553
535
  const subscription = SUBSCRIPTIONS.get(this);
554
536
 
555
- if (subscription !== undefined) {
537
+ if (typeof subscription === 'function') {
538
+ subscription();
539
+ SUBSCRIPTIONS.delete(this);
540
+ } else if (subscription !== undefined) {
556
541
  subscription.unsubscribe?.();
557
542
  SUBSCRIPTIONS.delete(this);
558
543
  }
@@ -596,6 +581,35 @@ export class ComputedSignal<T> {
596
581
  }
597
582
  }
598
583
 
584
+ function createSubscriptionState<T>(signal: ComputedSignal<T>): SubscriptionState<T> {
585
+ return {
586
+ get: () => signal._currentValue as T,
587
+ set: value => {
588
+ const version = signal._version;
589
+
590
+ if (version !== 0 && signal._opts.equals(value, signal._currentValue as T)) {
591
+ return;
592
+ }
593
+
594
+ TRACER?.emit({
595
+ type: TracerEventType.StartUpdate,
596
+ id: signal._opts.id,
597
+ });
598
+
599
+ signal._currentValue = value;
600
+ signal._version = version + 1;
601
+ signal._dirtyConsumers();
602
+
603
+ TRACER?.emit({
604
+ type: TracerEventType.EndUpdate,
605
+ id: signal._opts.id,
606
+ value: signal._currentValue,
607
+ preserveChildren: true,
608
+ });
609
+ },
610
+ };
611
+ }
612
+
599
613
  let STATE_ID = 0;
600
614
 
601
615
  export class StateSignal<T> implements StateSignal<T> {
@@ -703,15 +717,15 @@ export function createAsyncComputedSignal<T>(
703
717
  }
704
718
 
705
719
  export function createSubscriptionSignal<T>(
706
- subscribe: SignalSubscribe<T>,
720
+ subscribe: SignalSubscribe<T, []>,
707
721
  opts?: SignalOptions<T, unknown[]>,
708
722
  ): Signal<T | undefined>;
709
723
  export function createSubscriptionSignal<T>(
710
- subscribe: SignalSubscribe<T>,
724
+ subscribe: SignalSubscribe<T, []>,
711
725
  opts: SignalOptionsWithInit<T, unknown[]>,
712
726
  ): Signal<T>;
713
727
  export function createSubscriptionSignal<T>(
714
- subscribe: SignalSubscribe<T>,
728
+ subscribe: SignalSubscribe<T, []>,
715
729
  opts?: Partial<SignalOptionsWithInit<T, unknown[]>>,
716
730
  ): Signal<T> {
717
731
  return new ComputedSignal(SignalType.Subscription, subscribe, normalizeOpts(opts), opts?.initValue) as Signal<T>;
package/src/types.ts CHANGED
@@ -21,10 +21,15 @@ export type SignalSubscription = {
21
21
  unsubscribe?(): void;
22
22
  };
23
23
 
24
- export type SignalSubscribe<T> = (
25
- get: () => T | undefined,
26
- set: (value: T) => void,
27
- ) => SignalSubscription | undefined | void;
24
+ export interface SubscriptionState<T> {
25
+ get: () => T;
26
+ set: (value: T) => void;
27
+ }
28
+
29
+ export type SignalSubscribe<T, Args extends unknown[]> = (
30
+ state: SubscriptionState<T>,
31
+ ...args: Args
32
+ ) => SignalSubscription | (() => unknown) | undefined | void;
28
33
 
29
34
  export interface SignalOptions<T, Args extends unknown[]> {
30
35
  equals?: SignalEquals<T> | false;