@ersbeth/picoflow 0.2.3 → 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 (248) hide show
  1. package/.cursor/plans/update-js-e795d61b.plan.md +567 -0
  2. package/.gitlab-ci.yml +24 -0
  3. package/.vscode/settings.json +3 -3
  4. package/CHANGELOG.md +51 -0
  5. package/IMPLEMENTATION_GUIDE.md +1578 -0
  6. package/README.md +62 -25
  7. package/biome.json +32 -32
  8. package/dist/picoflow.js +557 -1099
  9. package/dist/types/advanced/array.d.ts +0 -6
  10. package/dist/types/advanced/array.d.ts.map +1 -1
  11. package/dist/types/advanced/index.d.ts +5 -5
  12. package/dist/types/advanced/index.d.ts.map +1 -1
  13. package/dist/types/advanced/map.d.ts +114 -23
  14. package/dist/types/advanced/map.d.ts.map +1 -1
  15. package/dist/types/advanced/resource.d.ts +51 -12
  16. package/dist/types/advanced/resource.d.ts.map +1 -1
  17. package/dist/types/advanced/resourceAsync.d.ts +28 -13
  18. package/dist/types/advanced/resourceAsync.d.ts.map +1 -1
  19. package/dist/types/advanced/stream.d.ts +74 -16
  20. package/dist/types/advanced/stream.d.ts.map +1 -1
  21. package/dist/types/advanced/streamAsync.d.ts +69 -15
  22. package/dist/types/advanced/streamAsync.d.ts.map +1 -1
  23. package/dist/types/basic/constant.d.ts +44 -16
  24. package/dist/types/basic/constant.d.ts.map +1 -1
  25. package/dist/types/basic/derivation.d.ts +73 -24
  26. package/dist/types/basic/derivation.d.ts.map +1 -1
  27. package/dist/types/basic/disposable.d.ts +65 -6
  28. package/dist/types/basic/disposable.d.ts.map +1 -1
  29. package/dist/types/basic/effect.d.ts +27 -16
  30. package/dist/types/basic/effect.d.ts.map +1 -1
  31. package/dist/types/basic/index.d.ts +7 -8
  32. package/dist/types/basic/index.d.ts.map +1 -1
  33. package/dist/types/basic/observable.d.ts +62 -13
  34. package/dist/types/basic/observable.d.ts.map +1 -1
  35. package/dist/types/basic/signal.d.ts +35 -6
  36. package/dist/types/basic/signal.d.ts.map +1 -1
  37. package/dist/types/basic/state.d.ts +25 -4
  38. package/dist/types/basic/state.d.ts.map +1 -1
  39. package/dist/types/basic/trackingContext.d.ts +33 -0
  40. package/dist/types/basic/trackingContext.d.ts.map +1 -0
  41. package/dist/types/creators.d.ts +271 -26
  42. package/dist/types/creators.d.ts.map +1 -1
  43. package/dist/types/index.d.ts +60 -7
  44. package/dist/types/index.d.ts.map +1 -1
  45. package/dist/types/solid/converters.d.ts +5 -5
  46. package/dist/types/solid/converters.d.ts.map +1 -1
  47. package/dist/types/solid/index.d.ts +2 -2
  48. package/dist/types/solid/index.d.ts.map +1 -1
  49. package/dist/types/solid/primitives.d.ts +96 -4
  50. package/dist/types/solid/primitives.d.ts.map +1 -1
  51. package/docs/.vitepress/config.mts +110 -0
  52. package/docs/api/classes/FlowArray.md +489 -0
  53. package/docs/api/classes/FlowConstant.md +350 -0
  54. package/docs/api/classes/FlowDerivation.md +334 -0
  55. package/docs/api/classes/FlowEffect.md +100 -0
  56. package/docs/api/classes/FlowMap.md +512 -0
  57. package/docs/api/classes/FlowObservable.md +306 -0
  58. package/docs/api/classes/FlowResource.md +380 -0
  59. package/docs/api/classes/FlowResourceAsync.md +362 -0
  60. package/docs/api/classes/FlowSignal.md +160 -0
  61. package/docs/api/classes/FlowState.md +368 -0
  62. package/docs/api/classes/FlowStream.md +367 -0
  63. package/docs/api/classes/FlowStreamAsync.md +364 -0
  64. package/docs/api/classes/SolidDerivation.md +75 -0
  65. package/docs/api/classes/SolidResource.md +91 -0
  66. package/docs/api/classes/SolidState.md +71 -0
  67. package/docs/api/classes/TrackingContext.md +33 -0
  68. package/docs/api/functions/array.md +58 -0
  69. package/docs/api/functions/constant.md +45 -0
  70. package/docs/api/functions/derivation.md +53 -0
  71. package/docs/api/functions/effect.md +49 -0
  72. package/docs/api/functions/from.md +220 -0
  73. package/docs/api/functions/isDisposable.md +49 -0
  74. package/docs/api/functions/map.md +57 -0
  75. package/docs/api/functions/resource.md +52 -0
  76. package/docs/api/functions/resourceAsync.md +50 -0
  77. package/docs/api/functions/signal.md +36 -0
  78. package/docs/api/functions/state.md +47 -0
  79. package/docs/api/functions/stream.md +53 -0
  80. package/docs/api/functions/streamAsync.md +50 -0
  81. package/docs/api/index.md +118 -0
  82. package/docs/api/interfaces/FlowDisposable.md +65 -0
  83. package/docs/api/interfaces/SolidObservable.md +19 -0
  84. package/docs/api/type-aliases/FlowArrayAction.md +49 -0
  85. package/docs/api/type-aliases/FlowStreamDisposer.md +15 -0
  86. package/docs/api/type-aliases/FlowStreamSetter.md +27 -0
  87. package/docs/api/type-aliases/FlowStreamUpdater.md +32 -0
  88. package/docs/api/type-aliases/NotPromise.md +18 -0
  89. package/docs/api/type-aliases/SolidGetter.md +17 -0
  90. package/docs/api/typedoc-sidebar.json +1 -0
  91. package/docs/examples/examples.md +2313 -0
  92. package/docs/examples/patterns.md +649 -0
  93. package/docs/guide/advanced/disposal.md +426 -0
  94. package/docs/guide/advanced/solidjs.md +221 -0
  95. package/docs/guide/advanced/upgrading.md +464 -0
  96. package/docs/guide/introduction/concepts.md +56 -0
  97. package/docs/guide/introduction/conventions.md +61 -0
  98. package/docs/guide/introduction/getting-started.md +134 -0
  99. package/docs/guide/introduction/lifecycle.md +371 -0
  100. package/docs/guide/primitives/array.md +400 -0
  101. package/docs/guide/primitives/constant.md +380 -0
  102. package/docs/guide/primitives/derivations.md +348 -0
  103. package/docs/guide/primitives/effects.md +458 -0
  104. package/docs/guide/primitives/map.md +387 -0
  105. package/docs/guide/primitives/overview.md +175 -0
  106. package/docs/guide/primitives/resources.md +858 -0
  107. package/docs/guide/primitives/signal.md +259 -0
  108. package/docs/guide/primitives/state.md +368 -0
  109. package/docs/guide/primitives/streams.md +931 -0
  110. package/docs/index.md +47 -0
  111. package/docs/public/logo.svg +1 -0
  112. package/package.json +57 -41
  113. package/src/advanced/array.ts +208 -210
  114. package/src/advanced/index.ts +7 -7
  115. package/src/advanced/map.ts +178 -68
  116. package/src/advanced/resource.ts +87 -43
  117. package/src/advanced/resourceAsync.ts +62 -42
  118. package/src/advanced/stream.ts +113 -50
  119. package/src/advanced/streamAsync.ts +120 -61
  120. package/src/basic/constant.ts +82 -49
  121. package/src/basic/derivation.ts +128 -84
  122. package/src/basic/disposable.ts +74 -15
  123. package/src/basic/effect.ts +85 -77
  124. package/src/basic/index.ts +7 -8
  125. package/src/basic/observable.ts +94 -36
  126. package/src/basic/signal.ts +133 -105
  127. package/src/basic/state.ts +46 -25
  128. package/src/basic/trackingContext.ts +45 -0
  129. package/src/creators.ts +297 -54
  130. package/src/index.ts +96 -43
  131. package/src/solid/converters.ts +186 -67
  132. package/src/solid/index.ts +8 -2
  133. package/src/solid/primitives.ts +167 -65
  134. package/test/array.test.ts +592 -612
  135. package/test/constant.test.ts +31 -33
  136. package/test/derivation.test.ts +531 -536
  137. package/test/effect.test.ts +21 -21
  138. package/test/map.test.ts +233 -137
  139. package/test/resource.test.ts +119 -121
  140. package/test/resourceAsync.test.ts +98 -100
  141. package/test/signal.test.ts +51 -55
  142. package/test/state.test.ts +186 -168
  143. package/test/stream.test.ts +189 -189
  144. package/test/streamAsync.test.ts +186 -186
  145. package/tsconfig.json +19 -18
  146. package/typedoc.json +37 -0
  147. package/vite.config.ts +23 -20
  148. package/vitest.config.ts +7 -7
  149. package/api/doc/index.md +0 -31
  150. package/api/doc/picoflow.array.md +0 -55
  151. package/api/doc/picoflow.constant.md +0 -55
  152. package/api/doc/picoflow.derivation.md +0 -55
  153. package/api/doc/picoflow.effect.md +0 -55
  154. package/api/doc/picoflow.flowarray._constructor_.md +0 -49
  155. package/api/doc/picoflow.flowarray._lastaction.md +0 -13
  156. package/api/doc/picoflow.flowarray.clear.md +0 -17
  157. package/api/doc/picoflow.flowarray.dispose.md +0 -55
  158. package/api/doc/picoflow.flowarray.get.md +0 -19
  159. package/api/doc/picoflow.flowarray.length.md +0 -13
  160. package/api/doc/picoflow.flowarray.md +0 -273
  161. package/api/doc/picoflow.flowarray.pop.md +0 -17
  162. package/api/doc/picoflow.flowarray.push.md +0 -53
  163. package/api/doc/picoflow.flowarray.set.md +0 -53
  164. package/api/doc/picoflow.flowarray.setitem.md +0 -69
  165. package/api/doc/picoflow.flowarray.shift.md +0 -17
  166. package/api/doc/picoflow.flowarray.splice.md +0 -85
  167. package/api/doc/picoflow.flowarray.unshift.md +0 -53
  168. package/api/doc/picoflow.flowarrayaction.md +0 -37
  169. package/api/doc/picoflow.flowconstant._constructor_.md +0 -49
  170. package/api/doc/picoflow.flowconstant.get.md +0 -25
  171. package/api/doc/picoflow.flowconstant.md +0 -88
  172. package/api/doc/picoflow.flowderivation._constructor_.md +0 -49
  173. package/api/doc/picoflow.flowderivation.get.md +0 -23
  174. package/api/doc/picoflow.flowderivation.md +0 -86
  175. package/api/doc/picoflow.flowdisposable.dispose.md +0 -55
  176. package/api/doc/picoflow.flowdisposable.md +0 -43
  177. package/api/doc/picoflow.floweffect._constructor_.md +0 -54
  178. package/api/doc/picoflow.floweffect.dispose.md +0 -21
  179. package/api/doc/picoflow.floweffect.disposed.md +0 -13
  180. package/api/doc/picoflow.floweffect.md +0 -131
  181. package/api/doc/picoflow.flowgetter.md +0 -15
  182. package/api/doc/picoflow.flowmap._lastdeleted.md +0 -21
  183. package/api/doc/picoflow.flowmap._lastset.md +0 -21
  184. package/api/doc/picoflow.flowmap.delete.md +0 -61
  185. package/api/doc/picoflow.flowmap.md +0 -133
  186. package/api/doc/picoflow.flowmap.setat.md +0 -77
  187. package/api/doc/picoflow.flowobservable.get.md +0 -19
  188. package/api/doc/picoflow.flowobservable.md +0 -68
  189. package/api/doc/picoflow.flowobservable.subscribe.md +0 -55
  190. package/api/doc/picoflow.flowresource._constructor_.md +0 -49
  191. package/api/doc/picoflow.flowresource.fetch.md +0 -27
  192. package/api/doc/picoflow.flowresource.get.md +0 -23
  193. package/api/doc/picoflow.flowresource.md +0 -100
  194. package/api/doc/picoflow.flowresourceasync._constructor_.md +0 -49
  195. package/api/doc/picoflow.flowresourceasync.fetch.md +0 -27
  196. package/api/doc/picoflow.flowresourceasync.get.md +0 -23
  197. package/api/doc/picoflow.flowresourceasync.md +0 -100
  198. package/api/doc/picoflow.flowsignal.dispose.md +0 -59
  199. package/api/doc/picoflow.flowsignal.disposed.md +0 -18
  200. package/api/doc/picoflow.flowsignal.md +0 -112
  201. package/api/doc/picoflow.flowsignal.trigger.md +0 -21
  202. package/api/doc/picoflow.flowstate.md +0 -52
  203. package/api/doc/picoflow.flowstate.set.md +0 -61
  204. package/api/doc/picoflow.flowstream._constructor_.md +0 -49
  205. package/api/doc/picoflow.flowstream.dispose.md +0 -21
  206. package/api/doc/picoflow.flowstream.get.md +0 -23
  207. package/api/doc/picoflow.flowstream.md +0 -100
  208. package/api/doc/picoflow.flowstreamasync._constructor_.md +0 -54
  209. package/api/doc/picoflow.flowstreamasync.dispose.md +0 -21
  210. package/api/doc/picoflow.flowstreamasync.get.md +0 -23
  211. package/api/doc/picoflow.flowstreamasync.md +0 -100
  212. package/api/doc/picoflow.flowstreamdisposer.md +0 -13
  213. package/api/doc/picoflow.flowstreamsetter.md +0 -13
  214. package/api/doc/picoflow.flowstreamupdater.md +0 -19
  215. package/api/doc/picoflow.flowwatcher.md +0 -15
  216. package/api/doc/picoflow.from.md +0 -55
  217. package/api/doc/picoflow.from_1.md +0 -55
  218. package/api/doc/picoflow.from_2.md +0 -55
  219. package/api/doc/picoflow.from_3.md +0 -55
  220. package/api/doc/picoflow.from_4.md +0 -55
  221. package/api/doc/picoflow.from_5.md +0 -55
  222. package/api/doc/picoflow.isdisposable.md +0 -55
  223. package/api/doc/picoflow.map.md +0 -59
  224. package/api/doc/picoflow.md +0 -544
  225. package/api/doc/picoflow.resource.md +0 -55
  226. package/api/doc/picoflow.resourceasync.md +0 -55
  227. package/api/doc/picoflow.signal.md +0 -19
  228. package/api/doc/picoflow.solidderivation._constructor_.md +0 -49
  229. package/api/doc/picoflow.solidderivation.get.md +0 -13
  230. package/api/doc/picoflow.solidderivation.md +0 -94
  231. package/api/doc/picoflow.solidgetter.md +0 -13
  232. package/api/doc/picoflow.solidobservable.get.md +0 -13
  233. package/api/doc/picoflow.solidobservable.md +0 -57
  234. package/api/doc/picoflow.solidresource._constructor_.md +0 -49
  235. package/api/doc/picoflow.solidresource.get.md +0 -13
  236. package/api/doc/picoflow.solidresource.latest.md +0 -13
  237. package/api/doc/picoflow.solidresource.md +0 -157
  238. package/api/doc/picoflow.solidresource.refetch.md +0 -13
  239. package/api/doc/picoflow.solidresource.state.md +0 -13
  240. package/api/doc/picoflow.solidstate._constructor_.md +0 -49
  241. package/api/doc/picoflow.solidstate.get.md +0 -13
  242. package/api/doc/picoflow.solidstate.md +0 -115
  243. package/api/doc/picoflow.solidstate.set.md +0 -13
  244. package/api/doc/picoflow.state.md +0 -55
  245. package/api/doc/picoflow.stream.md +0 -55
  246. package/api/doc/picoflow.streamasync.md +0 -55
  247. package/api/picoflow.public.api.md +0 -244
  248. package/api-extractor.json +0 -61
