@praxisjs/decorators 1.7.0 → 2.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 (266) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/component/component.d.ts +1 -1
  3. package/dist/component/component.d.ts.map +1 -1
  4. package/dist/component/component.js +8 -15
  5. package/dist/component/component.js.map +1 -1
  6. package/dist/component/lazy.d.ts +1 -1
  7. package/dist/component/lazy.d.ts.map +1 -1
  8. package/dist/component/lazy.js +44 -36
  9. package/dist/component/lazy.js.map +1 -1
  10. package/dist/create-class-decorator.d.ts +27 -5
  11. package/dist/create-class-decorator.d.ts.map +1 -1
  12. package/dist/create-class-decorator.js +53 -18
  13. package/dist/create-class-decorator.js.map +1 -1
  14. package/dist/create-field-decorator.d.ts +42 -3
  15. package/dist/create-field-decorator.d.ts.map +1 -1
  16. package/dist/create-field-decorator.js +63 -27
  17. package/dist/create-field-decorator.js.map +1 -1
  18. package/dist/create-getter-decorator.d.ts +81 -16
  19. package/dist/create-getter-decorator.d.ts.map +1 -1
  20. package/dist/create-getter-decorator.js +127 -15
  21. package/dist/create-getter-decorator.js.map +1 -1
  22. package/dist/create-lifecycle-method-decorator.d.ts +21 -3
  23. package/dist/create-lifecycle-method-decorator.d.ts.map +1 -1
  24. package/dist/create-lifecycle-method-decorator.js +25 -18
  25. package/dist/create-lifecycle-method-decorator.js.map +1 -1
  26. package/dist/create-method-decorator.d.ts +43 -3
  27. package/dist/create-method-decorator.d.ts.map +1 -1
  28. package/dist/create-method-decorator.js +49 -8
  29. package/dist/create-method-decorator.js.map +1 -1
  30. package/dist/events/command.d.ts +2 -1
  31. package/dist/events/command.d.ts.map +1 -1
  32. package/dist/events/command.js +10 -8
  33. package/dist/events/command.js.map +1 -1
  34. package/dist/events/emit.d.ts +2 -2
  35. package/dist/events/emit.d.ts.map +1 -1
  36. package/dist/events/emit.js +18 -24
  37. package/dist/events/emit.js.map +1 -1
  38. package/dist/events/on-command.d.ts +2 -1
  39. package/dist/events/on-command.d.ts.map +1 -1
  40. package/dist/events/on-command.js +12 -14
  41. package/dist/events/on-command.js.map +1 -1
  42. package/dist/field.d.ts +51 -0
  43. package/dist/field.d.ts.map +1 -0
  44. package/dist/field.js +82 -0
  45. package/dist/field.js.map +1 -0
  46. package/dist/functions/bind.d.ts +2 -1
  47. package/dist/functions/bind.d.ts.map +1 -1
  48. package/dist/functions/bind.js +4 -4
  49. package/dist/functions/bind.js.map +1 -1
  50. package/dist/functions/debounce.d.ts +2 -2
  51. package/dist/functions/debounce.d.ts.map +1 -1
  52. package/dist/functions/debounce.js +25 -23
  53. package/dist/functions/debounce.js.map +1 -1
  54. package/dist/functions/log.d.ts +2 -1
  55. package/dist/functions/log.d.ts.map +1 -1
  56. package/dist/functions/log.js +38 -40
  57. package/dist/functions/log.js.map +1 -1
  58. package/dist/functions/memo.d.ts +2 -1
  59. package/dist/functions/memo.d.ts.map +1 -1
  60. package/dist/functions/memo.js +41 -40
  61. package/dist/functions/memo.js.map +1 -1
  62. package/dist/functions/once.d.ts +2 -1
  63. package/dist/functions/once.d.ts.map +1 -1
  64. package/dist/functions/once.js +10 -14
  65. package/dist/functions/once.js.map +1 -1
  66. package/dist/functions/retry.d.ts +2 -1
  67. package/dist/functions/retry.d.ts.map +1 -1
  68. package/dist/functions/retry.js +21 -23
  69. package/dist/functions/retry.js.map +1 -1
  70. package/dist/functions/throttle.d.ts +2 -1
  71. package/dist/functions/throttle.d.ts.map +1 -1
  72. package/dist/functions/throttle.js +12 -14
  73. package/dist/functions/throttle.js.map +1 -1
  74. package/dist/functions/until.d.ts +2 -1
  75. package/dist/functions/until.d.ts.map +1 -1
  76. package/dist/functions/until.js +13 -14
  77. package/dist/functions/until.js.map +1 -1
  78. package/dist/functions/watch.d.ts +1 -1
  79. package/dist/functions/watch.d.ts.map +1 -1
  80. package/dist/functions/watch.js +36 -34
  81. package/dist/functions/watch.js.map +1 -1
  82. package/dist/functions/when.d.ts +1 -1
  83. package/dist/functions/when.d.ts.map +1 -1
  84. package/dist/functions/when.js +19 -21
  85. package/dist/functions/when.js.map +1 -1
  86. package/dist/index.d.ts +5 -5
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +2 -2
  89. package/dist/index.js.map +1 -1
  90. package/dist/lifecycle.d.ts +24 -0
  91. package/dist/lifecycle.d.ts.map +1 -0
  92. package/dist/lifecycle.js +55 -0
  93. package/dist/lifecycle.js.map +1 -0
  94. package/dist/properties/compose.d.ts +3 -2
  95. package/dist/properties/compose.d.ts.map +1 -1
  96. package/dist/properties/compose.js +33 -19
  97. package/dist/properties/compose.js.map +1 -1
  98. package/dist/properties/computed.d.ts +12 -10
  99. package/dist/properties/computed.d.ts.map +1 -1
  100. package/dist/properties/computed.js +26 -21
  101. package/dist/properties/computed.js.map +1 -1
  102. package/dist/properties/deep-state.d.ts +2 -1
  103. package/dist/properties/deep-state.d.ts.map +1 -1
  104. package/dist/properties/deep-state.js +66 -60
  105. package/dist/properties/deep-state.js.map +1 -1
  106. package/dist/properties/function-prop.d.ts +2 -1
  107. package/dist/properties/function-prop.d.ts.map +1 -1
  108. package/dist/properties/function-prop.js +20 -22
  109. package/dist/properties/function-prop.js.map +1 -1
  110. package/dist/properties/history.d.ts +2 -2
  111. package/dist/properties/history.d.ts.map +1 -1
  112. package/dist/properties/history.js +34 -33
  113. package/dist/properties/history.js.map +1 -1
  114. package/dist/properties/index.d.ts +1 -0
  115. package/dist/properties/index.d.ts.map +1 -1
  116. package/dist/properties/index.js +1 -0
  117. package/dist/properties/index.js.map +1 -1
  118. package/dist/properties/persisted.d.ts +2 -1
  119. package/dist/properties/persisted.d.ts.map +1 -1
  120. package/dist/properties/persisted.js +24 -18
  121. package/dist/properties/persisted.js.map +1 -1
  122. package/dist/properties/prop.d.ts +2 -1
  123. package/dist/properties/prop.d.ts.map +1 -1
  124. package/dist/properties/prop.js +26 -22
  125. package/dist/properties/prop.js.map +1 -1
  126. package/dist/properties/ref.d.ts +2 -1
  127. package/dist/properties/ref.d.ts.map +1 -1
  128. package/dist/properties/ref.js +11 -8
  129. package/dist/properties/ref.js.map +1 -1
  130. package/dist/properties/resource.d.ts.map +1 -1
  131. package/dist/properties/resource.js +21 -16
  132. package/dist/properties/resource.js.map +1 -1
  133. package/dist/properties/selector.d.ts +12 -0
  134. package/dist/properties/selector.d.ts.map +1 -0
  135. package/dist/properties/selector.js +25 -0
  136. package/dist/properties/selector.js.map +1 -0
  137. package/dist/properties/slot.d.ts +2 -1
  138. package/dist/properties/slot.d.ts.map +1 -1
  139. package/dist/properties/slot.js +22 -20
  140. package/dist/properties/slot.js.map +1 -1
  141. package/dist/properties/state.d.ts.map +1 -1
  142. package/dist/properties/state.js +27 -16
  143. package/dist/properties/state.js.map +1 -1
  144. package/dist/properties/synced.d.ts +2 -1
  145. package/dist/properties/synced.d.ts.map +1 -1
  146. package/dist/properties/synced.js +27 -19
  147. package/dist/properties/synced.js.map +1 -1
  148. package/dist/shared-method.d.ts +14 -0
  149. package/dist/shared-method.d.ts.map +1 -0
  150. package/dist/shared-method.js +87 -0
  151. package/dist/shared-method.js.map +1 -0
  152. package/package.json +14 -5
  153. package/src/component/component.ts +8 -27
  154. package/src/component/lazy.ts +51 -41
  155. package/src/create-class-decorator.ts +90 -31
  156. package/src/create-field-decorator.ts +120 -43
  157. package/src/create-getter-decorator.ts +263 -66
  158. package/src/create-lifecycle-method-decorator.ts +50 -28
  159. package/src/create-method-decorator.ts +119 -13
  160. package/src/events/command.ts +11 -9
  161. package/src/events/emit.ts +16 -22
  162. package/src/events/on-command.ts +17 -18
  163. package/src/field.ts +116 -0
  164. package/src/functions/bind.ts +4 -4
  165. package/src/functions/debounce.ts +25 -23
  166. package/src/functions/log.ts +44 -46
  167. package/src/functions/memo.ts +39 -37
  168. package/src/functions/once.ts +10 -13
  169. package/src/functions/retry.ts +19 -21
  170. package/src/functions/throttle.ts +12 -13
  171. package/src/functions/until.ts +14 -14
  172. package/src/functions/watch.ts +37 -36
  173. package/src/functions/when.ts +18 -20
  174. package/src/index.ts +12 -5
  175. package/src/lifecycle.ts +74 -0
  176. package/src/properties/compose.ts +33 -20
  177. package/src/properties/computed.ts +35 -45
  178. package/src/properties/deep-state.ts +66 -64
  179. package/src/properties/function-prop.ts +25 -27
  180. package/src/properties/history.ts +34 -35
  181. package/src/properties/index.ts +1 -0
  182. package/src/properties/persisted.ts +30 -20
  183. package/src/properties/prop.ts +30 -22
  184. package/src/properties/ref.ts +12 -10
  185. package/src/properties/resource.ts +22 -17
  186. package/src/properties/selector.ts +45 -0
  187. package/src/properties/slot.ts +28 -22
  188. package/src/properties/state.ts +32 -16
  189. package/src/properties/synced.ts +31 -19
  190. package/src/shared-method.ts +101 -0
  191. package/dist/__tests__/component.test.d.ts +0 -2
  192. package/dist/__tests__/component.test.d.ts.map +0 -1
  193. package/dist/__tests__/component.test.js +0 -147
  194. package/dist/__tests__/component.test.js.map +0 -1
  195. package/dist/__tests__/compose.test.d.ts +0 -2
  196. package/dist/__tests__/compose.test.d.ts.map +0 -1
  197. package/dist/__tests__/compose.test.js +0 -164
  198. package/dist/__tests__/compose.test.js.map +0 -1
  199. package/dist/__tests__/deep-state.test.d.ts +0 -2
  200. package/dist/__tests__/deep-state.test.d.ts.map +0 -1
  201. package/dist/__tests__/deep-state.test.js +0 -221
  202. package/dist/__tests__/deep-state.test.js.map +0 -1
  203. package/dist/__tests__/events.test.d.ts +0 -2
  204. package/dist/__tests__/events.test.d.ts.map +0 -1
  205. package/dist/__tests__/events.test.js +0 -295
  206. package/dist/__tests__/events.test.js.map +0 -1
  207. package/dist/__tests__/functions.test.d.ts +0 -2
  208. package/dist/__tests__/functions.test.d.ts.map +0 -1
  209. package/dist/__tests__/functions.test.js +0 -618
  210. package/dist/__tests__/functions.test.js.map +0 -1
  211. package/dist/__tests__/lazy.test.d.ts +0 -2
  212. package/dist/__tests__/lazy.test.d.ts.map +0 -1
  213. package/dist/__tests__/lazy.test.js +0 -227
  214. package/dist/__tests__/lazy.test.js.map +0 -1
  215. package/dist/__tests__/memo.test.d.ts +0 -2
  216. package/dist/__tests__/memo.test.d.ts.map +0 -1
  217. package/dist/__tests__/memo.test.js +0 -249
  218. package/dist/__tests__/memo.test.js.map +0 -1
  219. package/dist/__tests__/properties-browser.test.d.ts +0 -2
  220. package/dist/__tests__/properties-browser.test.d.ts.map +0 -1
  221. package/dist/__tests__/properties-browser.test.js +0 -227
  222. package/dist/__tests__/properties-browser.test.js.map +0 -1
  223. package/dist/__tests__/properties.test.d.ts +0 -2
  224. package/dist/__tests__/properties.test.d.ts.map +0 -1
  225. package/dist/__tests__/properties.test.js +0 -1253
  226. package/dist/__tests__/properties.test.js.map +0 -1
  227. package/dist/__tests__/ref.test.d.ts +0 -2
  228. package/dist/__tests__/ref.test.d.ts.map +0 -1
  229. package/dist/__tests__/ref.test.js +0 -106
  230. package/dist/__tests__/ref.test.js.map +0 -1
  231. package/dist/__tests__/resource.test.d.ts +0 -2
  232. package/dist/__tests__/resource.test.d.ts.map +0 -1
  233. package/dist/__tests__/resource.test.js +0 -217
  234. package/dist/__tests__/resource.test.js.map +0 -1
  235. package/dist/__tests__/slot.test.d.ts +0 -2
  236. package/dist/__tests__/slot.test.d.ts.map +0 -1
  237. package/dist/__tests__/slot.test.js +0 -147
  238. package/dist/__tests__/slot.test.js.map +0 -1
  239. package/dist/__tests__/synced.test.d.ts +0 -2
  240. package/dist/__tests__/synced.test.d.ts.map +0 -1
  241. package/dist/__tests__/synced.test.js +0 -161
  242. package/dist/__tests__/synced.test.js.map +0 -1
  243. package/dist/__tests__/until.test.d.ts +0 -2
  244. package/dist/__tests__/until.test.d.ts.map +0 -1
  245. package/dist/__tests__/until.test.js +0 -88
  246. package/dist/__tests__/until.test.js.map +0 -1
  247. package/dist/create-accessor-decorator.d.ts +0 -14
  248. package/dist/create-accessor-decorator.d.ts.map +0 -1
  249. package/dist/create-accessor-decorator.js +0 -27
  250. package/dist/create-accessor-decorator.js.map +0 -1
  251. package/src/__tests__/component.test.ts +0 -167
  252. package/src/__tests__/compose.test.ts +0 -202
  253. package/src/__tests__/deep-state.test.ts +0 -265
  254. package/src/__tests__/events.test.ts +0 -366
  255. package/src/__tests__/functions.test.ts +0 -803
  256. package/src/__tests__/lazy.test.ts +0 -296
  257. package/src/__tests__/memo.test.ts +0 -293
  258. package/src/__tests__/properties-browser.test.ts +0 -288
  259. package/src/__tests__/properties.test.ts +0 -1556
  260. package/src/__tests__/ref.test.ts +0 -131
  261. package/src/__tests__/resource.test.ts +0 -246
  262. package/src/__tests__/slot.test.ts +0 -179
  263. package/src/__tests__/synced.test.ts +0 -193
  264. package/src/__tests__/until.test.ts +0 -113
  265. package/src/create-accessor-decorator.ts +0 -45
  266. package/tsconfig.json +0 -9
