@ersbeth/picoflow 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/.vscode/settings.json +5 -0
  2. package/README.md +151 -0
  3. package/api/doc/index.md +31 -0
  4. package/api/doc/picoflow.derivation.md +55 -0
  5. package/api/doc/picoflow.effect.md +55 -0
  6. package/api/doc/picoflow.flowderivation._constructor_.md +49 -0
  7. package/api/doc/picoflow.flowderivation.get.md +23 -0
  8. package/api/doc/picoflow.flowderivation.md +86 -0
  9. package/api/doc/picoflow.flowdisposer.md +13 -0
  10. package/api/doc/picoflow.floweffect._constructor_.md +49 -0
  11. package/api/doc/picoflow.floweffect.dispose.md +21 -0
  12. package/api/doc/picoflow.floweffect.disposed.md +13 -0
  13. package/api/doc/picoflow.floweffect.md +131 -0
  14. package/api/doc/picoflow.flowgetter.md +15 -0
  15. package/api/doc/picoflow.flowmap._lastdeleted.md +21 -0
  16. package/api/doc/picoflow.flowmap._lastset.md +21 -0
  17. package/api/doc/picoflow.flowmap.delete.md +57 -0
  18. package/api/doc/picoflow.flowmap.md +135 -0
  19. package/api/doc/picoflow.flowmap.setat.md +73 -0
  20. package/api/doc/picoflow.flowobservable.get.md +19 -0
  21. package/api/doc/picoflow.flowobservable.md +54 -0
  22. package/api/doc/picoflow.flowresource._constructor_.md +65 -0
  23. package/api/doc/picoflow.flowresource.fetch.md +27 -0
  24. package/api/doc/picoflow.flowresource.get.md +23 -0
  25. package/api/doc/picoflow.flowresource.md +100 -0
  26. package/api/doc/picoflow.flowsetter.md +13 -0
  27. package/api/doc/picoflow.flowsignal.dispose.md +25 -0
  28. package/api/doc/picoflow.flowsignal.disposed.md +18 -0
  29. package/api/doc/picoflow.flowsignal.md +111 -0
  30. package/api/doc/picoflow.flowsignal.trigger.md +25 -0
  31. package/api/doc/picoflow.flowstate._constructor_.md +49 -0
  32. package/api/doc/picoflow.flowstate.get.md +23 -0
  33. package/api/doc/picoflow.flowstate.md +100 -0
  34. package/api/doc/picoflow.flowstate.set.md +61 -0
  35. package/api/doc/picoflow.flowstream._constructor_.md +65 -0
  36. package/api/doc/picoflow.flowstream.dispose.md +21 -0
  37. package/api/doc/picoflow.flowstream.get.md +23 -0
  38. package/api/doc/picoflow.flowstream.md +100 -0
  39. package/api/doc/picoflow.flowupdater.md +19 -0
  40. package/api/doc/picoflow.flowwatcher.md +15 -0
  41. package/api/doc/picoflow.map.md +59 -0
  42. package/api/doc/picoflow.md +287 -0
  43. package/api/doc/picoflow.resource.md +71 -0
  44. package/api/doc/picoflow.signal.md +19 -0
  45. package/api/doc/picoflow.state.md +55 -0
  46. package/api/doc/picoflow.stream.md +71 -0
  47. package/api/picoflow.api.md +145 -0
  48. package/api-extractor.json +60 -0
  49. package/biome.json +34 -0
  50. package/dist/picoflow.js +572 -0
  51. package/dist/types/creators.d.ts +70 -0
  52. package/dist/types/creators.d.ts.map +1 -0
  53. package/dist/types/derivation.d.ts +58 -0
  54. package/dist/types/derivation.d.ts.map +1 -0
  55. package/dist/types/effect.d.ts +108 -0
  56. package/dist/types/effect.d.ts.map +1 -0
  57. package/dist/types/index.d.ts +18 -0
  58. package/dist/types/index.d.ts.map +1 -0
  59. package/dist/types/map.d.ts +75 -0
  60. package/dist/types/map.d.ts.map +1 -0
  61. package/dist/types/observable.d.ts +40 -0
  62. package/dist/types/observable.d.ts.map +1 -0
  63. package/dist/types/resource.d.ts +46 -0
  64. package/dist/types/resource.d.ts.map +1 -0
  65. package/dist/types/signal.d.ts +111 -0
  66. package/dist/types/signal.d.ts.map +1 -0
  67. package/dist/types/state.d.ts +39 -0
  68. package/dist/types/state.d.ts.map +1 -0
  69. package/dist/types/stream.d.ts +71 -0
  70. package/dist/types/stream.d.ts.map +1 -0
  71. package/package.json +40 -0
  72. package/src/creators.ts +101 -0
  73. package/src/derivation.ts +96 -0
  74. package/src/effect.ts +152 -0
  75. package/src/index.ts +30 -0
  76. package/src/map.ts +83 -0
  77. package/src/observable.ts +50 -0
  78. package/src/resource.ts +64 -0
  79. package/src/signal.ts +166 -0
  80. package/src/state.ts +52 -0
  81. package/src/stream.ts +99 -0
  82. package/test/derivation.test.ts +422 -0
  83. package/test/map.test.ts +106 -0
  84. package/test/resource.test.ts +127 -0
  85. package/test/signal.test.ts +59 -0
  86. package/test/state.test.ts +89 -0
  87. package/test/stream.test.ts +171 -0
  88. package/tsconfig.json +22 -0
  89. package/vite.config.ts +26 -0
  90. package/vitest.config.ts +11 -0
