@thomas-siegfried/tapout 0.0.1
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.
- package/LICENSE +21 -0
- package/README.md +1206 -0
- package/dist/applyBindings.d.ts +10 -0
- package/dist/applyBindings.d.ts.map +1 -0
- package/dist/applyBindings.js +261 -0
- package/dist/applyBindings.js.map +1 -0
- package/dist/arrayToDomMapping.d.ts +14 -0
- package/dist/arrayToDomMapping.d.ts.map +1 -0
- package/dist/arrayToDomMapping.js +219 -0
- package/dist/arrayToDomMapping.js.map +1 -0
- package/dist/attributeInterpolationMarkup.d.ts +6 -0
- package/dist/attributeInterpolationMarkup.d.ts.map +1 -0
- package/dist/attributeInterpolationMarkup.js +70 -0
- package/dist/attributeInterpolationMarkup.js.map +1 -0
- package/dist/bindingContext.d.ts +38 -0
- package/dist/bindingContext.d.ts.map +1 -0
- package/dist/bindingContext.js +138 -0
- package/dist/bindingContext.js.map +1 -0
- package/dist/bindingEvent.d.ts +33 -0
- package/dist/bindingEvent.d.ts.map +1 -0
- package/dist/bindingEvent.js +144 -0
- package/dist/bindingEvent.js.map +1 -0
- package/dist/bindingProvider.d.ts +38 -0
- package/dist/bindingProvider.d.ts.map +1 -0
- package/dist/bindingProvider.js +145 -0
- package/dist/bindingProvider.js.map +1 -0
- package/dist/bindings.d.ts +2 -0
- package/dist/bindings.d.ts.map +1 -0
- package/dist/bindings.js +862 -0
- package/dist/bindings.js.map +1 -0
- package/dist/compareArrays.d.ts +13 -0
- package/dist/compareArrays.d.ts.map +1 -0
- package/dist/compareArrays.js +91 -0
- package/dist/compareArrays.js.map +1 -0
- package/dist/componentBinding.d.ts +4 -0
- package/dist/componentBinding.d.ts.map +1 -0
- package/dist/componentBinding.js +240 -0
- package/dist/componentBinding.js.map +1 -0
- package/dist/componentDecorator.d.ts +22 -0
- package/dist/componentDecorator.d.ts.map +1 -0
- package/dist/componentDecorator.js +32 -0
- package/dist/componentDecorator.js.map +1 -0
- package/dist/components.d.ts +34 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +256 -0
- package/dist/components.js.map +1 -0
- package/dist/computed.d.ts +54 -0
- package/dist/computed.d.ts.map +1 -0
- package/dist/computed.js +376 -0
- package/dist/computed.js.map +1 -0
- package/dist/configure.d.ts +4 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +48 -0
- package/dist/configure.js.map +1 -0
- package/dist/controlFlowBindings.d.ts +2 -0
- package/dist/controlFlowBindings.d.ts.map +1 -0
- package/dist/controlFlowBindings.js +151 -0
- package/dist/controlFlowBindings.js.map +1 -0
- package/dist/core.d.ts +29 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +27 -0
- package/dist/core.js.map +1 -0
- package/dist/decorators.d.ts +40 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +276 -0
- package/dist/decorators.js.map +1 -0
- package/dist/dependencyDetection.d.ts +21 -0
- package/dist/dependencyDetection.d.ts.map +1 -0
- package/dist/dependencyDetection.js +53 -0
- package/dist/dependencyDetection.js.map +1 -0
- package/dist/disposable.d.ts +12 -0
- package/dist/disposable.d.ts.map +1 -0
- package/dist/disposable.js +29 -0
- package/dist/disposable.js.map +1 -0
- package/dist/domData.d.ts +6 -0
- package/dist/domData.d.ts.map +1 -0
- package/dist/domData.js +40 -0
- package/dist/domData.js.map +1 -0
- package/dist/domNodeDisposal.d.ts +7 -0
- package/dist/domNodeDisposal.d.ts.map +1 -0
- package/dist/domNodeDisposal.js +78 -0
- package/dist/domNodeDisposal.js.map +1 -0
- package/dist/effects.d.ts +13 -0
- package/dist/effects.d.ts.map +1 -0
- package/dist/effects.js +24 -0
- package/dist/effects.js.map +1 -0
- package/dist/event.d.ts +38 -0
- package/dist/event.d.ts.map +1 -0
- package/dist/event.js +138 -0
- package/dist/event.js.map +1 -0
- package/dist/expressionRewriting.d.ts +22 -0
- package/dist/expressionRewriting.d.ts.map +1 -0
- package/dist/expressionRewriting.js +169 -0
- package/dist/expressionRewriting.js.map +1 -0
- package/dist/extenders.d.ts +17 -0
- package/dist/extenders.d.ts.map +1 -0
- package/dist/extenders.js +75 -0
- package/dist/extenders.js.map +1 -0
- package/dist/filters.d.ts +5 -0
- package/dist/filters.d.ts.map +1 -0
- package/dist/filters.js +81 -0
- package/dist/filters.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/interpolationMarkup.d.ts +6 -0
- package/dist/interpolationMarkup.d.ts.map +1 -0
- package/dist/interpolationMarkup.js +110 -0
- package/dist/interpolationMarkup.js.map +1 -0
- package/dist/memoization.d.ts +7 -0
- package/dist/memoization.d.ts.map +1 -0
- package/dist/memoization.js +59 -0
- package/dist/memoization.js.map +1 -0
- package/dist/namespacedBindings.d.ts +7 -0
- package/dist/namespacedBindings.d.ts.map +1 -0
- package/dist/namespacedBindings.js +81 -0
- package/dist/namespacedBindings.js.map +1 -0
- package/dist/observable.d.ts +13 -0
- package/dist/observable.d.ts.map +1 -0
- package/dist/observable.js +39 -0
- package/dist/observable.js.map +1 -0
- package/dist/observableArray.d.ts +58 -0
- package/dist/observableArray.d.ts.map +1 -0
- package/dist/observableArray.js +328 -0
- package/dist/observableArray.js.map +1 -0
- package/dist/options.d.ts +14 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +12 -0
- package/dist/options.js.map +1 -0
- package/dist/selectExtensions.d.ts +4 -0
- package/dist/selectExtensions.d.ts.map +1 -0
- package/dist/selectExtensions.js +67 -0
- package/dist/selectExtensions.js.map +1 -0
- package/dist/slotBinding.d.ts +2 -0
- package/dist/slotBinding.d.ts.map +1 -0
- package/dist/slotBinding.js +63 -0
- package/dist/slotBinding.js.map +1 -0
- package/dist/subscribable.d.ts +57 -0
- package/dist/subscribable.d.ts.map +1 -0
- package/dist/subscribable.js +211 -0
- package/dist/subscribable.js.map +1 -0
- package/dist/tasks.d.ts +8 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +68 -0
- package/dist/tasks.js.map +1 -0
- package/dist/templateEngine.d.ts +37 -0
- package/dist/templateEngine.d.ts.map +1 -0
- package/dist/templateEngine.js +60 -0
- package/dist/templateEngine.js.map +1 -0
- package/dist/templateRendering.d.ts +8 -0
- package/dist/templateRendering.d.ts.map +1 -0
- package/dist/templateRendering.js +322 -0
- package/dist/templateRendering.js.map +1 -0
- package/dist/templateRewriting.d.ts +7 -0
- package/dist/templateRewriting.d.ts.map +1 -0
- package/dist/templateRewriting.js +63 -0
- package/dist/templateRewriting.js.map +1 -0
- package/dist/templateSources.d.ts +19 -0
- package/dist/templateSources.d.ts.map +1 -0
- package/dist/templateSources.js +110 -0
- package/dist/templateSources.js.map +1 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +100 -0
- package/dist/utils.js.map +1 -0
- package/dist/utilsDom.d.ts +8 -0
- package/dist/utilsDom.d.ts.map +1 -0
- package/dist/utilsDom.js +81 -0
- package/dist/utilsDom.js.map +1 -0
- package/dist/virtualElements.d.ts +12 -0
- package/dist/virtualElements.d.ts.map +1 -0
- package/dist/virtualElements.js +141 -0
- package/dist/virtualElements.js.map +1 -0
- package/dist/wireParams.d.ts +6 -0
- package/dist/wireParams.d.ts.map +1 -0
- package/dist/wireParams.js +39 -0
- package/dist/wireParams.js.map +1 -0
- package/package.json +55 -0
- package/src/applyBindings.ts +372 -0
- package/src/arrayToDomMapping.ts +300 -0
- package/src/attributeInterpolationMarkup.ts +91 -0
- package/src/bindingContext.ts +207 -0
- package/src/bindingEvent.ts +198 -0
- package/src/bindingProvider.ts +260 -0
- package/src/bindings.ts +963 -0
- package/src/compareArrays.ts +122 -0
- package/src/componentBinding.ts +318 -0
- package/src/componentDecorator.ts +62 -0
- package/src/components.ts +367 -0
- package/src/computed.ts +439 -0
- package/src/configure.ts +52 -0
- package/src/controlFlowBindings.ts +206 -0
- package/src/core.ts +60 -0
- package/src/decorators.ts +407 -0
- package/src/dependencyDetection.ts +76 -0
- package/src/disposable.ts +36 -0
- package/src/domData.ts +42 -0
- package/src/domNodeDisposal.ts +94 -0
- package/src/effects.ts +29 -0
- package/src/event.ts +173 -0
- package/src/expressionRewriting.ts +219 -0
- package/src/extenders.ts +102 -0
- package/src/filters.ts +91 -0
- package/src/index.ts +150 -0
- package/src/interpolationMarkup.ts +130 -0
- package/src/memoization.ts +71 -0
- package/src/namespacedBindings.ts +132 -0
- package/src/observable.ts +48 -0
- package/src/observableArray.ts +397 -0
- package/src/options.ts +25 -0
- package/src/selectExtensions.ts +68 -0
- package/src/slotBinding.ts +84 -0
- package/src/subscribable.ts +266 -0
- package/src/tasks.ts +79 -0
- package/src/templateEngine.ts +108 -0
- package/src/templateRendering.ts +399 -0
- package/src/templateRewriting.ts +94 -0
- package/src/templateSources.ts +134 -0
- package/src/utils.ts +123 -0
- package/src/utilsDom.ts +87 -0
- package/src/virtualElements.ts +153 -0
- package/src/wireParams.ts +49 -0
package/src/core.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export { options } from './options.js';
|
|
2
|
+
|
|
3
|
+
export const version = '1.0.0';
|
|
4
|
+
|
|
5
|
+
export { Subscribable, Subscription, isSubscribable, isReadableSubscribable, valuesArePrimitiveAndEqual } from './subscribable.js';
|
|
6
|
+
export type { SubscriptionCallback, ReadableSubscribable } from './subscribable.js';
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
begin,
|
|
10
|
+
end,
|
|
11
|
+
registerDependency,
|
|
12
|
+
ignore,
|
|
13
|
+
getDependenciesCount,
|
|
14
|
+
getDependencies,
|
|
15
|
+
isInitial,
|
|
16
|
+
getCurrentComputed,
|
|
17
|
+
} from './dependencyDetection.js';
|
|
18
|
+
export type { TrackingFrame } from './dependencyDetection.js';
|
|
19
|
+
|
|
20
|
+
export { Observable, isObservable } from './observable.js';
|
|
21
|
+
|
|
22
|
+
export { ObservableArray, isObservableArray, DESTROY, isDestroyed } from './observableArray.js';
|
|
23
|
+
|
|
24
|
+
export { compareArrays, findMovesInArrayComparison } from './compareArrays.js';
|
|
25
|
+
export type { ArrayChange, CompareArraysOptions } from './compareArrays.js';
|
|
26
|
+
|
|
27
|
+
export { Computed, PureComputed, isComputed, isPureComputed } from './computed.js';
|
|
28
|
+
export type { ComputedOptions } from './computed.js';
|
|
29
|
+
|
|
30
|
+
export { registerExtender, throttle, debounce } from './extenders.js';
|
|
31
|
+
export type { ExtenderHandler, ExtenderMap, ExtenderOptions, RateLimitOptions } from './extenders.js';
|
|
32
|
+
|
|
33
|
+
export { effect, observe } from './effects.js';
|
|
34
|
+
export type { EffectHandle } from './effects.js';
|
|
35
|
+
|
|
36
|
+
export { Event, EventSubscribable, EventSubscription, AggregateEvent, isEvent, isEventSubscribable, isAggregateEvent } from './event.js';
|
|
37
|
+
export type { EventCallback, Constructor } from './event.js';
|
|
38
|
+
|
|
39
|
+
export { DisposableGroup } from './disposable.js';
|
|
40
|
+
export type { Disposable } from './disposable.js';
|
|
41
|
+
|
|
42
|
+
export { reactive, reactiveArray, computed, getObservable, replaceObservable } from './decorators.js';
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
unwrapObservable, peekObservable, toJS, toJSON, when,
|
|
46
|
+
catchFunctionErrors,
|
|
47
|
+
} from './utils.js';
|
|
48
|
+
|
|
49
|
+
export { schedule, cancel, runEarly, resetForTesting } from './tasks.js';
|
|
50
|
+
import * as tasks from './tasks.js';
|
|
51
|
+
export { tasks };
|
|
52
|
+
|
|
53
|
+
import { Observable } from './observable.js';
|
|
54
|
+
import { Computed } from './computed.js';
|
|
55
|
+
|
|
56
|
+
export function isWritableObservable(value: unknown): value is Observable<unknown> | Computed<unknown> {
|
|
57
|
+
if (value instanceof Observable) return true;
|
|
58
|
+
if (value instanceof Computed) return value.hasWriteFunction;
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type Subscribable, isSubscribable } from './subscribable.js';
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- variance: detection is type-agnostic
|
|
4
|
+
type AnySubscribable = Subscribable<any>;
|
|
5
|
+
|
|
6
|
+
export interface TrackingFrame {
|
|
7
|
+
callback: (subscribable: AnySubscribable, id: number) => void;
|
|
8
|
+
computed?: {
|
|
9
|
+
getDependenciesCount(): number;
|
|
10
|
+
getDependencies(): AnySubscribable[];
|
|
11
|
+
};
|
|
12
|
+
isInitial?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let lastId = 0;
|
|
16
|
+
const outerFrames: Array<TrackingFrame | undefined> = [];
|
|
17
|
+
let currentFrame: TrackingFrame | undefined;
|
|
18
|
+
|
|
19
|
+
export function getId(): number {
|
|
20
|
+
return ++lastId;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function begin(frame?: TrackingFrame): void {
|
|
24
|
+
outerFrames.push(currentFrame);
|
|
25
|
+
currentFrame = frame;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function end(): void {
|
|
29
|
+
currentFrame = outerFrames.pop();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function registerDependency(subscribable: AnySubscribable): void {
|
|
33
|
+
if (currentFrame) {
|
|
34
|
+
if (!isSubscribable(subscribable)) {
|
|
35
|
+
throw new Error('Only subscribable things can act as dependencies');
|
|
36
|
+
}
|
|
37
|
+
currentFrame.callback(
|
|
38
|
+
subscribable,
|
|
39
|
+
subscribable._id || (subscribable._id = getId()),
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function ignore<R>(callback: () => R): R {
|
|
45
|
+
try {
|
|
46
|
+
begin();
|
|
47
|
+
return callback();
|
|
48
|
+
} finally {
|
|
49
|
+
end();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getDependenciesCount(): number | undefined {
|
|
54
|
+
if (currentFrame?.computed) {
|
|
55
|
+
return currentFrame.computed.getDependenciesCount();
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function getDependencies(): AnySubscribable[] | undefined {
|
|
61
|
+
if (currentFrame?.computed) {
|
|
62
|
+
return currentFrame.computed.getDependencies();
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function isInitial(): boolean | undefined {
|
|
68
|
+
if (currentFrame) {
|
|
69
|
+
return currentFrame.isInitial;
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getCurrentComputed(): TrackingFrame['computed'] | undefined {
|
|
75
|
+
return currentFrame?.computed;
|
|
76
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface Disposable {
|
|
2
|
+
dispose(): void;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export class DisposableGroup implements Disposable {
|
|
6
|
+
private _disposables: Disposable[] = [];
|
|
7
|
+
private _isDisposed = false;
|
|
8
|
+
|
|
9
|
+
add<T extends Disposable>(disposable: T): T {
|
|
10
|
+
if (this._isDisposed) {
|
|
11
|
+
disposable.dispose();
|
|
12
|
+
return disposable;
|
|
13
|
+
}
|
|
14
|
+
this._disposables.push(disposable);
|
|
15
|
+
return disposable;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
dispose(): void {
|
|
19
|
+
if (!this._isDisposed) {
|
|
20
|
+
this._isDisposed = true;
|
|
21
|
+
const items = this._disposables;
|
|
22
|
+
this._disposables = [];
|
|
23
|
+
for (let i = 0; i < items.length; i++) {
|
|
24
|
+
items[i].dispose();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get isDisposed(): boolean {
|
|
30
|
+
return this._isDisposed;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get count(): number {
|
|
34
|
+
return this._disposables.length;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/domData.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const dataStore = new WeakMap<object, Map<string, unknown>>();
|
|
2
|
+
let nextId = 0;
|
|
3
|
+
|
|
4
|
+
export function domDataGet(node: object, key: string): unknown {
|
|
5
|
+
return dataStore.get(node)?.get(key);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function domDataSet(node: object, key: string, value: unknown): void {
|
|
9
|
+
if (value === undefined) {
|
|
10
|
+
const data = dataStore.get(node);
|
|
11
|
+
if (data) {
|
|
12
|
+
data.delete(key);
|
|
13
|
+
if (data.size === 0) dataStore.delete(node);
|
|
14
|
+
}
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
let data = dataStore.get(node);
|
|
18
|
+
if (!data) {
|
|
19
|
+
data = new Map();
|
|
20
|
+
dataStore.set(node, data);
|
|
21
|
+
}
|
|
22
|
+
data.set(key, value);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function domDataGetOrSet(node: object, key: string, value: unknown): unknown {
|
|
26
|
+
let data = dataStore.get(node);
|
|
27
|
+
if (!data) {
|
|
28
|
+
data = new Map();
|
|
29
|
+
dataStore.set(node, data);
|
|
30
|
+
}
|
|
31
|
+
if (data.has(key)) return data.get(key);
|
|
32
|
+
data.set(key, value);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function domDataClear(node: object): boolean {
|
|
37
|
+
return dataStore.delete(node);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function domDataNextKey(): string {
|
|
41
|
+
return `__tapout_${nextId++}`;
|
|
42
|
+
}
|