@thomas-siegfried/tapout 0.0.1 → 0.0.2

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 (47) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1205 -1205
  3. package/package.json +55 -55
  4. package/src/applyBindings.ts +372 -372
  5. package/src/arrayToDomMapping.ts +300 -300
  6. package/src/attributeInterpolationMarkup.ts +91 -91
  7. package/src/bindingContext.ts +207 -207
  8. package/src/bindingEvent.ts +198 -198
  9. package/src/bindingProvider.ts +260 -260
  10. package/src/bindings.ts +963 -963
  11. package/src/compareArrays.ts +122 -122
  12. package/src/componentBinding.ts +318 -318
  13. package/src/componentDecorator.ts +62 -62
  14. package/src/components.ts +367 -367
  15. package/src/computed.ts +439 -439
  16. package/src/configure.ts +52 -52
  17. package/src/controlFlowBindings.ts +206 -206
  18. package/src/core.ts +60 -60
  19. package/src/decorators.ts +407 -407
  20. package/src/dependencyDetection.ts +76 -76
  21. package/src/disposable.ts +36 -36
  22. package/src/domData.ts +42 -42
  23. package/src/domNodeDisposal.ts +94 -94
  24. package/src/effects.ts +29 -29
  25. package/src/event.ts +173 -173
  26. package/src/expressionRewriting.ts +219 -219
  27. package/src/extenders.ts +102 -102
  28. package/src/filters.ts +91 -91
  29. package/src/index.ts +150 -150
  30. package/src/interpolationMarkup.ts +130 -130
  31. package/src/memoization.ts +71 -71
  32. package/src/namespacedBindings.ts +132 -132
  33. package/src/observable.ts +48 -48
  34. package/src/observableArray.ts +397 -397
  35. package/src/options.ts +25 -25
  36. package/src/selectExtensions.ts +68 -68
  37. package/src/slotBinding.ts +84 -84
  38. package/src/subscribable.ts +266 -266
  39. package/src/tasks.ts +79 -79
  40. package/src/templateEngine.ts +108 -108
  41. package/src/templateRendering.ts +399 -399
  42. package/src/templateRewriting.ts +94 -94
  43. package/src/templateSources.ts +134 -134
  44. package/src/utils.ts +123 -123
  45. package/src/utilsDom.ts +87 -87
  46. package/src/virtualElements.ts +153 -153
  47. package/src/wireParams.ts +49 -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
+ }