@@ -0,0 +1,422 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { derivation, effect, resource, signal, state } from "#package";
3
+
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("Effect is disposed");
25
+ });
26
+
27
+ test("get throws when state is disposed", () => {
28
+ const $state = state(1);
29
+ const $derivation = derivation((get) => get($state) * 2);
30
+ expect($derivation.get()).toBe(2);
31
+
32
+ $state.set(2);
33
+ expect($derivation.get()).toBe(4);
34
+
35
+ $state.dispose();
36
+ expect(() => $derivation.get()).toThrow("Effect is disposed");
37
+ });
38
+
39
+ test("is updated (chained dependencies)", () => {
40
+ const $state = state(1);
41
+ const $derivation1 = derivation((get) => get($state) * 2);
42
+ const $derivation2 = derivation((get) => get($derivation1) * 2);
43
+
44
+ expect($derivation2.get()).toBe(4);
45
+
46
+ $state.set(2);
47
+ expect($derivation2.get()).toBe(8);
48
+ });
49
+
50
+ test("is updated (multiple dependencies)", () => {
51
+ const $state1 = state(1);
52
+ const $state2 = state(2);
53
+ const $derivation = derivation(
54
+ (get) => get($state1) + get($state2),
55
+ );
56
+
57
+ expect($derivation.get()).toBe(3);
58
+
59
+ $state1.set(2);
60
+ expect($derivation.get()).toBe(4);
61
+
62
+ $state2.set(3);
63
+ expect($derivation.get()).toBe(5);
64
+ });
65
+
66
+ test("is updated (multiple dependants)", () => {
67
+ const $state = state(1);
68
+ const $derivation1 = derivation((get) => get($state) * 2);
69
+ const $derivation2 = derivation((get) => get($state) * 3);
70
+
71
+ expect($derivation1.get()).toBe(2);
72
+ expect($derivation2.get()).toBe(3);
73
+
74
+ $state.set(2);
75
+ expect($derivation1.get()).toBe(4);
76
+ expect($derivation2.get()).toBe(6);
77
+
78
+ $state.set(3);
79
+ expect($derivation1.get()).toBe(6);
80
+ expect($derivation2.get()).toBe(9);
81
+ });
82
+
83
+ test("is updated (async)", async () => {
84
+ const $state = state(1);
85
+ const $derivation = derivation(async (get) => get($state) * 2);
86
+
87
+ expect(await $derivation.get()).toBe(2);
88
+
89
+ $state.set(2);
90
+ expect(await $derivation.get()).toBe(4);
91
+ });
92
+
93
+ test("is updated (async chain)", async () => {
94
+ const $state = state(1);
95
+ const $derivation1 = derivation(async (get) => get($state) * 2);
96
+ const $derivation2 = derivation(
97
+ async (get) => (await get($derivation1)) * 2,
98
+ );
99
+
100
+ expect(await $derivation2.get()).toBe(4);
101
+
102
+ $state.set(2);
103
+ expect(await $derivation2.get()).toBe(8);
104
+ });
105
+ });
106
+
107
+ describe("from resource", () => {
108
+ test("from resource is updated", async () => {
109
+ let resourceCounter = 0;
110
+ const fetchResource = async () => {
111
+ resourceCounter++;
112
+ return resourceCounter;
113
+ };
114
+ const $resource = resource(fetchResource, 0);
115
+ const $derivation = derivation((get) => get($resource) * 2);
116
+
117
+ expect($derivation.get()).toBe(0);
118
+
119
+ await $resource.fetch();
120
+ expect($derivation.get()).toBe(2);
121
+
122
+ await $resource.fetch();
123
+ expect($derivation.get()).toBe(4);
124
+
125
+ await $resource.fetch();
126
+ expect($derivation.get()).toBe(6);
127
+ });
128
+ });
129
+ });
130
+
131
+ describe("effect", () => {
132
+ describe("from signal", () => {
133
+ test("called when triggered", () => {
134
+ const $signal = signal();
135
+ const $derivation = derivation((_, watch) => {
136
+ watch($signal);
137
+ });
138
+ const effectFn = vi.fn();
139
+ effect((_, watch) => {
140
+ watch($derivation);
141
+ effectFn();
142
+ });
143
+
144
+ expect(effectFn).toHaveBeenCalledTimes(1);
145
+
146
+ $signal.trigger();
147
+ expect(effectFn).toHaveBeenCalledTimes(2);
148
+
149
+ $signal.trigger();
150
+ expect(effectFn).toHaveBeenCalledTimes(3);
151
+
152
+ $signal.trigger();
153
+ expect(effectFn).toHaveBeenCalledTimes(4);
154
+ });
155
+
156
+ test("called when triggered (chained dependencies)", () => {
157
+ const $signal = signal();
158
+ const $derivation1 = derivation((_, watch) => watch($signal));
159
+ const $derivation2 = derivation((_, watch) => watch($derivation1));
160
+ const effectFn = vi.fn();
161
+ effect((_, watch) => {
162
+ watch($derivation2);
163
+ effectFn();
164
+ });
165
+
166
+ expect(effectFn).toHaveBeenCalledTimes(1);
167
+
168
+ $signal.trigger();
169
+ expect(effectFn).toHaveBeenCalledTimes(2);
170
+ });
171
+
172
+ test("called when triggered (multiple dependencies)", () => {
173
+ const $signal1 = signal();
174
+ const $signal2 = signal();
175
+ const $derivation = derivation((_, watch) => {
176
+ watch($signal1);
177
+ watch($signal2);
178
+ });
179
+ const effectFn = vi.fn();
180
+ effect((_, watch) => {
181
+ watch($derivation);
182
+ effectFn();
183
+ });
184
+
185
+ expect(effectFn).toHaveBeenCalledTimes(1);
186
+
187
+ $signal1.trigger();
188
+ expect(effectFn).toHaveBeenCalledTimes(2);
189
+
190
+ $signal2.trigger();
191
+ expect(effectFn).toHaveBeenCalledTimes(3);
192
+ });
193
+
194
+ test("called when is updated (multiple dependants)", () => {
195
+ const $signal = signal();
196
+ const $derivation1 = derivation((_, watch) => watch($signal));
197
+ const $derivation2 = derivation((_, watch) => watch($signal));
198
+ const effect1Fn = vi.fn();
199
+ const effect2Fn = vi.fn();
200
+
201
+ effect((get) => effect1Fn(get($derivation1)));
202
+ effect((get) => effect2Fn(get($derivation2)));
203
+
204
+ expect(effect1Fn).toHaveBeenCalledTimes(1);
205
+ expect(effect2Fn).toHaveBeenCalledTimes(1);
206
+
207
+ $signal.trigger();
208
+ expect(effect1Fn).toHaveBeenCalledTimes(2);
209
+ expect(effect2Fn).toHaveBeenCalledTimes(2);
210
+
211
+ $signal.trigger();
212
+ expect(effect1Fn).toHaveBeenCalledTimes(3);
213
+ expect(effect2Fn).toHaveBeenCalledTimes(3);
214
+ });
215
+
216
+ test("called when triggered (with state)", () => {
217
+ const $signal = signal();
218
+ const $state = state(1);
219
+ const $derivation = derivation((get, watch) => {
220
+ watch($signal);
221
+ return get($state) * 2;
222
+ });
223
+ const effectFn = vi.fn();
224
+ effect((get) => effectFn(get($derivation)));
225
+
226
+ expect(effectFn).toHaveBeenCalledTimes(1);
227
+ expect(effectFn).toHaveBeenLastCalledWith(2);
228
+
229
+ $signal.trigger();
230
+ expect(effectFn).toHaveBeenCalledTimes(2);
231
+ expect(effectFn).toHaveBeenLastCalledWith(2);
232
+
233
+ $signal.trigger();
234
+ expect(effectFn).toHaveBeenCalledTimes(3);
235
+ expect(effectFn).toHaveBeenLastCalledWith(2);
236
+
237
+ $state.set(2);
238
+ expect(effectFn).toHaveBeenCalledTimes(4);
239
+ expect(effectFn).toHaveBeenLastCalledWith(4);
240
+
241
+ $signal.trigger();
242
+ expect(effectFn).toHaveBeenCalledTimes(5);
243
+ expect(effectFn).toHaveBeenLastCalledWith(4);
244
+ });
245
+ });
246
+
247
+ describe("from state", () => {
248
+ test("called when is updated", () => {
249
+ const $state = state(1);
250
+ const $derivation = derivation((get) => get($state) * 2);
251
+ const effectFn = vi.fn();
252
+ effect((get) => effectFn(get($derivation)));
253
+
254
+ expect(effectFn).toHaveBeenCalledTimes(1);
255
+ expect(effectFn).toHaveBeenLastCalledWith(2);
256
+
257
+ $state.set(2);
258
+ expect(effectFn).toHaveBeenCalledTimes(2);
259
+ expect(effectFn).toHaveBeenLastCalledWith(4);
260
+ });
261
+
262
+ test("called when is updated (chained dependencies)", () => {
263
+ const $state = state(1);
264
+ const $derivation1 = derivation((get) => get($state) * 2);
265
+ const $derivation2 = derivation((get) => get($derivation1) * 2);
266
+ const effectFn = vi.fn();
267
+ effect((get) => effectFn(get($derivation2)));
268
+
269
+ expect(effectFn).toHaveBeenCalledTimes(1);
270
+ expect(effectFn).toHaveBeenLastCalledWith(4);
271
+
272
+ $state.set(2);
273
+ expect(effectFn).toHaveBeenCalledTimes(2);
274
+ expect(effectFn).toHaveBeenLastCalledWith(8);
275
+ });
276
+
277
+ test("called when is updated (multiple dependencies)", () => {
278
+ const $state1 = state(1);
279
+ const $state2 = state(2);
280
+ const $derivation = derivation(
281
+ (get) => get($state1) + get($state2),
282
+ );
283
+ const effectFn = vi.fn();
284
+ effect((get) => effectFn(get($derivation)));
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((get) => get($state) * 2);
301
+ const $derivation2 = derivation((get) => get($state) * 3);
302
+ const effect1Fn = vi.fn();
303
+ const effect2Fn = vi.fn();
304
+
305
+ effect((get) => effect1Fn(get($derivation1)));
306
+ effect((get) => effect2Fn(get($derivation2)));
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 (get) => get($state) * 2);
329
+ const effectFn = vi.fn();
330
+ effect((get) => effectFn(get($derivation)));
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 (get) => get($state) * 2);
343
+ const $derivation2 = derivation(
344
+ async (get) => (await get($derivation1)) * 2,
345
+ );
346
+ const effectFn = vi.fn();
347
+ effect((get) => effectFn(get($derivation2)));
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((get) => get($state) * 2);
360
+ const effectFn = vi.fn();
361
+ effect((get) => effectFn(get($derivation)));
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 disposed", () => {
378
+ const $state = state(1);
379
+ const $derivation = derivation((get) => get($state) * 2);
380
+ const effectFn = vi.fn();
381
+ const $effect = effect((get) => effectFn(get($derivation)));
382
+
383
+ expect(effectFn).toHaveBeenCalledTimes(1);
384
+ expect(effectFn).toHaveBeenLastCalledWith(2);
385
+
386
+ $state.set(2);
387
+ expect(effectFn).toHaveBeenCalledTimes(2);
388
+ expect(effectFn).toHaveBeenLastCalledWith(4);
389
+
390
+ $effect.dispose();
391
+
392
+ $state.set(3);
393
+ expect(effectFn).toHaveBeenCalledTimes(2);
394
+ expect(effectFn).toHaveBeenLastCalledWith(4);
395
+ });
396
+ });
397
+
398
+ describe("from resource", () => {
399
+ test("called when is updated", async () => {
400
+ let resourceCounter = 0;
401
+ const fetchResource = async () => {
402
+ resourceCounter++;
403
+ return resourceCounter;
404
+ };
405
+ const $resource = resource(fetchResource, 0);
406
+ const $derivation = derivation((get) => get($resource) * 2);
407
+ const effectFn = vi.fn();
408
+ effect((get) => effectFn(get($derivation)));
409
+
410
+ expect(effectFn).toHaveBeenCalledTimes(1);
411
+ expect(effectFn).toHaveBeenLastCalledWith(0);
412
+
413
+ await $resource.fetch();
414
+ expect(effectFn).toHaveBeenCalledTimes(2);
415
+ expect(effectFn).toHaveBeenLastCalledWith(2);
416
+
417
+ await $resource.fetch();
418
+ expect(effectFn).toHaveBeenCalledTimes(3);
419
+ expect(effectFn).toHaveBeenLastCalledWith(4);
420
+ });
421
+ });
422
+ });
@@ -0,0 +1,106 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { effect, map } from "#package";
3
+
4
+ describe("map", () => {
5
+ test("is updated", () => {
6
+ const $map = map();
7
+
8
+ expect(Array.from($map.get().entries())).toEqual([]);
9
+
10
+ $map.setAt("key1", 1);
11
+ expect($map.get().get("key1")).toBe(1);
12
+ expect($map.$lastSet.get()).toEqual({ key: "key1", value: 1 });
13
+
14
+ $map.setAt("key2", 2);
15
+ expect($map.get().get("key1")).toBe(1);
16
+ expect($map.get().get("key2")).toBe(2);
17
+ expect($map.$lastSet.get()).toEqual({ key: "key2", value: 2 });
18
+
19
+ $map.setAt("key1", 3);
20
+ expect($map.get().get("key1")).toBe(3);
21
+ expect($map.get().get("key2")).toBe(2);
22
+ expect($map.$lastSet.get()).toEqual({ key: "key1", value: 3 });
23
+
24
+ $map.delete("key1");
25
+ expect($map.get().get("key1")).toBe(undefined);
26
+ expect($map.get().get("key2")).toBe(2);
27
+ expect($map.$lastDeleted.get()).toEqual({ key: "key1", value: 3 });
28
+ });
29
+ });
30
+
31
+ describe("effect", () => {
32
+ test("called when map is updated", () => {
33
+ const $map = map();
34
+ const onSetFn = vi.fn();
35
+ const onSetAtFn = vi.fn();
36
+ const onDeleteFn = vi.fn();
37
+
38
+ effect((get) => onSetFn(get($map)));
39
+ effect((get) => onSetAtFn(get($map.$lastSet)));
40
+ effect((get) => onDeleteFn(get($map.$lastDeleted)));
41
+
42
+ expect(onSetFn).toHaveBeenCalledTimes(1);
43
+ expect(onSetFn).toHaveBeenLastCalledWith(new Map());
44
+ expect(onSetAtFn).toHaveBeenCalledTimes(1);
45
+ expect(onSetAtFn).toHaveBeenLastCalledWith({
46
+ key: undefined,
47
+ value: undefined,
48
+ });
49
+ expect(onDeleteFn).toHaveBeenCalledTimes(1);
50
+ expect(onDeleteFn).toHaveBeenLastCalledWith({
51
+ key: undefined,
52
+ value: undefined,
53
+ });
54
+
55
+ $map.setAt("key1", 1);
56
+ expect(onSetFn).toHaveBeenCalledTimes(2);
57
+ expect(onSetFn).toHaveBeenLastCalledWith(new Map([["key1", 1]]));
58
+ expect(onSetAtFn).toHaveBeenCalledTimes(2);
59
+ expect(onSetAtFn).toHaveBeenLastCalledWith({ key: "key1", value: 1 });
60
+ expect(onDeleteFn).toHaveBeenCalledTimes(1);
61
+ expect(onDeleteFn).toHaveBeenLastCalledWith({
62
+ key: undefined,
63
+ value: undefined,
64
+ });
65
+
66
+ $map.setAt("key2", 2);
67
+ expect(onSetFn).toHaveBeenCalledTimes(3);
68
+ expect(onSetFn).toHaveBeenLastCalledWith(
69
+ new Map([
70
+ ["key1", 1],
71
+ ["key2", 2],
72
+ ]),
73
+ );
74
+ expect(onSetAtFn).toHaveBeenCalledTimes(3);
75
+ expect(onSetAtFn).toHaveBeenLastCalledWith({ key: "key2", value: 2 });
76
+ expect(onDeleteFn).toHaveBeenCalledTimes(1);
77
+ expect(onDeleteFn).toHaveBeenLastCalledWith({
78
+ key: undefined,
79
+ value: undefined,
80
+ });
81
+
82
+ $map.setAt("key1", 3);
83
+ expect(onSetFn).toHaveBeenCalledTimes(4);
84
+ expect(onSetFn).toHaveBeenLastCalledWith(
85
+ new Map([
86
+ ["key1", 3],
87
+ ["key2", 2],
88
+ ]),
89
+ );
90
+ expect(onSetAtFn).toHaveBeenCalledTimes(4);
91
+ expect(onSetAtFn).toHaveBeenLastCalledWith({ key: "key1", value: 3 });
92
+ expect(onDeleteFn).toHaveBeenCalledTimes(1);
93
+ expect(onDeleteFn).toHaveBeenLastCalledWith({
94
+ key: undefined,
95
+ value: undefined,
96
+ });
97
+
98
+ $map.delete("key1");
99
+ expect(onSetFn).toHaveBeenCalledTimes(5);
100
+ expect(onSetFn).toHaveBeenLastCalledWith(new Map([["key2", 2]]));
101
+ expect(onSetAtFn).toHaveBeenCalledTimes(4);
102
+ expect(onSetAtFn).toHaveBeenLastCalledWith({ key: "key1", value: 3 });
103
+ expect(onDeleteFn).toHaveBeenCalledTimes(2);
104
+ expect(onDeleteFn).toHaveBeenLastCalledWith({ key: "key1", value: 3 });
105
+ });
106
+ });
@@ -0,0 +1,127 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { effect, resource } from "#package";
3
+
4
+ describe("resource", () => {
5
+ test("is updated", async () => {
6
+ let resourceCounter = 0;
7
+ const fetchResource = async () => {
8
+ resourceCounter++;
9
+ return resourceCounter;
10
+ };
11
+
12
+ const $resource = resource(fetchResource, 0);
13
+ expect($resource.get()).toBe(0);
14
+
15
+ await $resource.fetch();
16
+ expect($resource.get()).toBe(1);
17
+
18
+ await $resource.fetch();
19
+ expect($resource.get()).toBe(2);
20
+ });
21
+
22
+ test("fetch throws when disposed", async () => {
23
+ let resourceCounter = 0;
24
+ const fetchResource = async () => {
25
+ resourceCounter++;
26
+ return resourceCounter;
27
+ };
28
+
29
+ const $resource = resource(fetchResource, 0);
30
+ expect($resource.get()).toBe(0);
31
+
32
+ await $resource.fetch();
33
+ expect($resource.get()).toBe(1);
34
+
35
+ $resource.dispose();
36
+ await expect(() => $resource.fetch()).rejects.toThrow(
37
+ "Resource is disposed",
38
+ );
39
+ });
40
+
41
+ test("get throws when disposed", async () => {
42
+ let resourceCounter = 0;
43
+ const fetchResource = async () => {
44
+ resourceCounter++;
45
+ return resourceCounter;
46
+ };
47
+
48
+ const $resource = resource(fetchResource, 0);
49
+ expect($resource.get()).toBe(0);
50
+
51
+ await $resource.fetch();
52
+ expect($resource.get()).toBe(1);
53
+
54
+ $resource.dispose();
55
+ expect(() => $resource.get()).toThrow("Resource is disposed");
56
+ });
57
+ });
58
+
59
+ describe("effect", () => {
60
+ test("called when resource updated", async () => {
61
+ let resourceCounter = 0;
62
+ const fetchResource = async () => {
63
+ resourceCounter++;
64
+ return resourceCounter;
65
+ };
66
+
67
+ const $resource = resource(fetchResource, 0);
68
+ const effectFn = vi.fn();
69
+ effect((get) => effectFn(get($resource)));
70
+
71
+ expect(effectFn).toHaveBeenCalledTimes(1);
72
+ expect(effectFn).toHaveBeenLastCalledWith(0);
73
+
74
+ await $resource.fetch();
75
+ expect(effectFn).toHaveBeenCalledTimes(2);
76
+ expect(effectFn).toHaveBeenLastCalledWith(1);
77
+
78
+ await $resource.fetch();
79
+ expect(effectFn).toHaveBeenCalledTimes(3);
80
+ expect(effectFn).toHaveBeenLastCalledWith(2);
81
+ });
82
+
83
+ test("NOT updated when value don't change", async () => {
84
+ const fetchResource = async () => {
85
+ return 0;
86
+ };
87
+
88
+ const $resource = resource(fetchResource, 0);
89
+ const effectFn = vi.fn();
90
+ effect((get) => effectFn(get($resource)));
91
+
92
+ expect(effectFn).toHaveBeenCalledTimes(1);
93
+ expect(effectFn).toHaveBeenLastCalledWith(0);
94
+
95
+ await $resource.fetch();
96
+ expect(effectFn).toHaveBeenCalledTimes(1);
97
+ expect(effectFn).toHaveBeenLastCalledWith(0);
98
+
99
+ await $resource.fetch();
100
+ expect(effectFn).toHaveBeenCalledTimes(1);
101
+ expect(effectFn).toHaveBeenLastCalledWith(0);
102
+ });
103
+
104
+ test("NOT called when disposed", async () => {
105
+ let resourceCounter = 0;
106
+ const fetchResource = async () => {
107
+ resourceCounter++;
108
+ return resourceCounter;
109
+ };
110
+
111
+ const $resource = resource(fetchResource, 0);
112
+ const effectFn = vi.fn();
113
+ const $effect = effect((get) => effectFn(get($resource)));
114
+
115
+ expect(effectFn).toHaveBeenCalledTimes(1);
116
+ expect(effectFn).toHaveBeenLastCalledWith(0);
117
+
118
+ await $resource.fetch();
119
+ expect(effectFn).toHaveBeenCalledTimes(2);
120
+ expect(effectFn).toHaveBeenLastCalledWith(1);
121
+
122
+ $effect.dispose();
123
+ await $resource.fetch();
124
+ expect(effectFn).toHaveBeenCalledTimes(2);
125
+ expect(effectFn).toHaveBeenLastCalledWith(1);
126
+ });
127
+ });