@thomas-siegfried/tapout 0.0.1 → 0.0.3

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 (52) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1207 -1205
  3. package/decorator-config.md +220 -0
  4. package/dist/wireParams.d.ts.map +1 -1
  5. package/dist/wireParams.js +27 -4
  6. package/dist/wireParams.js.map +1 -1
  7. package/llms.txt +360 -0
  8. package/package.json +57 -55
  9. package/src/applyBindings.ts +372 -372
  10. package/src/arrayToDomMapping.ts +300 -300
  11. package/src/attributeInterpolationMarkup.ts +91 -91
  12. package/src/bindingContext.ts +207 -207
  13. package/src/bindingEvent.ts +198 -198
  14. package/src/bindingProvider.ts +260 -260
  15. package/src/bindings.ts +963 -963
  16. package/src/compareArrays.ts +122 -122
  17. package/src/componentBinding.ts +318 -318
  18. package/src/componentDecorator.ts +62 -62
  19. package/src/components.ts +367 -367
  20. package/src/computed.ts +439 -439
  21. package/src/configure.ts +52 -52
  22. package/src/controlFlowBindings.ts +206 -206
  23. package/src/core.ts +60 -60
  24. package/src/decorators.ts +407 -407
  25. package/src/dependencyDetection.ts +76 -76
  26. package/src/disposable.ts +36 -36
  27. package/src/domData.ts +42 -42
  28. package/src/domNodeDisposal.ts +94 -94
  29. package/src/effects.ts +29 -29
  30. package/src/event.ts +173 -173
  31. package/src/expressionRewriting.ts +219 -219
  32. package/src/extenders.ts +102 -102
  33. package/src/filters.ts +91 -91
  34. package/src/index.ts +150 -150
  35. package/src/interpolationMarkup.ts +130 -130
  36. package/src/memoization.ts +71 -71
  37. package/src/namespacedBindings.ts +132 -132
  38. package/src/observable.ts +48 -48
  39. package/src/observableArray.ts +397 -397
  40. package/src/options.ts +25 -25
  41. package/src/selectExtensions.ts +68 -68
  42. package/src/slotBinding.ts +84 -84
  43. package/src/subscribable.ts +266 -266
  44. package/src/tasks.ts +79 -79
  45. package/src/templateEngine.ts +108 -108
  46. package/src/templateRendering.ts +399 -399
  47. package/src/templateRewriting.ts +94 -94
  48. package/src/templateSources.ts +134 -134
  49. package/src/utils.ts +123 -123
  50. package/src/utilsDom.ts +87 -87
  51. package/src/virtualElements.ts +153 -153
  52. package/src/wireParams.ts +69 -49
