@praxisjs/decorators 1.6.1 → 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 (270) hide show
  1. package/CHANGELOG.md +46 -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/index.d.ts +1 -1
  55. package/dist/functions/index.d.ts.map +1 -1
  56. package/dist/functions/index.js.map +1 -1
  57. package/dist/functions/log.d.ts +2 -1
  58. package/dist/functions/log.d.ts.map +1 -1
  59. package/dist/functions/log.js +38 -40
  60. package/dist/functions/log.js.map +1 -1
  61. package/dist/functions/memo.d.ts +9 -1
  62. package/dist/functions/memo.d.ts.map +1 -1
  63. package/dist/functions/memo.js +44 -30
  64. package/dist/functions/memo.js.map +1 -1
  65. package/dist/functions/once.d.ts +2 -1
  66. package/dist/functions/once.d.ts.map +1 -1
  67. package/dist/functions/once.js +10 -14
  68. package/dist/functions/once.js.map +1 -1
  69. package/dist/functions/retry.d.ts +2 -1
  70. package/dist/functions/retry.d.ts.map +1 -1
  71. package/dist/functions/retry.js +21 -23
  72. package/dist/functions/retry.js.map +1 -1
  73. package/dist/functions/throttle.d.ts +2 -1
  74. package/dist/functions/throttle.d.ts.map +1 -1
  75. package/dist/functions/throttle.js +12 -14
  76. package/dist/functions/throttle.js.map +1 -1
  77. package/dist/functions/until.d.ts +2 -1
  78. package/dist/functions/until.d.ts.map +1 -1
  79. package/dist/functions/until.js +13 -14
  80. package/dist/functions/until.js.map +1 -1
  81. package/dist/functions/watch.d.ts +1 -1
  82. package/dist/functions/watch.d.ts.map +1 -1
  83. package/dist/functions/watch.js +36 -34
  84. package/dist/functions/watch.js.map +1 -1
  85. package/dist/functions/when.d.ts +1 -1
  86. package/dist/functions/when.d.ts.map +1 -1
  87. package/dist/functions/when.js +19 -21
  88. package/dist/functions/when.js.map +1 -1
  89. package/dist/index.d.ts +6 -6
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +2 -2
  92. package/dist/index.js.map +1 -1
  93. package/dist/lifecycle.d.ts +24 -0
  94. package/dist/lifecycle.d.ts.map +1 -0
  95. package/dist/lifecycle.js +55 -0
  96. package/dist/lifecycle.js.map +1 -0
  97. package/dist/properties/compose.d.ts +3 -2
  98. package/dist/properties/compose.d.ts.map +1 -1
  99. package/dist/properties/compose.js +33 -19
  100. package/dist/properties/compose.js.map +1 -1
  101. package/dist/properties/computed.d.ts +12 -10
  102. package/dist/properties/computed.d.ts.map +1 -1
  103. package/dist/properties/computed.js +26 -21
  104. package/dist/properties/computed.js.map +1 -1
  105. package/dist/properties/deep-state.d.ts +2 -1
  106. package/dist/properties/deep-state.d.ts.map +1 -1
  107. package/dist/properties/deep-state.js +66 -49
  108. package/dist/properties/deep-state.js.map +1 -1
  109. package/dist/properties/function-prop.d.ts +2 -1
  110. package/dist/properties/function-prop.d.ts.map +1 -1
  111. package/dist/properties/function-prop.js +20 -22
  112. package/dist/properties/function-prop.js.map +1 -1
  113. package/dist/properties/history.d.ts +2 -2
  114. package/dist/properties/history.d.ts.map +1 -1
  115. package/dist/properties/history.js +34 -31
  116. package/dist/properties/history.js.map +1 -1
  117. package/dist/properties/index.d.ts +1 -0
  118. package/dist/properties/index.d.ts.map +1 -1
  119. package/dist/properties/index.js +1 -0
  120. package/dist/properties/index.js.map +1 -1
  121. package/dist/properties/persisted.d.ts +2 -1
  122. package/dist/properties/persisted.d.ts.map +1 -1
  123. package/dist/properties/persisted.js +30 -19
  124. package/dist/properties/persisted.js.map +1 -1
  125. package/dist/properties/prop.d.ts +2 -1
  126. package/dist/properties/prop.d.ts.map +1 -1
  127. package/dist/properties/prop.js +26 -22
  128. package/dist/properties/prop.js.map +1 -1
  129. package/dist/properties/ref.d.ts +2 -1
  130. package/dist/properties/ref.d.ts.map +1 -1
  131. package/dist/properties/ref.js +11 -8
  132. package/dist/properties/ref.js.map +1 -1
  133. package/dist/properties/resource.d.ts.map +1 -1
  134. package/dist/properties/resource.js +21 -16
  135. package/dist/properties/resource.js.map +1 -1
  136. package/dist/properties/selector.d.ts +12 -0
  137. package/dist/properties/selector.d.ts.map +1 -0
  138. package/dist/properties/selector.js +25 -0
  139. package/dist/properties/selector.js.map +1 -0
  140. package/dist/properties/slot.d.ts +2 -1
  141. package/dist/properties/slot.d.ts.map +1 -1
  142. package/dist/properties/slot.js +22 -20
  143. package/dist/properties/slot.js.map +1 -1
  144. package/dist/properties/state.d.ts.map +1 -1
  145. package/dist/properties/state.js +27 -16
  146. package/dist/properties/state.js.map +1 -1
  147. package/dist/properties/synced.d.ts +2 -1
  148. package/dist/properties/synced.d.ts.map +1 -1
  149. package/dist/properties/synced.js +27 -19
  150. package/dist/properties/synced.js.map +1 -1
  151. package/dist/shared-method.d.ts +14 -0
  152. package/dist/shared-method.d.ts.map +1 -0
  153. package/dist/shared-method.js +87 -0
  154. package/dist/shared-method.js.map +1 -0
  155. package/package.json +14 -5
  156. package/src/component/component.ts +8 -27
  157. package/src/component/lazy.ts +51 -41
  158. package/src/create-class-decorator.ts +90 -31
  159. package/src/create-field-decorator.ts +120 -43
  160. package/src/create-getter-decorator.ts +263 -66
  161. package/src/create-lifecycle-method-decorator.ts +50 -28
  162. package/src/create-method-decorator.ts +119 -13
  163. package/src/events/command.ts +11 -9
  164. package/src/events/emit.ts +16 -22
  165. package/src/events/on-command.ts +17 -18
  166. package/src/field.ts +116 -0
  167. package/src/functions/bind.ts +4 -4
  168. package/src/functions/debounce.ts +25 -23
  169. package/src/functions/index.ts +1 -1
  170. package/src/functions/log.ts +44 -46
  171. package/src/functions/memo.ts +54 -28
  172. package/src/functions/once.ts +10 -13
  173. package/src/functions/retry.ts +19 -21
  174. package/src/functions/throttle.ts +12 -13
  175. package/src/functions/until.ts +14 -14
  176. package/src/functions/watch.ts +37 -36
  177. package/src/functions/when.ts +18 -20
  178. package/src/index.ts +13 -5
  179. package/src/lifecycle.ts +74 -0
  180. package/src/properties/compose.ts +33 -20
  181. package/src/properties/computed.ts +35 -45
  182. package/src/properties/deep-state.ts +68 -53
  183. package/src/properties/function-prop.ts +25 -27
  184. package/src/properties/history.ts +34 -33
  185. package/src/properties/index.ts +1 -0
  186. package/src/properties/persisted.ts +41 -27
  187. package/src/properties/prop.ts +30 -22
  188. package/src/properties/ref.ts +12 -10
  189. package/src/properties/resource.ts +22 -17
  190. package/src/properties/selector.ts +45 -0
  191. package/src/properties/slot.ts +28 -22
  192. package/src/properties/state.ts +32 -16
  193. package/src/properties/synced.ts +31 -19
  194. package/src/shared-method.ts +101 -0
  195. package/dist/__tests__/component.test.d.ts +0 -2
  196. package/dist/__tests__/component.test.d.ts.map +0 -1
  197. package/dist/__tests__/component.test.js +0 -147
  198. package/dist/__tests__/component.test.js.map +0 -1
  199. package/dist/__tests__/compose.test.d.ts +0 -2
  200. package/dist/__tests__/compose.test.d.ts.map +0 -1
  201. package/dist/__tests__/compose.test.js +0 -164
  202. package/dist/__tests__/compose.test.js.map +0 -1
  203. package/dist/__tests__/deep-state.test.d.ts +0 -2
  204. package/dist/__tests__/deep-state.test.d.ts.map +0 -1
  205. package/dist/__tests__/deep-state.test.js +0 -150
  206. package/dist/__tests__/deep-state.test.js.map +0 -1
  207. package/dist/__tests__/events.test.d.ts +0 -2
  208. package/dist/__tests__/events.test.d.ts.map +0 -1
  209. package/dist/__tests__/events.test.js +0 -295
  210. package/dist/__tests__/events.test.js.map +0 -1
  211. package/dist/__tests__/functions.test.d.ts +0 -2
  212. package/dist/__tests__/functions.test.d.ts.map +0 -1
  213. package/dist/__tests__/functions.test.js +0 -618
  214. package/dist/__tests__/functions.test.js.map +0 -1
  215. package/dist/__tests__/lazy.test.d.ts +0 -2
  216. package/dist/__tests__/lazy.test.d.ts.map +0 -1
  217. package/dist/__tests__/lazy.test.js +0 -227
  218. package/dist/__tests__/lazy.test.js.map +0 -1
  219. package/dist/__tests__/memo.test.d.ts +0 -2
  220. package/dist/__tests__/memo.test.d.ts.map +0 -1
  221. package/dist/__tests__/memo.test.js +0 -168
  222. package/dist/__tests__/memo.test.js.map +0 -1
  223. package/dist/__tests__/properties-browser.test.d.ts +0 -2
  224. package/dist/__tests__/properties-browser.test.d.ts.map +0 -1
  225. package/dist/__tests__/properties-browser.test.js +0 -178
  226. package/dist/__tests__/properties-browser.test.js.map +0 -1
  227. package/dist/__tests__/properties.test.d.ts +0 -2
  228. package/dist/__tests__/properties.test.d.ts.map +0 -1
  229. package/dist/__tests__/properties.test.js +0 -1226
  230. package/dist/__tests__/properties.test.js.map +0 -1
  231. package/dist/__tests__/ref.test.d.ts +0 -2
  232. package/dist/__tests__/ref.test.d.ts.map +0 -1
  233. package/dist/__tests__/ref.test.js +0 -106
  234. package/dist/__tests__/ref.test.js.map +0 -1
  235. package/dist/__tests__/resource.test.d.ts +0 -2
  236. package/dist/__tests__/resource.test.d.ts.map +0 -1
  237. package/dist/__tests__/resource.test.js +0 -217
  238. package/dist/__tests__/resource.test.js.map +0 -1
  239. package/dist/__tests__/slot.test.d.ts +0 -2
  240. package/dist/__tests__/slot.test.d.ts.map +0 -1
  241. package/dist/__tests__/slot.test.js +0 -147
  242. package/dist/__tests__/slot.test.js.map +0 -1
  243. package/dist/__tests__/synced.test.d.ts +0 -2
  244. package/dist/__tests__/synced.test.d.ts.map +0 -1
  245. package/dist/__tests__/synced.test.js +0 -161
  246. package/dist/__tests__/synced.test.js.map +0 -1
  247. package/dist/__tests__/until.test.d.ts +0 -2
  248. package/dist/__tests__/until.test.d.ts.map +0 -1
  249. package/dist/__tests__/until.test.js +0 -88
  250. package/dist/__tests__/until.test.js.map +0 -1
  251. package/dist/create-accessor-decorator.d.ts +0 -14
  252. package/dist/create-accessor-decorator.d.ts.map +0 -1
  253. package/dist/create-accessor-decorator.js +0 -27
  254. package/dist/create-accessor-decorator.js.map +0 -1
  255. package/src/__tests__/component.test.ts +0 -167
  256. package/src/__tests__/compose.test.ts +0 -202
  257. package/src/__tests__/deep-state.test.ts +0 -185
  258. package/src/__tests__/events.test.ts +0 -366
  259. package/src/__tests__/functions.test.ts +0 -803
  260. package/src/__tests__/lazy.test.ts +0 -296
  261. package/src/__tests__/memo.test.ts +0 -208
  262. package/src/__tests__/properties-browser.test.ts +0 -234
  263. package/src/__tests__/properties.test.ts +0 -1526
  264. package/src/__tests__/ref.test.ts +0 -131
  265. package/src/__tests__/resource.test.ts +0 -246
  266. package/src/__tests__/slot.test.ts +0 -179
  267. package/src/__tests__/synced.test.ts +0 -193
  268. package/src/__tests__/until.test.ts +0 -113
  269. package/src/create-accessor-decorator.ts +0 -45
  270. package/tsconfig.json +0 -9
