@praxisjs/decorators 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/dist/__tests__/component.test.js +10 -1
- package/dist/__tests__/component.test.js.map +1 -1
- package/dist/__tests__/compose.test.d.ts +2 -0
- package/dist/__tests__/compose.test.d.ts.map +1 -0
- package/dist/__tests__/compose.test.js +171 -0
- package/dist/__tests__/compose.test.js.map +1 -0
- package/dist/__tests__/events.test.js +26 -0
- package/dist/__tests__/events.test.js.map +1 -1
- package/dist/__tests__/functions.test.js +142 -47
- package/dist/__tests__/functions.test.js.map +1 -1
- package/dist/__tests__/lazy.test.js +5 -7
- package/dist/__tests__/lazy.test.js.map +1 -1
- package/dist/__tests__/memo.test.js +61 -25
- package/dist/__tests__/memo.test.js.map +1 -1
- package/dist/__tests__/resource.test.d.ts +2 -0
- package/dist/__tests__/resource.test.d.ts.map +1 -0
- package/dist/__tests__/resource.test.js +103 -0
- package/dist/__tests__/resource.test.js.map +1 -0
- package/dist/__tests__/slot.test.js +32 -0
- package/dist/__tests__/slot.test.js.map +1 -1
- package/dist/__tests__/virtual.test.js +56 -15
- package/dist/__tests__/virtual.test.js.map +1 -1
- package/dist/component/component.d.ts +1 -1
- package/dist/component/component.d.ts.map +1 -1
- package/dist/component/component.js +15 -10
- package/dist/component/component.js.map +1 -1
- package/dist/component/lazy.d.ts.map +1 -1
- package/dist/component/lazy.js +29 -30
- package/dist/component/lazy.js.map +1 -1
- package/dist/component/virtual.d.ts.map +1 -1
- package/dist/component/virtual.js +46 -50
- package/dist/component/virtual.js.map +1 -1
- package/dist/create-class-decorator.d.ts +16 -0
- package/dist/create-class-decorator.d.ts.map +1 -0
- package/dist/create-class-decorator.js +35 -0
- package/dist/create-class-decorator.js.map +1 -0
- package/dist/create-field-decorator.d.ts +14 -0
- package/dist/create-field-decorator.d.ts.map +1 -0
- package/dist/create-field-decorator.js +37 -0
- package/dist/create-field-decorator.js.map +1 -0
- package/dist/create-getter-decorator.d.ts +18 -0
- package/dist/create-getter-decorator.d.ts.map +1 -0
- package/dist/create-getter-decorator.js +15 -0
- package/dist/create-getter-decorator.js.map +1 -0
- package/dist/create-lifecycle-method-decorator.d.ts +6 -0
- package/dist/create-lifecycle-method-decorator.d.ts.map +1 -0
- package/dist/create-lifecycle-method-decorator.js +22 -0
- package/dist/create-lifecycle-method-decorator.js.map +1 -0
- package/dist/create-method-decorator.d.ts +6 -0
- package/dist/create-method-decorator.d.ts.map +1 -0
- package/dist/create-method-decorator.js +14 -0
- package/dist/create-method-decorator.js.map +1 -0
- package/dist/events/emit.d.ts +1 -1
- package/dist/events/emit.d.ts.map +1 -1
- package/dist/events/emit.js +24 -29
- package/dist/events/emit.js.map +1 -1
- package/dist/events/on-command.d.ts +1 -2
- package/dist/events/on-command.d.ts.map +1 -1
- package/dist/events/on-command.js +15 -32
- package/dist/events/on-command.js.map +1 -1
- package/dist/functions/bind.d.ts +1 -2
- package/dist/functions/bind.d.ts.map +1 -1
- package/dist/functions/bind.js +4 -11
- package/dist/functions/bind.js.map +1 -1
- package/dist/functions/debounce.d.ts +1 -2
- package/dist/functions/debounce.d.ts.map +1 -1
- package/dist/functions/debounce.js +12 -20
- package/dist/functions/debounce.js.map +1 -1
- package/dist/functions/log.d.ts +1 -2
- package/dist/functions/log.d.ts.map +1 -1
- package/dist/functions/log.js +40 -38
- package/dist/functions/log.js.map +1 -1
- package/dist/functions/memo.d.ts +1 -2
- package/dist/functions/memo.d.ts.map +1 -1
- package/dist/functions/memo.js +15 -27
- package/dist/functions/memo.js.map +1 -1
- package/dist/functions/once.d.ts +1 -2
- package/dist/functions/once.d.ts.map +1 -1
- package/dist/functions/once.js +14 -12
- package/dist/functions/once.js.map +1 -1
- package/dist/functions/retry.d.ts +1 -2
- package/dist/functions/retry.d.ts.map +1 -1
- package/dist/functions/retry.js +23 -20
- package/dist/functions/retry.js.map +1 -1
- package/dist/functions/throttle.d.ts +1 -2
- package/dist/functions/throttle.d.ts.map +1 -1
- package/dist/functions/throttle.js +9 -16
- package/dist/functions/throttle.js.map +1 -1
- package/dist/functions/watch.d.ts +1 -1
- package/dist/functions/watch.d.ts.map +1 -1
- package/dist/functions/watch.js +25 -39
- package/dist/functions/watch.js.map +1 -1
- package/dist/functions/when.d.ts +1 -2
- package/dist/functions/when.d.ts.map +1 -1
- package/dist/functions/when.js +10 -26
- package/dist/functions/when.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/properties/compose.d.ts +3 -0
- package/dist/properties/compose.d.ts.map +1 -0
- package/dist/properties/compose.js +40 -0
- package/dist/properties/compose.js.map +1 -0
- package/dist/properties/computed.d.ts +1 -2
- package/dist/properties/computed.d.ts.map +1 -1
- package/dist/properties/computed.js +10 -9
- package/dist/properties/computed.js.map +1 -1
- package/dist/properties/history.d.ts.map +1 -1
- package/dist/properties/history.js +37 -32
- package/dist/properties/history.js.map +1 -1
- package/dist/properties/index.d.ts +2 -0
- package/dist/properties/index.d.ts.map +1 -1
- package/dist/properties/index.js +2 -0
- package/dist/properties/index.js.map +1 -1
- package/dist/properties/persisted.d.ts +1 -2
- package/dist/properties/persisted.d.ts.map +1 -1
- package/dist/properties/persisted.js +13 -29
- package/dist/properties/persisted.js.map +1 -1
- package/dist/properties/prop.d.ts +1 -2
- package/dist/properties/prop.d.ts.map +1 -1
- package/dist/properties/prop.js +21 -25
- package/dist/properties/prop.js.map +1 -1
- package/dist/properties/resource.d.ts +14 -0
- package/dist/properties/resource.d.ts.map +1 -0
- package/dist/properties/resource.js +27 -0
- package/dist/properties/resource.js.map +1 -0
- package/dist/properties/slot.d.ts +1 -2
- package/dist/properties/slot.d.ts.map +1 -1
- package/dist/properties/slot.js +15 -20
- package/dist/properties/slot.js.map +1 -1
- package/dist/properties/state.d.ts +1 -2
- package/dist/properties/state.d.ts.map +1 -1
- package/dist/properties/state.js +13 -18
- package/dist/properties/state.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/component.test.ts +10 -0
- package/src/__tests__/compose.test.ts +202 -0
- package/src/__tests__/events.test.ts +32 -0
- package/src/__tests__/functions.test.ts +177 -46
- package/src/__tests__/lazy.test.ts +3 -5
- package/src/__tests__/memo.test.ts +76 -25
- package/src/__tests__/resource.test.ts +113 -0
- package/src/__tests__/slot.test.ts +38 -0
- package/src/__tests__/virtual.test.ts +26 -7
- package/src/component/component.ts +27 -12
- package/src/component/lazy.ts +33 -37
- package/src/component/virtual.tsx +62 -75
- package/src/create-class-decorator.ts +61 -0
- package/src/create-field-decorator.ts +66 -0
- package/src/create-getter-decorator.ts +52 -0
- package/src/create-lifecycle-method-decorator.ts +38 -0
- package/src/create-method-decorator.ts +26 -0
- package/src/events/emit.ts +22 -33
- package/src/events/on-command.ts +22 -47
- package/src/functions/bind.ts +4 -15
- package/src/functions/debounce.ts +11 -26
- package/src/functions/log.ts +40 -43
- package/src/functions/memo.ts +15 -45
- package/src/functions/once.ts +13 -16
- package/src/functions/retry.ts +21 -22
- package/src/functions/throttle.ts +9 -23
- package/src/functions/watch.ts +27 -43
- package/src/functions/when.ts +11 -36
- package/src/index.ts +28 -0
- package/src/properties/compose.ts +53 -0
- package/src/properties/computed.ts +11 -13
- package/src/properties/history.ts +42 -41
- package/src/properties/index.ts +2 -0
- package/src/properties/persisted.ts +17 -58
- package/src/properties/prop.ts +21 -31
- package/src/properties/resource.ts +34 -0
- package/src/properties/slot.ts +17 -30
- package/src/properties/state.ts +14 -24
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { StatefulComponent } from "@praxisjs/core";
|
|
2
|
+
|
|
3
|
+
export interface FieldBinding {
|
|
4
|
+
/** Replaces the decorated property. If absent, the original property is left untouched. */
|
|
5
|
+
descriptor?: PropertyDescriptor;
|
|
6
|
+
/** Extra properties to define on the instance (e.g. companion properties like `nameHistory`). */
|
|
7
|
+
additional?: Record<string, PropertyDescriptor>;
|
|
8
|
+
onMount?: () => void;
|
|
9
|
+
onUnmount?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface FieldBehavior {
|
|
13
|
+
bind(
|
|
14
|
+
instance: StatefulComponent,
|
|
15
|
+
name: string,
|
|
16
|
+
initialValue: unknown,
|
|
17
|
+
): FieldBinding;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createFieldDecorator(behavior: FieldBehavior) {
|
|
21
|
+
return function (
|
|
22
|
+
_value: undefined,
|
|
23
|
+
context: ClassFieldDecoratorContext<StatefulComponent>,
|
|
24
|
+
): void {
|
|
25
|
+
context.addInitializer(function (this: unknown) {
|
|
26
|
+
const instance = this as StatefulComponent & Record<string, unknown>;
|
|
27
|
+
const name = context.name as string;
|
|
28
|
+
const initialValue = instance[name];
|
|
29
|
+
|
|
30
|
+
const { descriptor, additional, onMount, onUnmount } = behavior.bind(
|
|
31
|
+
instance,
|
|
32
|
+
name,
|
|
33
|
+
initialValue,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
if (descriptor) {
|
|
37
|
+
Reflect.deleteProperty(instance, name);
|
|
38
|
+
Object.defineProperty(instance, name, {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
...descriptor,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const [key, desc] of Object.entries(additional ?? {})) {
|
|
46
|
+
Object.defineProperty(instance, key, {
|
|
47
|
+
configurable: true,
|
|
48
|
+
...desc,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (onMount ?? onUnmount) {
|
|
53
|
+
const prevMount = instance.onMount?.bind(instance);
|
|
54
|
+
const prevUnmount = instance.onUnmount?.bind(instance);
|
|
55
|
+
instance.onMount = () => {
|
|
56
|
+
prevMount?.();
|
|
57
|
+
onMount?.();
|
|
58
|
+
};
|
|
59
|
+
instance.onUnmount = () => {
|
|
60
|
+
prevUnmount?.();
|
|
61
|
+
onUnmount?.();
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface GetterBehavior {
|
|
2
|
+
/**
|
|
3
|
+
* Given the original getter and the current instance, returns a function
|
|
4
|
+
* that produces the getter value. Called on every property access.
|
|
5
|
+
*/
|
|
6
|
+
wrap(
|
|
7
|
+
original: (this: object) => unknown,
|
|
8
|
+
instance: object,
|
|
9
|
+
): () => unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createGetterDecorator(behavior: GetterBehavior) {
|
|
13
|
+
return function <This, T>(
|
|
14
|
+
target: (this: This) => T,
|
|
15
|
+
_context: ClassGetterDecoratorContext<This, T>,
|
|
16
|
+
): (this: This) => T {
|
|
17
|
+
return function (this: This): T {
|
|
18
|
+
return behavior.wrap(
|
|
19
|
+
target as (this: object) => unknown,
|
|
20
|
+
this as object,
|
|
21
|
+
)() as T;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface GetterObserverBehavior {
|
|
27
|
+
/**
|
|
28
|
+
* Called once per instance when the class is initialized.
|
|
29
|
+
* Use this to set up observation side-effects on the getter without
|
|
30
|
+
* changing its return value (e.g. tracking changes for devtools).
|
|
31
|
+
*/
|
|
32
|
+
observe(
|
|
33
|
+
getter: (this: object) => unknown,
|
|
34
|
+
instance: object,
|
|
35
|
+
name: string,
|
|
36
|
+
): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createGetterObserverDecorator(behavior: GetterObserverBehavior) {
|
|
40
|
+
return function <This, T>(
|
|
41
|
+
target: (this: This) => T,
|
|
42
|
+
context: ClassGetterDecoratorContext<This, T>,
|
|
43
|
+
): void {
|
|
44
|
+
context.addInitializer(function (this: unknown) {
|
|
45
|
+
behavior.observe(
|
|
46
|
+
target as (this: object) => unknown,
|
|
47
|
+
this as object,
|
|
48
|
+
context.name as string,
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { StatefulComponent } from "@praxisjs/core";
|
|
2
|
+
|
|
3
|
+
export interface LifecycleMethodBehavior<
|
|
4
|
+
T extends StatefulComponent = StatefulComponent,
|
|
5
|
+
> {
|
|
6
|
+
register(
|
|
7
|
+
callback: (...args: unknown[]) => void,
|
|
8
|
+
instance: T,
|
|
9
|
+
): (() => void) | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createLifecycleMethodDecorator<
|
|
13
|
+
T extends StatefulComponent = StatefulComponent,
|
|
14
|
+
>(behavior: LifecycleMethodBehavior<T>) {
|
|
15
|
+
return function (
|
|
16
|
+
value: (this: T, ...args: unknown[]) => void,
|
|
17
|
+
_context: ClassMethodDecoratorContext<T>,
|
|
18
|
+
): void {
|
|
19
|
+
_context.addInitializer(function (this: unknown) {
|
|
20
|
+
const instance = this as T;
|
|
21
|
+
const prevMount = instance.onMount?.bind(instance);
|
|
22
|
+
const prevUnmount = instance.onUnmount?.bind(instance);
|
|
23
|
+
let cleanup: (() => void) | undefined;
|
|
24
|
+
|
|
25
|
+
instance.onMount = function (this: T) {
|
|
26
|
+
prevMount?.call(this);
|
|
27
|
+
const result = behavior.register(value.bind(this), this);
|
|
28
|
+
if (result) cleanup = result;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
instance.onUnmount = function (this: T) {
|
|
32
|
+
prevUnmount?.call(this);
|
|
33
|
+
cleanup?.();
|
|
34
|
+
cleanup = undefined;
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { StatefulComponent } from "@praxisjs/core";
|
|
2
|
+
|
|
3
|
+
export interface MethodBehavior {
|
|
4
|
+
wrap(
|
|
5
|
+
original: (...args: unknown[]) => unknown,
|
|
6
|
+
instance: object,
|
|
7
|
+
name: string,
|
|
8
|
+
): (...args: unknown[]) => unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createMethodDecorator(behavior: MethodBehavior) {
|
|
12
|
+
return function (
|
|
13
|
+
value: (this: object, ...args: unknown[]) => unknown,
|
|
14
|
+
context: ClassMethodDecoratorContext<StatefulComponent>,
|
|
15
|
+
): void {
|
|
16
|
+
context.addInitializer(function (this: unknown) {
|
|
17
|
+
const instance = this as object;
|
|
18
|
+
const name = context.name as string;
|
|
19
|
+
Object.defineProperty(instance, name, {
|
|
20
|
+
value: behavior.wrap(value, instance, name),
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
}
|
package/src/events/emit.ts
CHANGED
|
@@ -1,42 +1,31 @@
|
|
|
1
1
|
import type { StatefulComponent } from "@praxisjs/core";
|
|
2
2
|
|
|
3
|
+
import { createMethodDecorator } from "../create-method-decorator";
|
|
3
4
|
import { readProp } from "./helper";
|
|
4
5
|
|
|
5
6
|
export function Emit(propName: string) {
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
this: StatefulComponent,
|
|
13
|
-
...args: unknown[]
|
|
14
|
-
): unknown {
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
16
|
-
const result = value.apply(this, args);
|
|
7
|
+
return createMethodDecorator({
|
|
8
|
+
wrap(original, instance) {
|
|
9
|
+
const comp = instance as StatefulComponent;
|
|
10
|
+
return (...args: unknown[]) => {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
|
|
12
|
+
const result = original.apply(instance, args) as any;
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
const callback = readProp(comp, propName);
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
16
|
+
if (typeof callback !== "function") return result;
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
if (result !== undefined) {
|
|
19
|
+
(callback as (v: unknown) => void)(result);
|
|
20
|
+
} else if (args.length > 0) {
|
|
21
|
+
(callback as (...a: unknown[]) => void)(...args);
|
|
22
|
+
} else {
|
|
23
|
+
(callback as () => void)();
|
|
24
|
+
}
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const bound = wrapped.bind(this as StatefulComponent);
|
|
35
|
-
Object.defineProperty(this as object, name, {
|
|
36
|
-
value: bound,
|
|
37
|
-
configurable: true,
|
|
38
|
-
writable: true,
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
};
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
});
|
|
42
31
|
}
|
package/src/events/on-command.ts
CHANGED
|
@@ -1,54 +1,29 @@
|
|
|
1
|
-
import type { StatefulComponent } from "@praxisjs/core";
|
|
2
1
|
|
|
2
|
+
import { createLifecycleMethodDecorator } from "../create-lifecycle-method-decorator";
|
|
3
3
|
import { readProp } from "./helper";
|
|
4
4
|
|
|
5
5
|
import type { Command } from "./command";
|
|
6
6
|
|
|
7
7
|
export function OnCommand(propName: string) {
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (typeof command.subscribe !== "function") {
|
|
34
|
-
console.warn(
|
|
35
|
-
`[OnCommand] prop "${propName}" is not a valid Command in ${this.constructor.name}`,
|
|
36
|
-
);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const bound = value.bind(this);
|
|
41
|
-
const unsub = command.subscribe((...args: unknown[]) => {
|
|
42
|
-
bound(...args);
|
|
43
|
-
});
|
|
44
|
-
cleanups.set(this, unsub);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
instance.onUnmount = function (this: StatefulComponent) {
|
|
48
|
-
originalOnUnmount?.call(this);
|
|
49
|
-
cleanups.get(this)?.();
|
|
50
|
-
cleanups.delete(this);
|
|
51
|
-
};
|
|
52
|
-
});
|
|
53
|
-
};
|
|
8
|
+
return createLifecycleMethodDecorator({
|
|
9
|
+
register(callback, instance) {
|
|
10
|
+
const command = readProp(instance, propName) as Command<unknown> | undefined;
|
|
11
|
+
|
|
12
|
+
if (!command) {
|
|
13
|
+
console.warn(
|
|
14
|
+
`[OnCommand] prop "${propName}" was not provided to ${(instance.constructor as { name?: string }).name ?? "unknown"}`,
|
|
15
|
+
);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (typeof command.subscribe !== "function") {
|
|
20
|
+
console.warn(
|
|
21
|
+
`[OnCommand] prop "${propName}" is not a valid Command in ${(instance.constructor as { name?: string }).name ?? "unknown"}`,
|
|
22
|
+
);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return command.subscribe((...args: unknown[]) => { callback(...args); });
|
|
27
|
+
},
|
|
28
|
+
});
|
|
54
29
|
}
|
package/src/functions/bind.ts
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createMethodDecorator } from "../create-method-decorator";
|
|
2
2
|
|
|
3
3
|
export function Bind() {
|
|
4
|
-
return
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
): void {
|
|
8
|
-
context.addInitializer(function (this: unknown) {
|
|
9
|
-
const name = context.name as string;
|
|
10
|
-
const bound = value.bind(this as object);
|
|
11
|
-
Object.defineProperty(this as object, name, {
|
|
12
|
-
value: bound,
|
|
13
|
-
configurable: true,
|
|
14
|
-
writable: true,
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
};
|
|
4
|
+
return createMethodDecorator({
|
|
5
|
+
wrap: (original, instance) => original.bind(instance),
|
|
6
|
+
});
|
|
18
7
|
}
|
|
@@ -1,31 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createMethodDecorator } from "../create-method-decorator";
|
|
2
2
|
|
|
3
3
|
export function Debounce(ms: number) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const name = context.name as string;
|
|
13
|
-
const bound = (...args: unknown[]) => {
|
|
14
|
-
const existing = timers.get(instance);
|
|
15
|
-
if (existing !== undefined) clearTimeout(existing);
|
|
16
|
-
|
|
17
|
-
const timer = setTimeout(() => {
|
|
18
|
-
timers.delete(instance);
|
|
19
|
-
value.apply(instance, args);
|
|
4
|
+
return createMethodDecorator({
|
|
5
|
+
wrap(original, instance) {
|
|
6
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
7
|
+
return (...args: unknown[]) => {
|
|
8
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
9
|
+
timer = setTimeout(() => {
|
|
10
|
+
timer = undefined;
|
|
11
|
+
original.apply(instance, args);
|
|
20
12
|
}, ms);
|
|
21
|
-
|
|
22
|
-
timers.set(instance, timer);
|
|
23
13
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
configurable: true,
|
|
27
|
-
writable: true,
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
};
|
|
14
|
+
},
|
|
15
|
+
});
|
|
31
16
|
}
|
package/src/functions/log.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createMethodDecorator } from "../create-method-decorator";
|
|
2
2
|
|
|
3
3
|
export interface LogOptions {
|
|
4
4
|
level?: "log" | "warn" | "error" | "debug";
|
|
@@ -17,52 +17,49 @@ export function Log(options: LogOptions = {}) {
|
|
|
17
17
|
devOnly = true,
|
|
18
18
|
} = options;
|
|
19
19
|
|
|
20
|
-
return
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
return createMethodDecorator({
|
|
21
|
+
wrap(original, instance, name) {
|
|
22
|
+
return (...args: unknown[]) => {
|
|
23
|
+
if (
|
|
24
|
+
devOnly &&
|
|
25
|
+
typeof process !== "undefined" &&
|
|
26
|
+
process.env.NODE_ENV === "production"
|
|
27
|
+
) {
|
|
28
|
+
return original.apply(instance, args);
|
|
29
|
+
}
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
process.env.NODE_ENV === "production"
|
|
31
|
-
) {
|
|
32
|
-
return value.apply(this, args);
|
|
33
|
-
}
|
|
31
|
+
const className =
|
|
32
|
+
(instance.constructor as { name?: string }).name ?? "Unknown";
|
|
33
|
+
const label = `[${className}.${name}]`;
|
|
34
|
+
const logger = console[level].bind(console);
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
(this.constructor as { name?: string }).name ?? "Unknown";
|
|
37
|
-
const label = `[${className}.${methodKey}]`;
|
|
38
|
-
const logger = console[level].bind(console);
|
|
36
|
+
if (logArgs) logger(`${label} args:`, args);
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
const start = time ? performance.now() : 0;
|
|
39
|
+
const output = original.apply(instance, args);
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
if (output instanceof Promise) {
|
|
42
|
+
return (output as Promise<unknown>)
|
|
43
|
+
.then((resolved) => {
|
|
44
|
+
const elapsed = time
|
|
45
|
+
? ` (${(performance.now() - start).toFixed(2)}ms)`
|
|
46
|
+
: "";
|
|
47
|
+
if (result) logger(`${label} resolved:`, resolved, elapsed);
|
|
48
|
+
return resolved;
|
|
49
|
+
})
|
|
50
|
+
.catch((e: unknown) => {
|
|
51
|
+
logger(`${label} rejected:`, e);
|
|
52
|
+
throw e;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
44
55
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
? ` (${(performance.now() - start).toFixed(2)}ms)`
|
|
50
|
-
: "";
|
|
51
|
-
if (result) logger(`${label} resolved:`, resolved, elapsed);
|
|
52
|
-
return resolved;
|
|
53
|
-
})
|
|
54
|
-
.catch((e: unknown) => {
|
|
55
|
-
logger(`${label} rejected:`, e);
|
|
56
|
-
throw e;
|
|
57
|
-
});
|
|
58
|
-
}
|
|
56
|
+
const elapsed = time
|
|
57
|
+
? ` (${(performance.now() - start).toFixed(2)}ms)`
|
|
58
|
+
: "";
|
|
59
|
+
if (result) logger(`${label} returned:`, output, elapsed);
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return output;
|
|
66
|
-
};
|
|
67
|
-
};
|
|
61
|
+
return output;
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
68
65
|
}
|
package/src/functions/memo.ts
CHANGED
|
@@ -1,38 +1,7 @@
|
|
|
1
|
-
import type { StatefulComponent } from "@praxisjs/core";
|
|
2
1
|
import { computed } from "@praxisjs/core/internal";
|
|
3
2
|
import type { Computed } from "@praxisjs/shared";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
object,
|
|
7
|
-
Map<string, Map<string, Computed<unknown>>>
|
|
8
|
-
>();
|
|
9
|
-
|
|
10
|
-
function getCache(
|
|
11
|
-
instance: object,
|
|
12
|
-
methodName: string,
|
|
13
|
-
argKey: string,
|
|
14
|
-
factory: () => unknown,
|
|
15
|
-
): Computed<unknown> {
|
|
16
|
-
let methodMap = instanceCache.get(instance);
|
|
17
|
-
if (!methodMap) {
|
|
18
|
-
methodMap = new Map();
|
|
19
|
-
instanceCache.set(instance, methodMap);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
let argMap = methodMap.get(methodName);
|
|
23
|
-
if (!argMap) {
|
|
24
|
-
argMap = new Map();
|
|
25
|
-
methodMap.set(methodName, argMap);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
let cached = argMap.get(argKey);
|
|
29
|
-
if (!cached) {
|
|
30
|
-
cached = computed(factory);
|
|
31
|
-
argMap.set(argKey, cached);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return cached;
|
|
35
|
-
}
|
|
4
|
+
import { createMethodDecorator } from "../create-method-decorator";
|
|
36
5
|
|
|
37
6
|
function serializeArgs(args: unknown[]) {
|
|
38
7
|
if (args.length === 0) return "__no_args__";
|
|
@@ -46,17 +15,18 @@ function serializeArgs(args: unknown[]) {
|
|
|
46
15
|
}
|
|
47
16
|
|
|
48
17
|
export function Memo() {
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
18
|
+
return createMethodDecorator({
|
|
19
|
+
wrap(original, instance, _name) {
|
|
20
|
+
const cache = new Map<string, Computed<unknown>>();
|
|
21
|
+
return (...args: unknown[]) => {
|
|
22
|
+
const key = serializeArgs(args);
|
|
23
|
+
let memoized = cache.get(key);
|
|
24
|
+
if (!memoized) {
|
|
25
|
+
memoized = computed(() => original.apply(instance, args));
|
|
26
|
+
cache.set(key, memoized);
|
|
27
|
+
}
|
|
28
|
+
return memoized();
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
});
|
|
62
32
|
}
|
package/src/functions/once.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createMethodDecorator } from "../create-method-decorator";
|
|
2
2
|
|
|
3
3
|
export function Once() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return result;
|
|
17
|
-
};
|
|
18
|
-
};
|
|
4
|
+
return createMethodDecorator({
|
|
5
|
+
wrap(original, instance) {
|
|
6
|
+
let called = false;
|
|
7
|
+
let result: unknown;
|
|
8
|
+
return (...args: unknown[]) => {
|
|
9
|
+
if (called) return result;
|
|
10
|
+
result = original.apply(instance, args);
|
|
11
|
+
called = true;
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
});
|
|
19
16
|
}
|
package/src/functions/retry.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createMethodDecorator } from "../create-method-decorator";
|
|
2
2
|
|
|
3
3
|
export interface RetryOptions {
|
|
4
4
|
delay?: number;
|
|
@@ -9,30 +9,29 @@ export interface RetryOptions {
|
|
|
9
9
|
export function Retry(maxAttempts: number, options: RetryOptions = {}) {
|
|
10
10
|
const { delay = 0, backoff = 1, onRetry } = options;
|
|
11
11
|
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let lastError: Error = new Error("Unknown error");
|
|
18
|
-
let currentDelay = delay;
|
|
12
|
+
return createMethodDecorator({
|
|
13
|
+
wrap(original, instance) {
|
|
14
|
+
return async (...args: unknown[]) => {
|
|
15
|
+
let lastError: Error = new Error("Unknown error");
|
|
16
|
+
let currentDelay = delay;
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
19
|
+
try {
|
|
20
|
+
return await (original.apply(instance, args) as Promise<unknown>);
|
|
21
|
+
} catch (e: unknown) {
|
|
22
|
+
lastError = e instanceof Error ? e : new Error(String(e));
|
|
23
|
+
if (attempt === maxAttempts) break;
|
|
24
|
+
onRetry?.(lastError, attempt);
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
if (currentDelay > 0) {
|
|
27
|
+
await new Promise((res) => setTimeout(res, currentDelay));
|
|
28
|
+
currentDelay = Math.round(currentDelay * backoff);
|
|
29
|
+
}
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
|
-
}
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
throw lastError;
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
});
|
|
38
37
|
}
|