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.
Files changed (287) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/CHANGELOG.md +21 -0
  3. package/build/react.js +19 -0
  4. package/build/transform.js +19 -0
  5. package/dist/cjs/config.d.ts +8 -3
  6. package/dist/cjs/config.d.ts.map +1 -1
  7. package/dist/cjs/config.js +14 -8
  8. package/dist/cjs/config.js.map +1 -1
  9. package/dist/cjs/debug.d.ts +2 -2
  10. package/dist/cjs/debug.d.ts.map +1 -1
  11. package/dist/cjs/debug.js +3 -3
  12. package/dist/cjs/debug.js.map +1 -1
  13. package/dist/cjs/hooks.d.ts +14 -42
  14. package/dist/cjs/hooks.d.ts.map +1 -1
  15. package/dist/cjs/hooks.js +19 -240
  16. package/dist/cjs/hooks.js.map +1 -1
  17. package/dist/cjs/index.d.ts +5 -3
  18. package/dist/cjs/index.d.ts.map +1 -1
  19. package/dist/cjs/index.js +18 -18
  20. package/dist/cjs/index.js.map +1 -1
  21. package/dist/cjs/internals/async.d.ts +50 -0
  22. package/dist/cjs/internals/async.d.ts.map +1 -0
  23. package/dist/cjs/internals/async.js +390 -0
  24. package/dist/cjs/internals/async.js.map +1 -0
  25. package/dist/cjs/internals/connect.d.ts +4 -0
  26. package/dist/cjs/internals/connect.d.ts.map +1 -0
  27. package/dist/cjs/internals/connect.js +37 -0
  28. package/dist/cjs/internals/connect.js.map +1 -0
  29. package/dist/cjs/internals/consumer.d.ts +6 -0
  30. package/dist/cjs/internals/consumer.d.ts.map +1 -0
  31. package/dist/cjs/internals/consumer.js +13 -0
  32. package/dist/cjs/internals/consumer.js.map +1 -0
  33. package/dist/cjs/internals/contexts.d.ts +33 -0
  34. package/dist/cjs/internals/contexts.d.ts.map +1 -0
  35. package/dist/cjs/internals/contexts.js +103 -0
  36. package/dist/cjs/internals/contexts.js.map +1 -0
  37. package/dist/cjs/internals/derived.d.ts +66 -0
  38. package/dist/cjs/internals/derived.d.ts.map +1 -0
  39. package/dist/cjs/internals/derived.js +128 -0
  40. package/dist/cjs/internals/derived.js.map +1 -0
  41. package/dist/cjs/internals/dirty.d.ts +5 -0
  42. package/dist/cjs/internals/dirty.d.ts.map +1 -0
  43. package/dist/cjs/internals/dirty.js +79 -0
  44. package/dist/cjs/internals/dirty.js.map +1 -0
  45. package/dist/cjs/internals/edge.d.ts +32 -0
  46. package/dist/cjs/internals/edge.d.ts.map +1 -0
  47. package/dist/cjs/internals/edge.js +59 -0
  48. package/dist/cjs/internals/edge.js.map +1 -0
  49. package/dist/cjs/internals/get.d.ts +10 -0
  50. package/dist/cjs/internals/get.d.ts.map +1 -0
  51. package/dist/cjs/internals/get.js +255 -0
  52. package/dist/cjs/internals/get.js.map +1 -0
  53. package/dist/cjs/internals/scheduling.d.ts +12 -0
  54. package/dist/cjs/internals/scheduling.d.ts.map +1 -0
  55. package/dist/cjs/internals/scheduling.js +117 -0
  56. package/dist/cjs/internals/scheduling.js.map +1 -0
  57. package/dist/cjs/internals/state.d.ts +18 -0
  58. package/dist/cjs/internals/state.d.ts.map +1 -0
  59. package/dist/cjs/internals/state.js +88 -0
  60. package/dist/cjs/internals/state.js.map +1 -0
  61. package/dist/cjs/internals/utils/debug-name.d.ts +2 -0
  62. package/dist/cjs/internals/utils/debug-name.d.ts.map +1 -0
  63. package/dist/cjs/internals/utils/debug-name.js +14 -0
  64. package/dist/cjs/internals/utils/debug-name.js.map +1 -0
  65. package/dist/cjs/internals/utils/equals.d.ts +3 -0
  66. package/dist/cjs/internals/utils/equals.d.ts.map +1 -0
  67. package/dist/cjs/internals/utils/equals.js +13 -0
  68. package/dist/cjs/internals/utils/equals.js.map +1 -0
  69. package/dist/cjs/internals/utils/hash.d.ts +7 -0
  70. package/dist/cjs/internals/utils/hash.d.ts.map +1 -0
  71. package/dist/cjs/internals/utils/hash.js +181 -0
  72. package/dist/cjs/internals/utils/hash.js.map +1 -0
  73. package/dist/cjs/internals/utils/stringify.d.ts +3 -0
  74. package/dist/cjs/internals/utils/stringify.d.ts.map +1 -0
  75. package/dist/cjs/{utils.js → internals/utils/stringify.js} +5 -27
  76. package/dist/cjs/internals/utils/stringify.js.map +1 -0
  77. package/dist/cjs/internals/utils/type-utils.d.ts +6 -0
  78. package/dist/cjs/internals/utils/type-utils.d.ts.map +1 -0
  79. package/dist/cjs/internals/utils/type-utils.js +22 -0
  80. package/dist/cjs/internals/utils/type-utils.js.map +1 -0
  81. package/dist/cjs/react/context.d.ts +1 -1
  82. package/dist/cjs/react/context.d.ts.map +1 -1
  83. package/dist/cjs/react/provider.d.ts +4 -3
  84. package/dist/cjs/react/provider.d.ts.map +1 -1
  85. package/dist/cjs/react/provider.js +7 -3
  86. package/dist/cjs/react/provider.js.map +1 -1
  87. package/dist/cjs/react/setup.d.ts.map +1 -1
  88. package/dist/cjs/react/setup.js +2 -1
  89. package/dist/cjs/react/setup.js.map +1 -1
  90. package/dist/cjs/react/signal-value.d.ts +5 -1
  91. package/dist/cjs/react/signal-value.d.ts.map +1 -1
  92. package/dist/cjs/react/signal-value.js +35 -45
  93. package/dist/cjs/react/signal-value.js.map +1 -1
  94. package/dist/cjs/trace.d.ts +32 -28
  95. package/dist/cjs/trace.d.ts.map +1 -1
  96. package/dist/cjs/trace.js +14 -16
  97. package/dist/cjs/trace.js.map +1 -1
  98. package/dist/cjs/transform.d.ts +6 -0
  99. package/dist/cjs/transform.d.ts.map +1 -0
  100. package/dist/cjs/transform.js +92 -0
  101. package/dist/cjs/transform.js.map +1 -0
  102. package/dist/cjs/types.d.ts +32 -40
  103. package/dist/cjs/types.d.ts.map +1 -1
  104. package/dist/esm/config.d.ts +8 -3
  105. package/dist/esm/config.d.ts.map +1 -1
  106. package/dist/esm/config.js +12 -7
  107. package/dist/esm/config.js.map +1 -1
  108. package/dist/esm/debug.d.ts +2 -2
  109. package/dist/esm/debug.d.ts.map +1 -1
  110. package/dist/esm/debug.js +2 -2
  111. package/dist/esm/debug.js.map +1 -1
  112. package/dist/esm/hooks.d.ts +14 -42
  113. package/dist/esm/hooks.d.ts.map +1 -1
  114. package/dist/esm/hooks.js +17 -226
  115. package/dist/esm/hooks.js.map +1 -1
  116. package/dist/esm/index.d.ts +5 -3
  117. package/dist/esm/index.d.ts.map +1 -1
  118. package/dist/esm/index.js +5 -3
  119. package/dist/esm/index.js.map +1 -1
  120. package/dist/esm/internals/async.d.ts +50 -0
  121. package/dist/esm/internals/async.d.ts.map +1 -0
  122. package/dist/esm/internals/async.js +383 -0
  123. package/dist/esm/internals/async.js.map +1 -0
  124. package/dist/esm/internals/connect.d.ts +4 -0
  125. package/dist/esm/internals/connect.d.ts.map +1 -0
  126. package/dist/esm/internals/connect.js +33 -0
  127. package/dist/esm/internals/connect.js.map +1 -0
  128. package/dist/esm/internals/consumer.d.ts +6 -0
  129. package/dist/esm/internals/consumer.d.ts.map +1 -0
  130. package/dist/esm/internals/consumer.js +9 -0
  131. package/dist/esm/internals/consumer.js.map +1 -0
  132. package/dist/esm/internals/contexts.d.ts +33 -0
  133. package/dist/esm/internals/contexts.d.ts.map +1 -0
  134. package/dist/esm/internals/contexts.js +92 -0
  135. package/dist/esm/internals/contexts.js.map +1 -0
  136. package/dist/esm/internals/derived.d.ts +66 -0
  137. package/dist/esm/internals/derived.d.ts.map +1 -0
  138. package/dist/esm/internals/derived.js +118 -0
  139. package/dist/esm/internals/derived.js.map +1 -0
  140. package/dist/esm/internals/dirty.d.ts +5 -0
  141. package/dist/esm/internals/dirty.d.ts.map +1 -0
  142. package/dist/esm/internals/dirty.js +75 -0
  143. package/dist/esm/internals/dirty.js.map +1 -0
  144. package/dist/esm/internals/edge.d.ts +32 -0
  145. package/dist/esm/internals/edge.d.ts.map +1 -0
  146. package/dist/esm/internals/edge.js +54 -0
  147. package/dist/esm/internals/edge.js.map +1 -0
  148. package/dist/esm/internals/get.d.ts +10 -0
  149. package/dist/esm/internals/get.d.ts.map +1 -0
  150. package/dist/esm/internals/get.js +247 -0
  151. package/dist/esm/internals/get.js.map +1 -0
  152. package/dist/esm/internals/scheduling.d.ts +12 -0
  153. package/dist/esm/internals/scheduling.d.ts.map +1 -0
  154. package/dist/esm/internals/scheduling.js +106 -0
  155. package/dist/esm/internals/scheduling.js.map +1 -0
  156. package/dist/esm/internals/state.d.ts +18 -0
  157. package/dist/esm/internals/state.d.ts.map +1 -0
  158. package/dist/esm/internals/state.js +82 -0
  159. package/dist/esm/internals/state.js.map +1 -0
  160. package/dist/esm/internals/utils/debug-name.d.ts +2 -0
  161. package/dist/esm/internals/utils/debug-name.d.ts.map +1 -0
  162. package/dist/esm/internals/utils/debug-name.js +11 -0
  163. package/dist/esm/internals/utils/debug-name.js.map +1 -0
  164. package/dist/esm/internals/utils/equals.d.ts +3 -0
  165. package/dist/esm/internals/utils/equals.d.ts.map +1 -0
  166. package/dist/esm/internals/utils/equals.js +9 -0
  167. package/dist/esm/internals/utils/equals.js.map +1 -0
  168. package/dist/esm/internals/utils/hash.d.ts +7 -0
  169. package/dist/esm/internals/utils/hash.d.ts.map +1 -0
  170. package/dist/esm/internals/utils/hash.js +174 -0
  171. package/dist/esm/internals/utils/hash.js.map +1 -0
  172. package/dist/esm/internals/utils/stringify.d.ts +3 -0
  173. package/dist/esm/internals/utils/stringify.d.ts.map +1 -0
  174. package/dist/esm/{utils.js → internals/utils/stringify.js} +4 -25
  175. package/dist/esm/internals/utils/stringify.js.map +1 -0
  176. package/dist/esm/internals/utils/type-utils.d.ts +6 -0
  177. package/dist/esm/internals/utils/type-utils.d.ts.map +1 -0
  178. package/dist/esm/internals/utils/type-utils.js +15 -0
  179. package/dist/esm/internals/utils/type-utils.js.map +1 -0
  180. package/dist/esm/react/context.d.ts +1 -1
  181. package/dist/esm/react/context.d.ts.map +1 -1
  182. package/dist/esm/react/provider.d.ts +4 -3
  183. package/dist/esm/react/provider.d.ts.map +1 -1
  184. package/dist/esm/react/provider.js +6 -2
  185. package/dist/esm/react/provider.js.map +1 -1
  186. package/dist/esm/react/setup.d.ts.map +1 -1
  187. package/dist/esm/react/setup.js +3 -2
  188. package/dist/esm/react/setup.js.map +1 -1
  189. package/dist/esm/react/signal-value.d.ts +5 -1
  190. package/dist/esm/react/signal-value.d.ts.map +1 -1
  191. package/dist/esm/react/signal-value.js +34 -45
  192. package/dist/esm/react/signal-value.js.map +1 -1
  193. package/dist/esm/trace.d.ts +32 -28
  194. package/dist/esm/trace.d.ts.map +1 -1
  195. package/dist/esm/trace.js +13 -15
  196. package/dist/esm/trace.js.map +1 -1
  197. package/dist/esm/transform.d.ts +6 -0
  198. package/dist/esm/transform.d.ts.map +1 -0
  199. package/dist/esm/transform.js +89 -0
  200. package/dist/esm/transform.js.map +1 -0
  201. package/dist/esm/types.d.ts +32 -40
  202. package/dist/esm/types.d.ts.map +1 -1
  203. package/package.json +23 -4
  204. package/src/__tests__/__snapshots__/context.test.ts.snap +2101 -0
  205. package/src/__tests__/__snapshots__/nesting.test.ts.snap +16201 -0
  206. package/src/__tests__/__snapshots__/params-and-state.test.ts.snap +1879 -0
  207. package/src/__tests__/async-task.test.ts +327 -0
  208. package/src/__tests__/context.test.ts +517 -0
  209. package/src/__tests__/nesting.test.ts +298 -0
  210. package/src/__tests__/params-and-state.test.ts +230 -0
  211. package/src/__tests__/reactive-async.test.ts +548 -0
  212. package/src/__tests__/reactive-sync.test.ts +130 -0
  213. package/src/__tests__/subscription.test.ts +510 -0
  214. package/src/__tests__/utils/async.ts +1 -1
  215. package/src/__tests__/utils/instrumented-hooks.ts +229 -124
  216. package/src/__tests__/utils/permute.ts +25 -14
  217. package/src/config.ts +19 -9
  218. package/src/debug.ts +2 -2
  219. package/src/hooks.ts +46 -380
  220. package/src/index.ts +7 -24
  221. package/src/internals/async.ts +556 -0
  222. package/src/internals/connect.ts +41 -0
  223. package/src/internals/consumer.ts +13 -0
  224. package/src/internals/contexts.ts +133 -0
  225. package/src/internals/derived.ts +208 -0
  226. package/src/internals/dirty.ts +91 -0
  227. package/src/internals/edge.ts +109 -0
  228. package/src/internals/get.ts +298 -0
  229. package/src/internals/scheduling.ts +140 -0
  230. package/src/internals/state.ts +111 -0
  231. package/src/internals/utils/debug-name.ts +14 -0
  232. package/src/internals/utils/equals.ts +12 -0
  233. package/src/internals/utils/hash.ts +221 -0
  234. package/src/{utils.ts → internals/utils/stringify.ts} +3 -29
  235. package/src/internals/utils/type-utils.ts +19 -0
  236. package/src/react/__tests__/async.test.tsx +704 -0
  237. package/src/react/__tests__/basic.test.tsx +95 -0
  238. package/src/react/__tests__/contexts.test.tsx +99 -0
  239. package/src/react/__tests__/subscriptions.test.tsx +49 -0
  240. package/src/react/__tests__/utils.tsx +40 -0
  241. package/src/react/context.ts +1 -1
  242. package/src/react/provider.tsx +12 -4
  243. package/src/react/setup.ts +3 -2
  244. package/src/react/signal-value.ts +47 -67
  245. package/src/trace.ts +43 -38
  246. package/src/transform.ts +113 -0
  247. package/src/types.ts +56 -46
  248. package/transform.js +19 -0
  249. package/vitest.workspace.ts +38 -2
  250. package/dist/cjs/scheduling.d.ts +0 -11
  251. package/dist/cjs/scheduling.d.ts.map +0 -1
  252. package/dist/cjs/scheduling.js +0 -108
  253. package/dist/cjs/scheduling.js.map +0 -1
  254. package/dist/cjs/signals.d.ts +0 -73
  255. package/dist/cjs/signals.d.ts.map +0 -1
  256. package/dist/cjs/signals.js +0 -632
  257. package/dist/cjs/signals.js.map +0 -1
  258. package/dist/cjs/utils.d.ts +0 -4
  259. package/dist/cjs/utils.d.ts.map +0 -1
  260. package/dist/cjs/utils.js.map +0 -1
  261. package/dist/esm/scheduling.d.ts +0 -11
  262. package/dist/esm/scheduling.d.ts.map +0 -1
  263. package/dist/esm/scheduling.js +0 -97
  264. package/dist/esm/scheduling.js.map +0 -1
  265. package/dist/esm/signals.d.ts +0 -73
  266. package/dist/esm/signals.d.ts.map +0 -1
  267. package/dist/esm/signals.js +0 -614
  268. package/dist/esm/signals.js.map +0 -1
  269. package/dist/esm/utils.d.ts +0 -4
  270. package/dist/esm/utils.d.ts.map +0 -1
  271. package/dist/esm/utils.js.map +0 -1
  272. package/src/__tests__/hooks/async-computed.test.ts +0 -190
  273. package/src/__tests__/hooks/async-task.test.ts +0 -334
  274. package/src/__tests__/hooks/computed.test.ts +0 -126
  275. package/src/__tests__/hooks/context.test.ts +0 -527
  276. package/src/__tests__/hooks/nesting.test.ts +0 -303
  277. package/src/__tests__/hooks/params-and-state.test.ts +0 -168
  278. package/src/__tests__/hooks/subscription.test.ts +0 -97
  279. package/src/__tests__/signals/async.test.ts +0 -416
  280. package/src/__tests__/signals/basic.test.ts +0 -399
  281. package/src/__tests__/signals/subscription.test.ts +0 -632
  282. package/src/__tests__/signals/watcher.test.ts +0 -253
  283. package/src/__tests__/utils/builders.ts +0 -22
  284. package/src/__tests__/utils/instrumented-signals.ts +0 -291
  285. package/src/react/__tests__/react.test.tsx +0 -227
  286. package/src/scheduling.ts +0 -130
  287. package/src/signals.ts +0 -824