@@ -1,288 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
3
-
4
- import { StatefulComponent } from "@praxisjs/core";
5
-
6
- import { Persisted } from "../properties/persisted";
7
- import { Slot, initSlots, getSlot } from "../properties/slot";
8
-
9
- beforeEach(() => {
10
- localStorage.clear();
11
- });
12
-
13
- // Helper: creates a ClassFieldDecoratorContext mock
14
- function fieldCtx(name: string) {
15
- const initializers: Array<(this: unknown) => void> = [];
16
- return {
17
- ctx: {
18
- name,
19
- kind: "field" as const,
20
- addInitializer(fn: (this: unknown) => void) {
21
- initializers.push(fn);
22
- },
23
- } as ClassFieldDecoratorContext,
24
- run(instance: unknown) {
25
- initializers.forEach((fn) => { fn.call(instance); });
26
- },
27
- };
28
- }
29
-
30
- // Minimal concrete component
31
- class TestComponent extends StatefulComponent {
32
- render() { return null; }
33
- }
34
-
35
- // ── Persisted ─────────────────────────────────────────────────────────────────
36
-
37
- describe("Persisted decorator", () => {
38
- it("reads back the initial value when nothing is stored", () => {
39
- const { ctx, run } = fieldCtx("theme");
40
- Persisted<string>("theme")(undefined, ctx);
41
-
42
- const instance = new TestComponent();
43
- (instance as unknown as Record<string, unknown>).theme = "light";
44
- run(instance);
45
-
46
- expect((instance as unknown as Record<string, unknown>).theme).toBe("light");
47
- });
48
-
49
- it("persists set value to localStorage", () => {
50
- const { ctx, run } = fieldCtx("lang");
51
- Persisted<string>("lang")(undefined, ctx);
52
-
53
- const instance = new TestComponent();
54
- (instance as unknown as Record<string, unknown>).lang = "en";
55
- run(instance);
56
-
57
- (instance as unknown as Record<string, unknown>).lang = "pt";
58
- expect(localStorage.getItem("lang")).toBe('"pt"');
59
- expect((instance as unknown as Record<string, unknown>).lang).toBe("pt");
60
- });
61
-
62
- it("reads existing localStorage value over the initialValue", () => {
63
- localStorage.setItem("color", '"red"');
64
- const { ctx, run } = fieldCtx("color");
65
- Persisted<string>("color")(undefined, ctx);
66
-
67
- const instance = new TestComponent();
68
- (instance as unknown as Record<string, unknown>).color = "blue";
69
- run(instance);
70
-
71
- expect((instance as unknown as Record<string, unknown>).color).toBe("red");
72
- });
73
-
74
- it("uses the property name as storage key when no explicit key given", () => {
75
- const { ctx, run } = fieldCtx("count");
76
- Persisted<number>()(undefined, ctx);
77
-
78
- const instance = new TestComponent();
79
- (instance as unknown as Record<string, unknown>).count = 0;
80
- run(instance);
81
-
82
- (instance as unknown as Record<string, unknown>).count = 7;
83
- expect(localStorage.getItem("count")).toBe("7");
84
- });
85
-
86
- it("returns separate values per instance", () => {
87
- const { ctx: ctxA, run: runA } = fieldCtx("val");
88
- Persisted<number>("valA")(undefined, ctxA);
89
-
90
- const { ctx: ctxB, run: runB } = fieldCtx("val");
91
- Persisted<number>("valB")(undefined, ctxB);
92
-
93
- const a = new TestComponent();
94
- (a as unknown as Record<string, unknown>).val = 1;
95
- runA(a);
96
-
97
- const b = new TestComponent();
98
- (b as unknown as Record<string, unknown>).val = 2;
99
- runB(b);
100
-
101
- (a as unknown as Record<string, unknown>).val = 10;
102
- (b as unknown as Record<string, unknown>).val = 20;
103
-
104
- expect((a as unknown as Record<string, unknown>).val).toBe(10);
105
- expect((b as unknown as Record<string, unknown>).val).toBe(20);
106
- });
107
- });
108
-
109
- // ── Slot ──────────────────────────────────────────────────────────────────────
110
-
111
- describe("Slot decorator", () => {
112
- it("returns an empty array before initSlots is called", () => {
113
- const { ctx, run } = fieldCtx("default");
114
- Slot()(undefined, ctx);
115
-
116
- const instance = new TestComponent();
117
- run(instance);
118
-
119
- expect((instance as unknown as Record<string, unknown>).default).toEqual([]);
120
- });
121
-
122
- it("returns default slot children after initSlots", () => {
123
- const { ctx, run } = fieldCtx("default");
124
- Slot()(undefined, ctx);
125
-
126
- const instance = new TestComponent();
127
- run(instance);
128
-
129
- const el = document.createElement("span");
130
- initSlots(instance, [el]);
131
-
132
- expect((instance as unknown as Record<string, unknown>).default).toContain(el);
133
- });
134
-
135
- it("resolves named slot children", () => {
136
- const { ctx, run } = fieldCtx("header");
137
- Slot("header")(undefined, ctx);
138
-
139
- const instance = new TestComponent();
140
- run(instance);
141
-
142
- const el = document.createElement("div");
143
- el.setAttribute("slot", "header");
144
- initSlots(instance, [el]);
145
-
146
- const result = (instance as unknown as Record<string, unknown>).header as unknown[];
147
- expect(result).toHaveLength(1);
148
- expect(result[0]).toBe(el);
149
- });
150
-
151
- it("named slot element has slot attribute removed", () => {
152
- const { ctx, run } = fieldCtx("footer");
153
- Slot("footer")(undefined, ctx);
154
-
155
- const instance = new TestComponent();
156
- run(instance);
157
-
158
- const el = document.createElement("div");
159
- el.setAttribute("slot", "footer");
160
- initSlots(instance, [el]);
161
-
162
- expect(el.getAttribute("slot")).toBeNull();
163
- });
164
-
165
- it("warns when attempting to set a slot directly (dev mode)", () => {
166
- const originalEnv = process.env.NODE_ENV;
167
- process.env.NODE_ENV = "development";
168
-
169
- const { ctx, run } = fieldCtx("content");
170
- Slot("content")(undefined, ctx);
171
-
172
- const instance = new TestComponent();
173
- run(instance);
174
-
175
- const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
176
- (instance as unknown as Record<string, unknown>).content = ["something"];
177
- expect(warn).toHaveBeenCalledWith(expect.stringContaining("[Slot]"));
178
- warn.mockRestore();
179
-
180
- process.env.NODE_ENV = originalEnv;
181
- });
182
-
183
- it("uses property name as slot name when no name given and prop is not 'default'", () => {
184
- const { ctx, run } = fieldCtx("sidebar");
185
- Slot()(undefined, ctx);
186
-
187
- const instance = new TestComponent();
188
- run(instance);
189
-
190
- const el = document.createElement("aside");
191
- el.setAttribute("slot", "sidebar");
192
- initSlots(instance, [el]);
193
-
194
- const result = (instance as unknown as Record<string, unknown>).sidebar as unknown[];
195
- expect(result).toHaveLength(1);
196
- });
197
- });
198
-
199
- // ── initSlots / getSlot helpers ───────────────────────────────────────────────
200
-
201
- describe("initSlots / getSlot", () => {
202
- it("splits children into named and default slots", () => {
203
- const named = document.createElement("div");
204
- named.setAttribute("slot", "header");
205
-
206
- const plain = document.createElement("p");
207
-
208
- const instance = {};
209
- initSlots(instance, [named, plain]);
210
-
211
- expect(getSlot(instance, "header")).toContain(named);
212
- expect(getSlot(instance, "default")).toContain(plain);
213
- });
214
-
215
- it("handles null children gracefully", () => {
216
- const instance = {};
217
- initSlots(instance, null);
218
- expect(getSlot(instance, "default")).toEqual([]);
219
- });
220
-
221
- it("flattens nested arrays", () => {
222
- const a = document.createElement("span");
223
- const b = document.createElement("span");
224
- const instance = {};
225
- initSlots(instance, [[a, b]]);
226
- expect(getSlot(instance, "default")).toHaveLength(2);
227
- });
228
-
229
- it("returns empty array for unknown slot", () => {
230
- const instance = {};
231
- initSlots(instance, []);
232
- expect(getSlot(instance, "nonexistent")).toEqual([]);
233
- });
234
- });
235
-
236
- describe("Persisted decorator — unmount cleanup", () => {
237
- function mountPersisted(name: string, key: string) {
238
- const { ctx, run } = fieldCtx(name);
239
- Persisted<string>(key)(undefined, ctx);
240
- const instance = new TestComponent();
241
- (instance as unknown as Record<string, unknown>)[name] = "initial";
242
- run(instance);
243
- return instance as unknown as Record<string, unknown> & { onUnmount?: () => void };
244
- }
245
-
246
- function storageEvent(key: string, newValue: string) {
247
- window.dispatchEvent(new StorageEvent("storage", { key, newValue, storageArea: localStorage }));
248
- }
249
-
250
- it("keeps syncing other tabs while mounted", () => {
251
- const instance = mountPersisted("a", "sync-key");
252
- storageEvent("sync-key", '"remote"');
253
- expect(instance.a).toBe("remote");
254
- });
255
-
256
- it("stops listening for storage events after unmount", () => {
257
- const instance = mountPersisted("a", "gone-key");
258
- instance.onUnmount?.();
259
- storageEvent("gone-key", '"remote"');
260
- expect(instance.a).toBe("initial");
261
- });
262
-
263
- it("repeated mount/unmount cycles do not accumulate window listeners", () => {
264
- const add = vi.spyOn(window, "addEventListener");
265
- const remove = vi.spyOn(window, "removeEventListener");
266
- for (let i = 0; i < 5; i++) mountPersisted("a", "cycle-key").onUnmount?.();
267
- const added = add.mock.calls.filter(([type]) => type === "storage").length;
268
- const removed = remove.mock.calls.filter(([type]) => type === "storage").length;
269
- expect(added).toBe(5);
270
- expect(removed).toBe(5);
271
- add.mockRestore();
272
- remove.mockRestore();
273
- });
274
-
275
- it("two fields sharing a key on one instance share one signal", () => {
276
- const first = fieldCtx("a");
277
- const second = fieldCtx("b");
278
- Persisted<string>("shared-key")(undefined, first.ctx);
279
- Persisted<string>("shared-key")(undefined, second.ctx);
280
- const instance = new TestComponent() as unknown as Record<string, unknown>;
281
- instance.a = "x";
282
- instance.b = "y";
283
- first.run(instance);
284
- second.run(instance);
285
- instance.a = "changed";
286
- expect(instance.b).toBe("changed");
287
- });
288
- });