@ersbeth/picoflow 0.0.1 → 0.2.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 (169) hide show
  1. package/api/doc/index.md +1 -1
  2. package/api/doc/picoflow.array.md +55 -0
  3. package/api/doc/picoflow.constant.md +55 -0
  4. package/api/doc/picoflow.derivation.md +1 -1
  5. package/api/doc/picoflow.effect.md +1 -1
  6. package/api/doc/picoflow.flowarray._constructor_.md +49 -0
  7. package/api/doc/picoflow.flowarray._lastaction.md +13 -0
  8. package/api/doc/picoflow.flowarray.clear.md +17 -0
  9. package/api/doc/picoflow.flowarray.dispose.md +55 -0
  10. package/api/doc/picoflow.flowarray.get.md +19 -0
  11. package/api/doc/picoflow.flowarray.length.md +13 -0
  12. package/api/doc/picoflow.flowarray.md +273 -0
  13. package/api/doc/picoflow.flowarray.pop.md +17 -0
  14. package/api/doc/picoflow.flowarray.push.md +53 -0
  15. package/api/doc/picoflow.flowarray.set.md +53 -0
  16. package/api/doc/picoflow.flowarray.setitem.md +69 -0
  17. package/api/doc/picoflow.flowarray.shift.md +17 -0
  18. package/api/doc/picoflow.flowarray.splice.md +85 -0
  19. package/api/doc/picoflow.flowarray.unshift.md +53 -0
  20. package/api/doc/picoflow.flowarrayaction.md +37 -0
  21. package/api/doc/picoflow.flowconstant._constructor_.md +49 -0
  22. package/api/doc/picoflow.flowconstant.get.md +25 -0
  23. package/api/doc/picoflow.flowconstant.md +88 -0
  24. package/api/doc/picoflow.flowderivation._constructor_.md +2 -2
  25. package/api/doc/picoflow.flowderivation.get.md +2 -2
  26. package/api/doc/picoflow.flowderivation.md +2 -2
  27. package/api/doc/picoflow.flowdisposable.dispose.md +55 -0
  28. package/api/doc/picoflow.flowdisposable.md +43 -0
  29. package/api/doc/picoflow.floweffect._constructor_.md +7 -2
  30. package/api/doc/picoflow.floweffect.dispose.md +3 -3
  31. package/api/doc/picoflow.floweffect.disposed.md +1 -1
  32. package/api/doc/picoflow.floweffect.md +4 -4
  33. package/api/doc/picoflow.flowgetter.md +2 -2
  34. package/api/doc/picoflow.flowmap._lastdeleted.md +1 -1
  35. package/api/doc/picoflow.flowmap._lastset.md +1 -1
  36. package/api/doc/picoflow.flowmap.delete.md +6 -2
  37. package/api/doc/picoflow.flowmap.md +5 -7
  38. package/api/doc/picoflow.flowmap.setat.md +6 -2
  39. package/api/doc/picoflow.flowobservable.get.md +3 -3
  40. package/api/doc/picoflow.flowobservable.md +18 -4
  41. package/api/doc/picoflow.flowobservable.subscribe.md +55 -0
  42. package/api/doc/picoflow.flowresource._constructor_.md +2 -18
  43. package/api/doc/picoflow.flowresource.fetch.md +1 -1
  44. package/api/doc/picoflow.flowresource.get.md +4 -4
  45. package/api/doc/picoflow.flowresource.md +4 -4
  46. package/api/doc/picoflow.flowresourceasync._constructor_.md +49 -0
  47. package/api/doc/picoflow.flowresourceasync.fetch.md +27 -0
  48. package/api/doc/picoflow.flowresourceasync.get.md +23 -0
  49. package/api/doc/picoflow.flowresourceasync.md +100 -0
  50. package/api/doc/picoflow.flowsignal.dispose.md +42 -8
  51. package/api/doc/picoflow.flowsignal.disposed.md +2 -2
  52. package/api/doc/picoflow.flowsignal.md +8 -7
  53. package/api/doc/picoflow.flowsignal.trigger.md +3 -7
  54. package/api/doc/picoflow.flowstate.md +4 -52
  55. package/api/doc/picoflow.flowstate.set.md +5 -5
  56. package/api/doc/picoflow.flowstream._constructor_.md +3 -19
  57. package/api/doc/picoflow.flowstream.dispose.md +1 -1
  58. package/api/doc/picoflow.flowstream.get.md +4 -4
  59. package/api/doc/picoflow.flowstream.md +5 -5
  60. package/api/doc/picoflow.flowstreamasync._constructor_.md +54 -0
  61. package/api/doc/picoflow.flowstreamasync.dispose.md +21 -0
  62. package/api/doc/picoflow.flowstreamasync.get.md +23 -0
  63. package/api/doc/picoflow.flowstreamasync.md +100 -0
  64. package/api/doc/picoflow.flowstreamdisposer.md +13 -0
  65. package/api/doc/picoflow.flowstreamsetter.md +13 -0
  66. package/api/doc/picoflow.flowstreamupdater.md +19 -0
  67. package/api/doc/picoflow.flowwatcher.md +1 -1
  68. package/api/doc/picoflow.isdisposable.md +55 -0
  69. package/api/doc/picoflow.map.md +1 -1
  70. package/api/doc/picoflow.md +149 -13
  71. package/api/doc/picoflow.resource.md +2 -18
  72. package/api/doc/picoflow.resourceasync.md +55 -0
  73. package/api/doc/picoflow.signal.md +1 -1
  74. package/api/doc/picoflow.state.md +3 -3
  75. package/api/doc/picoflow.stream.md +2 -18
  76. package/api/doc/picoflow.streamasync.md +55 -0
  77. package/api/picoflow.public.api.md +192 -0
  78. package/api-extractor.json +2 -1
  79. package/dist/picoflow.js +513 -305
  80. package/dist/types/advanced/array.d.ts +116 -0
  81. package/dist/types/advanced/array.d.ts.map +1 -0
  82. package/dist/types/advanced/index.d.ts +9 -0
  83. package/dist/types/advanced/index.d.ts.map +1 -0
  84. package/dist/types/{map.d.ts → advanced/map.d.ts} +12 -12
  85. package/dist/types/advanced/map.d.ts.map +1 -0
  86. package/dist/types/advanced/resource.d.ts +39 -0
  87. package/dist/types/advanced/resource.d.ts.map +1 -0
  88. package/dist/types/{resource.d.ts → advanced/resourceAsync.d.ts} +6 -11
  89. package/dist/types/advanced/resourceAsync.d.ts.map +1 -0
  90. package/dist/types/advanced/stream.d.ts +59 -0
  91. package/dist/types/advanced/stream.d.ts.map +1 -0
  92. package/dist/types/advanced/streamAsync.d.ts +43 -0
  93. package/dist/types/advanced/streamAsync.d.ts.map +1 -0
  94. package/dist/types/basic/constant.d.ts +32 -0
  95. package/dist/types/basic/constant.d.ts.map +1 -0
  96. package/dist/types/basic/derivation.d.ts +40 -0
  97. package/dist/types/basic/derivation.d.ts.map +1 -0
  98. package/dist/types/basic/disposable.d.ts +23 -0
  99. package/dist/types/basic/disposable.d.ts.map +1 -0
  100. package/dist/types/basic/effect.d.ts +56 -0
  101. package/dist/types/basic/effect.d.ts.map +1 -0
  102. package/dist/types/basic/index.d.ts +11 -0
  103. package/dist/types/basic/index.d.ts.map +1 -0
  104. package/dist/types/basic/observable.d.ts +34 -0
  105. package/dist/types/basic/observable.d.ts.map +1 -0
  106. package/dist/types/basic/signal.d.ts +40 -0
  107. package/dist/types/basic/signal.d.ts.map +1 -0
  108. package/dist/types/basic/state.d.ts +26 -0
  109. package/dist/types/basic/state.d.ts.map +1 -0
  110. package/dist/types/creators.d.ts +38 -13
  111. package/dist/types/creators.d.ts.map +1 -1
  112. package/dist/types/index.d.ts +4 -9
  113. package/dist/types/index.d.ts.map +1 -1
  114. package/package.json +1 -1
  115. package/src/advanced/array.ts +224 -0
  116. package/src/advanced/index.ts +12 -0
  117. package/src/{map.ts → advanced/map.ts} +14 -14
  118. package/src/advanced/resource.ts +56 -0
  119. package/src/{resource.ts → advanced/resourceAsync.ts} +9 -16
  120. package/src/advanced/stream.ts +87 -0
  121. package/src/advanced/streamAsync.ts +82 -0
  122. package/src/basic/constant.ts +64 -0
  123. package/src/basic/derivation.ts +86 -0
  124. package/src/basic/disposable.ts +27 -0
  125. package/src/basic/effect.ts +96 -0
  126. package/src/basic/index.ts +10 -0
  127. package/src/basic/observable.ts +51 -0
  128. package/src/basic/signal.ts +117 -0
  129. package/src/basic/state.ts +39 -0
  130. package/src/creators.ts +66 -15
  131. package/src/index.ts +26 -11
  132. package/test/array.test.ts +620 -0
  133. package/test/constant.test.ts +46 -0
  134. package/test/derivation.test.ts +30 -6
  135. package/test/effect.test.ts +29 -0
  136. package/test/map.test.ts +38 -0
  137. package/test/resource.test.ts +18 -16
  138. package/test/resourceAsync.test.ts +108 -0
  139. package/test/signal.test.ts +18 -1
  140. package/test/state.test.ts +107 -2
  141. package/test/stream.test.ts +38 -13
  142. package/test/streamAsync.test.ts +194 -0
  143. package/tsconfig.json +3 -1
  144. package/api/doc/picoflow.flowdisposer.md +0 -13
  145. package/api/doc/picoflow.flowsetter.md +0 -13
  146. package/api/doc/picoflow.flowstate._constructor_.md +0 -49
  147. package/api/doc/picoflow.flowstate.get.md +0 -23
  148. package/api/doc/picoflow.flowupdater.md +0 -19
  149. package/api/picoflow.api.md +0 -145
  150. package/dist/types/derivation.d.ts +0 -58
  151. package/dist/types/derivation.d.ts.map +0 -1
  152. package/dist/types/effect.d.ts +0 -108
  153. package/dist/types/effect.d.ts.map +0 -1
  154. package/dist/types/map.d.ts.map +0 -1
  155. package/dist/types/observable.d.ts +0 -40
  156. package/dist/types/observable.d.ts.map +0 -1
  157. package/dist/types/resource.d.ts.map +0 -1
  158. package/dist/types/signal.d.ts +0 -111
  159. package/dist/types/signal.d.ts.map +0 -1
  160. package/dist/types/state.d.ts +0 -39
  161. package/dist/types/state.d.ts.map +0 -1
  162. package/dist/types/stream.d.ts +0 -71
  163. package/dist/types/stream.d.ts.map +0 -1
  164. package/src/derivation.ts +0 -96
  165. package/src/effect.ts +0 -152
  166. package/src/observable.ts +0 -50
  167. package/src/signal.ts +0 -166
  168. package/src/state.ts +0 -52
  169. package/src/stream.ts +0 -99