@@ -1,202 +0,0 @@
1
- import { describe, it, expect, vi } from "vitest";
2
-
3
- import { StatefulComponent } from "@praxisjs/core";
4
- import { signal } from "@praxisjs/core/internal";
5
- import type { Composable } from "@praxisjs/core";
6
-
7
- import { Compose } from "../properties/compose";
8
-
9
- function fieldCtx(name: string) {
10
- const initializers: Array<(this: unknown) => void> = [];
11
- return {
12
- ctx: {
13
- name,
14
- kind: "field" as const,
15
- addInitializer(fn: (this: unknown) => void) {
16
- initializers.push(fn);
17
- },
18
- } as ClassFieldDecoratorContext,
19
- run(instance: unknown) {
20
- initializers.forEach((fn) => { fn.call(instance); });
21
- },
22
- };
23
- }
24
-
25
- class TestComponent extends StatefulComponent {
26
- render() { return null; }
27
- }
28
-
29
- // Simple composable returning plain values
30
- class CounterComposable implements Composable {
31
- count = 0;
32
- setup() {
33
- return { count: this.count, label: "counter" };
34
- }
35
- }
36
-
37
- // Composable returning a signal (reactive property)
38
- class ReactiveComposable implements Composable {
39
- _count = signal(0);
40
- setup() {
41
- return { count: this._count };
42
- }
43
- }
44
-
45
- // Composable with lifecycle hooks
46
- class LifecycleComposable implements Composable {
47
- onMount = vi.fn();
48
- onUnmount = vi.fn();
49
- setup() {
50
- return { active: true };
51
- }
52
- }
53
-
54
- // Composable that uses constructor args
55
- class ArgsComposable implements Composable {
56
- constructor(private multiplier: number) {}
57
- setup() {
58
- return { multiplier: this.multiplier };
59
- }
60
- }
61
-
62
- // Composable returning a plain function (non-reactive) in its view
63
- class PlainFnComposable {
64
- setup() {
65
- return { handler: () => 42 };
66
- }
67
- }
68
-
69
- describe("@Compose decorator", () => {
70
- it("creates a view getter on the field", () => {
71
- const { ctx, run } = fieldCtx("counter");
72
- Compose(CounterComposable)(undefined, ctx);
73
-
74
- const instance = new TestComponent();
75
- run(instance);
76
-
77
- const view = (instance as unknown as Record<string, unknown>).counter as Record<string, unknown>;
78
- expect(view).toBeDefined();
79
- expect(view.count).toBe(0);
80
- expect(view.label).toBe("counter");
81
- });
82
-
83
- it("reactive signal values are resolved via getter", () => {
84
- const { ctx, run } = fieldCtx("reactive");
85
- Compose(ReactiveComposable)(undefined, ctx);
86
-
87
- const instance = new TestComponent();
88
- run(instance);
89
-
90
- const view = (instance as unknown as Record<string, unknown>).reactive as Record<string, unknown>;
91
- expect(view.count).toBe(0);
92
- });
93
-
94
- it("calls onMount when component mounts", () => {
95
- const { ctx, run } = fieldCtx("lc");
96
- Compose(LifecycleComposable)(undefined, ctx);
97
-
98
- const instance = new TestComponent();
99
- run(instance);
100
-
101
- const view = (instance as unknown as Record<string, unknown>).lc as Record<string, unknown>;
102
- expect(view).toBeDefined();
103
-
104
- // Trigger onMount
105
- instance.onMount!();
106
- const lc = new LifecycleComposable();
107
- // The composable's onMount would have been called
108
- // We verify the instance has an onMount wiring
109
- expect(typeof instance.onMount).toBe("function");
110
- });
111
-
112
- it("calls onUnmount when component unmounts", () => {
113
- const { ctx, run } = fieldCtx("lc2");
114
- Compose(LifecycleComposable)(undefined, ctx);
115
-
116
- const instance = new TestComponent();
117
- run(instance);
118
- expect(typeof instance.onUnmount).toBe("function");
119
- });
120
-
121
- it("invoking instance.onUnmount() executes the composable's onUnmount", () => {
122
- const unmountSpy = vi.fn();
123
-
124
- class SpyComposable {
125
- onUnmount = unmountSpy;
126
- setup() { return {}; }
127
- }
128
-
129
- const { ctx, run } = fieldCtx("spyComp");
130
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
- Compose(SpyComposable as any)(undefined, ctx);
132
-
133
- const instance = new TestComponent();
134
- run(instance);
135
-
136
- instance.onUnmount!();
137
- expect(unmountSpy).toHaveBeenCalled();
138
- });
139
-
140
- it("passes constructor args to the composable", () => {
141
- const { ctx, run } = fieldCtx("args");
142
- Compose(ArgsComposable, 5)(undefined, ctx);
143
-
144
- const instance = new TestComponent();
145
- run(instance);
146
-
147
- const view = (instance as unknown as Record<string, unknown>).args as Record<string, unknown>;
148
- expect(view.multiplier).toBe(5);
149
- });
150
-
151
- it("resolves string constructor args from instance properties", () => {
152
- const { ctx, run } = fieldCtx("resolved");
153
- Compose(ArgsComposable, "myMultiplier")(undefined, ctx);
154
-
155
- const instance = new TestComponent();
156
- (instance as unknown as Record<string, unknown>).myMultiplier = 10;
157
- run(instance);
158
-
159
- const view = (instance as unknown as Record<string, unknown>).resolved as Record<string, unknown>;
160
- expect(view.multiplier).toBe(10);
161
- });
162
-
163
- it("each instance gets its own composable view", () => {
164
- const { ctx, run } = fieldCtx("c");
165
- Compose(CounterComposable)(undefined, ctx);
166
-
167
- const a = new TestComponent();
168
- const b = new TestComponent();
169
- run(a);
170
- run(b);
171
-
172
- const viewA = (a as unknown as Record<string, unknown>).c;
173
- const viewB = (b as unknown as Record<string, unknown>).c;
174
- expect(viewA).not.toBe(viewB);
175
- });
176
-
177
- it("plain function value in view is stored as-is (not treated as reactive)", () => {
178
- const { ctx, run } = fieldCtx("pfn");
179
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
180
- Compose(PlainFnComposable as any)(undefined, ctx);
181
-
182
- const instance = new TestComponent();
183
- run(instance);
184
-
185
- const view = (instance as unknown as Record<string, unknown>).pfn as Record<string, unknown>;
186
- // handler is a plain function — isReactive returns false, stored directly
187
- expect(typeof view.handler).toBe("function");
188
- expect((view.handler as () => number)()).toBe(42);
189
- });
190
-
191
- it("view getter always returns same view object", () => {
192
- const { ctx, run } = fieldCtx("stable");
193
- Compose(CounterComposable)(undefined, ctx);
194
-
195
- const instance = new TestComponent();
196
- run(instance);
197
-
198
- const v1 = (instance as unknown as Record<string, unknown>).stable;
199
- const v2 = (instance as unknown as Record<string, unknown>).stable;
200
- expect(v1).toBe(v2);
201
- });
202
- });
@@ -1,185 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
-
3
- import { StatefulComponent } from "@praxisjs/core";
4
- import { effect } from "@praxisjs/core/internal";
5
-
6
- import { DeepState } from "../properties/deep-state";
7
-
8
- class TestComponent extends StatefulComponent {
9
- render() { return null; }
10
- }
11
-
12
- function fieldCtx(name: string) {
13
- const initializers: Array<(this: unknown) => void> = [];
14
- return {
15
- ctx: {
16
- name,
17
- kind: "field" as const,
18
- addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
19
- } as ClassFieldDecoratorContext,
20
- run(instance: unknown) {
21
- initializers.forEach((fn) => { fn.call(instance); });
22
- },
23
- };
24
- }
25
-
26
- describe("@DeepState decorator", () => {
27
- it("reads initial value", () => {
28
- const { ctx, run } = fieldCtx("config");
29
- DeepState()(undefined, ctx);
30
- const instance = new TestComponent();
31
- (instance as unknown as Record<string, unknown>).config = { theme: "light" };
32
- run(instance);
33
-
34
- const inst = instance as unknown as { config: { theme: string } };
35
- expect(inst.config.theme).toBe("light");
36
- });
37
-
38
- it("nested object mutation triggers reactive effect", () => {
39
- const { ctx, run } = fieldCtx("config");
40
- DeepState()(undefined, ctx);
41
- const instance = new TestComponent();
42
- (instance as unknown as Record<string, unknown>).config = { theme: { mode: "light" } };
43
- run(instance);
44
-
45
- const inst = instance as unknown as { config: { theme: { mode: string } } };
46
- const log: string[] = [];
47
- effect(() => { log.push(inst.config.theme.mode); });
48
-
49
- inst.config.theme.mode = "dark";
50
- expect(log).toEqual(["light", "dark"]);
51
- });
52
-
53
- it("array push triggers reactive effect", () => {
54
- const { ctx, run } = fieldCtx("items");
55
- DeepState()(undefined, ctx);
56
- const instance = new TestComponent();
57
- (instance as unknown as Record<string, unknown>).items = [1, 2];
58
- run(instance);
59
-
60
- const inst = instance as unknown as { items: number[] };
61
- const log: number[][] = [];
62
- effect(() => { log.push([...inst.items]); });
63
-
64
- inst.items.push(3);
65
- expect(log[log.length - 1]).toEqual([1, 2, 3]);
66
- });
67
-
68
- it("array index assignment triggers reactive effect", () => {
69
- const { ctx, run } = fieldCtx("list");
70
- DeepState()(undefined, ctx);
71
- const instance = new TestComponent();
72
- (instance as unknown as Record<string, unknown>).list = ["a", "b"];
73
- run(instance);
74
-
75
- const inst = instance as unknown as { list: string[] };
76
- const log: string[][] = [];
77
- effect(() => { log.push([...inst.list]); });
78
-
79
- inst.list[0] = "z";
80
- expect(log[log.length - 1]).toEqual(["z", "b"]);
81
- });
82
-
83
- it("property deletion triggers reactive effect", () => {
84
- const { ctx, run } = fieldCtx("obj");
85
- DeepState()(undefined, ctx);
86
- const instance = new TestComponent();
87
- (instance as unknown as Record<string, unknown>).obj = { a: 1, b: 2 };
88
- run(instance);
89
-
90
- const inst = instance as unknown as { obj: Record<string, number> };
91
- const log: string[][] = [];
92
- effect(() => { log.push(Object.keys(inst.obj)); });
93
-
94
- delete inst.obj.a;
95
- expect(log[log.length - 1]).not.toContain("a");
96
- });
97
-
98
- it("direct assignment replaces root and triggers effect", () => {
99
- const { ctx, run } = fieldCtx("data");
100
- DeepState()(undefined, ctx);
101
- const instance = new TestComponent();
102
- (instance as unknown as Record<string, unknown>).data = { x: 1 };
103
- run(instance);
104
-
105
- const inst = instance as unknown as { data: { x: number } };
106
- const log: number[] = [];
107
- effect(() => { log.push(inst.data.x); });
108
-
109
- inst.data = { x: 99 };
110
- expect(log).toEqual([1, 99]);
111
- });
112
-
113
- it("mutations on new root after reassignment are reactive", () => {
114
- const { ctx, run } = fieldCtx("cfg");
115
- DeepState()(undefined, ctx);
116
- const instance = new TestComponent();
117
- (instance as unknown as Record<string, unknown>).cfg = { v: 0 };
118
- run(instance);
119
-
120
- const inst = instance as unknown as { cfg: { v: number } };
121
- inst.cfg = { v: 10 };
122
-
123
- const log: number[] = [];
124
- effect(() => { log.push(inst.cfg.v); });
125
-
126
- inst.cfg.v = 20;
127
- expect(log).toEqual([10, 20]);
128
- });
129
-
130
- it("null initial value is handled gracefully", () => {
131
- const { ctx, run } = fieldCtx("maybe");
132
- DeepState()(undefined, ctx);
133
- const instance = new TestComponent();
134
- (instance as unknown as Record<string, unknown>).maybe = null;
135
- run(instance);
136
-
137
- const inst = instance as unknown as { maybe: null | { x: number } };
138
- expect(inst.maybe).toBeNull();
139
-
140
- const log: unknown[] = [];
141
- effect(() => { log.push(inst.maybe); });
142
-
143
- inst.maybe = { x: 5 };
144
- expect(log).toEqual([null, { x: 5 }]);
145
- });
146
-
147
- it("setter with null value — proxy not created, value is null", () => {
148
- const { ctx, run } = fieldCtx("nullable");
149
- DeepState()(undefined, ctx);
150
- const instance = new TestComponent();
151
- (instance as unknown as Record<string, unknown>).nullable = { x: 1 };
152
- run(instance);
153
-
154
- const inst = instance as unknown as { nullable: { x: number } | null };
155
- inst.nullable = null;
156
- expect(inst.nullable).toBeNull();
157
- });
158
-
159
- it("setter with primitive value — value stored directly without proxy", () => {
160
- const { ctx, run } = fieldCtx("num");
161
- DeepState()(undefined, ctx);
162
- const instance = new TestComponent();
163
- (instance as unknown as Record<string, unknown>).num = { v: 0 };
164
- run(instance);
165
-
166
- const inst = instance as unknown as { num: number | { v: number } };
167
- inst.num = 99;
168
- expect(inst.num).toBe(99);
169
- });
170
-
171
- it("deeply nested mutation triggers effect", () => {
172
- const { ctx, run } = fieldCtx("deep");
173
- DeepState()(undefined, ctx);
174
- const instance = new TestComponent();
175
- (instance as unknown as Record<string, unknown>).deep = { a: { b: { c: 0 } } };
176
- run(instance);
177
-
178
- const inst = instance as unknown as { deep: { a: { b: { c: number } } } };
179
- const log: number[] = [];
180
- effect(() => { log.push(inst.deep.a.b.c); });
181
-
182
- inst.deep.a.b.c = 42;
183
- expect(log).toEqual([0, 42]);
184
- });
185
- });