signalium 0.3.8 → 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 +15 -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
@@ -0,0 +1,517 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { createContext, useContext, withContexts, state } from '../index.js';
3
+ import { permute } from './utils/permute.js';
4
+ import { nextTick } from './utils/async.js';
5
+ import { reactive } from './utils/instrumented-hooks.js';
6
+
7
+ describe('contexts', () => {
8
+ test('throws when useContext is used outside of a signal', () => {
9
+ expect(() => {
10
+ useContext(createContext('test'));
11
+ }).toThrow('useContext must be used within a signal hook');
12
+ });
13
+
14
+ test('async computed maintains context ownership across await boundaries', async () => {
15
+ const ctx = createContext('default');
16
+
17
+ const inner = reactive(async () => {
18
+ await Promise.resolve();
19
+ return 'inner-value';
20
+ });
21
+
22
+ const outer = reactive(async () => {
23
+ const result = await inner();
24
+
25
+ // Use context after awaiting inner result
26
+ const contextValue = useContext(ctx);
27
+ return result + '-' + contextValue;
28
+ });
29
+
30
+ // Test in parent scope
31
+ expect(outer).toHaveValueAndCounts(undefined, { compute: 1 });
32
+
33
+ // Wait for async computation to complete
34
+ await nextTick();
35
+ await nextTick();
36
+ expect(outer).toHaveValueAndCounts('inner-value-default', { compute: 1 });
37
+
38
+ // Test in child scope
39
+ expect(outer.withContexts([ctx, 'child'])).toHaveValueAndCounts(undefined, { compute: 1 });
40
+
41
+ // Verify parent scope maintains separate computed
42
+ await nextTick();
43
+ await nextTick();
44
+
45
+ expect(outer.withContexts([ctx, 'child'])).toHaveValueAndCounts('inner-value-child', { compute: 1 });
46
+ expect(outer).toHaveValueAndCounts('inner-value-default', { compute: 1 });
47
+ });
48
+
49
+ test('async task maintains context ownership across await boundaries', async () => {
50
+ const ctx = createContext('default');
51
+
52
+ const task = reactive(async () => {
53
+ await Promise.resolve();
54
+ });
55
+ });
56
+
57
+ permute(1, create => {
58
+ test('computed signals are cached per context scope', async () => {
59
+ const ctx = createContext('default');
60
+ const value = state(0);
61
+
62
+ const computed = create(
63
+ () => {
64
+ return useContext(ctx) + value.get();
65
+ },
66
+ {
67
+ desc: 'subscription',
68
+ },
69
+ );
70
+
71
+ computed();
72
+
73
+ await nextTick();
74
+
75
+ // Same scope should reuse computation
76
+ expect(computed).toHaveSignalValue('default0').toMatchSnapshot();
77
+ expect(computed).toHaveSignalValue('default0').toMatchSnapshot();
78
+
79
+ const result = withContexts([[ctx, 'other']], () => {
80
+ // Different scope should compute again
81
+ return computed();
82
+ });
83
+
84
+ await nextTick();
85
+
86
+ expect(computed.withContexts([ctx, 'other']))
87
+ .toHaveSignalValue('other0')
88
+ .toMatchSnapshot();
89
+ expect(computed.withContexts([ctx, 'other']))
90
+ .toHaveSignalValue('other0')
91
+ .toMatchSnapshot();
92
+
93
+ expect(computed).toHaveSignalValue('default0').toMatchSnapshot();
94
+ });
95
+
96
+ // test.skip('computed forks when accessing forked context after being shared', async () => {
97
+ // const ctx = createContext('default');
98
+ // const value = state(0);
99
+
100
+ // const computed = create(() => {
101
+ // // Initially only depends on value, not context
102
+ // const v = value.get();
103
+ // if (v > 0) {
104
+ // // After value changes, depends on context
105
+ // return useContext(ctx);
106
+ // }
107
+ // return 'default';
108
+ // });
109
+
110
+ // // Initially computed is shared between scopes since it doesn't use context
111
+ // expect(computed).withContexts([ctx, 'scope1']).toHaveValueAndCounts('default', { compute: 1 });
112
+ // expect(computed).withContexts([ctx, 'scope2']).toHaveValueAndCounts('default', { compute: 1 });
113
+
114
+ // // Change value to make computed use context
115
+ // value.set(1);
116
+
117
+ // await nextTick();
118
+
119
+ // // Now computed should fork and use the different context values
120
+ // expect(computed).withContexts([ctx, 'scope1']).toHaveValueAndCounts('scope1', { compute: 3 });
121
+ // expect(computed).withContexts([ctx, 'scope2']).toHaveValueAndCounts('scope2', { compute: 3 });
122
+
123
+ // // Ensure that computed is cached correctly
124
+ // expect(computed).withContexts([ctx, 'scope1']).toHaveValueAndCounts('scope1', { compute: 3 });
125
+ // expect(computed).withContexts([ctx, 'scope2']).toHaveValueAndCounts('scope2', { compute: 3 });
126
+ // });
127
+
128
+ // test.skip('computed forks correctly regardless of access order', () => {
129
+ // const ctx = createContext('default');
130
+ // const value = state(0);
131
+
132
+ // const computed = create(() => {
133
+ // // Initially only depends on value, not context
134
+ // const v = value.get();
135
+ // if (v > 0) {
136
+ // // After value changes, depends on context
137
+ // return useContext(ctx);
138
+ // }
139
+ // return v;
140
+ // });
141
+
142
+ // // Create two scopes with different context values, but access in reverse order
143
+ // expect(computed).withContexts([ctx, 'scope1']).toHaveValueAndCounts(0, { compute: 1 });
144
+
145
+ // expect(computed).withContexts([ctx, 'scope2']).toHaveValueAndCounts(0, { compute: 1 }); // Still shared since no context dependency
146
+
147
+ // // Change value to make computed use context
148
+ // value.set(1);
149
+
150
+ // // Now computed should fork and use the different context values
151
+ // // Access in reverse order compared to first test
152
+ // expect(computed).withContexts([ctx, 'scope2']).toHaveValueAndCounts('scope2', { compute: 2 });
153
+
154
+ // expect(computed).withContexts([ctx, 'scope1']).toHaveValueAndCounts('scope1', { compute: 3 });
155
+
156
+ // // Ensure that computed is cached correctly
157
+ // expect(computed).withContexts([ctx, 'scope1']).toHaveValueAndCounts('scope1', { compute: 3 });
158
+
159
+ // expect(computed).withContexts([ctx, 'scope2']).toHaveValueAndCounts('scope2', { compute: 3 });
160
+ // });
161
+
162
+ // test.skip('computed ownership transfers correctly between parent and child scopes', () => {
163
+ // const ctx = createContext('default');
164
+ // const value = state(0);
165
+
166
+ // const computed = create(() => {
167
+ // // Initially only depends on value, not context
168
+ // const v = value.get();
169
+ // if (v > 0) {
170
+ // // After value changes, depends on context
171
+ // return useContext(ctx) + v;
172
+ // }
173
+ // return v;
174
+ // });
175
+
176
+ // // Initially access in parent scope
177
+ // expect(computed).toHaveValueAndCounts(0, { compute: 1 });
178
+
179
+ // // Child scope reuses original computed instance since no context dependency
180
+ // expect(computed).withContexts([ctx, 'child']).toHaveValueAndCounts(0, { compute: 1 });
181
+
182
+ // // Change value to make computed use context
183
+ // value.set(1);
184
+
185
+ // // Child scope takes ownership of parent instance
186
+ // expect(computed).withContexts([ctx, 'child']).toHaveValueAndCounts('child1', { compute: 2 });
187
+
188
+ // // Parent scope gets its own computed instance
189
+ // expect(computed).toHaveValueAndCounts('default1', { compute: 3 });
190
+
191
+ // // Third scope gets its own computed instance
192
+ // expect(computed).withContexts([ctx, 'third']).toHaveValueAndCounts('third1', { compute: 4 });
193
+
194
+ // // Ensure computeds are cached correctly
195
+ // expect(computed).withContexts([ctx, 'child']).toHaveValueAndCounts('child1', { compute: 4 });
196
+
197
+ // expect(computed).toHaveValueAndCounts('default1', { compute: 4 });
198
+
199
+ // // Verify all scopes maintain their separate computeds
200
+ // value.set(2);
201
+
202
+ // expect(computed).withContexts([ctx, 'child']).toHaveValueAndCounts('child2', { compute: 5 });
203
+
204
+ // expect(computed).toHaveValueAndCounts('default2', { compute: 6 });
205
+
206
+ // expect(computed).withContexts([ctx, 'third']).toHaveValueAndCounts('third2', { compute: 7 });
207
+ // });
208
+ });
209
+
210
+ permute(2, (create1, create2) => {
211
+ test('contexts are properly scoped', async () => {
212
+ const ctx = createContext('default');
213
+
214
+ const computed1 = create1(() => {
215
+ return useContext(ctx);
216
+ });
217
+
218
+ computed1.watch();
219
+
220
+ await nextTick();
221
+
222
+ expect(computed1).toHaveSignalValue('default').toMatchSnapshot();
223
+
224
+ const computed2 = create2(() => {
225
+ return withContexts([[ctx, 'override']], () => {
226
+ return computed1();
227
+ });
228
+ });
229
+
230
+ computed2.watch();
231
+
232
+ await nextTick();
233
+
234
+ expect(computed2).toHaveSignalValue('override').toMatchSnapshot();
235
+ // expect(computed1).toHaveSignalValue('default').toMatchSnapshot();
236
+ });
237
+
238
+ // test.skip('context dependencies are tracked correctly', () => {
239
+ // const ctx1 = createContext('default1');
240
+ // const ctx2 = createContext('default2');
241
+
242
+ // const computed1 = create1(() => {
243
+ // // Only depends on ctx1
244
+ // return useContext(ctx1);
245
+ // });
246
+
247
+ // const computed2 = create2(() => {
248
+ // // Depends on both contexts
249
+ // return computed1() + useContext(ctx2);
250
+ // });
251
+
252
+ // expect(computed2).toHaveValueAndCounts('default1default2', { compute: 1 });
253
+ // expect(computed1).toHaveCounts({ compute: 1 });
254
+
255
+ // expect(computed2).withContexts([ctx1, 'override1']).toHaveValueAndCounts('override1default2', { compute: 2 });
256
+ // expect(computed1).toHaveCounts({ compute: 2 });
257
+
258
+ // expect(computed2).withContexts([ctx2, 'override2']).toHaveValueAndCounts('default1override2', { compute: 3 });
259
+ // expect(computed1).toHaveCounts({ compute: 2 });
260
+
261
+ // expect(computed2)
262
+ // .withContexts([ctx1, 'override1'], [ctx2, 'override2'])
263
+ // .toHaveValueAndCounts('override1override2', { compute: 4 });
264
+ // expect(computed1).toHaveCounts({ compute: 3 });
265
+
266
+ // // Should reuse cached value since ctx2 didn't change
267
+ // expect(computed1).withContexts([ctx2, 'override1']).toHaveValueAndCounts('default1', { compute: 3 });
268
+ // });
269
+
270
+ test('context scopes inherit from parent scope when nested in computeds', async () => {
271
+ const ctx1 = createContext('default1');
272
+ const ctx2 = createContext('default2');
273
+
274
+ const computed1 = create1(() => {
275
+ return useContext(ctx1) + useContext(ctx2);
276
+ });
277
+
278
+ const computed2 = create2(() => {
279
+ return (
280
+ useContext(ctx2) +
281
+ withContexts([[ctx2, ':inner-override2']], () => {
282
+ return computed1();
283
+ })
284
+ );
285
+ });
286
+
287
+ computed2.watch();
288
+ computed2.withContexts([ctx1, 'override1']).watch();
289
+ computed2.withContexts([ctx2, 'override2']).watch();
290
+ computed2.withContexts([ctx1, 'override1'], [ctx2, 'override2']).watch();
291
+
292
+ await nextTick();
293
+
294
+ expect(computed2).toHaveSignalValue('default2default1:inner-override2').toMatchSnapshot();
295
+ expect(computed1).toMatchSnapshot();
296
+
297
+ expect(computed2.withContexts([ctx1, 'override1']))
298
+ .toHaveSignalValue('default2override1:inner-override2')
299
+ .toMatchSnapshot();
300
+ expect(computed1).toMatchSnapshot();
301
+
302
+ await nextTick();
303
+
304
+ expect(computed2.withContexts([ctx1, 'override1'], [ctx2, 'override2']))
305
+ .toHaveSignalValue('override2override1:inner-override2')
306
+ .toMatchSnapshot();
307
+ expect(computed1).toMatchSnapshot();
308
+
309
+ expect(computed2.withContexts([ctx2, 'override2']))
310
+ .toHaveSignalValue('override2default1:inner-override2')
311
+ .toMatchSnapshot();
312
+ expect(computed1).toMatchSnapshot();
313
+ });
314
+ });
315
+
316
+ // permute(3, (create1, create2, create3) => {
317
+ // test.skip('the gauntlet (params + state + context)', async () => {
318
+ // const ctx = createContext('ctxdefault');
319
+ // const value = state('value');
320
+
321
+ // const inner1 = create1((a: number) => {
322
+ // if (a === 3) {
323
+ // return ['inner1', useContext(ctx)];
324
+ // } else if (a === 4) {
325
+ // return ['inner1', value.get()];
326
+ // }
327
+
328
+ // return ['inner1'];
329
+ // });
330
+
331
+ // const inner2 = create2((a: number) => {
332
+ // if (a === 3) {
333
+ // return value.get() === 'value' ? ['inner2'] : ['inner2', useContext(ctx)];
334
+ // } else if (a === 4) {
335
+ // return withContexts([[ctx, 'ctxinneroverride']], () => {
336
+ // return ['inner2', inner1(3), value.get()];
337
+ // });
338
+ // }
339
+
340
+ // return ['inner2', inner1(a)];
341
+ // });
342
+
343
+ // const outer = create3((a: number) => {
344
+ // if (a === 1) {
345
+ // return [inner1(1), inner2(2)];
346
+ // } else if (a === 2) {
347
+ // return [inner1(2), inner2(3)];
348
+ // } else if (a === 3) {
349
+ // return [inner1(3), inner2(4)];
350
+ // } else if (a === 4) {
351
+ // return [useContext(ctx), inner2(4)];
352
+ // } else if (a === 5) {
353
+ // return [inner1(5), value.get()];
354
+ // }
355
+ // });
356
+
357
+ // // a === 1
358
+ // expect(outer)
359
+ // .withParams(1)
360
+ // .toHaveValueAndCounts([['inner1'], ['inner2', ['inner1']]], { compute: 1 });
361
+ // expect(inner1).toHaveCounts({ compute: 2 });
362
+ // expect(inner2).toHaveCounts({ compute: 1 });
363
+
364
+ // expect(outer)
365
+ // .withContexts([ctx, 'ctxoverride'])
366
+ // .withParams(1)
367
+ // .toHaveValueAndCounts([['inner1'], ['inner2', ['inner1']]], { compute: 1 });
368
+ // expect(inner1).toHaveCounts({ compute: 2 });
369
+ // expect(inner2).toHaveCounts({ compute: 1 });
370
+
371
+ // // a === 2
372
+ // expect(outer)
373
+ // .withParams(2)
374
+ // .toHaveValueAndCounts([['inner1'], ['inner2']], {
375
+ // compute: 2,
376
+ // });
377
+ // expect(inner1).toHaveCounts({ compute: 2 });
378
+ // expect(inner2).toHaveCounts({ compute: 2 });
379
+
380
+ // expect(outer)
381
+ // .withParams(2)
382
+ // .withContexts([ctx, 'ctxoverride'])
383
+ // .toHaveValueAndCounts([['inner1'], ['inner2']], {
384
+ // compute: 2,
385
+ // });
386
+ // expect(inner1).toHaveCounts({ compute: 2 });
387
+ // expect(inner2).toHaveCounts({ compute: 2 });
388
+
389
+ // // a === 3
390
+ // expect(outer)
391
+ // .withParams(3)
392
+ // .toHaveValueAndCounts(
393
+ // [
394
+ // ['inner1', 'ctxdefault'],
395
+ // ['inner2', ['inner1', 'ctxinneroverride'], 'value'],
396
+ // ],
397
+ // {
398
+ // compute: 3,
399
+ // },
400
+ // );
401
+ // expect(inner1).toHaveCounts({ compute: 4 });
402
+ // expect(inner2).toHaveCounts({ compute: 3 });
403
+
404
+ // expect(outer)
405
+ // .withParams(3)
406
+ // .withContexts([ctx, 'ctxoverride'])
407
+ // .toHaveValueAndCounts(
408
+ // [
409
+ // ['inner1', 'ctxoverride'],
410
+ // ['inner2', ['inner1', 'ctxinneroverride'], 'value'],
411
+ // ],
412
+ // {
413
+ // compute: 4,
414
+ // },
415
+ // );
416
+ // expect(inner1).toHaveCounts({ compute: 6 });
417
+ // expect(inner2).toHaveCounts({ compute: 4 });
418
+
419
+ // // a === 4
420
+ // expect(outer)
421
+ // .withParams(4)
422
+ // .toHaveValueAndCounts(['ctxdefault', ['inner2', ['inner1', 'ctxinneroverride'], 'value']], {
423
+ // compute: 5,
424
+ // });
425
+ // expect(inner1).toHaveCounts({ compute: 6 });
426
+ // expect(inner2).toHaveCounts({ compute: 4 });
427
+
428
+ // expect(outer)
429
+ // .withParams(4)
430
+ // .withContexts([ctx, 'ctxoverride'])
431
+ // .toHaveValueAndCounts(['ctxoverride', ['inner2', ['inner1', 'ctxinneroverride'], 'value']], {
432
+ // compute: 6,
433
+ // });
434
+ // expect(inner1).toHaveCounts({ compute: 6 });
435
+ // expect(inner2).toHaveCounts({ compute: 4 });
436
+
437
+ // // a === 5
438
+ // expect(outer)
439
+ // .withParams(5)
440
+ // .toHaveValueAndCounts([['inner1'], 'value'], {
441
+ // compute: 7,
442
+ // });
443
+ // expect(inner1).toHaveCounts({ compute: 7 });
444
+ // expect(inner2).toHaveCounts({ compute: 4 });
445
+
446
+ // expect(outer)
447
+ // .withParams(5)
448
+ // .withContexts([ctx, 'ctxoverride'])
449
+ // .toHaveValueAndCounts([['inner1'], 'value'], {
450
+ // compute: 8,
451
+ // });
452
+ // expect(inner1).toHaveCounts({ compute: 8 });
453
+ // expect(inner2).toHaveCounts({ compute: 4 });
454
+
455
+ // value.set('value2');
456
+ // await nextTick();
457
+
458
+ // // a === 1
459
+ // expect(outer)
460
+ // .withParams(1)
461
+ // .toHaveSignalValue([['inner1'], ['inner2', ['inner1']]]);
462
+
463
+ // expect(outer)
464
+ // .withContexts([ctx, 'ctxoverride'])
465
+ // .withParams(1)
466
+ // .toHaveSignalValue([['inner1'], ['inner2', ['inner1']]]);
467
+
468
+ // // a === 2
469
+ // expect(outer)
470
+ // .withParams(2)
471
+ // .toHaveSignalValue([['inner1'], ['inner2', 'ctxdefault']]);
472
+
473
+ // expect(outer)
474
+ // .withParams(2)
475
+ // .withContexts([ctx, 'ctxoverride'])
476
+ // .toHaveSignalValue([['inner1'], ['inner2', 'ctxoverride']]);
477
+
478
+ // // a === 3
479
+ // expect(outer)
480
+ // .withParams(3)
481
+ // .toHaveSignalValue([
482
+ // ['inner1', 'ctxdefault'],
483
+ // ['inner2', ['inner1', 'ctxinneroverride'], 'value2'],
484
+ // ]);
485
+
486
+ // expect(outer)
487
+ // .withParams(3)
488
+ // .withContexts([ctx, 'ctxoverride'])
489
+ // .toHaveSignalValue([
490
+ // ['inner1', 'ctxoverride'],
491
+ // ['inner2', ['inner1', 'ctxinneroverride'], 'value2'],
492
+ // ]);
493
+
494
+ // // a === 4
495
+ // expect(outer)
496
+ // .withParams(4)
497
+ // .toHaveSignalValue(['ctxdefault', ['inner2', ['inner1', 'ctxinneroverride'], 'value2']]);
498
+
499
+ // expect(outer)
500
+ // .withParams(4)
501
+ // .withContexts([ctx, 'ctxoverride'])
502
+ // .toHaveSignalValue(['ctxoverride', ['inner2', ['inner1', 'ctxinneroverride'], 'value2']]);
503
+
504
+ // // a === 5
505
+ // expect(outer)
506
+ // .withParams(5)
507
+ // .toHaveSignalValue([['inner1'], 'value2']);
508
+ // expect(outer)
509
+ // .withParams(5)
510
+ // .withContexts([ctx, 'ctxoverride'])
511
+ // .toHaveSignalValue([['inner1'], 'value2']);
512
+
513
+ // expect(inner1).toHaveCounts({ compute: 10 });
514
+ // expect(inner2).toHaveCounts({ compute: 9 });
515
+ // });
516
+ // });
517
+ });