@@ -2,543 +2,538 @@ import { describe, expect, test, vi } from "vitest";
2
2
  import { derivation, effect, resource, signal, state } from "#package";
3
3
 
4
4
  describe("derivation", () => {
5
- describe("from state", () => {
6
- test("is updated", () => {
7
- const $state = state(1);
8
- const $derivation = derivation((get) => get($state) * 2);
9
- expect($derivation.get()).toBe(2);
10
-
11
- $state.set(2);
12
- expect($derivation.get()).toBe(4);
13
- });
14
-
15
- test("get throws when disposed", () => {
16
- const $state = state(1);
17
- const $derivation = derivation((get) => get($state) * 2);
18
- expect($derivation.get()).toBe(2);
19
-
20
- $state.set(2);
21
- expect($derivation.get()).toBe(4);
22
-
23
- $derivation.dispose();
24
- expect(() => $derivation.get()).toThrow(
25
- "[PicoFlow] Primitive is disposed",
26
- );
27
- });
28
-
29
- test("get throws when state is disposed", () => {
30
- const $state = state(1);
31
- const $derivation = derivation((get) => get($state) * 2);
32
- expect($derivation.get()).toBe(2);
33
-
34
- $state.set(2);
35
- expect($derivation.get()).toBe(4);
36
-
37
- $state.dispose();
38
- expect(() => $derivation.get()).toThrow(
39
- "[PicoFlow] Primitive is disposed",
40
- );
41
- });
42
-
43
- test("is updated (chained dependencies)", () => {
44
- const $state = state(1);
45
- const $derivation1 = derivation((get) => get($state) * 2);
46
- const $derivation2 = derivation((get) => get($derivation1) * 2);
47
-
48
- expect($derivation2.get()).toBe(4);
49
-
50
- $state.set(2);
51
- expect($derivation2.get()).toBe(8);
52
- });
53
-
54
- test("is updated (multiple dependencies)", () => {
55
- const $state1 = state(1);
56
- const $state2 = state(2);
57
- const $derivation = derivation(
58
- (get) => get($state1) + get($state2),
59
- );
60
-
61
- expect($derivation.get()).toBe(3);
62
-
63
- $state1.set(2);
64
- expect($derivation.get()).toBe(4);
65
-
66
- $state2.set(3);
67
- expect($derivation.get()).toBe(5);
68
- });
69
-
70
- test("is updated (multiple dependants)", () => {
71
- const $state = state(1);
72
- const $derivation1 = derivation((get) => get($state) * 2);
73
- const $derivation2 = derivation((get) => get($state) * 3);
74
-
75
- expect($derivation1.get()).toBe(2);
76
- expect($derivation2.get()).toBe(3);
77
-
78
- $state.set(2);
79
- expect($derivation1.get()).toBe(4);
80
- expect($derivation2.get()).toBe(6);
81
-
82
- $state.set(3);
83
- expect($derivation1.get()).toBe(6);
84
- expect($derivation2.get()).toBe(9);
85
- });
86
-
87
- test("is updated (async)", async () => {
88
- const $state = state(1);
89
- const $derivation = derivation(async (get) => get($state) * 2);
90
-
91
- expect(await $derivation.get()).toBe(2);
92
-
93
- $state.set(2);
94
- expect(await $derivation.get()).toBe(4);
95
- });
96
-
97
- test("is updated (async chain)", async () => {
98
- const $state = state(1);
99
- const $derivation1 = derivation(async (get) => get($state) * 2);
100
- const $derivation2 = derivation(
101
- async (get) => (await get($derivation1)) * 2,
102
- );
103
-
104
- expect(await $derivation2.get()).toBe(4);
105
-
106
- $state.set(2);
107
- expect(await $derivation2.get()).toBe(8);
108
- });
109
- });
110
-
111
- describe("from resource", () => {
112
- test("from resource is updated", async () => {
113
- let resourceCounter = 0;
114
- const fetchResource = async () => {
115
- resourceCounter++;
116
- return resourceCounter;
117
- };
118
- const $resource = resource(fetchResource);
119
- const $derivation = derivation((get) => (get($resource) ?? 0) * 2);
120
-
121
- expect($derivation.get()).toBe(0);
122
-
123
- await $resource.fetch();
124
- expect($derivation.get()).toBe(2);
125
-
126
- await $resource.fetch();
127
- expect($derivation.get()).toBe(4);
128
-
129
- await $resource.fetch();
130
- expect($derivation.get()).toBe(6);
131
- });
132
- });
5
+ describe("from state", () => {
6
+ test("is updated", () => {
7
+ const $state = state(1);
8
+ const $derivation = derivation((t) => $state.get(t) * 2);
9
+ expect($derivation.pick()).toBe(2);
10
+
11
+ $state.set(2);
12
+ expect($derivation.pick()).toBe(4);
13
+ });
14
+
15
+ test("get throws when disposed", () => {
16
+ const $state = state(1);
17
+ const $derivation = derivation((t) => $state.get(t) * 2);
18
+ expect($derivation.pick()).toBe(2);
19
+
20
+ $state.set(2);
21
+ expect($derivation.pick()).toBe(4);
22
+
23
+ $derivation.dispose();
24
+ expect(() => $derivation.pick()).toThrow(
25
+ "[PicoFlow] Primitive is disposed",
26
+ );
27
+ });
28
+
29
+ test("get throws when state is disposed", () => {
30
+ const $state = state(1);
31
+ const $derivation = derivation((t) => $state.get(t) * 2);
32
+ expect($derivation.pick()).toBe(2);
33
+
34
+ $state.set(2);
35
+ expect($derivation.pick()).toBe(4);
36
+
37
+ $state.dispose();
38
+ expect(() => $derivation.pick()).toThrow(
39
+ "[PicoFlow] Primitive is disposed",
40
+ );
41
+ });
42
+
43
+ test("is updated (chained dependencies)", () => {
44
+ const $state = state(1);
45
+ const $derivation1 = derivation((t) => $state.get(t) * 2);
46
+ const $derivation2 = derivation((t) => $derivation1.get(t) * 2);
47
+
48
+ expect($derivation2.pick()).toBe(4);
49
+
50
+ $state.set(2);
51
+ expect($derivation2.pick()).toBe(8);
52
+ });
53
+
54
+ test("is updated (multiple dependencies)", () => {
55
+ const $state1 = state(1);
56
+ const $state2 = state(2);
57
+ const $derivation = derivation((t) => $state1.get(t) + $state2.get(t));
58
+
59
+ expect($derivation.pick()).toBe(3);
60
+
61
+ $state1.set(2);
62
+ expect($derivation.pick()).toBe(4);
63
+
64
+ $state2.set(3);
65
+ expect($derivation.pick()).toBe(5);
66
+ });
67
+
68
+ test("is updated (multiple dependants)", () => {
69
+ const $state = state(1);
70
+ const $derivation1 = derivation((t) => $state.get(t) * 2);
71
+ const $derivation2 = derivation((t) => $state.get(t) * 3);
72
+
73
+ expect($derivation1.pick()).toBe(2);
74
+ expect($derivation2.pick()).toBe(3);
75
+
76
+ $state.set(2);
77
+ expect($derivation1.pick()).toBe(4);
78
+ expect($derivation2.pick()).toBe(6);
79
+
80
+ $state.set(3);
81
+ expect($derivation1.pick()).toBe(6);
82
+ expect($derivation2.pick()).toBe(9);
83
+ });
84
+
85
+ test("is updated (async)", async () => {
86
+ const $state = state(1);
87
+ const $derivation = derivation(async (t) => $state.get(t) * 2);
88
+
89
+ expect(await $derivation.pick()).toBe(2);
90
+
91
+ $state.set(2);
92
+ expect(await $derivation.pick()).toBe(4);
93
+ });
94
+
95
+ test("is updated (async chain)", async () => {
96
+ const $state = state(1);
97
+ const $derivation1 = derivation(async (t) => $state.get(t) * 2);
98
+ const $derivation2 = derivation(
99
+ async (t) => (await $derivation1.get(t)) * 2,
100
+ );
101
+
102
+ expect(await $derivation2.pick()).toBe(4);
103
+
104
+ $state.set(2);
105
+ expect(await $derivation2.pick()).toBe(8);
106
+ });
107
+ });
108
+
109
+ describe("from resource", () => {
110
+ test("from resource is updated", async () => {
111
+ let resourceCounter = 0;
112
+ const fetchResource = async () => {
113
+ resourceCounter++;
114
+ return resourceCounter;
115
+ };
116
+ const $resource = resource(fetchResource);
117
+ const $derivation = derivation((t) => ($resource.get(t) ?? 0) * 2);
118
+
119
+ expect($derivation.pick()).toBe(0);
120
+
121
+ await $resource.fetch();
122
+ expect($derivation.pick()).toBe(2);
123
+
124
+ await $resource.fetch();
125
+ expect($derivation.pick()).toBe(4);
126
+
127
+ await $resource.fetch();
128
+ expect($derivation.pick()).toBe(6);
129
+ });
130
+ });
133
131
  });