package/src/decorators.ts CHANGED
@@ -1,407 +1,407 @@
1
- import { Observable } from './observable.js';
2
- import { ObservableArray } from './observableArray.js';
3
- import { Computed } from './computed.js';
4
- import type { Subscribable } from './subscribable.js';
5
- import type { ExtenderOptions } from './extenders.js';
6
- import { registerDependency } from './dependencyDetection.js';
7
-
8
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
- type AnySubscribable = Subscribable<any>;
10
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
- type AnyAccessorValue = ClassAccessorDecoratorTarget<any, any>;
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- type AnyAccessorContext = ClassAccessorDecoratorContext<any, any>;
14
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
- type AnyAccessorResult = ClassAccessorDecoratorResult<any, any>;
16
-
17
- // --- Observable registry for getObservable() ---
18
-
19
- const accessorRegistry = new Map<string | symbol, WeakMap<object, AnySubscribable>>();
20
- const computedStorage = new WeakMap<object, Map<string | symbol, Computed<unknown>>>();
21
-
22
- function storeObservable(instance: object, key: string | symbol, obs: AnySubscribable): void {
23
- let instanceMap = accessorRegistry.get(key);
24
- if (!instanceMap) {
25
- instanceMap = new WeakMap();
26
- accessorRegistry.set(key, instanceMap);
27
- }
28
- instanceMap.set(instance, obs);
29
- }
30
-
31
- function lookupObservable(instance: object, key: string | symbol): AnySubscribable | undefined {
32
- const instanceMap = accessorRegistry.get(key);
33
- if (instanceMap) {
34
- return instanceMap.get(instance);
35
- }
36
- const compMap = computedStorage.get(instance);
37
- if (compMap) {
38
- return compMap.get(key);
39
- }
40
- return undefined;
41
- }
42
-
43
- // --- Detection helper ---
44
-
45
- function isStage3Context(arg: unknown): arg is { kind: string; name: string | symbol } {
46
- return typeof arg === 'object' && arg !== null && 'kind' in arg;
47
- }
48
-
49
- // --- @reactive: Stage 3 accessor path ---
50
-
51
- function createStage3Reactive(extenders?: ExtenderOptions) {
52
- return (_value: AnyAccessorValue, context: AnyAccessorContext): AnyAccessorResult => ({
53
- init(initialValue: unknown): unknown {
54
- const obs = new Observable(initialValue);
55
- if (extenders) obs.extend(extenders);
56
- storeObservable(this as object, context.name, obs);
57
- return obs;
58
- },
59
- get(this: object): unknown {
60
- const obs = lookupObservable(this, context.name) as Observable<unknown>;
61
- return obs.get();
62
- },
63
- set(this: object, newValue: unknown): void {
64
- const obs = lookupObservable(this, context.name) as Observable<unknown>;
65
- obs.set(newValue);
66
- },
67
- });
68
- }
69
-
70
- // --- @reactive: Legacy property decorator path ---
71
-
72
- interface LegacyFieldDescriptor {
73
- configurable?: boolean;
74
- enumerable?: boolean;
75
- writable?: boolean;
76
- initializer?: (this: object) => unknown;
77
- }
78
-
79
- function applyLegacyReactive(
80
- target: object,
81
- propertyKey: string | symbol,
82
- descriptor?: LegacyFieldDescriptor,
83
- extenders?: ExtenderOptions,
84
- ): PropertyDescriptor {
85
- const initializer = descriptor?.initializer;
86
- const newDesc: PropertyDescriptor = {
87
- configurable: true,
88
- enumerable: true,
89
- get(this: object) {
90
- let obs = lookupObservable(this, propertyKey);
91
- if (!obs) {
92
- const initValue = initializer ? initializer.call(this) : undefined;
93
- obs = new Observable<unknown>(initValue);
94
- if (extenders) obs.extend(extenders);
95
- storeObservable(this, propertyKey, obs);
96
- }
97
- return (obs as Observable<unknown>).get();
98
- },
99
- set(this: object, value: unknown) {
100
- let obs = lookupObservable(this, propertyKey);
101
- if (!obs) {
102
- obs = new Observable<unknown>(value);
103
- if (extenders) obs.extend(extenders);
104
- storeObservable(this, propertyKey, obs);
105
- } else {
106
- (obs as Observable<unknown>).set(value);
107
- }
108
- },
109
- };
110
- if (!descriptor || typeof descriptor.initializer === 'undefined') {
111
- Object.defineProperty(target, propertyKey, newDesc);
112
- }
113
- return newDesc;
114
- }
115
-
116
- // --- @reactive: Unified decorator ---
117
-
118
- /**
119
- * A decorator function returned by reactive(options) / reactiveArray(options).
120
- * Works as either a Stage 3 accessor decorator or a legacy property decorator.
121
- */
122
- export interface ReactivePropertyDecorator {
123
- <This, Value>(value: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>): ClassAccessorDecoratorResult<This, Value>;
124
- (target: object, propertyKey: string | symbol): void;
125
- }
126
-
127
- export function reactive<This, Value>(value: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>): ClassAccessorDecoratorResult<This, Value>;
128
- export function reactive(target: object, propertyKey: string | symbol): void;
129
- export function reactive(target: object, propertyKey: string | symbol, descriptor: LegacyFieldDescriptor): PropertyDescriptor;
130
- export function reactive(options: ExtenderOptions): ReactivePropertyDecorator;
131
- export function reactive(
132
- valueOrOptions: unknown,
133
- contextOrKey?: unknown,
134
- descriptor?: LegacyFieldDescriptor,
135
- ): unknown {
136
- if (contextOrKey === undefined) {
137
- const ext = valueOrOptions as ExtenderOptions;
138
- return function (target: unknown, contextOrKey2: unknown, descriptor2?: LegacyFieldDescriptor) {
139
- if (isStage3Context(contextOrKey2)) {
140
- return createStage3Reactive(ext)(target as AnyAccessorValue, contextOrKey2 as AnyAccessorContext);
141
- }
142
- return applyLegacyReactive(target as object, contextOrKey2 as string | symbol, descriptor2, ext);
143
- };
144
- }
145
- if (isStage3Context(contextOrKey)) {
146
- return createStage3Reactive()(valueOrOptions as AnyAccessorValue, contextOrKey as AnyAccessorContext);
147
- }
148
- return applyLegacyReactive(valueOrOptions as object, contextOrKey as string | symbol, descriptor);
149
- }
150
-
151
- // --- @reactiveArray: Stage 3 accessor path ---
152
-
153
- function createStage3ReactiveArray(extenders?: ExtenderOptions) {
154
- return (_value: AnyAccessorValue, context: AnyAccessorContext): AnyAccessorResult => ({
155
- init(initialValue: unknown): unknown {
156
- const arr = Array.isArray(initialValue) ? initialValue : [];
157
- const obs = new ObservableArray(arr);
158
- if (extenders) obs.extend(extenders);
159
- storeObservable(this as object, context.name, obs);
160
- return obs;
161
- },
162
- get(this: object): unknown {
163
- const obs = lookupObservable(this, context.name) as ObservableArray<unknown>;
164
- registerDependency(obs);
165
- return obs;
166
- },
167
- set(this: object, newValue: unknown): void {
168
- const obs = lookupObservable(this, context.name) as ObservableArray<unknown>;
169
- obs.set(newValue as unknown[]);
170
- },
171
- });
172
- }
173
-
174
- // --- @reactiveArray: Legacy property decorator path ---
175
-
176
- function applyLegacyReactiveArray(
177
- target: object,
178
- propertyKey: string | symbol,
179
- descriptor?: LegacyFieldDescriptor,
180
- extenders?: ExtenderOptions,
181
- ): PropertyDescriptor {
182
- const initializer = descriptor?.initializer;
183
- const newDesc: PropertyDescriptor = {
184
- configurable: true,
185
- enumerable: true,
186
- get(this: object) {
187
- let obs = lookupObservable(this, propertyKey);
188
- if (!obs) {
189
- const initValue = initializer ? initializer.call(this) : undefined;
190
- const arr = Array.isArray(initValue) ? initValue : [];
191
- obs = new ObservableArray<unknown>(arr);
192
- if (extenders) obs.extend(extenders);
193
- storeObservable(this, propertyKey, obs);
194
- }
195
- registerDependency(obs);
196
- return obs;
197
- },
198
- set(this: object, value: unknown) {
199
- let obs = lookupObservable(this, propertyKey);
200
- if (!obs) {
201
- const arr = Array.isArray(value) ? value : [];
202
- obs = new ObservableArray<unknown>(arr);
203
- if (extenders) obs.extend(extenders);
204
- storeObservable(this, propertyKey, obs);
205
- } else {
206
- (obs as ObservableArray<unknown>).set(value as unknown[]);
207
- }
208
- },
209
- };
210
- if (!descriptor || typeof descriptor.initializer === 'undefined') {
211
- Object.defineProperty(target, propertyKey, newDesc);
212
- }
213
- return newDesc;
214
- }
215
-
216
- // --- @reactiveArray: Unified decorator ---
217
-
218
- export function reactiveArray<This, Value>(value: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>): ClassAccessorDecoratorResult<This, Value>;
219
- export function reactiveArray(target: object, propertyKey: string | symbol): void;
220
- export function reactiveArray(target: object, propertyKey: string | symbol, descriptor: LegacyFieldDescriptor): PropertyDescriptor;
221
- export function reactiveArray(options: ExtenderOptions): ReactivePropertyDecorator;
222
- export function reactiveArray(
223
- valueOrOptions: unknown,
224
- contextOrKey?: unknown,
225
- descriptor?: LegacyFieldDescriptor,
226
- ): unknown {
227
- if (contextOrKey === undefined) {
228
- const ext = valueOrOptions as ExtenderOptions;
229
- return function (target: unknown, contextOrKey2: unknown, descriptor2?: LegacyFieldDescriptor) {
230
- if (isStage3Context(contextOrKey2)) {
231
- return createStage3ReactiveArray(ext)(target as AnyAccessorValue, contextOrKey2 as AnyAccessorContext);
232
- }
233
- return applyLegacyReactiveArray(target as object, contextOrKey2 as string | symbol, descriptor2, ext);
234
- };
235
- }
236
- if (isStage3Context(contextOrKey)) {
237
- return createStage3ReactiveArray()(valueOrOptions as AnyAccessorValue, contextOrKey as AnyAccessorContext);
238
- }
239
- return applyLegacyReactiveArray(valueOrOptions as object, contextOrKey as string | symbol, descriptor);
240
- }
241
-
242
- // --- @computed ---
243
-
244
- function getOrCreateComputed<T>(
245
- instance: object,
246
- key: string | symbol,
247
- readFn: () => T,
248
- writeFn?: (value: T) => void,
249
- ): Computed<T> {
250
- let map = computedStorage.get(instance);
251
- if (!map) {
252
- map = new Map();
253
- computedStorage.set(instance, map);
254
- }
255
- let comp = map.get(key) as Computed<T> | undefined;
256
- if (!comp) {
257
- comp = new Computed<T>({
258
- read: readFn,
259
- write: writeFn,
260
- deferEvaluation: true,
261
- });
262
- map.set(key, comp as Computed<unknown>);
263
- }
264
- return comp;
265
- }
266
-
267
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
268
- type AnyFunction = (...args: any[]) => any;
269
-
270
- // Stage 3 @computed implementation
271
- function applyStage3Computed(
272
- value: AnyFunction,
273
- context: { kind: string; name: string | symbol },
274
- ): AnyFunction | void {
275
- const { kind, name } = context;
276
-
277
- if (kind === 'getter') {
278
- const originalGetter = value;
279
- return function (this: object) {
280
- const comp = getOrCreateComputed(this, name, originalGetter.bind(this));
281
- return comp.get();
282
- };
283
- }
284
-
285
- if (kind === 'setter') {
286
- const originalSetter = value;
287
- return function (this: object, newValue: unknown): void {
288
- const map = computedStorage.get(this);
289
- const comp = map?.get(name) as Computed<unknown> | undefined;
290
- if (comp) {
291
- comp.set(newValue);
292
- } else {
293
- originalSetter.call(this, newValue);
294
- }
295
- };
296
- }
297
-
298
- if (kind === 'method') {
299
- const originalMethod = value;
300
- return function (this: object): unknown {
301
- const comp = getOrCreateComputed(this, name, originalMethod.bind(this));
302
- return comp.get();
303
- };
304
- }
305
- }
306
-
307
- // Legacy @computed implementation (method / getter+setter decorator)
308
- function applyLegacyComputed(
309
- _target: object,
310
- propertyKey: string | symbol,
311
- descriptor: PropertyDescriptor,
312
- ): PropertyDescriptor {
313
- if (descriptor.get) {
314
- const originalGetter = descriptor.get;
315
- const originalSetter = descriptor.set;
316
-
317
- descriptor.get = function (this: object) {
318
- const comp = getOrCreateComputed(
319
- this, propertyKey,
320
- originalGetter.bind(this),
321
- originalSetter ? originalSetter.bind(this) : undefined,
322
- );
323
- return comp.get();
324
- };
325
-
326
- if (originalSetter) {
327
- descriptor.set = function (this: object, value: unknown): void {
328
- const map = computedStorage.get(this);
329
- const comp = map?.get(propertyKey) as Computed<unknown> | undefined;
330
- if (comp) {
331
- comp.set(value);
332
- } else {
333
- originalSetter.call(this, value);
334
- }
335
- };
336
- }
337
-
338
- return descriptor;
339
- }
340
-
341
- if (typeof descriptor.value === 'function') {
342
- const originalMethod = descriptor.value as AnyFunction;
343
- descriptor.value = function (this: object): unknown {
344
- const comp = getOrCreateComputed(this, propertyKey, originalMethod.bind(this));
345
- return comp.get();
346
- };
347
- return descriptor;
348
- }
349
-
350
- return descriptor;
351
- }
352
-
353
- // --- @computed: Unified decorator ---
354
-
355
- /**
356
- * A decorator function returned by or used as @computed.
357
- * Works as either a Stage 3 getter/setter/method decorator or a legacy method decorator.
358
- */
359
- export interface ComputedMethodDecorator {
360
- <This, Value>(value: AnyFunction, context: ClassGetterDecoratorContext<This, Value> | ClassMethodDecoratorContext<This, AnyFunction> | ClassSetterDecoratorContext<This, Value>): AnyFunction | void;
361
- (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
362
- }
363
-
364
- // Stage 3 overloads
365
- export function computed<This, Value>(
366
- value: AnyFunction,
367
- context: ClassGetterDecoratorContext<This, Value> | ClassMethodDecoratorContext<This, AnyFunction> | ClassSetterDecoratorContext<This, Value>,
368
- ): AnyFunction | void;
369
- // Legacy overload
370
- export function computed(
371
- target: object,
372
- propertyKey: string | symbol,
373
- descriptor: PropertyDescriptor,
374
- ): PropertyDescriptor | void;
375
- // Implementation
376
- export function computed(
377
- valueOrTarget: unknown,
378
- contextOrKey?: unknown,
379
- descriptor?: PropertyDescriptor,
380
- ): unknown {
381
- if (isStage3Context(contextOrKey)) {
382
- return applyStage3Computed(
383
- valueOrTarget as AnyFunction,
384
- contextOrKey as { kind: string; name: string | symbol },
385
- );
386
- }
387
- return applyLegacyComputed(
388
- valueOrTarget as object,
389
- contextOrKey as string | symbol,
390
- descriptor!,
391
- );
392
- }
393
-
394
- // --- getObservable() / replaceObservable() ---
395
-
396
- export function getObservable(target: object, key: string | symbol): AnySubscribable | undefined {
397
- let obs = lookupObservable(target, key);
398
- if (!obs) {
399
- void (target as Record<string | symbol, unknown>)[key];
400
- obs = lookupObservable(target, key);
401
- }
402
- return obs;
403
- }
404
-
405
- export function replaceObservable(instance: object, key: string | symbol, obs: AnySubscribable): void {
406
- storeObservable(instance, key, obs);
407
- }
1
+ import { Observable } from './observable.js';
2
+ import { ObservableArray } from './observableArray.js';
3
+ import { Computed } from './computed.js';
4
+ import type { Subscribable } from './subscribable.js';
5
+ import type { ExtenderOptions } from './extenders.js';
6
+ import { registerDependency } from './dependencyDetection.js';
7
+
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ type AnySubscribable = Subscribable<any>;
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ type AnyAccessorValue = ClassAccessorDecoratorTarget<any, any>;
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ type AnyAccessorContext = ClassAccessorDecoratorContext<any, any>;
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ type AnyAccessorResult = ClassAccessorDecoratorResult<any, any>;
16
+
17
+ // --- Observable registry for getObservable() ---
18
+
19
+ const accessorRegistry = new Map<string | symbol, WeakMap<object, AnySubscribable>>();
20
+ const computedStorage = new WeakMap<object, Map<string | symbol, Computed<unknown>>>();
21
+
22
+ function storeObservable(instance: object, key: string | symbol, obs: AnySubscribable): void {
23
+ let instanceMap = accessorRegistry.get(key);
24
+ if (!instanceMap) {
25
+ instanceMap = new WeakMap();
26
+ accessorRegistry.set(key, instanceMap);
27
+ }
28
+ instanceMap.set(instance, obs);
29
+ }
30
+
31
+ function lookupObservable(instance: object, key: string | symbol): AnySubscribable | undefined {
32
+ const instanceMap = accessorRegistry.get(key);
33
+ if (instanceMap) {
34
+ return instanceMap.get(instance);
35
+ }
36
+ const compMap = computedStorage.get(instance);
37
+ if (compMap) {
38
+ return compMap.get(key);
39
+ }
40
+ return undefined;
41
+ }
42
+
43
+ // --- Detection helper ---
44
+
45
+ function isStage3Context(arg: unknown): arg is { kind: string; name: string | symbol } {
46
+ return typeof arg === 'object' && arg !== null && 'kind' in arg;
47
+ }
48
+
49
+ // --- @reactive: Stage 3 accessor path ---
50
+
51
+ function createStage3Reactive(extenders?: ExtenderOptions) {
52
+ return (_value: AnyAccessorValue, context: AnyAccessorContext): AnyAccessorResult => ({
53
+ init(initialValue: unknown): unknown {
54
+ const obs = new Observable(initialValue);
55
+ if (extenders) obs.extend(extenders);
56
+ storeObservable(this as object, context.name, obs);
57
+ return obs;
58
+ },
59
+ get(this: object): unknown {
60
+ const obs = lookupObservable(this, context.name) as Observable<unknown>;
61
+ return obs.get();
62
+ },
63
+ set(this: object, newValue: unknown): void {
64
+ const obs = lookupObservable(this, context.name) as Observable<unknown>;
65
+ obs.set(newValue);
66
+ },
67
+ });
68
+ }
69
+
70
+ // --- @reactive: Legacy property decorator path ---
71
+
72
+ interface LegacyFieldDescriptor {
73
+ configurable?: boolean;
74
+ enumerable?: boolean;
75
+ writable?: boolean;
76
+ initializer?: (this: object) => unknown;
77
+ }
78
+
79
+ function applyLegacyReactive(
80
+ target: object,
81
+ propertyKey: string | symbol,
82
+ descriptor?: LegacyFieldDescriptor,
83
+ extenders?: ExtenderOptions,
84
+ ): PropertyDescriptor {
85
+ const initializer = descriptor?.initializer;
86
+ const newDesc: PropertyDescriptor = {
87
+ configurable: true,
88
+ enumerable: true,
89
+ get(this: object) {
90
+ let obs = lookupObservable(this, propertyKey);
91
+ if (!obs) {
92
+ const initValue = initializer ? initializer.call(this) : undefined;
93
+ obs = new Observable<unknown>(initValue);
94
+ if (extenders) obs.extend(extenders);
95
+ storeObservable(this, propertyKey, obs);
96
+ }
97
+ return (obs as Observable<unknown>).get();
98
+ },
99
+ set(this: object, value: unknown) {
100
+ let obs = lookupObservable(this, propertyKey);
101
+ if (!obs) {
102
+ obs = new Observable<unknown>(value);
103
+ if (extenders) obs.extend(extenders);
104
+ storeObservable(this, propertyKey, obs);
105
+ } else {
106
+ (obs as Observable<unknown>).set(value);
107
+ }
108
+ },
109
+ };
110
+ if (!descriptor || typeof descriptor.initializer === 'undefined') {
111
+ Object.defineProperty(target, propertyKey, newDesc);
112
+ }
113
+ return newDesc;
114
+ }
115
+
116
+ // --- @reactive: Unified decorator ---
117
+
118
+ /**
119
+ * A decorator function returned by reactive(options) / reactiveArray(options).
120
+ * Works as either a Stage 3 accessor decorator or a legacy property decorator.
121
+ */
122
+ export interface ReactivePropertyDecorator {
123
+ <This, Value>(value: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>): ClassAccessorDecoratorResult<This, Value>;
124
+ (target: object, propertyKey: string | symbol): void;
125
+ }
126
+
127
+ export function reactive<This, Value>(value: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>): ClassAccessorDecoratorResult<This, Value>;
128
+ export function reactive(target: object, propertyKey: string | symbol): void;
129
+ export function reactive(target: object, propertyKey: string | symbol, descriptor: LegacyFieldDescriptor): PropertyDescriptor;
130
+ export function reactive(options: ExtenderOptions): ReactivePropertyDecorator;
131
+ export function reactive(
132
+ valueOrOptions: unknown,
133
+ contextOrKey?: unknown,
134
+ descriptor?: LegacyFieldDescriptor,
135
+ ): unknown {
136
+ if (contextOrKey === undefined) {
137
+ const ext = valueOrOptions as ExtenderOptions;
138
+ return function (target: unknown, contextOrKey2: unknown, descriptor2?: LegacyFieldDescriptor) {
139
+ if (isStage3Context(contextOrKey2)) {
140
+ return createStage3Reactive(ext)(target as AnyAccessorValue, contextOrKey2 as AnyAccessorContext);
141
+ }
142
+ return applyLegacyReactive(target as object, contextOrKey2 as string | symbol, descriptor2, ext);
143
+ };
144
+ }
145
+ if (isStage3Context(contextOrKey)) {
146
+ return createStage3Reactive()(valueOrOptions as AnyAccessorValue, contextOrKey as AnyAccessorContext);
147
+ }
148
+ return applyLegacyReactive(valueOrOptions as object, contextOrKey as string | symbol, descriptor);
149
+ }
150
+
151
+ // --- @reactiveArray: Stage 3 accessor path ---
152
+
153
+ function createStage3ReactiveArray(extenders?: ExtenderOptions) {
154
+ return (_value: AnyAccessorValue, context: AnyAccessorContext): AnyAccessorResult => ({
155
+ init(initialValue: unknown): unknown {
156
+ const arr = Array.isArray(initialValue) ? initialValue : [];
157
+ const obs = new ObservableArray(arr);
158
+ if (extenders) obs.extend(extenders);
159
+ storeObservable(this as object, context.name, obs);
160
+ return obs;
161
+ },
162
+ get(this: object): unknown {
163
+ const obs = lookupObservable(this, context.name) as ObservableArray<unknown>;
164
+ registerDependency(obs);
165
+ return obs;
166
+ },
167
+ set(this: object, newValue: unknown): void {
168
+ const obs = lookupObservable(this, context.name) as ObservableArray<unknown>;
169
+ obs.set(newValue as unknown[]);
170
+ },
171
+ });
172
+ }
173
+
174
+ // --- @reactiveArray: Legacy property decorator path ---
175
+
176
+ function applyLegacyReactiveArray(
177
+ target: object,
178
+ propertyKey: string | symbol,
179
+ descriptor?: LegacyFieldDescriptor,
180
+ extenders?: ExtenderOptions,
181
+ ): PropertyDescriptor {
182
+ const initializer = descriptor?.initializer;
183
+ const newDesc: PropertyDescriptor = {
184
+ configurable: true,
185
+ enumerable: true,
186
+ get(this: object) {
187
+ let obs = lookupObservable(this, propertyKey);
188
+ if (!obs) {
189
+ const initValue = initializer ? initializer.call(this) : undefined;
190
+ const arr = Array.isArray(initValue) ? initValue : [];
191
+ obs = new ObservableArray<unknown>(arr);
192
+ if (extenders) obs.extend(extenders);
193
+ storeObservable(this, propertyKey, obs);
194
+ }
195
+ registerDependency(obs);
196
+ return obs;
197
+ },
198
+ set(this: object, value: unknown) {
199
+ let obs = lookupObservable(this, propertyKey);
200
+ if (!obs) {
201
+ const arr = Array.isArray(value) ? value : [];
202
+ obs = new ObservableArray<unknown>(arr);
203
+ if (extenders) obs.extend(extenders);
204
+ storeObservable(this, propertyKey, obs);
205
+ } else {
206
+ (obs as ObservableArray<unknown>).set(value as unknown[]);
207
+ }
208
+ },
209
+ };
210
+ if (!descriptor || typeof descriptor.initializer === 'undefined') {
211
+ Object.defineProperty(target, propertyKey, newDesc);
212
+ }
213
+ return newDesc;
214
+ }
215
+
216
+ // --- @reactiveArray: Unified decorator ---
217
+
218
+ export function reactiveArray<This, Value>(value: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>): ClassAccessorDecoratorResult<This, Value>;
219
+ export function reactiveArray(target: object, propertyKey: string | symbol): void;
220
+ export function reactiveArray(target: object, propertyKey: string | symbol, descriptor: LegacyFieldDescriptor): PropertyDescriptor;
221
+ export function reactiveArray(options: ExtenderOptions): ReactivePropertyDecorator;
222
+ export function reactiveArray(
223
+ valueOrOptions: unknown,
224
+ contextOrKey?: unknown,
225
+ descriptor?: LegacyFieldDescriptor,
226
+ ): unknown {
227
+ if (contextOrKey === undefined) {
228
+ const ext = valueOrOptions as ExtenderOptions;
229
+ return function (target: unknown, contextOrKey2: unknown, descriptor2?: LegacyFieldDescriptor) {
230
+ if (isStage3Context(contextOrKey2)) {
231
+ return createStage3ReactiveArray(ext)(target as AnyAccessorValue, contextOrKey2 as AnyAccessorContext);
232
+ }
233
+ return applyLegacyReactiveArray(target as object, contextOrKey2 as string | symbol, descriptor2, ext);
234
+ };
235
+ }
236
+ if (isStage3Context(contextOrKey)) {
237
+ return createStage3ReactiveArray()(valueOrOptions as AnyAccessorValue, contextOrKey as AnyAccessorContext);
238
+ }
239
+ return applyLegacyReactiveArray(valueOrOptions as object, contextOrKey as string | symbol, descriptor);
240
+ }
241
+
242
+ // --- @computed ---
243
+
244
+ function getOrCreateComputed<T>(
245
+ instance: object,
246
+ key: string | symbol,
247
+ readFn: () => T,
248
+ writeFn?: (value: T) => void,
249
+ ): Computed<T> {
250
+ let map = computedStorage.get(instance);
251
+ if (!map) {
252
+ map = new Map();
253
+ computedStorage.set(instance, map);
254
+ }
255
+ let comp = map.get(key) as Computed<T> | undefined;
256
+ if (!comp) {
257
+ comp = new Computed<T>({
258
+ read: readFn,
259
+ write: writeFn,
260
+ deferEvaluation: true,
261
+ });
262
+ map.set(key, comp as Computed<unknown>);
263
+ }
264
+ return comp;
265
+ }
266
+
267
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
268
+ type AnyFunction = (...args: any[]) => any;
269
+
270
+ // Stage 3 @computed implementation
271
+ function applyStage3Computed(
272
+ value: AnyFunction,
273
+ context: { kind: string; name: string | symbol },
274
+ ): AnyFunction | void {
275
+ const { kind, name } = context;
276
+
277
+ if (kind === 'getter') {
278
+ const originalGetter = value;
279
+ return function (this: object) {
280
+ const comp = getOrCreateComputed(this, name, originalGetter.bind(this));
281
+ return comp.get();
282
+ };
283
+ }
284
+
285
+ if (kind === 'setter') {
286
+ const originalSetter = value;
287
+ return function (this: object, newValue: unknown): void {
288
+ const map = computedStorage.get(this);
289
+ const comp = map?.get(name) as Computed<unknown> | undefined;
290
+ if (comp) {
291
+ comp.set(newValue);
292
+ } else {
293
+ originalSetter.call(this, newValue);
294
+ }
295
+ };
296
+ }
297
+
298
+ if (kind === 'method') {
299
+ const originalMethod = value;
300
+ return function (this: object): unknown {
301
+ const comp = getOrCreateComputed(this, name, originalMethod.bind(this));
302
+ return comp.get();
303
+ };
304
+ }
305
+ }
306
+
307
+ // Legacy @computed implementation (method / getter+setter decorator)
308
+ function applyLegacyComputed(
309
+ _target: object,
310
+ propertyKey: string | symbol,
311
+ descriptor: PropertyDescriptor,
312
+ ): PropertyDescriptor {
313
+ if (descriptor.get) {
314
+ const originalGetter = descriptor.get;
315
+ const originalSetter = descriptor.set;
316
+
317
+ descriptor.get = function (this: object) {
318
+ const comp = getOrCreateComputed(
319
+ this, propertyKey,
320
+ originalGetter.bind(this),
321
+ originalSetter ? originalSetter.bind(this) : undefined,
322
+ );
323
+ return comp.get();
324
+ };
325
+
326
+ if (originalSetter) {
327
+ descriptor.set = function (this: object, value: unknown): void {
328
+ const map = computedStorage.get(this);
329
+ const comp = map?.get(propertyKey) as Computed<unknown> | undefined;
330
+ if (comp) {
331
+ comp.set(value);
332
+ } else {
333
+ originalSetter.call(this, value);
334
+ }
335
+ };
336
+ }
337
+
338
+ return descriptor;
339
+ }
340
+
341
+ if (typeof descriptor.value === 'function') {
342
+ const originalMethod = descriptor.value as AnyFunction;
343
+ descriptor.value = function (this: object): unknown {
344
+ const comp = getOrCreateComputed(this, propertyKey, originalMethod.bind(this));
345
+ return comp.get();
346
+ };
347
+ return descriptor;
348
+ }
349
+
350
+ return descriptor;
351
+ }
352
+
353
+ // --- @computed: Unified decorator ---
354
+
355
+ /**
356
+ * A decorator function returned by or used as @computed.
357
+ * Works as either a Stage 3 getter/setter/method decorator or a legacy method decorator.
358
+ */
359
+ export interface ComputedMethodDecorator {
360
+ <This, Value>(value: AnyFunction, context: ClassGetterDecoratorContext<This, Value> | ClassMethodDecoratorContext<This, AnyFunction> | ClassSetterDecoratorContext<This, Value>): AnyFunction | void;
361
+ (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
362
+ }
363
+
364
+ // Stage 3 overloads
365
+ export function computed<This, Value>(
366
+ value: AnyFunction,
367
+ context: ClassGetterDecoratorContext<This, Value> | ClassMethodDecoratorContext<This, AnyFunction> | ClassSetterDecoratorContext<This, Value>,
368
+ ): AnyFunction | void;
369
+ // Legacy overload
370
+ export function computed(
371
+ target: object,
372
+ propertyKey: string | symbol,
373
+ descriptor: PropertyDescriptor,
374
+ ): PropertyDescriptor | void;
375
+ // Implementation
376
+ export function computed(
377
+ valueOrTarget: unknown,
378
+ contextOrKey?: unknown,
379
+ descriptor?: PropertyDescriptor,
380
+ ): unknown {
381
+ if (isStage3Context(contextOrKey)) {
382
+ return applyStage3Computed(
383
+ valueOrTarget as AnyFunction,
384
+ contextOrKey as { kind: string; name: string | symbol },
385
+ );
386
+ }
387
+ return applyLegacyComputed(
388
+ valueOrTarget as object,
389
+ contextOrKey as string | symbol,
390
+ descriptor!,
391
+ );
392
+ }
393
+
394
+ // --- getObservable() / replaceObservable() ---
395
+
396
+ export function getObservable(target: object, key: string | symbol): AnySubscribable | undefined {
397
+ let obs = lookupObservable(target, key);
398
+ if (!obs) {
399
+ void (target as Record<string | symbol, unknown>)[key];
400
+ obs = lookupObservable(target, key);
401
+ }
402
+ return obs;
403
+ }
404
+
405
+ export function replaceObservable(instance: object, key: string | symbol, obs: AnySubscribable): void {
406
+ storeObservable(instance, key, obs);
407
+ }