package/src/signals.ts DELETED
@@ -1,824 +0,0 @@
1
- import {
2
- scheduleConnect,
3
- scheduleDirty,
4
- scheduleDisconnect,
5
- scheduleEffect,
6
- schedulePull,
7
- scheduleWatcher,
8
- } from './scheduling.js';
9
- import WeakRef from './weakref.js';
10
- import { TRACER as TRACER, TracerEventType, VisualizerNodeType } from './trace.js';
11
- import {
12
- AsyncResult,
13
- AsyncSignal,
14
- AsyncTask,
15
- Signal,
16
- SignalAsyncCompute,
17
- SignalCompute,
18
- SignalEquals,
19
- SignalOptions,
20
- SignalOptionsWithInit,
21
- SignalSubscribe,
22
- SignalSubscription,
23
- SubscriptionState,
24
- Watcher,
25
- WatcherListenerOptions,
26
- } from './types.js';
27
- import { COMPUTED_OWNERS, getOverrideScope, setOverrideScope } from './hooks.js';
28
-
29
- let CURRENT_ORD = 0;
30
- let CURRENT_CONSUMER: ComputedSignal<any> | undefined;
31
- let CURRENT_IS_WAITING: boolean = false;
32
-
33
- // Should not leave the file so it doesn't become an interop issue
34
- const enum SignalType {
35
- Computed,
36
- Subscription,
37
- Async,
38
- Watcher,
39
- }
40
-
41
- const SUBSCRIPTIONS = new WeakMap<ComputedSignal<any>, SignalSubscription | (() => unknown) | undefined | void>();
42
- const ACTIVE_ASYNCS = new WeakMap<ComputedSignal<any>, Promise<unknown>>();
43
-
44
- const enum SignalState {
45
- Clean,
46
- MaybeDirty,
47
- Dirty,
48
- }
49
-
50
- const WAITING = Symbol();
51
-
52
- interface Link {
53
- dep: ComputedSignal<any>;
54
- sub: WeakRef<ComputedSignal<any>>;
55
- ord: number;
56
- version: number;
57
- consumedAt: number;
58
-
59
- nextDirty: Link | undefined;
60
- }
61
-
62
- const FALSE_EQUALS: SignalEquals<unknown> = () => false;
63
-
64
- export function signalTypeToVisualizerType(type: SignalType): VisualizerNodeType {
65
- switch (type) {
66
- case SignalType.Computed:
67
- return VisualizerNodeType.Computed;
68
- case SignalType.Subscription:
69
- return VisualizerNodeType.Subscription;
70
- case SignalType.Async:
71
- return VisualizerNodeType.AsyncComputed;
72
- case SignalType.Watcher:
73
- return VisualizerNodeType.Watcher;
74
- }
75
- }
76
-
77
- interface InternalSignalOptions<T> extends SignalOptions<T, unknown[]> {
78
- equals: SignalEquals<T>;
79
- id: string;
80
- subscribers?: ((value: T) => void)[];
81
- }
82
-
83
- export class ComputedSignal<T> {
84
- _type: SignalType;
85
-
86
- _deps = new Map<ComputedSignal<any>, Link>();
87
-
88
- _dirtyDep: Link | undefined = undefined;
89
- _subs = new Set<Link>();
90
- _state: SignalState = SignalState.Dirty;
91
- _version: number = 0;
92
- _computedCount: number = 0;
93
- _connectedCount: number = 0;
94
- _currentValue: T | AsyncResult<T> | undefined;
95
- _compute: SignalCompute<T> | SignalAsyncCompute<T> | SignalSubscribe<T, []>;
96
-
97
- _opts: InternalSignalOptions<T>;
98
- _ref: WeakRef<ComputedSignal<T>> = new WeakRef(this);
99
-
100
- constructor(
101
- type: SignalType,
102
- compute: SignalCompute<T> | SignalAsyncCompute<T> | SignalSubscribe<T, []>,
103
- opts: InternalSignalOptions<T>,
104
- initValue?: T,
105
- ) {
106
- this._type = type;
107
- this._compute = compute;
108
- this._opts = opts;
109
-
110
- this._currentValue =
111
- type !== SignalType.Async
112
- ? initValue
113
- : ({
114
- result: initValue,
115
- error: undefined,
116
- isReady: initValue !== undefined,
117
-
118
- isPending: true,
119
- isError: false,
120
- isSuccess: false,
121
- didResolve: false,
122
-
123
- invalidate: () => {
124
- this._state = SignalState.Dirty;
125
- this._dirty();
126
- },
127
-
128
- await: () => {
129
- if (CURRENT_CONSUMER === undefined || CURRENT_CONSUMER._type !== SignalType.Async) {
130
- throw new Error(
131
- 'Cannot await an async signal outside of an async signal. If you are using an async function, you must use signal.await() for all async signals _before_ the first language-level `await` keyword statement (e.g. it must be synchronous).',
132
- );
133
- }
134
-
135
- TRACER?.emit({
136
- type: TracerEventType.StartLoading,
137
- id: CURRENT_CONSUMER._opts.id,
138
- });
139
-
140
- const value = this._currentValue as AsyncResult<T>;
141
-
142
- if (value.isPending) {
143
- const currentConsumer = CURRENT_CONSUMER;
144
- ACTIVE_ASYNCS.get(this)?.finally(() => schedulePull(currentConsumer));
145
-
146
- CURRENT_IS_WAITING = true;
147
- throw WAITING;
148
- } else if (value.isError) {
149
- throw value.error;
150
- }
151
-
152
- return value.result as T;
153
- },
154
- } as AsyncResult<T>);
155
- }
156
-
157
- get(): T | AsyncResult<T> {
158
- if (CURRENT_CONSUMER !== undefined) {
159
- const { _deps: deps, _computedCount: computedCount, _connectedCount: connectedCount } = CURRENT_CONSUMER;
160
- const prevLink = deps.get(this);
161
-
162
- if (prevLink === undefined) {
163
- TRACER?.emit({
164
- type: TracerEventType.Connected,
165
- id: CURRENT_CONSUMER._opts.id,
166
- childId: this._opts.id,
167
- name: this._opts.desc,
168
- params: this._opts.params,
169
- nodeType: signalTypeToVisualizerType(this._type),
170
- });
171
- }
172
-
173
- const ord = CURRENT_ORD++;
174
-
175
- this._check(!prevLink && connectedCount > 0);
176
-
177
- if (prevLink === undefined) {
178
- const newLink = {
179
- dep: this,
180
- sub: CURRENT_CONSUMER._ref,
181
- ord,
182
- version: this._version,
183
- consumedAt: CURRENT_CONSUMER._computedCount,
184
- nextDirty: undefined,
185
- };
186
-
187
- deps.set(this, newLink);
188
- this._subs.add(newLink);
189
- } else if (prevLink.consumedAt !== computedCount) {
190
- prevLink.ord = ord;
191
- prevLink.version = this._version;
192
- prevLink.consumedAt = computedCount;
193
- this._subs.add(prevLink);
194
- }
195
- } else {
196
- this._check();
197
- }
198
-
199
- return this._currentValue!;
200
- }
201
-
202
- _check(shouldWatch = false, connectCount = 1, immediate = false): number {
203
- let state = this._state;
204
- let connectedCount = this._connectedCount;
205
-
206
- const wasConnected = connectedCount > 0;
207
- const shouldConnect = shouldWatch && !wasConnected;
208
-
209
- if (shouldWatch) {
210
- this._connectedCount = connectedCount = connectedCount + connectCount;
211
- }
212
-
213
- if (shouldConnect) {
214
- if (this._type === SignalType.Subscription) {
215
- state = SignalState.Dirty;
216
- } else {
217
- for (const [dep, link] of this._deps) {
218
- if (link.version !== dep._check(true)) {
219
- state = SignalState.Dirty;
220
- break;
221
- }
222
- }
223
- }
224
- }
225
-
226
- if (state === SignalState.Clean) {
227
- return this._version;
228
- }
229
-
230
- if (state === SignalState.MaybeDirty) {
231
- let dirty = this._dirtyDep;
232
-
233
- while (dirty !== undefined) {
234
- const dep = dirty.dep;
235
-
236
- if (dirty.version !== dep._check()) {
237
- state = SignalState.Dirty;
238
- break;
239
- }
240
-
241
- dirty = dirty.nextDirty;
242
- }
243
- }
244
-
245
- if (state === SignalState.Dirty) {
246
- this._run(wasConnected, shouldConnect, immediate);
247
- } else {
248
- this._resetDirty();
249
- }
250
-
251
- this._state = SignalState.Clean;
252
- this._dirtyDep = undefined;
253
-
254
- return this._version;
255
- }
256
-
257
- _run(wasConnected: boolean, shouldConnect: boolean, immediate = false) {
258
- TRACER?.emit({
259
- type: TracerEventType.StartUpdate,
260
- id: this._opts.id,
261
- });
262
-
263
- const { _type: type } = this;
264
-
265
- const prevConsumer = CURRENT_CONSUMER;
266
-
267
- try {
268
- // eslint-disable-next-line @typescript-eslint/no-this-alias
269
- CURRENT_CONSUMER = this;
270
-
271
- this._computedCount++;
272
-
273
- switch (type) {
274
- case SignalType.Computed: {
275
- const version = this._version;
276
- const prevValue = this._currentValue as T | undefined;
277
- const nextValue = (this._compute as SignalCompute<T>)(prevValue);
278
-
279
- if (version === 0 || !this._opts.equals(prevValue!, nextValue)) {
280
- this._currentValue = nextValue;
281
- this._version = version + 1;
282
- }
283
-
284
- break;
285
- }
286
-
287
- case SignalType.Async: {
288
- const value: AsyncResult<T> = this._currentValue as AsyncResult<T>;
289
-
290
- let nextValue;
291
-
292
- try {
293
- CURRENT_IS_WAITING = false;
294
- nextValue = (this._compute as SignalAsyncCompute<T>)(value?.result);
295
- } catch (e) {
296
- if (e !== WAITING) {
297
- value.error = e;
298
- value.isPending = false;
299
- value.isError = true;
300
- this._version++;
301
- break;
302
- }
303
- }
304
-
305
- if (CURRENT_IS_WAITING) {
306
- if (!value.isPending) {
307
- value.isPending = true;
308
- value.isError = false;
309
- value.isSuccess = false;
310
- this._version++;
311
- }
312
-
313
- if (nextValue instanceof Promise) {
314
- nextValue.catch((e: unknown) => {
315
- if (e !== WAITING) {
316
- value.error = e;
317
- value.isPending = false;
318
- value.isError = true;
319
- this._version++;
320
- }
321
- });
322
- }
323
- } else if (nextValue instanceof Promise) {
324
- const currentVersion = ++this._version;
325
-
326
- TRACER?.emit({
327
- type: TracerEventType.StartLoading,
328
- id: this._opts.id,
329
- });
330
-
331
- nextValue = nextValue
332
- .then(
333
- result => {
334
- if (currentVersion !== this._version) {
335
- return;
336
- }
337
-
338
- value.result = result;
339
- value.isReady = true;
340
- value.didResolve = true;
341
-
342
- value.isPending = false;
343
- value.isSuccess = true;
344
-
345
- this._version++;
346
- scheduleDirty(this);
347
- },
348
- error => {
349
- if (currentVersion !== this._version || error === WAITING) {
350
- return;
351
- }
352
-
353
- value.error = error;
354
- value.isPending = false;
355
- value.isError = true;
356
- this._version++;
357
- scheduleDirty(this);
358
- },
359
- )
360
- .finally(() => {
361
- TRACER?.emit({
362
- type: TracerEventType.EndLoading,
363
- id: this._opts.id,
364
- value: value,
365
- });
366
- });
367
-
368
- ACTIVE_ASYNCS.set(this, nextValue);
369
-
370
- value.isPending = true;
371
- value.isError = false;
372
- value.isSuccess = false;
373
- } else {
374
- value.result = nextValue as T;
375
- value.isReady = true;
376
- value.isPending = false;
377
- value.isSuccess = true;
378
- value.isError = false;
379
-
380
- this._version++;
381
-
382
- TRACER?.emit({
383
- type: TracerEventType.EndLoading,
384
- id: this._opts.id,
385
- value: value,
386
- });
387
- }
388
-
389
- break;
390
- }
391
-
392
- case SignalType.Subscription: {
393
- if (shouldConnect) {
394
- const subscription = (this._compute as SignalSubscribe<T, []>)(createSubscriptionState(this));
395
- SUBSCRIPTIONS.set(this, subscription);
396
- } else {
397
- const subscription = SUBSCRIPTIONS.get(this);
398
-
399
- if (typeof subscription === 'function') {
400
- subscription();
401
- const nextSubscription = (this._compute as SignalSubscribe<T, []>)(createSubscriptionState(this));
402
- SUBSCRIPTIONS.set(this, nextSubscription);
403
- } else if (subscription !== undefined) {
404
- subscription.update?.();
405
- }
406
- }
407
-
408
- break;
409
- }
410
-
411
- default: {
412
- const version = this._version;
413
- const prevValue = this._currentValue as T | undefined;
414
- const nextValue = (this._compute as SignalCompute<T>)(prevValue);
415
-
416
- if (version === 0 || !this._opts.equals(prevValue!, nextValue)) {
417
- this._currentValue = nextValue;
418
- this._version = version + 1;
419
-
420
- if (immediate) {
421
- this._runEffects();
422
- } else {
423
- scheduleEffect(this);
424
- }
425
- }
426
-
427
- break;
428
- }
429
- }
430
- } finally {
431
- TRACER?.emit({
432
- type: TracerEventType.EndUpdate,
433
- id: this._opts.id,
434
- value: this._currentValue,
435
- });
436
-
437
- if (this._type !== SignalType.Watcher) {
438
- const deps = this._deps;
439
-
440
- for (const link of deps.values()) {
441
- if (link.consumedAt === this._computedCount) continue;
442
-
443
- const dep = link.dep;
444
-
445
- if (wasConnected) {
446
- scheduleDisconnect(dep);
447
- }
448
-
449
- TRACER?.emit({
450
- type: TracerEventType.Disconnected,
451
- id: this._opts.id,
452
- childId: dep._opts.id,
453
- });
454
-
455
- deps.delete(dep);
456
- dep._subs.delete(link);
457
- }
458
- }
459
-
460
- CURRENT_CONSUMER = prevConsumer;
461
- }
462
- }
463
-
464
- _resetDirty() {
465
- let dirty = this._dirtyDep;
466
-
467
- while (dirty !== undefined) {
468
- dirty.dep._subs.add(dirty);
469
-
470
- let nextDirty = dirty.nextDirty;
471
- dirty.nextDirty = undefined;
472
- dirty = nextDirty;
473
- }
474
- }
475
-
476
- _dirty() {
477
- if (this._type === SignalType.Subscription) {
478
- if (this._connectedCount > 0) {
479
- scheduleWatcher(this);
480
- }
481
-
482
- // else do nothing, only schedule if connected
483
- } else if (this._type === SignalType.Watcher) {
484
- scheduleWatcher(this);
485
- } else {
486
- this._dirtyConsumers();
487
- }
488
- }
489
-
490
- _dirtyConsumers() {
491
- for (const link of this._subs.values()) {
492
- const sub = link.sub.deref();
493
-
494
- if (sub === undefined) continue;
495
-
496
- switch (sub._state) {
497
- case SignalState.MaybeDirty: {
498
- let dirty = sub._dirtyDep;
499
- const ord = link.ord;
500
- if (dirty!.ord > ord) {
501
- sub._dirtyDep = link;
502
- link.nextDirty = dirty;
503
- } else {
504
- let nextDirty = dirty!.nextDirty;
505
- while (nextDirty !== undefined && nextDirty!.ord < ord) {
506
- dirty = nextDirty;
507
- nextDirty = dirty.nextDirty;
508
- }
509
- link.nextDirty = nextDirty;
510
- dirty!.nextDirty = link;
511
- }
512
- break;
513
- }
514
- case SignalState.Clean: {
515
- sub._state = SignalState.MaybeDirty;
516
- sub._dirtyDep = link;
517
- link.nextDirty = undefined;
518
- sub._dirty();
519
- }
520
- }
521
- }
522
-
523
- this._subs = new Set();
524
- }
525
-
526
- _disconnect(count = 1) {
527
- this._connectedCount -= count;
528
-
529
- if (this._connectedCount > 0) {
530
- return;
531
- } else if (this._connectedCount < 0) {
532
- throw new Error('Signal disconnect count cannot be negative');
533
- }
534
-
535
- if (this._type === SignalType.Subscription) {
536
- const subscription = SUBSCRIPTIONS.get(this);
537
-
538
- if (typeof subscription === 'function') {
539
- subscription();
540
- SUBSCRIPTIONS.delete(this);
541
- } else if (subscription !== undefined) {
542
- subscription.unsubscribe?.();
543
- SUBSCRIPTIONS.delete(this);
544
- }
545
- }
546
-
547
- for (const link of this._deps.values()) {
548
- const dep = link.dep;
549
-
550
- dep._disconnect();
551
- }
552
- }
553
-
554
- _runEffects() {
555
- for (const subscriber of this._opts.subscribers!) {
556
- subscriber(this._currentValue as T);
557
- }
558
- }
559
-
560
- addListener(subscriber: (value: T) => void, opts?: WatcherListenerOptions) {
561
- const subscribers = this._opts.subscribers!;
562
- const index = subscribers.indexOf(subscriber);
563
-
564
- if (index === -1) {
565
- subscribers.push(subscriber);
566
-
567
- if (opts?.immediate) {
568
- this._check(true, 1, true);
569
- } else {
570
- scheduleConnect(this);
571
- }
572
- }
573
-
574
- return () => {
575
- const index = subscribers.indexOf(subscriber);
576
-
577
- if (index !== -1) {
578
- subscribers.splice(index, 1);
579
- scheduleDisconnect(this);
580
- }
581
- };
582
- }
583
- }
584
-
585
- function createSubscriptionState<T>(signal: ComputedSignal<T>): SubscriptionState<T> {
586
- return {
587
- get: () => signal._currentValue as T,
588
- set: value => {
589
- const version = signal._version;
590
-
591
- if (version !== 0 && signal._opts.equals(value, signal._currentValue as T)) {
592
- return;
593
- }
594
-
595
- TRACER?.emit({
596
- type: TracerEventType.StartUpdate,
597
- id: signal._opts.id,
598
- });
599
-
600
- signal._currentValue = value;
601
- signal._version = version + 1;
602
- signal._dirtyConsumers();
603
-
604
- TRACER?.emit({
605
- type: TracerEventType.EndUpdate,
606
- id: signal._opts.id,
607
- value: signal._currentValue,
608
- preserveChildren: true,
609
- });
610
- },
611
- };
612
- }
613
-
614
- let STATE_ID = 0;
615
-
616
- export class StateSignal<T> implements StateSignal<T> {
617
- _subs: WeakRef<ComputedSignal<unknown>>[] = [];
618
- _desc: string;
619
-
620
- constructor(
621
- private _value: T,
622
- private _equals: SignalEquals<T> = (a, b) => a === b,
623
- desc: string = 'state',
624
- ) {
625
- this._desc = `${desc}${STATE_ID++}`;
626
- }
627
-
628
- get(): T {
629
- if (CURRENT_CONSUMER !== undefined) {
630
- TRACER?.emit({
631
- type: TracerEventType.ConsumeState,
632
- id: CURRENT_CONSUMER._opts.id,
633
- childId: this._desc,
634
- value: this._value,
635
- setValue: (value: unknown) => {
636
- this.set(value as T);
637
- },
638
- });
639
- this._subs.push(CURRENT_CONSUMER._ref);
640
- }
641
-
642
- return this._value!;
643
- }
644
-
645
- set(value: T) {
646
- if (this._equals(value, this._value)) {
647
- return;
648
- }
649
-
650
- this._value = value;
651
- const subs = this._subs;
652
- const subsLength = subs.length;
653
-
654
- for (let i = 0; i < subsLength; i++) {
655
- const sub = subs[i].deref();
656
-
657
- if (sub === undefined) {
658
- continue;
659
- }
660
-
661
- switch (sub._state) {
662
- case SignalState.Clean:
663
- sub._state = SignalState.Dirty;
664
- sub._dirty();
665
- break;
666
- case SignalState.MaybeDirty:
667
- sub._state = SignalState.Dirty;
668
- break;
669
- }
670
- }
671
-
672
- this._subs = [];
673
- }
674
- }
675
-
676
- let UNKNOWN_SIGNAL_ID = 0;
677
-
678
- const normalizeOpts = <T>(
679
- opts?: SignalOptions<T, unknown[]> & { subscribers?: ((value: T) => void)[] },
680
- ): InternalSignalOptions<T> => {
681
- return {
682
- equals: opts?.equals === false ? FALSE_EQUALS : (opts?.equals ?? ((a, b) => a === b)),
683
- id: opts?.id ?? `unknownSignal${UNKNOWN_SIGNAL_ID++}`,
684
- desc: opts?.desc,
685
- params: opts?.params,
686
- };
687
- };
688
-
689
- export function createStateSignal<T>(
690
- initialValue: T,
691
- opts?: Omit<SignalOptions<T, unknown[]>, 'paramKey'>,
692
- ): StateSignal<T> {
693
- const equals = opts?.equals === false ? FALSE_EQUALS : (opts?.equals ?? ((a, b) => a === b));
694
-
695
- return new StateSignal(initialValue, equals, opts?.desc);
696
- }
697
-
698
- export function createComputedSignal<T>(
699
- compute: (prev: T | undefined) => T,
700
- opts?: SignalOptions<T, unknown[]>,
701
- ): Signal<T> {
702
- return new ComputedSignal(SignalType.Computed, compute, normalizeOpts(opts)) as Signal<T>;
703
- }
704
-
705
- export function createAsyncComputedSignal<T>(
706
- compute: (prev: T | undefined) => Promise<T>,
707
- opts?: SignalOptions<T, unknown[]>,
708
- ): AsyncSignal<T>;
709
- export function createAsyncComputedSignal<T>(
710
- compute: (prev: T | undefined) => Promise<T>,
711
- opts: SignalOptionsWithInit<T, unknown[]>,
712
- ): AsyncSignal<T>;
713
- export function createAsyncComputedSignal<T>(
714
- compute: (prev: T | undefined) => Promise<T>,
715
- opts?: Partial<SignalOptionsWithInit<T, unknown[]>>,
716
- ): AsyncSignal<T> {
717
- return new ComputedSignal(SignalType.Async, compute, normalizeOpts(opts), opts?.initValue) as AsyncSignal<T>;
718
- }
719
-
720
- export function createSubscriptionSignal<T>(
721
- subscribe: SignalSubscribe<T, []>,
722
- opts?: SignalOptions<T, unknown[]>,
723
- ): Signal<T | undefined>;
724
- export function createSubscriptionSignal<T>(
725
- subscribe: SignalSubscribe<T, []>,
726
- opts: SignalOptionsWithInit<T, unknown[]>,
727
- ): Signal<T>;
728
- export function createSubscriptionSignal<T>(
729
- subscribe: SignalSubscribe<T, []>,
730
- opts?: Partial<SignalOptionsWithInit<T, unknown[]>>,
731
- ): Signal<T> {
732
- return new ComputedSignal(SignalType.Subscription, subscribe, normalizeOpts(opts), opts?.initValue) as Signal<T>;
733
- }
734
-
735
- export function createAsyncTaskSignal<T, Args extends unknown[]>(
736
- fn: (...args: Args) => Promise<T>,
737
- ): Signal<AsyncTask<T, Args>> {
738
- let currentPromise: Promise<T> | undefined;
739
-
740
- const task: AsyncTask<T, Args> = {
741
- result: undefined,
742
- error: undefined,
743
- isPending: false,
744
- isSuccess: false,
745
- isError: false,
746
- isReady: false,
747
-
748
- async run(...params) {
749
- if (!task.isPending) {
750
- const prevOverrideScope = getOverrideScope();
751
- try {
752
- setOverrideScope(COMPUTED_OWNERS.get(signal as unknown as ComputedSignal<unknown>)!);
753
- currentPromise = run(...params);
754
- } finally {
755
- setOverrideScope(prevOverrideScope);
756
- }
757
- }
758
-
759
- return currentPromise!;
760
- },
761
- };
762
-
763
- const run = async (...params: Args) => {
764
- try {
765
- task.isPending = true;
766
- task.isError = false;
767
- task.isSuccess = false;
768
-
769
- signal.set(task);
770
-
771
- const result = await fn(...params);
772
-
773
- task.result = result;
774
- task.isSuccess = true;
775
- task.isReady = true;
776
-
777
- return result;
778
- } catch (error) {
779
- task.error = error;
780
- task.isError = true;
781
-
782
- throw error;
783
- } finally {
784
- task.isPending = false;
785
- signal.set(task);
786
- }
787
- };
788
-
789
- const signal = createStateSignal<AsyncTask<T, Args>>(task, { equals: false });
790
-
791
- return signal;
792
- }
793
-
794
- export function createWatcherSignal<T>(fn: (prev: T | undefined) => T, opts?: SignalOptions<T, unknown[]>): Watcher<T> {
795
- const normalizedOpts = normalizeOpts({
796
- equals: FALSE_EQUALS,
797
- subscribers: [],
798
- ...opts,
799
- });
800
-
801
- normalizedOpts.subscribers = [];
802
-
803
- return new ComputedSignal(SignalType.Watcher, fn, normalizedOpts);
804
- }
805
-
806
- export function getCurrentConsumer(): ComputedSignal<any> | undefined {
807
- return CURRENT_CONSUMER;
808
- }
809
-
810
- export function isTracking(): boolean {
811
- return CURRENT_CONSUMER !== undefined;
812
- }
813
-
814
- export function untrack<T = void>(fn: () => T): T {
815
- const prevConsumer = CURRENT_CONSUMER;
816
-
817
- try {
818
- CURRENT_CONSUMER = undefined;
819
-
820
- return fn();
821
- } finally {
822
- CURRENT_CONSUMER = prevConsumer;
823
- }
824
- }