134
132
 
135
133
  describe("effect", () => {
136
- describe("from signal", () => {
137
- test("called when triggered", () => {
138
- const $signal = signal();
139
- const $derivation = derivation((_, watch) => {
140
- watch($signal);
141
- });
142
- const effectFn = vi.fn();
143
- effect((_, watch) => {
144
- watch($derivation);
145
- effectFn();
146
- });
147
-
148
- expect(effectFn).toHaveBeenCalledTimes(1);
149
-
150
- $signal.trigger();
151
- expect(effectFn).toHaveBeenCalledTimes(2);
152
-
153
- $signal.trigger();
154
- expect(effectFn).toHaveBeenCalledTimes(3);
155
-
156
- $signal.trigger();
157
- expect(effectFn).toHaveBeenCalledTimes(4);
158
- });
159
-
160
- test("called when triggered (chained dependencies)", () => {
161
- const $signal = signal();
162
- const $derivation1 = derivation((_, watch) => watch($signal));
163
- const $derivation2 = derivation((_, watch) => watch($derivation1));
164
- const effectFn = vi.fn();
165
- effect((_, watch) => {
166
- watch($derivation2);
167
- effectFn();
168
- });
169
-
170
- expect(effectFn).toHaveBeenCalledTimes(1);
171
-
172
- $signal.trigger();
173
- expect(effectFn).toHaveBeenCalledTimes(2);
174
- });
175
-
176
- test("called when triggered (multiple dependencies)", () => {
177
- const $signal1 = signal();
178
- const $signal2 = signal();
179
- const $derivation = derivation((_, watch) => {
180
- watch($signal1);
181
- watch($signal2);
182
- });
183
- const effectFn = vi.fn();
184
- effect((_, watch) => {
185
- watch($derivation);
186
- effectFn();
187
- });
188
-
189
- expect(effectFn).toHaveBeenCalledTimes(1);
190
-
191
- $signal1.trigger();
192
- expect(effectFn).toHaveBeenCalledTimes(2);
193
-
194
- $signal2.trigger();
195
- expect(effectFn).toHaveBeenCalledTimes(3);
196
- });
197
-
198
- test("called when is updated (multiple dependants)", () => {
199
- const $signal = signal();
200
- const $derivation1 = derivation((_, watch) => watch($signal));
201
- const $derivation2 = derivation((_, watch) => watch($signal));
202
- const effect1Fn = vi.fn();
203
- const effect2Fn = vi.fn();
204
-
205
- effect((get) => effect1Fn(get($derivation1)));
206
- effect((get) => effect2Fn(get($derivation2)));
207
-
208
- expect(effect1Fn).toHaveBeenCalledTimes(1);
209
- expect(effect2Fn).toHaveBeenCalledTimes(1);
210
-
211
- $signal.trigger();
212
- expect(effect1Fn).toHaveBeenCalledTimes(2);
213
- expect(effect2Fn).toHaveBeenCalledTimes(2);
214
-
215
- $signal.trigger();
216
- expect(effect1Fn).toHaveBeenCalledTimes(3);
217
- expect(effect2Fn).toHaveBeenCalledTimes(3);
218
- });
219
-
220
- test("called when triggered (with state)", () => {
221
- const $signal = signal();
222
- const $state = state(1);
223
- const $derivation = derivation((get, watch) => {
224
- watch($signal);
225
- return get($state) * 2;
226
- });
227
- const effectFn = vi.fn();
228
- effect((get) => effectFn(get($derivation)));
229
-
230
- expect(effectFn).toHaveBeenCalledTimes(1);
231
- expect(effectFn).toHaveBeenLastCalledWith(2);
232
-
233
- $signal.trigger();
234
- expect(effectFn).toHaveBeenCalledTimes(2);
235
- expect(effectFn).toHaveBeenLastCalledWith(2);
236
-
237
- $signal.trigger();
238
- expect(effectFn).toHaveBeenCalledTimes(3);
239
- expect(effectFn).toHaveBeenLastCalledWith(2);
240
-
241
- $state.set(2);
242
- expect(effectFn).toHaveBeenCalledTimes(4);
243
- expect(effectFn).toHaveBeenLastCalledWith(4);
244
-
245
- $signal.trigger();
246
- expect(effectFn).toHaveBeenCalledTimes(5);
247
- expect(effectFn).toHaveBeenLastCalledWith(4);
248
- });
249
- });
250
-
251
- describe("from state", () => {
252
- test("called when is updated", () => {
253
- const $state = state(1);
254
- const $derivation = derivation((get) => get($state) * 2);
255
- const effectFn = vi.fn();
256
- effect((get) => effectFn(get($derivation)));
257
-
258
- expect(effectFn).toHaveBeenCalledTimes(1);
259
- expect(effectFn).toHaveBeenLastCalledWith(2);
260
-
261
- $state.set(2);
262
- expect(effectFn).toHaveBeenCalledTimes(2);
263
- expect(effectFn).toHaveBeenLastCalledWith(4);
264
- });
265
-
266
- test("called when is updated (chained dependencies)", () => {
267
- const $state = state(1);
268
- const $derivation1 = derivation((get) => get($state) * 2);
269
- const $derivation2 = derivation((get) => get($derivation1) * 2);
270
- const effectFn = vi.fn();
271
- effect((get) => effectFn(get($derivation2)));
272
-
273
- expect(effectFn).toHaveBeenCalledTimes(1);
274
- expect(effectFn).toHaveBeenLastCalledWith(4);
275
-
276
- $state.set(2);
277
- expect(effectFn).toHaveBeenCalledTimes(2);
278
- expect(effectFn).toHaveBeenLastCalledWith(8);
279
- });
280
-
281
- test("called when is updated (multiple dependencies)", () => {
282
- const $state1 = state(1);
283
- const $state2 = state(2);
284
- const $derivation = derivation(
285
- (get) => get($state1) + get($state2),
286
- );
287
- const effectFn = vi.fn();
288
- effect((get) => effectFn(get($derivation)));
289
-
290
- expect(effectFn).toHaveBeenCalledTimes(1);
291
- expect(effectFn).toHaveBeenLastCalledWith(3);
292
-
293
- $state1.set(2);
294
- expect(effectFn).toHaveBeenCalledTimes(2);
295
- expect(effectFn).toHaveBeenLastCalledWith(4);
296
-
297
- $state2.set(3);
298
- expect(effectFn).toHaveBeenCalledTimes(3);
299
- expect(effectFn).toHaveBeenLastCalledWith(5);
300
- });
301
-
302
- test("called when is updated (multiple dependants)", () => {
303
- const $state = state(1);
304
- const $derivation1 = derivation((get) => get($state) * 2);
305
- const $derivation2 = derivation((get) => get($state) * 3);
306
- const effect1Fn = vi.fn();
307
- const effect2Fn = vi.fn();
308
-
309
- effect((get) => effect1Fn(get($derivation1)));
310
- effect((get) => effect2Fn(get($derivation2)));
311
-
312
- expect(effect1Fn).toHaveBeenCalledTimes(1);
313
- expect(effect1Fn).toHaveBeenLastCalledWith(2);
314
- expect(effect2Fn).toHaveBeenCalledTimes(1);
315
- expect(effect2Fn).toHaveBeenLastCalledWith(3);
316
-
317
- $state.set(2);
318
- expect(effect1Fn).toHaveBeenCalledTimes(2);
319
- expect(effect1Fn).toHaveBeenLastCalledWith(4);
320
- expect(effect2Fn).toHaveBeenCalledTimes(2);
321
- expect(effect2Fn).toHaveBeenLastCalledWith(6);
322
-
323
- $state.set(3);
324
- expect(effect1Fn).toHaveBeenCalledTimes(3);
325
- expect(effect1Fn).toHaveBeenLastCalledWith(6);
326
- expect(effect2Fn).toHaveBeenCalledTimes(3);
327
- expect(effect2Fn).toHaveBeenLastCalledWith(9);
328
- });
329
-
330
- test("called when is updated (async)", async () => {
331
- const $state = state(1);
332
- const $derivation = derivation(async (get) => get($state) * 2);
333
- const effectFn = vi.fn();
334
- effect((get) => effectFn(get($derivation)));
335
-
336
- expect(effectFn).toHaveBeenCalledTimes(1);
337
- expect(await effectFn.mock.calls[0][0]).toEqual(2);
338
-
339
- $state.set(2);
340
- expect(effectFn).toHaveBeenCalledTimes(2);
341
- expect(await effectFn.mock.calls[1][0]).toEqual(4);
342
- });
343
-
344
- test("called when is updated (async chain)", async () => {
345
- const $state = state(1);
346
- const $derivation1 = derivation(async (get) => get($state) * 2);
347
- const $derivation2 = derivation(
348
- async (get) => (await get($derivation1)) * 2,
349
- );
350
- const effectFn = vi.fn();
351
- effect((get) => effectFn(get($derivation2)));
352
-
353
- expect(effectFn).toHaveBeenCalledTimes(1);
354
- expect(await effectFn.mock.calls[0][0]).toEqual(4);
355
-
356
- $state.set(2);
357
- expect(effectFn).toHaveBeenCalledTimes(2);
358
- expect(await effectFn.mock.calls[1][0]).toEqual(8);
359
- });
360
-
361
- test("NOT called when derivation is disposed", () => {
362
- const $state = state(1);
363
- const $derivation = derivation((get) => get($state) * 2);
364
- const effectFn = vi.fn();
365
- effect((get) => effectFn(get($derivation)));
366
-
367
- expect(effectFn).toHaveBeenCalledTimes(1);
368
- expect(effectFn).toHaveBeenLastCalledWith(2);
369
-
370
- $state.set(2);
371
- expect(effectFn).toHaveBeenCalledTimes(2);
372
- expect(effectFn).toHaveBeenLastCalledWith(4);
373
-
374
- $derivation.dispose();
375
-
376
- $state.set(3);
377
- expect(effectFn).toHaveBeenCalledTimes(2);
378
- expect(effectFn).toHaveBeenLastCalledWith(4);
379
- });
380
-
381
- test("NOT called when derivation is disposed (watch)", () => {
382
- const $state = state(1);
383
- const $derivation = derivation((get) => get($state) * 2);
384
- const effectFn = vi.fn();
385
- effect((_, watch) => {
386
- watch($derivation);
387
- effectFn();
388
- });
389
-
390
- expect(effectFn).toHaveBeenCalledTimes(1);
391
-
392
- $state.set(2);
393
- expect(effectFn).toHaveBeenCalledTimes(2);
394
-
395
- $derivation.dispose();
396
-
397
- $state.set(3);
398
- expect(effectFn).toHaveBeenCalledTimes(2);
399
- });
400
-
401
- test("NOT called when disposed", () => {
402
- const $state = state(1);
403
- const $derivation = derivation((get) => get($state) * 2);
404
- const effectFn = vi.fn();
405
- const $effect = effect((get) => effectFn(get($derivation)));
406
-
407
- expect(effectFn).toHaveBeenCalledTimes(1);
408
- expect(effectFn).toHaveBeenLastCalledWith(2);
409
-
410
- $state.set(2);
411
- expect(effectFn).toHaveBeenCalledTimes(2);
412
- expect(effectFn).toHaveBeenLastCalledWith(4);
413
-
414
- $effect.dispose();
415
-
416
- $state.set(3);
417
- expect(effectFn).toHaveBeenCalledTimes(2);
418
- expect(effectFn).toHaveBeenLastCalledWith(4);
419
- });
420
- });
421
-
422
- describe("from resource", () => {
423
- test("called when is updated", async () => {
424
- let resourceCounter = 0;
425
- const fetchResource = async () => {
426
- resourceCounter++;
427
- return resourceCounter;
428
- };
429
- const $resource = resource(fetchResource);
430
- const $derivation = derivation((get) => (get($resource) ?? 0) * 2);
431
- const effectFn = vi.fn();
432
- effect((get) => effectFn(get($derivation)));
433
-
434
- expect(effectFn).toHaveBeenCalledTimes(1);
435
- expect(effectFn).toHaveBeenLastCalledWith(0);
436
-
437
- await $resource.fetch();
438
- expect(effectFn).toHaveBeenCalledTimes(2);
439
- expect(effectFn).toHaveBeenLastCalledWith(2);
440
-
441
- await $resource.fetch();
442
- expect(effectFn).toHaveBeenCalledTimes(3);
443
- expect(effectFn).toHaveBeenLastCalledWith(4);
444
- });
445
- });
446
-
447
- describe("complex test", () => {
448
- test("test", () => {
449
- const obj1 = {
450
- cond: state(false),
451
- num: state(2),
452
- dispose: (options: { self: boolean }) => {
453
- obj1.cond.dispose(options);
454
- obj1.num.dispose(options);
455
- }
456
- }
457
- const obj2 = {
458
- cond: state(false),
459
- num: state(4),
460
- dispose: (options: { self: boolean }) => {
461
- obj2.cond.dispose(options);
462
- obj2.num.dispose(options);
463
- }
464
- }
465
- const $state = state(obj1);
466
- const $derivationCond = derivation((get) => get(get($state).cond));
467
- const $derivationNum = derivation((get) => get(get($state).num) * 2);
468
- const effectCondFn = vi.fn();
469
- const effectNumFn = vi.fn();
470
- effect((get) => effectCondFn(get($derivationCond)));
471
- effect((get) => effectNumFn(get($derivationNum)));
472
-
473
- expect(effectCondFn).toHaveBeenCalledTimes(1);
474
- expect(effectCondFn).toHaveBeenLastCalledWith(false);
475
- expect(effectNumFn).toHaveBeenCalledTimes(1);
476
- expect(effectNumFn).toHaveBeenLastCalledWith(4);
477
-
478
- $state.get().num.set(3);
479
- expect(effectCondFn).toHaveBeenCalledTimes(1);
480
- expect(effectCondFn).toHaveBeenLastCalledWith(false);
481
- expect(effectNumFn).toHaveBeenCalledTimes(2);
482
- expect(effectNumFn).toHaveBeenLastCalledWith(6);
483
-
484
- $state.get().cond.set(true);
485
- expect(effectCondFn).toHaveBeenCalledTimes(2);
486
- expect(effectCondFn).toHaveBeenLastCalledWith(true);
487
- expect(effectNumFn).toHaveBeenCalledTimes(2);
488
- expect(effectNumFn).toHaveBeenLastCalledWith(6);
489
-
490
- $state.set(obj2)
491
- expect(effectCondFn).toHaveBeenCalledTimes(3);
492
- expect(effectCondFn).toHaveBeenLastCalledWith(false);
493
- expect(effectNumFn).toHaveBeenCalledTimes(3);
494
- expect(effectNumFn).toHaveBeenLastCalledWith(8);
495
-
496
- $state.get().cond.set(true);
497
- expect(effectCondFn).toHaveBeenCalledTimes(4); // fails -> called 3 times
498
- expect(effectCondFn).toHaveBeenLastCalledWith(true);
499
- expect(effectNumFn).toHaveBeenCalledTimes(3);
500
- expect(effectNumFn).toHaveBeenLastCalledWith(8);
501
-
502
- $state.get().num.set(5);
503
- expect(effectCondFn).toHaveBeenCalledTimes(4);
504
- expect(effectCondFn).toHaveBeenLastCalledWith(true);
505
- expect(effectNumFn).toHaveBeenCalledTimes(4);
506
- expect(effectNumFn).toHaveBeenLastCalledWith(10);
507
- });
508
-
509
- test("test", () => {
510
- const obj = {
511
- cond: state(false),
512
- b: state(2)
513
- }
514
-
515
- const $state = state(obj);
516
- const $derivation = derivation((get) => {
517
- const cond = get(get($state).cond);
518
- if (cond) {
519
- return get(get($state).b) * 2;
520
- }
521
- return 0;
522
- });
523
-
524
-
525
- const effectFn = vi.fn();
526
- effect((get) => effectFn(get($derivation)));
527
-
528
- expect(effectFn).toHaveBeenCalledTimes(1);
529
- expect(effectFn).toHaveBeenLastCalledWith(0);
530
-
531
- $state.get().cond.set(true);
532
- expect(effectFn).toHaveBeenCalledTimes(2);
533
- expect(effectFn).toHaveBeenLastCalledWith(4);
534
-
535
- $state.set({ cond: state(false), b: state(3) })
536
- expect(effectFn).toHaveBeenCalledTimes(3);
537
- expect(effectFn).toHaveBeenLastCalledWith(0);
538
-
539
- $state.get().cond.set(true);
540
- expect(effectFn).toHaveBeenCalledTimes(4);
541
- expect(effectFn).toHaveBeenLastCalledWith(6);
542
- });
543
- });
134
+ describe("from signal", () => {
135
+ test("called when triggered", () => {
136
+ const $signal = signal();
137
+ const $derivation = derivation((t) => {
138
+ $signal.watch(t);
139
+ });
140
+ const effectFn = vi.fn();
141
+ effect((t) => {
142
+ $derivation.watch(t);
143
+ effectFn();
144
+ });
145
+
146
+ expect(effectFn).toHaveBeenCalledTimes(1);
147
+
148
+ $signal.trigger();
149
+ expect(effectFn).toHaveBeenCalledTimes(2);
150
+
151
+ $signal.trigger();
152
+ expect(effectFn).toHaveBeenCalledTimes(3);
153
+
154
+ $signal.trigger();
155
+ expect(effectFn).toHaveBeenCalledTimes(4);
156
+ });
157
+
158
+ test("called when triggered (chained dependencies)", () => {
159
+ const $signal = signal();
160
+ const $derivation1 = derivation((t) => $signal.watch(t));
161
+ const $derivation2 = derivation((t) => $derivation1.watch(t));
162
+ const effectFn = vi.fn();
163
+ effect((t) => {
164
+ $derivation2.watch(t);
165
+ effectFn();
166
+ });
167
+
168
+ expect(effectFn).toHaveBeenCalledTimes(1);
169
+
170
+ $signal.trigger();
171
+ expect(effectFn).toHaveBeenCalledTimes(2);
172
+ });
173
+
174
+ test("called when triggered (multiple dependencies)", () => {
175
+ const $signal1 = signal();
176
+ const $signal2 = signal();
177
+ const $derivation = derivation((t) => {
178
+ $signal1.watch(t);
179
+ $signal2.watch(t);
180
+ });
181
+ const effectFn = vi.fn();
182
+ effect((t) => {
183
+ $derivation.watch(t);
184
+ effectFn();
185
+ });
186
+
187
+ expect(effectFn).toHaveBeenCalledTimes(1);
188
+
189
+ $signal1.trigger();
190
+ expect(effectFn).toHaveBeenCalledTimes(2);
191
+
192
+ $signal2.trigger();
193
+ expect(effectFn).toHaveBeenCalledTimes(3);
194
+ });
195
+
196
+ test("called when is updated (multiple dependants)", () => {
197
+ const $signal = signal();
198
+ const $derivation1 = derivation((t) => $signal.watch(t));
199
+ const $derivation2 = derivation((t) => $signal.watch(t));
200
+ const effect1Fn = vi.fn();
201
+ const effect2Fn = vi.fn();
202
+
203
+ effect((t) => effect1Fn($derivation1.get(t)));
204
+ effect((t) => effect2Fn($derivation2.get(t)));
205
+
206
+ expect(effect1Fn).toHaveBeenCalledTimes(1);
207
+ expect(effect2Fn).toHaveBeenCalledTimes(1);
208
+
209
+ $signal.trigger();
210
+ expect(effect1Fn).toHaveBeenCalledTimes(2);
211
+ expect(effect2Fn).toHaveBeenCalledTimes(2);
212
+
213
+ $signal.trigger();
214
+ expect(effect1Fn).toHaveBeenCalledTimes(3);
215
+ expect(effect2Fn).toHaveBeenCalledTimes(3);
216
+ });
217
+
218
+ test("called when triggered (with state)", () => {
219
+ const $signal = signal();
220
+ const $state = state(1);
221
+ const $derivation = derivation((t) => {
222
+ $signal.watch(t);
223
+ return $state.get(t) * 2;
224
+ });
225
+ const effectFn = vi.fn();
226
+ effect((t) => effectFn($derivation.get(t)));
227
+
228
+ expect(effectFn).toHaveBeenCalledTimes(1);
229
+ expect(effectFn).toHaveBeenLastCalledWith(2);
230
+
231
+ $signal.trigger();
232
+ expect(effectFn).toHaveBeenCalledTimes(2);
233
+ expect(effectFn).toHaveBeenLastCalledWith(2);
234
+
235
+ $signal.trigger();
236
+ expect(effectFn).toHaveBeenCalledTimes(3);
237
+ expect(effectFn).toHaveBeenLastCalledWith(2);
238
+
239
+ $state.set(2);
240
+ expect(effectFn).toHaveBeenCalledTimes(4);
241
+ expect(effectFn).toHaveBeenLastCalledWith(4);
242
+
243
+ $signal.trigger();
244
+ expect(effectFn).toHaveBeenCalledTimes(5);
245
+ expect(effectFn).toHaveBeenLastCalledWith(4);
246
+ });
247
+ });
248
+
249
+ describe("from state", () => {
250
+ test("called when is updated", () => {
251
+ const $state = state(1);
252
+ const $derivation = derivation((t) => $state.get(t) * 2);
253
+ const effectFn = vi.fn();
254
+ effect((t) => effectFn($derivation.get(t)));
255
+
256
+ expect(effectFn).toHaveBeenCalledTimes(1);
257
+ expect(effectFn).toHaveBeenLastCalledWith(2);
258
+
259
+ $state.set(2);
260
+ expect(effectFn).toHaveBeenCalledTimes(2);
261
+ expect(effectFn).toHaveBeenLastCalledWith(4);
262
+ });
263
+
264
+ test("called when is updated (chained dependencies)", () => {
265
+ const $state = state(1);
266
+ const $derivation1 = derivation((t) => $state.get(t) * 2);
267
+ const $derivation2 = derivation((t) => $derivation1.get(t) * 2);
268
+ const effectFn = vi.fn();
269
+ effect((t) => effectFn($derivation2.get(t)));
270
+
271
+ expect(effectFn).toHaveBeenCalledTimes(1);
272
+ expect(effectFn).toHaveBeenLastCalledWith(4);
273
+
274
+ $state.set(2);
275
+ expect(effectFn).toHaveBeenCalledTimes(2);
276
+ expect(effectFn).toHaveBeenLastCalledWith(8);
277
+ });
278
+
279
+ test("called when is updated (multiple dependencies)", () => {
280
+ const $state1 = state(1);
281
+ const $state2 = state(2);
282
+ const $derivation = derivation((t) => $state1.get(t) + $state2.get(t));
283
+ const effectFn = vi.fn();
284
+ effect((t) => effectFn($derivation.get(t)));
285
+
286
+ expect(effectFn).toHaveBeenCalledTimes(1);
287
+ expect(effectFn).toHaveBeenLastCalledWith(3);
288
+
289
+ $state1.set(2);
290
+ expect(effectFn).toHaveBeenCalledTimes(2);
291
+ expect(effectFn).toHaveBeenLastCalledWith(4);
292
+
293
+ $state2.set(3);
294
+ expect(effectFn).toHaveBeenCalledTimes(3);
295
+ expect(effectFn).toHaveBeenLastCalledWith(5);
296
+ });
297
+
298
+ test("called when is updated (multiple dependants)", () => {
299
+ const $state = state(1);
300
+ const $derivation1 = derivation((t) => $state.get(t) * 2);
301
+ const $derivation2 = derivation((t) => $state.get(t) * 3);
302
+ const effect1Fn = vi.fn();
303
+ const effect2Fn = vi.fn();
304
+
305
+ effect((t) => effect1Fn($derivation1.get(t)));
306
+ effect((t) => effect2Fn($derivation2.get(t)));
307
+
308
+ expect(effect1Fn).toHaveBeenCalledTimes(1);
309
+ expect(effect1Fn).toHaveBeenLastCalledWith(2);
310
+ expect(effect2Fn).toHaveBeenCalledTimes(1);
311
+ expect(effect2Fn).toHaveBeenLastCalledWith(3);
312
+
313
+ $state.set(2);
314
+ expect(effect1Fn).toHaveBeenCalledTimes(2);
315
+ expect(effect1Fn).toHaveBeenLastCalledWith(4);
316
+ expect(effect2Fn).toHaveBeenCalledTimes(2);
317
+ expect(effect2Fn).toHaveBeenLastCalledWith(6);
318
+
319
+ $state.set(3);
320
+ expect(effect1Fn).toHaveBeenCalledTimes(3);
321
+ expect(effect1Fn).toHaveBeenLastCalledWith(6);
322
+ expect(effect2Fn).toHaveBeenCalledTimes(3);
323
+ expect(effect2Fn).toHaveBeenLastCalledWith(9);
324
+ });
325
+
326
+ test("called when is updated (async)", async () => {
327
+ const $state = state(1);
328
+ const $derivation = derivation(async (t) => $state.get(t) * 2);
329
+ const effectFn = vi.fn();
330
+ effect((t) => effectFn($derivation.get(t)));
331
+
332
+ expect(effectFn).toHaveBeenCalledTimes(1);
333
+ expect(await effectFn.mock.calls[0][0]).toEqual(2);
334
+
335
+ $state.set(2);
336
+ expect(effectFn).toHaveBeenCalledTimes(2);
337
+ expect(await effectFn.mock.calls[1][0]).toEqual(4);
338
+ });
339
+
340
+ test("called when is updated (async chain)", async () => {
341
+ const $state = state(1);
342
+ const $derivation1 = derivation(async (t) => $state.get(t) * 2);
343
+ const $derivation2 = derivation(
344
+ async (t) => (await $derivation1.get(t)) * 2,
345
+ );
346
+ const effectFn = vi.fn();
347
+ effect((t) => effectFn($derivation2.get(t)));
348
+
349
+ expect(effectFn).toHaveBeenCalledTimes(1);
350
+ expect(await effectFn.mock.calls[0][0]).toEqual(4);
351
+
352
+ $state.set(2);
353
+ expect(effectFn).toHaveBeenCalledTimes(2);
354
+ expect(await effectFn.mock.calls[1][0]).toEqual(8);
355
+ });
356
+
357
+ test("NOT called when derivation is disposed", () => {
358
+ const $state = state(1);
359
+ const $derivation = derivation((t) => $state.get(t) * 2);
360
+ const effectFn = vi.fn();
361
+ effect((t) => effectFn($derivation.get(t)));
362
+
363
+ expect(effectFn).toHaveBeenCalledTimes(1);
364
+ expect(effectFn).toHaveBeenLastCalledWith(2);
365
+
366
+ $state.set(2);
367
+ expect(effectFn).toHaveBeenCalledTimes(2);
368
+ expect(effectFn).toHaveBeenLastCalledWith(4);
369
+
370
+ $derivation.dispose();
371
+
372
+ $state.set(3);
373
+ expect(effectFn).toHaveBeenCalledTimes(2);
374
+ expect(effectFn).toHaveBeenLastCalledWith(4);
375
+ });
376
+
377
+ test("NOT called when derivation is disposed (watch)", () => {
378
+ const $state = state(1);
379
+ const $derivation = derivation((t) => $state.get(t) * 2);
380
+ const effectFn = vi.fn();
381
+ effect((t) => {
382
+ $derivation.watch(t);
383
+ effectFn();
384
+ });
385
+
386
+ expect(effectFn).toHaveBeenCalledTimes(1);
387
+
388
+ $state.set(2);
389
+ expect(effectFn).toHaveBeenCalledTimes(2);
390
+
391
+ $derivation.dispose();
392
+
393
+ $state.set(3);
394
+ expect(effectFn).toHaveBeenCalledTimes(2);
395
+ });
396
+
397
+ test("NOT called when disposed", () => {
398
+ const $state = state(1);
399
+ const $derivation = derivation((t) => $state.get(t) * 2);
400
+ const effectFn = vi.fn();
401
+ const $effect = effect((t) => effectFn($derivation.get(t)));
402
+
403
+ expect(effectFn).toHaveBeenCalledTimes(1);
404
+ expect(effectFn).toHaveBeenLastCalledWith(2);
405
+
406
+ $state.set(2);
407
+ expect(effectFn).toHaveBeenCalledTimes(2);
408
+ expect(effectFn).toHaveBeenLastCalledWith(4);
409
+
410
+ $effect.dispose();
411
+
412
+ $state.set(3);
413
+ expect(effectFn).toHaveBeenCalledTimes(2);
414
+ expect(effectFn).toHaveBeenLastCalledWith(4);
415
+ });
416
+ });
417
+
418
+ describe("from resource", () => {
419
+ test("called when is updated", async () => {
420
+ let resourceCounter = 0;
421
+ const fetchResource = async () => {
422
+ resourceCounter++;
423
+ return resourceCounter;
424
+ };
425
+ const $resource = resource(fetchResource);
426
+ const $derivation = derivation((t) => ($resource.get(t) ?? 0) * 2);
427
+ const effectFn = vi.fn();
428
+ effect((t) => effectFn($derivation.get(t)));
429
+
430
+ expect(effectFn).toHaveBeenCalledTimes(1);
431
+ expect(effectFn).toHaveBeenLastCalledWith(0);
432
+
433
+ await $resource.fetch();
434
+ expect(effectFn).toHaveBeenCalledTimes(2);
435
+ expect(effectFn).toHaveBeenLastCalledWith(2);
436
+
437
+ await $resource.fetch();
438
+ expect(effectFn).toHaveBeenCalledTimes(3);
439
+ expect(effectFn).toHaveBeenLastCalledWith(4);
440
+ });
441
+ });
442
+
443
+ describe("complex test", () => {
444
+ test("test", () => {
445
+ const obj1 = {
446
+ cond: state(false),
447
+ num: state(2),
448
+ dispose: (options: { self: boolean }) => {
449
+ obj1.cond.dispose(options);
450
+ obj1.num.dispose(options);
451
+ },
452
+ };
453
+ const obj2 = {
454
+ cond: state(false),
455
+ num: state(4),
456
+ dispose: (options: { self: boolean }) => {
457
+ obj2.cond.dispose(options);
458
+ obj2.num.dispose(options);
459
+ },
460
+ };
461
+ const $state = state(obj1);
462
+ const $derivationCond = derivation((t) => $state.get(t).cond.get(t));
463
+ const $derivationNum = derivation((t) => $state.get(t).num.get(t) * 2);
464
+ const effectCondFn = vi.fn();
465
+ const effectNumFn = vi.fn();
466
+ effect((t) => effectCondFn($derivationCond.get(t)));
467
+ effect((t) => effectNumFn($derivationNum.get(t)));
468
+
469
+ expect(effectCondFn).toHaveBeenCalledTimes(1);
470
+ expect(effectCondFn).toHaveBeenLastCalledWith(false);
471
+ expect(effectNumFn).toHaveBeenCalledTimes(1);
472
+ expect(effectNumFn).toHaveBeenLastCalledWith(4);
473
+
474
+ $state.pick().num.set(3);
475
+ expect(effectCondFn).toHaveBeenCalledTimes(1);
476
+ expect(effectCondFn).toHaveBeenLastCalledWith(false);
477
+ expect(effectNumFn).toHaveBeenCalledTimes(2);
478
+ expect(effectNumFn).toHaveBeenLastCalledWith(6);
479
+
480
+ $state.pick().cond.set(true);
481
+ expect(effectCondFn).toHaveBeenCalledTimes(2);
482
+ expect(effectCondFn).toHaveBeenLastCalledWith(true);
483
+ expect(effectNumFn).toHaveBeenCalledTimes(2);
484
+ expect(effectNumFn).toHaveBeenLastCalledWith(6);
485
+
486
+ $state.set(obj2);
487
+ expect(effectCondFn).toHaveBeenCalledTimes(3);
488
+ expect(effectCondFn).toHaveBeenLastCalledWith(false);
489
+ expect(effectNumFn).toHaveBeenCalledTimes(3);
490
+ expect(effectNumFn).toHaveBeenLastCalledWith(8);
491
+
492
+ $state.pick().cond.set(true);
493
+ expect(effectCondFn).toHaveBeenCalledTimes(4); // fails -> called 3 times
494
+ expect(effectCondFn).toHaveBeenLastCalledWith(true);
495
+ expect(effectNumFn).toHaveBeenCalledTimes(3);
496
+ expect(effectNumFn).toHaveBeenLastCalledWith(8);
497
+
498
+ $state.pick().num.set(5);
499
+ expect(effectCondFn).toHaveBeenCalledTimes(4);
500
+ expect(effectCondFn).toHaveBeenLastCalledWith(true);
501
+ expect(effectNumFn).toHaveBeenCalledTimes(4);
502
+ expect(effectNumFn).toHaveBeenLastCalledWith(10);
503
+ });
504
+
505
+ test("test", () => {
506
+ const obj = {
507
+ cond: state(false),
508
+ b: state(2),
509
+ };
510
+
511
+ const $state = state(obj);
512
+ const $derivation = derivation((t) => {
513
+ const cond = $state.get(t).cond.get(t);
514
+ if (cond) {
515
+ return $state.get(t).b.get(t) * 2;
516
+ }
517
+ return 0;
518
+ });
519
+
520
+ const effectFn = vi.fn();
521
+ effect((t) => effectFn($derivation.get(t)));
522
+
523
+ expect(effectFn).toHaveBeenCalledTimes(1);
524
+ expect(effectFn).toHaveBeenLastCalledWith(0);
525
+
526
+ $state.pick().cond.set(true);
527
+ expect(effectFn).toHaveBeenCalledTimes(2);
528
+ expect(effectFn).toHaveBeenLastCalledWith(4);
529
+
530
+ $state.set({ cond: state(false), b: state(3) });
531
+ expect(effectFn).toHaveBeenCalledTimes(3);
532
+ expect(effectFn).toHaveBeenLastCalledWith(0);
533
+
534
+ $state.pick().cond.set(true);
535
+ expect(effectFn).toHaveBeenCalledTimes(4);
536
+ expect(effectFn).toHaveBeenLastCalledWith(6);
537
+ });
538
+ });
544
539
  });