@@ -21,7 +21,9 @@ describe("derivation", () => {
21
21
  expect($derivation.get()).toBe(4);
22
22
 
23
23
  $derivation.dispose();
24
- expect(() => $derivation.get()).toThrow("Effect is disposed");
24
+ expect(() => $derivation.get()).toThrow(
25
+ "[PicoFlow] Primitive is disposed",
26
+ );
25
27
  });
26
28
 
27
29
  test("get throws when state is disposed", () => {
@@ -33,7 +35,9 @@ describe("derivation", () => {
33
35
  expect($derivation.get()).toBe(4);
34
36
 
35
37
  $state.dispose();
36
- expect(() => $derivation.get()).toThrow("Effect is disposed");
38
+ expect(() => $derivation.get()).toThrow(
39
+ "[PicoFlow] Primitive is disposed",
40
+ );
37
41
  });
38
42
 
39
43
  test("is updated (chained dependencies)", () => {
@@ -111,8 +115,8 @@ describe("derivation", () => {
111
115
  resourceCounter++;
112
116
  return resourceCounter;
113
117
  };
114
- const $resource = resource(fetchResource, 0);
115
- const $derivation = derivation((get) => get($resource) * 2);
118
+ const $resource = resource(fetchResource);
119
+ const $derivation = derivation((get) => (get($resource) ?? 0) * 2);
116
120
 
117
121
  expect($derivation.get()).toBe(0);
118
122
 
@@ -374,6 +378,26 @@ describe("effect", () => {
374
378
  expect(effectFn).toHaveBeenLastCalledWith(4);
375
379
  });
376
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
+
377
401
  test("NOT called when disposed", () => {
378
402
  const $state = state(1);
379
403
  const $derivation = derivation((get) => get($state) * 2);
@@ -402,8 +426,8 @@ describe("effect", () => {
402
426
  resourceCounter++;
403
427
  return resourceCounter;
404
428
  };
405
- const $resource = resource(fetchResource, 0);
406
- const $derivation = derivation((get) => get($resource) * 2);
429
+ const $resource = resource(fetchResource);
430
+ const $derivation = derivation((get) => (get($resource) ?? 0) * 2);
407
431
  const effectFn = vi.fn();
408
432
  effect((get) => effectFn(get($derivation)));
409
433
 
@@ -0,0 +1,29 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { effect, signal } from "#package";
3
+
4
+ describe("effect", () => {
5
+ test("disposed is correct", () => {
6
+ const $signal = signal();
7
+ const effectFn = vi.fn();
8
+ const $effect = effect((_, watch) => {
9
+ watch($signal);
10
+ effectFn();
11
+ });
12
+
13
+ expect($effect.disposed).toBe(false);
14
+ $effect.dispose();
15
+ expect($effect.disposed).toBe(true);
16
+ });
17
+
18
+ test("throw when disposed twice", () => {
19
+ const $signal = signal();
20
+ const effectFn = vi.fn();
21
+ const $effect = effect((_, watch) => {
22
+ watch($signal);
23
+ effectFn();
24
+ });
25
+
26
+ $effect.dispose();
27
+ expect(() => $effect.dispose()).toThrow("Effect is disposed");
28
+ });
29
+ });
package/test/map.test.ts CHANGED
@@ -2,6 +2,20 @@ import { describe, expect, test, vi } from "vitest";
2
2
  import { effect, map } from "#package";
3
3
 
4
4
  describe("map", () => {
5
+ test("is initialized", () => {
6
+ const $map = map({
7
+ key1: 1,
8
+ key2: 2,
9
+ key3: 3,
10
+ });
11
+
12
+ expect(Array.from($map.get().entries())).toEqual([
13
+ ["key1", 1],
14
+ ["key2", 2],
15
+ ["key3", 3],
16
+ ]);
17
+ });
18
+
5
19
  test("is updated", () => {
6
20
  const $map = map();
7
21
 
@@ -26,6 +40,30 @@ describe("map", () => {
26
40
  expect($map.get().get("key2")).toBe(2);
27
41
  expect($map.$lastDeleted.get()).toEqual({ key: "key1", value: 3 });
28
42
  });
43
+
44
+ test("get throws when disposed", () => {
45
+ const $map = map({ key1: 1, key2: 2, key3: 3 });
46
+ $map.dispose();
47
+ expect(() => $map.get()).toThrowError(
48
+ "[PicoFlow] Primitive is disposed",
49
+ );
50
+ });
51
+
52
+ test("set throws when disposed", () => {
53
+ const $map = map<string, number>();
54
+ $map.dispose();
55
+ expect(() => $map.setAt("key1", 2)).toThrowError(
56
+ "[PicoFlow] Primitive is disposed",
57
+ );
58
+ });
59
+
60
+ test("delete throws when disposed", () => {
61
+ const $map = map<string, number>({ key1: 1, key2: 2, key3: 3 });
62
+ $map.dispose();
63
+ expect(() => $map.delete("key1")).toThrowError(
64
+ "[PicoFlow] Primitive is disposed",
65
+ );
66
+ });
29
67
  });
30
68
 
31
69
  describe("effect", () => {
@@ -9,8 +9,8 @@ describe("resource", () => {
9
9
  return resourceCounter;
10
10
  };
11
11
 
12
- const $resource = resource(fetchResource, 0);
13
- expect($resource.get()).toBe(0);
12
+ const $resource = resource(fetchResource);
13
+ expect($resource.get()).toBe(undefined);
14
14
 
15
15
  await $resource.fetch();
16
16
  expect($resource.get()).toBe(1);
@@ -26,15 +26,15 @@ describe("resource", () => {
26
26
  return resourceCounter;
27
27
  };
28
28
 
29
- const $resource = resource(fetchResource, 0);
30
- expect($resource.get()).toBe(0);
29
+ const $resource = resource(fetchResource);
30
+ expect($resource.get()).toBe(undefined);
31
31
 
32
32
  await $resource.fetch();
33
33
  expect($resource.get()).toBe(1);
34
34
 
35
35
  $resource.dispose();
36
36
  await expect(() => $resource.fetch()).rejects.toThrow(
37
- "Resource is disposed",
37
+ "[PicoFlow] Primitive is disposed",
38
38
  );
39
39
  });
40
40
 
@@ -45,14 +45,16 @@ describe("resource", () => {
45
45
  return resourceCounter;
46
46
  };
47
47
 
48
- const $resource = resource(fetchResource, 0);
49
- expect($resource.get()).toBe(0);
48
+ const $resource = resource(fetchResource);
49
+ expect($resource.get()).toBe(undefined);
50
50
 
51
51
  await $resource.fetch();
52
52
  expect($resource.get()).toBe(1);
53
53
 
54
54
  $resource.dispose();
55
- expect(() => $resource.get()).toThrow("Resource is disposed");
55
+ expect(() => $resource.get()).toThrow(
56
+ "[PicoFlow] Primitive is disposed",
57
+ );
56
58
  });
57
59
  });
58
60
 
@@ -64,12 +66,12 @@ describe("effect", () => {
64
66
  return resourceCounter;
65
67
  };
66
68
 
67
- const $resource = resource(fetchResource, 0);
69
+ const $resource = resource(fetchResource);
68
70
  const effectFn = vi.fn();
69
71
  effect((get) => effectFn(get($resource)));
70
72
 
71
73
  expect(effectFn).toHaveBeenCalledTimes(1);
72
- expect(effectFn).toHaveBeenLastCalledWith(0);
74
+ expect(effectFn).toHaveBeenLastCalledWith(undefined);
73
75
 
74
76
  await $resource.fetch();
75
77
  expect(effectFn).toHaveBeenCalledTimes(2);
@@ -85,19 +87,19 @@ describe("effect", () => {
85
87
  return 0;
86
88
  };
87
89
 
88
- const $resource = resource(fetchResource, 0);
90
+ const $resource = resource(fetchResource);
89
91
  const effectFn = vi.fn();
90
92
  effect((get) => effectFn(get($resource)));
91
93
 
92
94
  expect(effectFn).toHaveBeenCalledTimes(1);
93
- expect(effectFn).toHaveBeenLastCalledWith(0);
95
+ expect(effectFn).toHaveBeenLastCalledWith(undefined);
94
96
 
95
97
  await $resource.fetch();
96
- expect(effectFn).toHaveBeenCalledTimes(1);
98
+ expect(effectFn).toHaveBeenCalledTimes(2);
97
99
  expect(effectFn).toHaveBeenLastCalledWith(0);
98
100
 
99
101
  await $resource.fetch();
100
- expect(effectFn).toHaveBeenCalledTimes(1);
102
+ expect(effectFn).toHaveBeenCalledTimes(2);
101
103
  expect(effectFn).toHaveBeenLastCalledWith(0);
102
104
  });
103
105
 
@@ -108,12 +110,12 @@ describe("effect", () => {
108
110
  return resourceCounter;
109
111
  };
110
112
 
111
- const $resource = resource(fetchResource, 0);
113
+ const $resource = resource(fetchResource);
112
114
  const effectFn = vi.fn();
113
115
  const $effect = effect((get) => effectFn(get($resource)));
114
116
 
115
117
  expect(effectFn).toHaveBeenCalledTimes(1);
116
- expect(effectFn).toHaveBeenLastCalledWith(0);
118
+ expect(effectFn).toHaveBeenLastCalledWith(undefined);
117
119
 
118
120
  await $resource.fetch();
119
121
  expect(effectFn).toHaveBeenCalledTimes(2);
@@ -0,0 +1,108 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { effect, resourceAsync } from "#package";
3
+
4
+ describe("resourceAsync", () => {
5
+ test("is updated", async () => {
6
+ let resourceCounter = 0;
7
+ const fetchResource = async () => {
8
+ resourceCounter++;
9
+ return resourceCounter;
10
+ };
11
+
12
+ const $resource = resourceAsync(fetchResource);
13
+ expect(await $resource.get()).toBe(1);
14
+
15
+ $resource.fetch();
16
+ expect(await $resource.get()).toBe(2);
17
+
18
+ $resource.fetch();
19
+ expect(await $resource.get()).toBe(3);
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 = resourceAsync(fetchResource);
30
+ expect(await $resource.get()).toBe(1);
31
+
32
+ $resource.fetch();
33
+ expect(await $resource.get()).toBe(2);
34
+
35
+ $resource.dispose();
36
+ await expect(() => $resource.fetch()).rejects.toThrow(
37
+ "[PicoFlow] Primitive 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 = resourceAsync(fetchResource);
49
+ expect(await $resource.get()).toBe(1);
50
+
51
+ $resource.fetch();
52
+ expect(await $resource.get()).toBe(2);
53
+
54
+ $resource.dispose();
55
+ expect(() => $resource.get()).toThrow(
56
+ "[PicoFlow] Primitive is disposed",
57
+ );
58
+ });
59
+ });
60
+
61
+ describe("effect", () => {
62
+ test("called when resource updated", async () => {
63
+ let resourceCounter = 0;
64
+ const fetchResource = async () => {
65
+ resourceCounter++;
66
+ return resourceCounter;
67
+ };
68
+
69
+ const $resource = resourceAsync(fetchResource);
70
+ const effectFn = vi.fn();
71
+ effect((get) => effectFn(get($resource)));
72
+
73
+ expect(effectFn).toHaveBeenCalledTimes(1);
74
+ expect(await effectFn.mock.calls[0][0]).toBe(1);
75
+
76
+ $resource.fetch();
77
+ expect(effectFn).toHaveBeenCalledTimes(2);
78
+ expect(await effectFn.mock.calls[1][0]).toBe(2);
79
+
80
+ await $resource.fetch();
81
+ expect(effectFn).toHaveBeenCalledTimes(3);
82
+ expect(await effectFn.mock.calls[2][0]).toBe(3);
83
+ });
84
+
85
+ test("NOT called when disposed", async () => {
86
+ let resourceCounter = 0;
87
+ const fetchResource = async () => {
88
+ resourceCounter++;
89
+ return resourceCounter;
90
+ };
91
+
92
+ const $resource = resourceAsync(fetchResource);
93
+ const effectFn = vi.fn();
94
+ const $effect = effect((get) => effectFn(get($resource)));
95
+
96
+ expect(effectFn).toHaveBeenCalledTimes(1);
97
+ expect(await effectFn.mock.calls[0][0]).toBe(1);
98
+
99
+ await $resource.fetch();
100
+ expect(effectFn).toHaveBeenCalledTimes(2);
101
+ expect(await effectFn.mock.calls[1][0]).toBe(2);
102
+
103
+ $effect.dispose();
104
+ await $resource.fetch();
105
+ expect(effectFn).toHaveBeenCalledTimes(2);
106
+ expect(await effectFn.mock.calls[1][0]).toBe(2);
107
+ });
108
+ });
@@ -2,6 +2,13 @@ import { describe, expect, test, vi } from "vitest";
2
2
  import { effect, signal } from "#package";
3
3
 
4
4
  describe("signal", () => {
5
+ test("disposed is correct", () => {
6
+ const $signal = signal();
7
+ expect($signal.disposed).toBe(false);
8
+ $signal.dispose();
9
+ expect($signal.disposed).toBe(true);
10
+ });
11
+
5
12
  test("throws when disposed", () => {
6
13
  const $signal = signal();
7
14
  const effectFn = vi.fn();
@@ -16,7 +23,17 @@ describe("signal", () => {
16
23
  expect(effectFn).toHaveBeenCalledTimes(2);
17
24
 
18
25
  $signal.dispose();
19
- expect(() => $signal.trigger()).toThrow("Signal is disposed");
26
+ expect(() => $signal.trigger()).toThrow(
27
+ "[PicoFlow] Primitive is disposed",
28
+ );
29
+ });
30
+
31
+ test("throws when disposed twice", () => {
32
+ const $signal = signal();
33
+ $signal.dispose();
34
+ expect(() => $signal.dispose()).toThrow(
35
+ "[PicoFlow] Primitive is disposed",
36
+ );
20
37
  });
21
38
  });
22
39
 
@@ -2,6 +2,16 @@ import { describe, expect, test, vi } from "vitest";
2
2
  import { effect, state } from "#package";
3
3
 
4
4
  describe("state", () => {
5
+ test("is initialized", () => {
6
+ const $state = state(1);
7
+ expect($state.get()).toBe(1);
8
+ });
9
+
10
+ test("is lazy initialized", () => {
11
+ const $state = state(() => 1);
12
+ expect($state.get()).toBe(1);
13
+ });
14
+
5
15
  test("is updated", () => {
6
16
  const $state = state(1);
7
17
  expect($state.get()).toBe(1);
@@ -10,6 +20,14 @@ describe("state", () => {
10
20
  expect($state.get()).toBe(2);
11
21
  });
12
22
 
23
+ test("is updated from current", () => {
24
+ const $state = state(1);
25
+ expect($state.get()).toBe(1);
26
+
27
+ $state.set((state) => state + 1);
28
+ expect($state.get()).toBe(2);
29
+ });
30
+
13
31
  test("set throws when disposed", () => {
14
32
  const $state = state(1);
15
33
 
@@ -20,7 +38,7 @@ describe("state", () => {
20
38
 
21
39
  $state.dispose();
22
40
 
23
- expect(() => $state.set(3)).toThrow("State is disposed");
41
+ expect(() => $state.set(3)).toThrow("[PicoFlow] Primitive is disposed");
24
42
  });
25
43
 
26
44
  test("get throws when disposed", () => {
@@ -33,11 +51,29 @@ describe("state", () => {
33
51
 
34
52
  $state.dispose();
35
53
 
36
- expect(() => $state.get()).toThrow("State is disposed");
54
+ expect(() => $state.get()).toThrow("[PicoFlow] Primitive is disposed");
37
55
  });
38
56
  });
39
57
 
40
58
  describe("effect", () => {
59
+ test("called with init value", () => {
60
+ const $state = state(1);
61
+ const effectFn = vi.fn();
62
+ effect((get) => effectFn(get($state)));
63
+
64
+ expect(effectFn).toHaveBeenCalledTimes(1);
65
+ expect(effectFn).toHaveBeenLastCalledWith(1);
66
+ });
67
+
68
+ test("called with lazy init value", () => {
69
+ const $state = state(() => 1);
70
+ const effectFn = vi.fn();
71
+ effect((get) => effectFn(get($state)));
72
+
73
+ expect(effectFn).toHaveBeenCalledTimes(1);
74
+ expect(effectFn).toHaveBeenLastCalledWith(1);
75
+ });
76
+
41
77
  test("called when updated", () => {
42
78
  const $state = state(1);
43
79
  const effectFn = vi.fn();
@@ -87,3 +123,72 @@ describe("effect", () => {
87
123
  expect(effectFn).toHaveBeenLastCalledWith(2);
88
124
  });
89
125
  });
126
+
127
+ describe("subscribe", () => {
128
+ test("called with init value", () => {
129
+ const $state = state(1);
130
+ const effectFn = vi.fn();
131
+ $state.subscribe((value) => effectFn(value));
132
+
133
+ expect(effectFn).toHaveBeenCalledTimes(1);
134
+ expect(effectFn).toHaveBeenLastCalledWith(1);
135
+ });
136
+
137
+ test("called with lazy init value", () => {
138
+ const $state = state(() => 1);
139
+ const effectFn = vi.fn();
140
+ $state.subscribe((value) => effectFn(value));
141
+
142
+ expect(effectFn).toHaveBeenCalledTimes(1);
143
+ expect(effectFn).toHaveBeenLastCalledWith(1);
144
+ });
145
+
146
+ test("called when updated", () => {
147
+ const $state = state(1);
148
+ const effectFn = vi.fn();
149
+ $state.subscribe((value) => effectFn(value));
150
+
151
+ expect(effectFn).toHaveBeenCalledTimes(1);
152
+ expect(effectFn).toHaveBeenLastCalledWith(1);
153
+
154
+ $state.set(2);
155
+ expect(effectFn).toHaveBeenCalledTimes(2);
156
+ expect(effectFn).toHaveBeenLastCalledWith(2);
157
+ });
158
+
159
+ test("NOT called when updated with the same value", () => {
160
+ const $state = state(1);
161
+ const effectFn = vi.fn();
162
+ $state.subscribe((value) => effectFn(value));
163
+
164
+ expect(effectFn).toHaveBeenCalledTimes(1);
165
+ expect(effectFn).toHaveBeenLastCalledWith(1);
166
+
167
+ $state.set(2);
168
+ expect(effectFn).toHaveBeenCalledTimes(2);
169
+ expect(effectFn).toHaveBeenLastCalledWith(2);
170
+
171
+ $state.set(2);
172
+ expect(effectFn).toHaveBeenCalledTimes(2);
173
+ expect(effectFn).toHaveBeenLastCalledWith(2);
174
+ });
175
+
176
+ test("NOT called when disposed", () => {
177
+ const $state = state(1);
178
+ const effectFn = vi.fn();
179
+ const unsubscribe = $state.subscribe((value) => effectFn(value));
180
+
181
+ expect(effectFn).toHaveBeenCalledTimes(1);
182
+ expect(effectFn).toHaveBeenLastCalledWith(1);
183
+
184
+ $state.set(2);
185
+ expect(effectFn).toHaveBeenCalledTimes(2);
186
+ expect(effectFn).toHaveBeenLastCalledWith(2);
187
+
188
+ unsubscribe();
189
+
190
+ $state.set(3);
191
+ expect(effectFn).toHaveBeenCalledTimes(2);
192
+ expect(effectFn).toHaveBeenLastCalledWith(2);
193
+ });
194
+ });
@@ -19,9 +19,9 @@ describe("stream", () => {
19
19
  return () => {
20
20
  counter.callback = undefined;
21
21
  };
22
- }, counter.count);
22
+ });
23
23
 
24
- expect($stream.get()).toBe(0);
24
+ expect($stream.get()).toBe(undefined);
25
25
 
26
26
  counter.increment();
27
27
  expect($stream.get()).toBe(1);
@@ -40,9 +40,9 @@ describe("stream", () => {
40
40
  set(counter);
41
41
  }, 1000);
42
42
  return () => clearInterval(interval);
43
- }, 0);
43
+ });
44
44
 
45
- expect($stream.get()).toBe(0);
45
+ expect($stream.get()).toBe(undefined);
46
46
 
47
47
  vi.advanceTimersToNextTimer();
48
48
  expect($stream.get()).toBe(1);
@@ -61,9 +61,9 @@ describe("stream", () => {
61
61
  set(counter);
62
62
  }, 1000);
63
63
  return () => clearInterval(interval);
64
- }, 0);
64
+ });
65
65
 
66
- expect($stream.get()).toBe(0);
66
+ expect($stream.get()).toBe(undefined);
67
67
 
68
68
  vi.advanceTimersToNextTimer();
69
69
  expect($stream.get()).toBe(1);
@@ -73,7 +73,7 @@ describe("stream", () => {
73
73
 
74
74
  $stream.dispose();
75
75
  vi.advanceTimersToNextTimer();
76
- expect(() => $stream.get()).toThrow("Stream is disposed");
76
+ expect(() => $stream.get()).toThrow("[PicoFlow] Primitive is disposed");
77
77
  });
78
78
  });
79
79
 
@@ -88,13 +88,13 @@ describe("effect", () => {
88
88
  set(counter);
89
89
  }, 1000);
90
90
  return () => clearInterval(interval);
91
- }, 0);
91
+ });
92
92
 
93
93
  const effectFn = vi.fn();
94
94
  effect((get) => effectFn(get($stream)));
95
95
 
96
96
  expect(effectFn).toHaveBeenCalledTimes(1);
97
- expect(effectFn).toHaveBeenLastCalledWith(0);
97
+ expect(effectFn).toHaveBeenLastCalledWith(undefined);
98
98
 
99
99
  vi.advanceTimersToNextTimer();
100
100
  expect(effectFn).toHaveBeenCalledTimes(2);
@@ -105,6 +105,31 @@ describe("effect", () => {
105
105
  expect(effectFn).toHaveBeenLastCalledWith(2);
106
106
  });
107
107
 
108
+ test("NOT called when stream is updated with same value", async () => {
109
+ vi.useFakeTimers();
110
+
111
+ const $stream = stream((set) => {
112
+ const interval = setInterval(() => {
113
+ set(5);
114
+ }, 1000);
115
+ return () => clearInterval(interval);
116
+ });
117
+
118
+ const effectFn = vi.fn();
119
+ effect((get) => effectFn(get($stream)));
120
+
121
+ expect(effectFn).toHaveBeenCalledTimes(1);
122
+ expect(effectFn).toHaveBeenLastCalledWith(undefined);
123
+
124
+ vi.advanceTimersToNextTimer();
125
+ expect(effectFn).toHaveBeenCalledTimes(2);
126
+ expect(effectFn).toHaveBeenLastCalledWith(5);
127
+
128
+ vi.advanceTimersToNextTimer();
129
+ expect(effectFn).toHaveBeenCalledTimes(2);
130
+ expect(effectFn).toHaveBeenLastCalledWith(5);
131
+ });
132
+
108
133
  test("NOT called when disposed", async () => {
109
134
  vi.useFakeTimers();
110
135
 
@@ -115,13 +140,13 @@ describe("effect", () => {
115
140
  set(counter);
116
141
  }, 1000);
117
142
  return () => clearInterval(interval);
118
- }, 0);
143
+ });
119
144
 
120
145
  const effectFn = vi.fn();
121
146
  const $effect = effect((get) => effectFn(get($stream)));
122
147
 
123
148
  expect(effectFn).toHaveBeenCalledTimes(1);
124
- expect(effectFn).toHaveBeenLastCalledWith(0);
149
+ expect(effectFn).toHaveBeenLastCalledWith(undefined);
125
150
 
126
151
  vi.advanceTimersToNextTimer();
127
152
  expect(effectFn).toHaveBeenCalledTimes(2);
@@ -147,13 +172,13 @@ describe("effect", () => {
147
172
  set(counter);
148
173
  }, 1000);
149
174
  return () => clearInterval(interval);
150
- }, 0);
175
+ });
151
176
 
152
177
  const effectFn = vi.fn();
153
178
  effect((get) => effectFn(get($stream)));
154
179
 
155
180
  expect(effectFn).toHaveBeenCalledTimes(1);
156
- expect(effectFn).toHaveBeenLastCalledWith(0);
181
+ expect(effectFn).toHaveBeenLastCalledWith(undefined);
157
182
 
158
183
  vi.advanceTimersToNextTimer();
159
184
  expect(effectFn).toHaveBeenCalledTimes(2);