signalium 0.3.7 → 1.0.0
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/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +21 -0
- package/build/react.js +19 -0
- package/build/transform.js +19 -0
- package/dist/cjs/config.d.ts +8 -3
- package/dist/cjs/config.d.ts.map +1 -1
- package/dist/cjs/config.js +14 -8
- package/dist/cjs/config.js.map +1 -1
- package/dist/cjs/debug.d.ts +2 -2
- package/dist/cjs/debug.d.ts.map +1 -1
- package/dist/cjs/debug.js +3 -3
- package/dist/cjs/debug.js.map +1 -1
- package/dist/cjs/hooks.d.ts +14 -42
- package/dist/cjs/hooks.d.ts.map +1 -1
- package/dist/cjs/hooks.js +19 -240
- package/dist/cjs/hooks.js.map +1 -1
- package/dist/cjs/index.d.ts +5 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +18 -18
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internals/async.d.ts +50 -0
- package/dist/cjs/internals/async.d.ts.map +1 -0
- package/dist/cjs/internals/async.js +390 -0
- package/dist/cjs/internals/async.js.map +1 -0
- package/dist/cjs/internals/connect.d.ts +4 -0
- package/dist/cjs/internals/connect.d.ts.map +1 -0
- package/dist/cjs/internals/connect.js +37 -0
- package/dist/cjs/internals/connect.js.map +1 -0
- package/dist/cjs/internals/consumer.d.ts +6 -0
- package/dist/cjs/internals/consumer.d.ts.map +1 -0
- package/dist/cjs/internals/consumer.js +13 -0
- package/dist/cjs/internals/consumer.js.map +1 -0
- package/dist/cjs/internals/contexts.d.ts +33 -0
- package/dist/cjs/internals/contexts.d.ts.map +1 -0
- package/dist/cjs/internals/contexts.js +103 -0
- package/dist/cjs/internals/contexts.js.map +1 -0
- package/dist/cjs/internals/derived.d.ts +66 -0
- package/dist/cjs/internals/derived.d.ts.map +1 -0
- package/dist/cjs/internals/derived.js +128 -0
- package/dist/cjs/internals/derived.js.map +1 -0
- package/dist/cjs/internals/dirty.d.ts +5 -0
- package/dist/cjs/internals/dirty.d.ts.map +1 -0
- package/dist/cjs/internals/dirty.js +79 -0
- package/dist/cjs/internals/dirty.js.map +1 -0
- package/dist/cjs/internals/edge.d.ts +32 -0
- package/dist/cjs/internals/edge.d.ts.map +1 -0
- package/dist/cjs/internals/edge.js +59 -0
- package/dist/cjs/internals/edge.js.map +1 -0
- package/dist/cjs/internals/get.d.ts +10 -0
- package/dist/cjs/internals/get.d.ts.map +1 -0
- package/dist/cjs/internals/get.js +255 -0
- package/dist/cjs/internals/get.js.map +1 -0
- package/dist/cjs/internals/scheduling.d.ts +12 -0
- package/dist/cjs/internals/scheduling.d.ts.map +1 -0
- package/dist/cjs/internals/scheduling.js +117 -0
- package/dist/cjs/internals/scheduling.js.map +1 -0
- package/dist/cjs/internals/state.d.ts +18 -0
- package/dist/cjs/internals/state.d.ts.map +1 -0
- package/dist/cjs/internals/state.js +88 -0
- package/dist/cjs/internals/state.js.map +1 -0
- package/dist/cjs/internals/utils/debug-name.d.ts +2 -0
- package/dist/cjs/internals/utils/debug-name.d.ts.map +1 -0
- package/dist/cjs/internals/utils/debug-name.js +14 -0
- package/dist/cjs/internals/utils/debug-name.js.map +1 -0
- package/dist/cjs/internals/utils/equals.d.ts +3 -0
- package/dist/cjs/internals/utils/equals.d.ts.map +1 -0
- package/dist/cjs/internals/utils/equals.js +13 -0
- package/dist/cjs/internals/utils/equals.js.map +1 -0
- package/dist/cjs/internals/utils/hash.d.ts +7 -0
- package/dist/cjs/internals/utils/hash.d.ts.map +1 -0
- package/dist/cjs/internals/utils/hash.js +181 -0
- package/dist/cjs/internals/utils/hash.js.map +1 -0
- package/dist/cjs/internals/utils/stringify.d.ts +3 -0
- package/dist/cjs/internals/utils/stringify.d.ts.map +1 -0
- package/dist/cjs/{utils.js → internals/utils/stringify.js} +5 -27
- package/dist/cjs/internals/utils/stringify.js.map +1 -0
- package/dist/cjs/internals/utils/type-utils.d.ts +6 -0
- package/dist/cjs/internals/utils/type-utils.d.ts.map +1 -0
- package/dist/cjs/internals/utils/type-utils.js +22 -0
- package/dist/cjs/internals/utils/type-utils.js.map +1 -0
- package/dist/cjs/react/context.d.ts +1 -1
- package/dist/cjs/react/context.d.ts.map +1 -1
- package/dist/cjs/react/provider.d.ts +4 -3
- package/dist/cjs/react/provider.d.ts.map +1 -1
- package/dist/cjs/react/provider.js +7 -3
- package/dist/cjs/react/provider.js.map +1 -1
- package/dist/cjs/react/setup.d.ts.map +1 -1
- package/dist/cjs/react/setup.js +2 -1
- package/dist/cjs/react/setup.js.map +1 -1
- package/dist/cjs/react/signal-value.d.ts +5 -1
- package/dist/cjs/react/signal-value.d.ts.map +1 -1
- package/dist/cjs/react/signal-value.js +35 -45
- package/dist/cjs/react/signal-value.js.map +1 -1
- package/dist/cjs/trace.d.ts +32 -28
- package/dist/cjs/trace.d.ts.map +1 -1
- package/dist/cjs/trace.js +14 -16
- package/dist/cjs/trace.js.map +1 -1
- package/dist/cjs/transform.d.ts +6 -0
- package/dist/cjs/transform.d.ts.map +1 -0
- package/dist/cjs/transform.js +92 -0
- package/dist/cjs/transform.js.map +1 -0
- package/dist/cjs/types.d.ts +32 -40
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/config.d.ts +8 -3
- package/dist/esm/config.d.ts.map +1 -1
- package/dist/esm/config.js +12 -7
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/debug.d.ts +2 -2
- package/dist/esm/debug.d.ts.map +1 -1
- package/dist/esm/debug.js +2 -2
- package/dist/esm/debug.js.map +1 -1
- package/dist/esm/hooks.d.ts +14 -42
- package/dist/esm/hooks.d.ts.map +1 -1
- package/dist/esm/hooks.js +17 -226
- package/dist/esm/hooks.js.map +1 -1
- package/dist/esm/index.d.ts +5 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internals/async.d.ts +50 -0
- package/dist/esm/internals/async.d.ts.map +1 -0
- package/dist/esm/internals/async.js +383 -0
- package/dist/esm/internals/async.js.map +1 -0
- package/dist/esm/internals/connect.d.ts +4 -0
- package/dist/esm/internals/connect.d.ts.map +1 -0
- package/dist/esm/internals/connect.js +33 -0
- package/dist/esm/internals/connect.js.map +1 -0
- package/dist/esm/internals/consumer.d.ts +6 -0
- package/dist/esm/internals/consumer.d.ts.map +1 -0
- package/dist/esm/internals/consumer.js +9 -0
- package/dist/esm/internals/consumer.js.map +1 -0
- package/dist/esm/internals/contexts.d.ts +33 -0
- package/dist/esm/internals/contexts.d.ts.map +1 -0
- package/dist/esm/internals/contexts.js +92 -0
- package/dist/esm/internals/contexts.js.map +1 -0
- package/dist/esm/internals/derived.d.ts +66 -0
- package/dist/esm/internals/derived.d.ts.map +1 -0
- package/dist/esm/internals/derived.js +118 -0
- package/dist/esm/internals/derived.js.map +1 -0
- package/dist/esm/internals/dirty.d.ts +5 -0
- package/dist/esm/internals/dirty.d.ts.map +1 -0
- package/dist/esm/internals/dirty.js +75 -0
- package/dist/esm/internals/dirty.js.map +1 -0
- package/dist/esm/internals/edge.d.ts +32 -0
- package/dist/esm/internals/edge.d.ts.map +1 -0
- package/dist/esm/internals/edge.js +54 -0
- package/dist/esm/internals/edge.js.map +1 -0
- package/dist/esm/internals/get.d.ts +10 -0
- package/dist/esm/internals/get.d.ts.map +1 -0
- package/dist/esm/internals/get.js +247 -0
- package/dist/esm/internals/get.js.map +1 -0
- package/dist/esm/internals/scheduling.d.ts +12 -0
- package/dist/esm/internals/scheduling.d.ts.map +1 -0
- package/dist/esm/internals/scheduling.js +106 -0
- package/dist/esm/internals/scheduling.js.map +1 -0
- package/dist/esm/internals/state.d.ts +18 -0
- package/dist/esm/internals/state.d.ts.map +1 -0
- package/dist/esm/internals/state.js +82 -0
- package/dist/esm/internals/state.js.map +1 -0
- package/dist/esm/internals/utils/debug-name.d.ts +2 -0
- package/dist/esm/internals/utils/debug-name.d.ts.map +1 -0
- package/dist/esm/internals/utils/debug-name.js +11 -0
- package/dist/esm/internals/utils/debug-name.js.map +1 -0
- package/dist/esm/internals/utils/equals.d.ts +3 -0
- package/dist/esm/internals/utils/equals.d.ts.map +1 -0
- package/dist/esm/internals/utils/equals.js +9 -0
- package/dist/esm/internals/utils/equals.js.map +1 -0
- package/dist/esm/internals/utils/hash.d.ts +7 -0
- package/dist/esm/internals/utils/hash.d.ts.map +1 -0
- package/dist/esm/internals/utils/hash.js +174 -0
- package/dist/esm/internals/utils/hash.js.map +1 -0
- package/dist/esm/internals/utils/stringify.d.ts +3 -0
- package/dist/esm/internals/utils/stringify.d.ts.map +1 -0
- package/dist/esm/{utils.js → internals/utils/stringify.js} +4 -25
- package/dist/esm/internals/utils/stringify.js.map +1 -0
- package/dist/esm/internals/utils/type-utils.d.ts +6 -0
- package/dist/esm/internals/utils/type-utils.d.ts.map +1 -0
- package/dist/esm/internals/utils/type-utils.js +15 -0
- package/dist/esm/internals/utils/type-utils.js.map +1 -0
- package/dist/esm/react/context.d.ts +1 -1
- package/dist/esm/react/context.d.ts.map +1 -1
- package/dist/esm/react/provider.d.ts +4 -3
- package/dist/esm/react/provider.d.ts.map +1 -1
- package/dist/esm/react/provider.js +6 -2
- package/dist/esm/react/provider.js.map +1 -1
- package/dist/esm/react/setup.d.ts.map +1 -1
- package/dist/esm/react/setup.js +3 -2
- package/dist/esm/react/setup.js.map +1 -1
- package/dist/esm/react/signal-value.d.ts +5 -1
- package/dist/esm/react/signal-value.d.ts.map +1 -1
- package/dist/esm/react/signal-value.js +34 -45
- package/dist/esm/react/signal-value.js.map +1 -1
- package/dist/esm/trace.d.ts +32 -28
- package/dist/esm/trace.d.ts.map +1 -1
- package/dist/esm/trace.js +13 -15
- package/dist/esm/trace.js.map +1 -1
- package/dist/esm/transform.d.ts +6 -0
- package/dist/esm/transform.d.ts.map +1 -0
- package/dist/esm/transform.js +89 -0
- package/dist/esm/transform.js.map +1 -0
- package/dist/esm/types.d.ts +32 -40
- package/dist/esm/types.d.ts.map +1 -1
- package/package.json +23 -4
- package/src/__tests__/__snapshots__/context.test.ts.snap +2101 -0
- package/src/__tests__/__snapshots__/nesting.test.ts.snap +16201 -0
- package/src/__tests__/__snapshots__/params-and-state.test.ts.snap +1879 -0
- package/src/__tests__/async-task.test.ts +327 -0
- package/src/__tests__/context.test.ts +517 -0
- package/src/__tests__/nesting.test.ts +298 -0
- package/src/__tests__/params-and-state.test.ts +230 -0
- package/src/__tests__/reactive-async.test.ts +548 -0
- package/src/__tests__/reactive-sync.test.ts +130 -0
- package/src/__tests__/subscription.test.ts +510 -0
- package/src/__tests__/utils/async.ts +1 -1
- package/src/__tests__/utils/instrumented-hooks.ts +229 -124
- package/src/__tests__/utils/permute.ts +25 -14
- package/src/config.ts +19 -9
- package/src/debug.ts +2 -2
- package/src/hooks.ts +46 -380
- package/src/index.ts +7 -24
- package/src/internals/async.ts +556 -0
- package/src/internals/connect.ts +41 -0
- package/src/internals/consumer.ts +13 -0
- package/src/internals/contexts.ts +133 -0
- package/src/internals/derived.ts +208 -0
- package/src/internals/dirty.ts +91 -0
- package/src/internals/edge.ts +109 -0
- package/src/internals/get.ts +298 -0
- package/src/internals/scheduling.ts +140 -0
- package/src/internals/state.ts +111 -0
- package/src/internals/utils/debug-name.ts +14 -0
- package/src/internals/utils/equals.ts +12 -0
- package/src/internals/utils/hash.ts +221 -0
- package/src/{utils.ts → internals/utils/stringify.ts} +3 -29
- package/src/internals/utils/type-utils.ts +19 -0
- package/src/react/__tests__/async.test.tsx +704 -0
- package/src/react/__tests__/basic.test.tsx +95 -0
- package/src/react/__tests__/contexts.test.tsx +99 -0
- package/src/react/__tests__/subscriptions.test.tsx +49 -0
- package/src/react/__tests__/utils.tsx +40 -0
- package/src/react/context.ts +1 -1
- package/src/react/provider.tsx +12 -4
- package/src/react/setup.ts +3 -2
- package/src/react/signal-value.ts +47 -67
- package/src/trace.ts +43 -38
- package/src/transform.ts +113 -0
- package/src/types.ts +56 -46
- package/transform.js +19 -0
- package/vitest.workspace.ts +38 -2
- package/dist/cjs/scheduling.d.ts +0 -11
- package/dist/cjs/scheduling.d.ts.map +0 -1
- package/dist/cjs/scheduling.js +0 -108
- package/dist/cjs/scheduling.js.map +0 -1
- package/dist/cjs/signals.d.ts +0 -73
- package/dist/cjs/signals.d.ts.map +0 -1
- package/dist/cjs/signals.js +0 -632
- package/dist/cjs/signals.js.map +0 -1
- package/dist/cjs/utils.d.ts +0 -4
- package/dist/cjs/utils.d.ts.map +0 -1
- package/dist/cjs/utils.js.map +0 -1
- package/dist/esm/scheduling.d.ts +0 -11
- package/dist/esm/scheduling.d.ts.map +0 -1
- package/dist/esm/scheduling.js +0 -97
- package/dist/esm/scheduling.js.map +0 -1
- package/dist/esm/signals.d.ts +0 -73
- package/dist/esm/signals.d.ts.map +0 -1
- package/dist/esm/signals.js +0 -614
- package/dist/esm/signals.js.map +0 -1
- package/dist/esm/utils.d.ts +0 -4
- package/dist/esm/utils.d.ts.map +0 -1
- package/dist/esm/utils.js.map +0 -1
- package/src/__tests__/hooks/async-computed.test.ts +0 -190
- package/src/__tests__/hooks/async-task.test.ts +0 -334
- package/src/__tests__/hooks/computed.test.ts +0 -126
- package/src/__tests__/hooks/context.test.ts +0 -527
- package/src/__tests__/hooks/nesting.test.ts +0 -303
- package/src/__tests__/hooks/params-and-state.test.ts +0 -168
- package/src/__tests__/hooks/subscription.test.ts +0 -97
- package/src/__tests__/signals/async.test.ts +0 -416
- package/src/__tests__/signals/basic.test.ts +0 -399
- package/src/__tests__/signals/subscription.test.ts +0 -632
- package/src/__tests__/signals/watcher.test.ts +0 -253
- package/src/__tests__/utils/builders.ts +0 -22
- package/src/__tests__/utils/instrumented-signals.ts +0 -291
- package/src/react/__tests__/react.test.tsx +0 -227
- package/src/scheduling.ts +0 -130
- package/src/signals.ts +0 -824
@@ -1,14 +1,17 @@
|
|
1
|
-
import { afterEach, Assertion,
|
1
|
+
import { afterEach, Assertion, expect } from 'vitest';
|
2
2
|
import {
|
3
|
-
|
4
|
-
asyncTask as _asyncTask,
|
5
|
-
computed as _computed,
|
3
|
+
reactive as _reactive,
|
6
4
|
subscription as _subscription,
|
7
|
-
watcher,
|
8
5
|
SignalSubscribe,
|
9
|
-
|
6
|
+
withContexts,
|
7
|
+
task as _task,
|
8
|
+
watcher,
|
10
9
|
} from '../../index.js';
|
11
|
-
import { SignalOptionsWithInit, SignalSubscription
|
10
|
+
import { ReactiveTask, ReactiveValue, SignalOptionsWithInit, SignalSubscription } from '../../types.js';
|
11
|
+
import { Context, ContextImpl, getCurrentScope, ROOT_SCOPE, SignalScope } from '../../internals/contexts.js';
|
12
|
+
import { createDerivedSignal, DerivedSignal } from '../../internals/derived.js';
|
13
|
+
import { ReactivePromise } from '../../internals/async.js';
|
14
|
+
import { hashValue } from '../../internals/utils/hash.js';
|
12
15
|
|
13
16
|
class SignalHookCounts {
|
14
17
|
name: string;
|
@@ -24,6 +27,7 @@ class SignalHookCounts {
|
|
24
27
|
unsubscribe = 0;
|
25
28
|
internalGet = 0;
|
26
29
|
internalSet = 0;
|
30
|
+
error = 0;
|
27
31
|
|
28
32
|
effect = 0;
|
29
33
|
|
@@ -35,15 +39,16 @@ class SignalHookCounts {
|
|
35
39
|
const countsKeys = Object.keys(new SignalHookCounts('')).filter(k => k !== 'name') as (keyof SignalHookCounts)[];
|
36
40
|
|
37
41
|
let currentOrder: string[] | undefined = [];
|
38
|
-
|
42
|
+
|
43
|
+
type ContextPair<T extends unknown[]> = {
|
44
|
+
[K in keyof T]: [Context<T[K]>, NoInfer<T[K]>];
|
45
|
+
};
|
39
46
|
|
40
47
|
interface CustomMatchers<R = unknown> {
|
41
|
-
|
48
|
+
toHaveSignalValue: (v: any) => Assertion<R>;
|
42
49
|
toHaveCounts: (counts: Partial<SignalHookCounts>) => Assertion<R>;
|
43
50
|
toHaveValueAndCounts: (v: any, counts: Partial<SignalHookCounts>) => Assertion<R>;
|
44
51
|
toHaveComputedOrder: (order: string[]) => Assertion<R>;
|
45
|
-
withParams: R extends (...args: infer P) => any ? (...args: P) => Assertion<R> : (...args: any[]) => Assertion<R>;
|
46
|
-
withContexts: (contexts: Record<symbol, any>) => Assertion<R>;
|
47
52
|
}
|
48
53
|
|
49
54
|
declare module 'vitest' {
|
@@ -53,66 +58,70 @@ declare module 'vitest' {
|
|
53
58
|
interface AsymmetricMatchersContaining extends CustomMatchers {}
|
54
59
|
}
|
55
60
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
57
|
-
const NEXT_ARGS = new WeakMap<Function, any[]>();
|
58
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
59
|
-
const NEXT_CONTEXTS = new WeakMap<Function, Record<symbol, any>>();
|
60
|
-
|
61
|
-
let w: Watcher<unknown> | undefined;
|
62
|
-
|
63
61
|
let unsubs: (() => void)[] = [];
|
64
62
|
|
65
63
|
afterEach(() => {
|
66
64
|
unsubs.forEach(unsub => unsub());
|
67
65
|
});
|
68
66
|
|
69
|
-
|
70
|
-
this: { equals(a: unknown, b: unknown): boolean },
|
71
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
72
|
-
hook: Function,
|
73
|
-
value: any,
|
74
|
-
) {
|
75
|
-
const args = NEXT_ARGS.get(hook) ?? [];
|
76
|
-
const contexts = NEXT_CONTEXTS.get(hook);
|
67
|
+
let TEST_ID = 0;
|
77
68
|
|
78
|
-
|
79
|
-
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
70
|
+
const WATCHERS = new WeakMap<Function, DerivedSignal<unknown, unknown[]>>();
|
80
71
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
return hook(...args);
|
85
|
-
});
|
86
|
-
} else {
|
87
|
-
return hook(...args);
|
88
|
-
}
|
89
|
-
});
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
73
|
+
function getWatcherForHook(hook: Function) {
|
74
|
+
let watcher = WATCHERS.get(hook);
|
90
75
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
76
|
+
if (!watcher) {
|
77
|
+
watcher = createDerivedSignal(
|
78
|
+
() => {
|
79
|
+
let result = hook();
|
80
|
+
|
81
|
+
if (result instanceof ReactivePromise) {
|
82
|
+
result = result.value;
|
83
|
+
}
|
84
|
+
|
85
|
+
return result;
|
99
86
|
},
|
100
|
-
|
101
|
-
|
87
|
+
undefined,
|
88
|
+
undefined,
|
89
|
+
undefined,
|
90
|
+
{ desc: 'test' + TEST_ID++ },
|
91
|
+
);
|
102
92
|
|
103
|
-
|
104
|
-
|
93
|
+
unsubs.push(watcher.addListener(() => {}));
|
94
|
+
|
95
|
+
WATCHERS.set(hook, watcher);
|
105
96
|
}
|
106
97
|
|
98
|
+
return watcher;
|
99
|
+
}
|
100
|
+
|
101
|
+
function toHaveSignalValue(
|
102
|
+
this: { equals(a: unknown, b: unknown): boolean },
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
104
|
+
hook: Function,
|
105
|
+
value: any,
|
106
|
+
) {
|
107
|
+
if (hook instanceof ReactivePromise) {
|
108
|
+
return {
|
109
|
+
pass: this.equals(hook.value, value),
|
110
|
+
message: () =>
|
111
|
+
`Expected subscription value to be ${JSON.stringify(value)}, but got ${JSON.stringify(hook.value)}`,
|
112
|
+
};
|
113
|
+
}
|
114
|
+
|
115
|
+
const signalValue = getWatcherForHook(hook).get();
|
116
|
+
|
107
117
|
return {
|
108
118
|
pass: this.equals(signalValue, value),
|
109
|
-
message: () => `Expected signal value to be ${
|
119
|
+
message: () => `Expected signal value to be ${value}, but got ${signalValue}`,
|
110
120
|
};
|
111
121
|
}
|
112
122
|
|
113
|
-
|
114
|
-
|
115
|
-
const signalCounts = COUNTS.get(hook);
|
123
|
+
function toHaveCounts(hook: { [COUNTS]: SignalHookCounts }, counts: SignalHookCounts) {
|
124
|
+
const signalCounts = hook[COUNTS];
|
116
125
|
|
117
126
|
if (!signalCounts) {
|
118
127
|
return {
|
@@ -138,32 +147,24 @@ function toHaveCounts(hook: Function, counts: SignalHookCounts) {
|
|
138
147
|
};
|
139
148
|
}
|
140
149
|
|
141
|
-
expect.
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
146
|
-
withParams(fn: Function, ...args: any[]) {
|
147
|
-
NEXT_ARGS.set(fn, args);
|
150
|
+
expect.addSnapshotSerializer({
|
151
|
+
serialize(val) {
|
152
|
+
const counts = val[COUNTS];
|
153
|
+
const value = getWatcherForHook(val).get();
|
148
154
|
|
149
|
-
return
|
150
|
-
pass: true,
|
151
|
-
message: () => 'Params match',
|
152
|
-
};
|
155
|
+
return JSON.stringify([value, counts], null, 2);
|
153
156
|
},
|
154
|
-
|
155
|
-
|
156
|
-
withContexts(fn: Function, contexts: Record<symbol, any>) {
|
157
|
-
NEXT_CONTEXTS.set(fn, contexts);
|
158
|
-
|
159
|
-
return {
|
160
|
-
pass: true,
|
161
|
-
message: () => 'Contexts match',
|
162
|
-
};
|
157
|
+
test(val) {
|
158
|
+
return val[COUNTS] !== undefined;
|
163
159
|
},
|
160
|
+
});
|
161
|
+
|
162
|
+
expect.extend({
|
163
|
+
toHaveSignalValue,
|
164
|
+
toHaveCounts,
|
164
165
|
|
165
166
|
toHaveValueAndCounts(signal, value, counts) {
|
166
|
-
const valueResult =
|
167
|
+
const valueResult = toHaveSignalValue.call(this, signal, value);
|
167
168
|
const countsResult = toHaveCounts.call(this, signal, counts);
|
168
169
|
|
169
170
|
return {
|
@@ -193,83 +194,187 @@ expect.extend({
|
|
193
194
|
},
|
194
195
|
});
|
195
196
|
|
196
|
-
|
197
|
-
const
|
197
|
+
function getContextKeys(scope: SignalScope): symbol[] {
|
198
|
+
const contexts = scope['contexts'];
|
198
199
|
|
199
|
-
|
200
|
-
|
200
|
+
const keys = Object.getOwnPropertySymbols(contexts);
|
201
|
+
|
202
|
+
if (scope['parentScope']) {
|
203
|
+
return [...getContextKeys(scope['parentScope']), ...keys];
|
201
204
|
}
|
202
205
|
|
203
|
-
|
206
|
+
return keys;
|
207
|
+
}
|
204
208
|
|
205
|
-
|
206
|
-
|
209
|
+
function getSortedContexts(scope: SignalScope) {
|
210
|
+
const keys = getContextKeys(scope).sort((a, b) => a.toString().localeCompare(b.toString()));
|
207
211
|
|
208
|
-
|
209
|
-
|
212
|
+
return keys.map(key => {
|
213
|
+
const context = scope['contexts'][key];
|
210
214
|
|
211
|
-
|
212
|
-
|
215
|
+
return [key, context];
|
216
|
+
});
|
217
|
+
}
|
213
218
|
|
214
|
-
|
215
|
-
|
219
|
+
function getCountsFor(name: string, map: Map<number, SignalHookCounts>, scope: SignalScope, args: unknown[] = []) {
|
220
|
+
const key = hashValue([args, getSortedContexts(scope)]);
|
221
|
+
let countsForArgs = map.get(key);
|
216
222
|
|
217
|
-
|
223
|
+
if (!countsForArgs) {
|
224
|
+
countsForArgs = new SignalHookCounts(name);
|
225
|
+
map.set(key, countsForArgs);
|
226
|
+
}
|
218
227
|
|
219
|
-
return
|
220
|
-
}
|
228
|
+
return countsForArgs;
|
229
|
+
}
|
221
230
|
|
222
|
-
|
223
|
-
const counts = new SignalHookCounts(opts?.desc ?? 'unknown');
|
231
|
+
const COUNTS = Symbol('counts');
|
224
232
|
|
225
|
-
|
226
|
-
|
233
|
+
export type SubscriptionWithCounts<T> = ReactivePromise<T> & {
|
234
|
+
[COUNTS]: SignalHookCounts;
|
235
|
+
};
|
227
236
|
|
228
|
-
|
229
|
-
|
237
|
+
export type ReactiveTaskWithCounts<T, Args extends unknown[]> = ReactiveTask<T, Args> & {
|
238
|
+
[COUNTS]: SignalHookCounts;
|
239
|
+
};
|
230
240
|
|
231
|
-
|
241
|
+
export type ReactiveFunctionWithCounts<T, Args extends unknown[]> = ((...args: Args) => ReactiveValue<T>) & {
|
242
|
+
[COUNTS]: SignalHookCounts;
|
243
|
+
};
|
232
244
|
|
233
|
-
|
245
|
+
export type ReactiveBuilderFunction<T, Args extends unknown[]> = ((...args: Args) => ReactiveValue<T>) & {
|
246
|
+
[COUNTS]: SignalHookCounts;
|
247
|
+
watch: () => () => void;
|
248
|
+
withParams: (...args: Args) => ReactiveBuilderFunction<T, []>;
|
249
|
+
withContexts: (...contexts: [Context<unknown>, unknown][]) => ReactiveBuilderFunction<T, Args>;
|
234
250
|
};
|
235
251
|
|
236
|
-
|
237
|
-
|
252
|
+
// Create a function-class hybrid for the builder pattern
|
253
|
+
function createBuilderFunction<T, Args extends unknown[]>(
|
254
|
+
originalFn: (...args: Args) => ReactiveValue<T>,
|
255
|
+
countsMap: Map<number, SignalHookCounts>,
|
256
|
+
args: Args,
|
257
|
+
contexts?: [Context<unknown>, unknown][],
|
258
|
+
): ReactiveBuilderFunction<T, []>;
|
259
|
+
function createBuilderFunction<T, Args extends unknown[]>(
|
260
|
+
originalFn: (...args: Args) => ReactiveValue<T>,
|
261
|
+
countsMap: Map<number, SignalHookCounts>,
|
262
|
+
args?: undefined,
|
263
|
+
contexts?: [Context<unknown>, unknown][],
|
264
|
+
): ReactiveBuilderFunction<T, Args>;
|
265
|
+
function createBuilderFunction<T, Args extends unknown[]>(
|
266
|
+
originalFn: (...args: Args) => ReactiveValue<T>,
|
267
|
+
countsMap: Map<number, SignalHookCounts>,
|
268
|
+
args?: Args,
|
269
|
+
contexts?: [Context<unknown>, unknown][],
|
270
|
+
): ReactiveBuilderFunction<T, Args> {
|
271
|
+
// Cast the function to include our additional properties
|
272
|
+
const builderFn = ((...passedArgs: Args) => {
|
273
|
+
if (args && passedArgs.length > 0) {
|
274
|
+
throw new Error('reactive function already has parameters');
|
275
|
+
}
|
276
|
+
|
277
|
+
let usedArgs = args ?? passedArgs;
|
278
|
+
|
279
|
+
const scope = getCurrentScope();
|
280
|
+
const counts = getCountsFor(originalFn.name, countsMap, scope, usedArgs);
|
281
|
+
|
282
|
+
// increment the get count since each time this is called, we're getting the value
|
283
|
+
counts.get++;
|
284
|
+
|
285
|
+
if (contexts) {
|
286
|
+
return withContexts(contexts, () => originalFn(...usedArgs));
|
287
|
+
}
|
288
|
+
|
289
|
+
return originalFn(...usedArgs);
|
290
|
+
}) as ReactiveBuilderFunction<T, Args>;
|
291
|
+
|
292
|
+
// Add the builder methods
|
293
|
+
builderFn.watch = (...args: Args) => {
|
294
|
+
const unsub = watcher(() => builderFn(...args)).addListener(() => {});
|
295
|
+
unsubs.push(unsub);
|
296
|
+
return unsub;
|
297
|
+
};
|
298
|
+
|
299
|
+
builderFn.withParams = (...withArgs: Args) => {
|
300
|
+
if (args) {
|
301
|
+
throw new Error('reactive function already has parameters');
|
302
|
+
}
|
303
|
+
|
304
|
+
return createBuilderFunction(originalFn, countsMap, withArgs, contexts);
|
305
|
+
};
|
306
|
+
|
307
|
+
builderFn.withContexts = (...withContexts: [Context<unknown>, unknown][]) => {
|
308
|
+
if (contexts) {
|
309
|
+
throw new Error('reactive function already has contexts');
|
310
|
+
}
|
311
|
+
|
312
|
+
return createBuilderFunction(originalFn, countsMap, args as Args, withContexts) as ReactiveBuilderFunction<T, Args>;
|
313
|
+
};
|
314
|
+
|
315
|
+
const scope = contexts ? ROOT_SCOPE.getChild(contexts as [ContextImpl<unknown>, unknown][]) : ROOT_SCOPE;
|
316
|
+
builderFn[COUNTS] = getCountsFor(originalFn.name, countsMap, scope, args);
|
317
|
+
|
318
|
+
return builderFn;
|
319
|
+
}
|
238
320
|
|
239
|
-
|
321
|
+
export function reactive<T, Args extends unknown[]>(
|
322
|
+
fn: (...args: Args) => T,
|
323
|
+
opts?: Partial<SignalOptionsWithInit<T, Args>>,
|
324
|
+
): ReactiveBuilderFunction<T, Args> {
|
325
|
+
const countsMap = new Map<number, SignalHookCounts>();
|
326
|
+
|
327
|
+
return createBuilderFunction(
|
328
|
+
_reactive((...args: any[]) => {
|
329
|
+
const scope = getCurrentScope();
|
330
|
+
const counts = getCountsFor(opts?.desc ?? 'unknownReactive', countsMap, scope, args);
|
331
|
+
|
332
|
+
counts.compute++;
|
333
|
+
|
334
|
+
return fn(...(args as any));
|
335
|
+
}, opts) as ReactiveFunctionWithCounts<T, Args>,
|
336
|
+
countsMap,
|
337
|
+
);
|
338
|
+
}
|
339
|
+
|
340
|
+
export const task: typeof _task = (fn, opts) => {
|
341
|
+
const counts = new SignalHookCounts(opts?.desc ?? 'unknownTask');
|
342
|
+
|
343
|
+
const wrapper = _task((...args: any[]) => {
|
240
344
|
counts.compute++;
|
241
345
|
|
242
346
|
return fn(...(args as any));
|
243
|
-
}, opts) as any
|
347
|
+
}, opts) as ReactiveTaskWithCounts<any, any>;
|
244
348
|
|
245
|
-
COUNTS
|
349
|
+
wrapper[COUNTS] = counts;
|
246
350
|
|
247
351
|
return wrapper;
|
248
352
|
};
|
249
353
|
|
250
|
-
export const subscription = <T
|
251
|
-
fn: SignalSubscribe<T
|
252
|
-
opts?: Partial<SignalOptionsWithInit<T,
|
253
|
-
): ReturnType<typeof _subscription<T
|
254
|
-
const counts = new SignalHookCounts(opts?.desc ?? '
|
354
|
+
export const subscription = <T>(
|
355
|
+
fn: SignalSubscribe<T>,
|
356
|
+
opts?: Partial<SignalOptionsWithInit<T, unknown[]>>,
|
357
|
+
): ReturnType<typeof _subscription<T>> => {
|
358
|
+
const counts = new SignalHookCounts(opts?.desc ?? 'unknownSubscription');
|
255
359
|
|
256
|
-
let wrapper = _subscription<T
|
360
|
+
let wrapper = _subscription<T>(({ get, set, setError }) => {
|
257
361
|
counts.subscribe++;
|
258
362
|
counts.compute++;
|
259
363
|
|
260
|
-
const result = fn(
|
261
|
-
{
|
262
|
-
|
263
|
-
|
264
|
-
return get();
|
265
|
-
},
|
266
|
-
set: v => {
|
267
|
-
counts.internalSet++;
|
268
|
-
set(v);
|
269
|
-
},
|
364
|
+
const result = fn({
|
365
|
+
get: () => {
|
366
|
+
counts.internalGet++;
|
367
|
+
return get();
|
270
368
|
},
|
271
|
-
|
272
|
-
|
369
|
+
set: v => {
|
370
|
+
counts.internalSet++;
|
371
|
+
set(v);
|
372
|
+
},
|
373
|
+
setError: (error: unknown) => {
|
374
|
+
counts.error++;
|
375
|
+
setError(error);
|
376
|
+
},
|
377
|
+
});
|
273
378
|
|
274
379
|
let subscriptionWrapper: SignalSubscription | (() => unknown) | undefined;
|
275
380
|
|
@@ -300,9 +405,9 @@ export const subscription = <T, Args extends unknown[]>(
|
|
300
405
|
}
|
301
406
|
|
302
407
|
return subscriptionWrapper;
|
303
|
-
}, opts)
|
408
|
+
}, opts) as SubscriptionWithCounts<T>;
|
304
409
|
|
305
|
-
COUNTS
|
410
|
+
wrapper[COUNTS] = counts;
|
306
411
|
|
307
|
-
return wrapper
|
412
|
+
return wrapper as ReturnType<typeof _subscription<T>>;
|
308
413
|
};
|
@@ -1,22 +1,24 @@
|
|
1
1
|
import { describe } from 'vitest';
|
2
|
-
import {
|
2
|
+
import { ReactiveBuilderFunction, reactive, subscription } from './instrumented-hooks.js';
|
3
3
|
import { SignalOptionsWithInit } from '../../types.js';
|
4
4
|
|
5
5
|
const createMethods = [
|
6
6
|
{
|
7
7
|
name: 'createComputed',
|
8
|
-
create:
|
8
|
+
create: reactive,
|
9
9
|
},
|
10
10
|
{
|
11
11
|
name: 'createAsyncComputed',
|
12
12
|
create: <T, Args extends unknown[]>(
|
13
13
|
fn: (...args: Args) => T | Promise<T>,
|
14
|
-
opts?: Partial<SignalOptionsWithInit<T
|
15
|
-
) => {
|
16
|
-
const computed =
|
14
|
+
opts?: Partial<SignalOptionsWithInit<Promise<T>, Args>>,
|
15
|
+
): ReactiveBuilderFunction<T, Args> => {
|
16
|
+
const computed = reactive(async (...args: Args) => {
|
17
|
+
return fn(...args);
|
18
|
+
}, opts);
|
17
19
|
|
18
|
-
return
|
19
|
-
return computed(...args).
|
20
|
+
return reactive((...args: Args) => {
|
21
|
+
return computed(...args).value as T;
|
20
22
|
});
|
21
23
|
},
|
22
24
|
},
|
@@ -25,15 +27,24 @@ const createMethods = [
|
|
25
27
|
create: function _createSubscription<T, Args extends unknown[]>(
|
26
28
|
fn: (...args: Args) => T,
|
27
29
|
opts?: Partial<SignalOptionsWithInit<T, Args>>,
|
28
|
-
):
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
return {
|
33
|
-
update: () => {
|
30
|
+
): ReactiveBuilderFunction<T, Args> {
|
31
|
+
const computed = reactive((...args: Args) => {
|
32
|
+
return subscription(
|
33
|
+
state => {
|
34
34
|
state.set(fn(...args));
|
35
|
+
|
36
|
+
return {
|
37
|
+
update: () => {
|
38
|
+
state.set(fn(...args));
|
39
|
+
},
|
40
|
+
};
|
35
41
|
},
|
36
|
-
|
42
|
+
opts as Partial<SignalOptionsWithInit<T, unknown[]>>,
|
43
|
+
);
|
44
|
+
});
|
45
|
+
|
46
|
+
return reactive((...args: Args) => {
|
47
|
+
return computed(...args).value as T;
|
37
48
|
}, opts);
|
38
49
|
},
|
39
50
|
},
|
package/src/config.ts
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
1
|
+
import { DerivedSignal } from './internals/derived.js';
|
2
|
+
import { SignalScope } from './internals/contexts.js';
|
3
|
+
import { ReactiveValue } from './types.js';
|
4
|
+
import { StateSignal } from './internals/state.js';
|
5
|
+
import { CURRENT_CONSUMER } from './internals/get.js';
|
3
6
|
|
4
7
|
export type FlushCallback = () => void;
|
5
8
|
|
@@ -10,7 +13,8 @@ interface SignalHooksConfig {
|
|
10
13
|
scheduleFlush: FlushFn;
|
11
14
|
runBatch: BatchFn;
|
12
15
|
getFrameworkScope: () => SignalScope | undefined;
|
13
|
-
|
16
|
+
useStateSignal: <T>(signal: StateSignal<T>) => T;
|
17
|
+
useDerivedSignal: <T>(signal: DerivedSignal<T, unknown[]>) => ReactiveValue<T>;
|
14
18
|
}
|
15
19
|
|
16
20
|
export let scheduleFlush: FlushFn = flushWatchers => {
|
@@ -23,20 +27,26 @@ export let runBatch: BatchFn = fn => fn();
|
|
23
27
|
|
24
28
|
export let getFrameworkScope: () => SignalScope | undefined = () => undefined;
|
25
29
|
|
26
|
-
let
|
30
|
+
let useFrameworkStateSignal: <T>(signal: StateSignal<T>) => T = signal => signal.get();
|
31
|
+
let useFrameworkDerivedSignal: <T>(signal: DerivedSignal<T, unknown[]>) => ReactiveValue<T> = signal => signal.get();
|
27
32
|
|
28
|
-
export function
|
29
|
-
if (
|
30
|
-
return
|
33
|
+
export function useDerivedSignal<T>(signal: DerivedSignal<T, any[]>): ReactiveValue<T> {
|
34
|
+
if (CURRENT_CONSUMER !== undefined) {
|
35
|
+
return signal.get();
|
31
36
|
} else {
|
32
37
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
33
|
-
return
|
38
|
+
return useFrameworkDerivedSignal(signal);
|
34
39
|
}
|
35
40
|
}
|
36
41
|
|
42
|
+
export function useStateSignal<T>(signal: StateSignal<T>): T {
|
43
|
+
return useFrameworkStateSignal(signal);
|
44
|
+
}
|
45
|
+
|
37
46
|
export function setConfig(cfg: Partial<SignalHooksConfig>) {
|
38
47
|
scheduleFlush = cfg.scheduleFlush ?? scheduleFlush;
|
39
48
|
runBatch = cfg.runBatch ?? runBatch;
|
40
49
|
getFrameworkScope = cfg.getFrameworkScope ?? getFrameworkScope;
|
41
|
-
|
50
|
+
useFrameworkStateSignal = cfg.useStateSignal ?? useFrameworkStateSignal;
|
51
|
+
useFrameworkDerivedSignal = cfg.useDerivedSignal ?? useFrameworkDerivedSignal;
|
42
52
|
}
|
package/src/debug.ts
CHANGED
@@ -5,10 +5,10 @@ export {
|
|
5
5
|
removeTracer,
|
6
6
|
VisualizerNode,
|
7
7
|
type VisualizerLink,
|
8
|
-
VisualizerNodeType,
|
9
8
|
TracerEventType,
|
10
9
|
Tracer,
|
11
10
|
TRACER,
|
11
|
+
SignalType,
|
12
12
|
} from './trace.js';
|
13
13
|
|
14
|
-
export { scheduleTracer } from './scheduling.js';
|
14
|
+
export { scheduleTracer } from './internals/scheduling.js';
|