signalium 0.2.8 → 0.3.1
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 +12 -0
- package/CHANGELOG.md +12 -0
- package/dist/cjs/config.d.ts +14 -5
- package/dist/cjs/config.d.ts.map +1 -1
- package/dist/cjs/config.js +23 -14
- package/dist/cjs/config.js.map +1 -1
- package/dist/cjs/debug.d.ts +3 -0
- package/dist/cjs/debug.d.ts.map +1 -0
- package/dist/cjs/debug.js +16 -0
- package/dist/cjs/debug.js.map +1 -0
- package/dist/cjs/hooks.d.ts +45 -0
- package/dist/cjs/hooks.d.ts.map +1 -0
- package/dist/cjs/hooks.js +263 -0
- package/dist/cjs/hooks.js.map +1 -0
- package/dist/cjs/index.d.ts +5 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +21 -8
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/context.d.ts +4 -0
- package/dist/cjs/react/context.d.ts.map +1 -0
- package/dist/cjs/react/context.js +10 -0
- package/dist/cjs/react/context.js.map +1 -0
- package/dist/cjs/react/index.d.ts +5 -0
- package/dist/cjs/react/index.d.ts.map +1 -0
- package/dist/cjs/react/index.js +12 -0
- package/dist/cjs/react/index.js.map +1 -0
- package/dist/cjs/react/provider.d.ts +7 -0
- package/dist/cjs/react/provider.d.ts.map +1 -0
- package/dist/cjs/react/provider.js +13 -0
- package/dist/cjs/react/provider.js.map +1 -0
- package/dist/cjs/react/setup.d.ts +2 -0
- package/dist/cjs/react/setup.d.ts.map +1 -0
- package/dist/cjs/react/setup.js +13 -0
- package/dist/cjs/react/setup.js.map +1 -0
- package/dist/cjs/react/signal-value.d.ts +2 -0
- package/dist/cjs/react/signal-value.d.ts.map +1 -0
- package/dist/cjs/react/signal-value.js +35 -0
- package/dist/cjs/react/signal-value.js.map +1 -0
- package/dist/cjs/react/state.d.ts +3 -0
- package/dist/cjs/react/state.d.ts.map +1 -0
- package/dist/cjs/react/state.js +13 -0
- package/dist/cjs/react/state.js.map +1 -0
- package/dist/cjs/scheduling.d.ts +5 -0
- package/dist/cjs/scheduling.d.ts.map +1 -1
- package/dist/cjs/scheduling.js +59 -5
- package/dist/cjs/scheduling.js.map +1 -1
- package/dist/cjs/signals.d.ts +28 -68
- package/dist/cjs/signals.d.ts.map +1 -1
- package/dist/cjs/signals.js +223 -76
- package/dist/cjs/signals.js.map +1 -1
- package/dist/cjs/trace.d.ts +127 -0
- package/dist/cjs/trace.d.ts.map +1 -0
- package/dist/cjs/trace.js +319 -0
- package/dist/cjs/trace.js.map +1 -0
- package/dist/cjs/types.d.ts +66 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils.d.ts +4 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/cjs/utils.js +80 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/esm/config.d.ts +14 -5
- package/dist/esm/config.d.ts.map +1 -1
- package/dist/esm/config.js +19 -11
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/debug.d.ts +3 -0
- package/dist/esm/debug.d.ts.map +1 -0
- package/dist/esm/debug.js +3 -0
- package/dist/esm/debug.js.map +1 -0
- package/dist/esm/hooks.d.ts +45 -0
- package/dist/esm/hooks.d.ts.map +1 -0
- package/dist/esm/hooks.js +246 -0
- package/dist/esm/hooks.js.map +1 -0
- package/dist/esm/index.d.ts +5 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/context.d.ts +4 -0
- package/dist/esm/react/context.d.ts.map +1 -0
- package/dist/esm/react/context.js +6 -0
- package/dist/esm/react/context.js.map +1 -0
- package/dist/esm/react/index.d.ts +5 -0
- package/dist/esm/react/index.d.ts.map +1 -0
- package/dist/esm/react/index.js +5 -0
- package/dist/esm/react/index.js.map +1 -0
- package/dist/esm/react/provider.d.ts +7 -0
- package/dist/esm/react/provider.d.ts.map +1 -0
- package/dist/esm/react/provider.js +10 -0
- package/dist/esm/react/provider.js.map +1 -0
- package/dist/esm/react/setup.d.ts +2 -0
- package/dist/esm/react/setup.d.ts.map +1 -0
- package/dist/esm/react/setup.js +10 -0
- package/dist/esm/react/setup.js.map +1 -0
- package/dist/esm/react/signal-value.d.ts +2 -0
- package/dist/esm/react/signal-value.d.ts.map +1 -0
- package/dist/esm/react/signal-value.js +32 -0
- package/dist/esm/react/signal-value.js.map +1 -0
- package/dist/esm/react/state.d.ts +3 -0
- package/dist/esm/react/state.d.ts.map +1 -0
- package/dist/esm/react/state.js +10 -0
- package/dist/esm/react/state.js.map +1 -0
- package/dist/esm/scheduling.d.ts +5 -0
- package/dist/esm/scheduling.d.ts.map +1 -1
- package/dist/esm/scheduling.js +51 -1
- package/dist/esm/scheduling.js.map +1 -1
- package/dist/esm/signals.d.ts +28 -68
- package/dist/esm/signals.d.ts.map +1 -1
- package/dist/esm/signals.js +215 -72
- package/dist/esm/signals.js.map +1 -1
- package/dist/esm/trace.d.ts +127 -0
- package/dist/esm/trace.d.ts.map +1 -0
- package/dist/esm/trace.js +311 -0
- package/dist/esm/trace.js.map +1 -0
- package/dist/esm/types.d.ts +66 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils.d.ts +4 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/dist/esm/utils.js +75 -0
- package/dist/esm/utils.js.map +1 -0
- package/package.json +43 -2
- package/src/__tests__/hooks/async-computed.test.ts +190 -0
- package/src/__tests__/hooks/async-task.test.ts +227 -0
- package/src/__tests__/hooks/computed.test.ts +126 -0
- package/src/__tests__/hooks/context.test.ts +527 -0
- package/src/__tests__/hooks/nesting.test.ts +303 -0
- package/src/__tests__/hooks/params-and-state.test.ts +168 -0
- package/src/__tests__/hooks/subscription.test.ts +97 -0
- package/src/__tests__/signals/async.test.ts +416 -0
- package/src/__tests__/signals/basic.test.ts +399 -0
- package/src/__tests__/signals/subscription.test.ts +632 -0
- package/src/__tests__/signals/watcher.test.ts +253 -0
- package/src/__tests__/utils/async.ts +6 -0
- package/src/__tests__/utils/builders.ts +22 -0
- package/src/__tests__/utils/instrumented-hooks.ts +309 -0
- package/src/__tests__/utils/instrumented-signals.ts +281 -0
- package/src/__tests__/utils/permute.ts +74 -0
- package/src/config.ts +32 -17
- package/src/debug.ts +14 -0
- package/src/hooks.ts +433 -0
- package/src/index.ts +28 -3
- package/src/react/__tests__/react.test.tsx +227 -0
- package/src/react/context.ts +8 -0
- package/src/react/index.ts +4 -0
- package/src/react/provider.tsx +18 -0
- package/src/react/setup.ts +10 -0
- package/src/react/signal-value.ts +49 -0
- package/src/react/state.ts +13 -0
- package/src/scheduling.ts +69 -1
- package/src/signals.ts +328 -169
- package/src/trace.ts +449 -0
- package/src/types.ts +86 -0
- package/src/utils.ts +83 -0
- package/tsconfig.json +2 -1
- package/vitest.workspace.ts +24 -0
- package/src/__tests__/async.test.ts +0 -426
- package/src/__tests__/basic.test.ts +0 -378
- package/src/__tests__/subscription.test.ts +0 -645
- package/src/__tests__/utils/instrumented.ts +0 -326
@@ -0,0 +1,18 @@
|
|
1
|
+
import { useContext } from 'react';
|
2
|
+
import { SignalScope, SignalStoreMap } from '../hooks.js';
|
3
|
+
import { ScopeContext } from './context.js';
|
4
|
+
|
5
|
+
export function ContextProvider({
|
6
|
+
children,
|
7
|
+
contexts,
|
8
|
+
inherit = true,
|
9
|
+
}: {
|
10
|
+
children: React.ReactNode;
|
11
|
+
contexts: SignalStoreMap;
|
12
|
+
inherit?: boolean;
|
13
|
+
}) {
|
14
|
+
const parentScope = useContext(ScopeContext);
|
15
|
+
const scope = new SignalScope(contexts, inherit ? parentScope : undefined);
|
16
|
+
|
17
|
+
return <ScopeContext.Provider value={scope}>{children}</ScopeContext.Provider>;
|
18
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import { useContext, useEffect, useRef, useState } from 'react';
|
2
|
+
import { ScopeContext } from './context.js';
|
3
|
+
import { watcher } from '../hooks.js';
|
4
|
+
|
5
|
+
export function useSignalValue<T>(key: string, fn: () => T): T {
|
6
|
+
const [, setVersion] = useState(0);
|
7
|
+
const scope = useContext(ScopeContext);
|
8
|
+
const ref = useRef<{
|
9
|
+
value: T | undefined;
|
10
|
+
unsub: (() => void) | undefined;
|
11
|
+
key: string | undefined;
|
12
|
+
}>({
|
13
|
+
value: undefined,
|
14
|
+
unsub: undefined,
|
15
|
+
key: undefined,
|
16
|
+
});
|
17
|
+
|
18
|
+
const currentKey = ref.current.key;
|
19
|
+
|
20
|
+
if (key !== currentKey) {
|
21
|
+
ref.current.unsub?.();
|
22
|
+
|
23
|
+
const w = watcher(fn, { scope });
|
24
|
+
|
25
|
+
let initialized = false;
|
26
|
+
|
27
|
+
ref.current.unsub = w.addListener(
|
28
|
+
value => {
|
29
|
+
ref.current.value = value;
|
30
|
+
|
31
|
+
// Trigger an update to the component
|
32
|
+
if (initialized) {
|
33
|
+
setVersion(v => v + 1);
|
34
|
+
}
|
35
|
+
|
36
|
+
initialized = true;
|
37
|
+
},
|
38
|
+
{
|
39
|
+
immediate: true,
|
40
|
+
},
|
41
|
+
);
|
42
|
+
|
43
|
+
ref.current.key = key;
|
44
|
+
}
|
45
|
+
|
46
|
+
useEffect(() => ref.current.unsub, []);
|
47
|
+
|
48
|
+
return ref.current.value!;
|
49
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { useRef } from 'react';
|
2
|
+
import { state } from '../hooks.js';
|
3
|
+
import { SignalOptions, WriteableSignal } from '../types.js';
|
4
|
+
|
5
|
+
export function useStateSignal<T>(value: T, opts?: SignalOptions<T, unknown[]>): WriteableSignal<T> {
|
6
|
+
const ref = useRef<WriteableSignal<T> | undefined>(undefined);
|
7
|
+
|
8
|
+
if (!ref.current) {
|
9
|
+
ref.current = state(value, opts);
|
10
|
+
}
|
11
|
+
|
12
|
+
return ref.current;
|
13
|
+
}
|
package/src/scheduling.ts
CHANGED
@@ -1,15 +1,33 @@
|
|
1
1
|
import { ComputedSignal } from './signals.js';
|
2
|
-
import { scheduleFlush, runBatch } from './config.js';
|
2
|
+
import { scheduleFlush as _scheduleFlush, runBatch } from './config.js';
|
3
|
+
import { Tracer } from './trace.js';
|
3
4
|
|
4
5
|
let PENDING_DIRTIES: ComputedSignal<any>[] = [];
|
5
6
|
let PENDING_PULLS: ComputedSignal<any>[] = [];
|
6
7
|
let PENDING_WATCHERS: ComputedSignal<any>[] = [];
|
8
|
+
let PENDING_CONNECTS = new Map<ComputedSignal<any>, number>();
|
7
9
|
let PENDING_DISCONNECTS = new Map<ComputedSignal<any>, number>();
|
10
|
+
let PENDING_EFFECTS: ComputedSignal<any>[] = [];
|
11
|
+
let PENDING_TRACERS: Tracer[] = [];
|
8
12
|
|
9
13
|
const microtask = () => Promise.resolve();
|
10
14
|
|
15
|
+
let currentFlush: { promise: Promise<void>; resolve: () => void } | null = null;
|
16
|
+
|
17
|
+
const scheduleFlush = (fn: () => void) => {
|
18
|
+
if (currentFlush) return;
|
19
|
+
|
20
|
+
let resolve: () => void;
|
21
|
+
const promise = new Promise<void>(r => (resolve = r));
|
22
|
+
|
23
|
+
currentFlush = { promise, resolve: resolve! };
|
24
|
+
|
25
|
+
_scheduleFlush(flushWatchers);
|
26
|
+
};
|
27
|
+
|
11
28
|
export const scheduleWatcher = (watcher: ComputedSignal<any>) => {
|
12
29
|
PENDING_WATCHERS.push(watcher);
|
30
|
+
|
13
31
|
scheduleFlush(flushWatchers);
|
14
32
|
};
|
15
33
|
|
@@ -23,6 +41,14 @@ export const schedulePull = (signal: ComputedSignal<any>) => {
|
|
23
41
|
scheduleFlush(flushWatchers);
|
24
42
|
};
|
25
43
|
|
44
|
+
export const scheduleConnect = (connect: ComputedSignal<any>) => {
|
45
|
+
const current = PENDING_CONNECTS.get(connect) ?? 0;
|
46
|
+
|
47
|
+
PENDING_CONNECTS.set(connect, current + 1);
|
48
|
+
|
49
|
+
scheduleFlush(flushWatchers);
|
50
|
+
};
|
51
|
+
|
26
52
|
export const scheduleDisconnect = (disconnect: ComputedSignal<any>) => {
|
27
53
|
const current = PENDING_DISCONNECTS.get(disconnect) ?? 0;
|
28
54
|
|
@@ -31,7 +57,21 @@ export const scheduleDisconnect = (disconnect: ComputedSignal<any>) => {
|
|
31
57
|
scheduleFlush(flushWatchers);
|
32
58
|
};
|
33
59
|
|
60
|
+
export const scheduleEffect = (signal: ComputedSignal<any>) => {
|
61
|
+
PENDING_EFFECTS.push(signal);
|
62
|
+
scheduleFlush(flushWatchers);
|
63
|
+
};
|
64
|
+
|
65
|
+
export const scheduleTracer = (tracer: Tracer) => {
|
66
|
+
PENDING_TRACERS.push(tracer);
|
67
|
+
scheduleFlush(flushWatchers);
|
68
|
+
};
|
69
|
+
|
34
70
|
const flushWatchers = async () => {
|
71
|
+
const flush = currentFlush!;
|
72
|
+
|
73
|
+
// Flush all the dirty signals and pulls recursively, clearing
|
74
|
+
// the microtask queue until they are all settled
|
35
75
|
while (PENDING_DIRTIES.length > 0 || PENDING_PULLS.length > 0) {
|
36
76
|
for (const dirty of PENDING_DIRTIES) {
|
37
77
|
dirty._dirtyConsumers();
|
@@ -47,16 +87,44 @@ const flushWatchers = async () => {
|
|
47
87
|
await microtask();
|
48
88
|
}
|
49
89
|
|
90
|
+
// Clear the flush so that if any more watchers are scheduled,
|
91
|
+
// they will be flushed in the next tick
|
92
|
+
currentFlush = null;
|
93
|
+
|
50
94
|
runBatch(() => {
|
51
95
|
for (const watcher of PENDING_WATCHERS) {
|
52
96
|
watcher._check();
|
53
97
|
}
|
54
98
|
|
99
|
+
for (const [signal, count] of PENDING_CONNECTS) {
|
100
|
+
signal._check(true, count);
|
101
|
+
}
|
102
|
+
|
55
103
|
for (const [signal, count] of PENDING_DISCONNECTS) {
|
56
104
|
signal._disconnect(count);
|
57
105
|
}
|
58
106
|
|
107
|
+
for (const signal of PENDING_EFFECTS) {
|
108
|
+
signal._runEffects();
|
109
|
+
}
|
110
|
+
|
111
|
+
for (const tracer of PENDING_TRACERS) {
|
112
|
+
tracer.flush();
|
113
|
+
}
|
114
|
+
|
59
115
|
PENDING_WATCHERS = [];
|
116
|
+
PENDING_CONNECTS.clear();
|
60
117
|
PENDING_DISCONNECTS.clear();
|
118
|
+
PENDING_EFFECTS = [];
|
119
|
+
PENDING_TRACERS = [];
|
61
120
|
});
|
121
|
+
|
122
|
+
// resolve the flush promise
|
123
|
+
flush.resolve();
|
124
|
+
};
|
125
|
+
|
126
|
+
export const settled = async () => {
|
127
|
+
while (currentFlush) {
|
128
|
+
await currentFlush.promise;
|
129
|
+
}
|
62
130
|
};
|