@praxisjs/devtools 0.2.5 → 0.2.6
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 +12 -0
- package/dist/index.d.ts +4 -25
- package/dist/index.js +957 -625
- package/package.json +6 -6
- package/src/__tests__/debug.test.ts +26 -12
- package/src/decorators/debug.ts +91 -147
- package/src/decorators/trace.ts +39 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisjs/devtools",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@unocss/reset": "^66.6.2",
|
|
15
|
-
"@praxisjs/core": "0.
|
|
16
|
-
"@praxisjs/decorators": "0.
|
|
17
|
-
"@praxisjs/jsx": "0.3.
|
|
18
|
-
"@praxisjs/
|
|
19
|
-
"@praxisjs/
|
|
15
|
+
"@praxisjs/core": "1.0.0",
|
|
16
|
+
"@praxisjs/decorators": "0.5.0",
|
|
17
|
+
"@praxisjs/jsx": "0.3.4",
|
|
18
|
+
"@praxisjs/runtime": "0.2.5",
|
|
19
|
+
"@praxisjs/shared": "0.2.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^25.3.0",
|
|
@@ -11,19 +11,31 @@ beforeEach(() => {
|
|
|
11
11
|
|
|
12
12
|
// ── Method decorator ──────────────────────────────────────────────────────────
|
|
13
13
|
|
|
14
|
+
function mockMethodCtx(name: string) {
|
|
15
|
+
const initializers: Array<(this: object) => void> = [];
|
|
16
|
+
return {
|
|
17
|
+
kind: "method" as const,
|
|
18
|
+
name,
|
|
19
|
+
addInitializer(fn: (this: object) => void) { initializers.push(fn); },
|
|
20
|
+
runInitializers(instance: object) { initializers.forEach((fn) => { fn.call(instance); }); },
|
|
21
|
+
} as unknown as ClassMethodDecoratorContext & { runInitializers(instance: object): void };
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
describe("Debug — method decorator", () => {
|
|
15
25
|
it("records a method:call timeline entry", () => {
|
|
16
|
-
const ctx =
|
|
26
|
+
const ctx = mockMethodCtx("doWork");
|
|
17
27
|
const original = vi.fn((_x: unknown) => "result");
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
Debug()(original as never, ctx);
|
|
20
30
|
|
|
21
|
-
class Comp {
|
|
31
|
+
class Comp { doWork(_x: unknown) { return "result"; } }
|
|
22
32
|
const instance = new Comp();
|
|
33
|
+
ctx.runInitializers(instance);
|
|
34
|
+
|
|
23
35
|
const registry = Registry.instance;
|
|
24
36
|
registry.registerComponent(instance, "Comp");
|
|
25
37
|
|
|
26
|
-
|
|
38
|
+
instance.doWork(42);
|
|
27
39
|
|
|
28
40
|
const entry = registry.getTimeline().find((t) => t.type === "method:call");
|
|
29
41
|
expect(entry).toBeDefined();
|
|
@@ -33,31 +45,33 @@ describe("Debug — method decorator", () => {
|
|
|
33
45
|
});
|
|
34
46
|
|
|
35
47
|
it("records throw result when method throws", () => {
|
|
36
|
-
const ctx =
|
|
48
|
+
const ctx = mockMethodCtx("boom");
|
|
37
49
|
const original = () => { throw new Error("fail"); };
|
|
38
50
|
|
|
39
|
-
|
|
51
|
+
Debug()(original as never, ctx);
|
|
40
52
|
|
|
41
|
-
class Comp {}
|
|
53
|
+
class Comp { boom() { throw new Error("fail"); } }
|
|
42
54
|
const instance = new Comp();
|
|
55
|
+
ctx.runInitializers(instance);
|
|
43
56
|
Registry.instance.registerComponent(instance, "Comp");
|
|
44
57
|
|
|
45
|
-
expect(() =>
|
|
58
|
+
expect(() => (instance as unknown as Record<string, () => void>).boom()).toThrow("fail");
|
|
46
59
|
|
|
47
60
|
const entry = Registry.instance.getTimeline().find((t) => t.type === "method:call");
|
|
48
61
|
expect(entry?.data.result).toContain("throw");
|
|
49
62
|
});
|
|
50
63
|
|
|
51
64
|
it("uses custom label from options", () => {
|
|
52
|
-
const ctx =
|
|
65
|
+
const ctx = mockMethodCtx("internal");
|
|
53
66
|
const original = vi.fn(() => undefined);
|
|
54
67
|
|
|
55
|
-
|
|
68
|
+
Debug({ label: "myLabel" })(original as never, ctx);
|
|
56
69
|
|
|
57
|
-
class Comp {}
|
|
70
|
+
class Comp { internal() { return undefined; } }
|
|
58
71
|
const instance = new Comp();
|
|
72
|
+
ctx.runInitializers(instance);
|
|
59
73
|
Registry.instance.registerComponent(instance, "Comp");
|
|
60
|
-
|
|
74
|
+
(instance as unknown as Record<string, () => void>).internal();
|
|
61
75
|
|
|
62
76
|
const entry = Registry.instance.getTimeline().find((t) => t.type === "method:call");
|
|
63
77
|
expect(entry?.label).toContain("myLabel");
|
package/src/decorators/debug.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { computed } from "@praxisjs/core/internal";
|
|
2
|
+
import {
|
|
3
|
+
createMethodDecorator,
|
|
4
|
+
createGetterObserverDecorator,
|
|
5
|
+
createFieldDecorator,
|
|
6
|
+
type FieldBinding,
|
|
7
|
+
} from "@praxisjs/decorators";
|
|
2
8
|
|
|
3
9
|
import { Registry } from "../core/registry";
|
|
4
10
|
|
|
@@ -15,8 +21,7 @@ interface TrackedComputed {
|
|
|
15
21
|
function isComputed(value: unknown): value is TrackedComputed {
|
|
16
22
|
return (
|
|
17
23
|
typeof value === "function" &&
|
|
18
|
-
typeof (value as unknown as Record<string, unknown>).subscribe ===
|
|
19
|
-
"function" &&
|
|
24
|
+
typeof (value as unknown as Record<string, unknown>).subscribe === "function" &&
|
|
20
25
|
!("set" in (value as object))
|
|
21
26
|
);
|
|
22
27
|
}
|
|
@@ -27,29 +32,14 @@ interface ComputedSlot {
|
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
interface DebugDecorator {
|
|
30
|
-
(
|
|
31
|
-
value: (...args: unknown[]) => unknown,
|
|
32
|
-
context: ClassMethodDecoratorContext,
|
|
33
|
-
): (...args: unknown[]) => unknown;
|
|
35
|
+
(value: (...args: unknown[]) => unknown, context: ClassMethodDecoratorContext): void;
|
|
34
36
|
(value: unknown, context: ClassGetterDecoratorContext): void;
|
|
35
37
|
(value: undefined, context: ClassFieldDecoratorContext): void;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/**
|
|
39
41
|
* Tracks state, computed values, and methods in the devtools panel.
|
|
40
|
-
*
|
|
41
|
-
* On @State() properties (stacked):
|
|
42
|
-
* @Debug()
|
|
43
|
-
* @State() count = 0;
|
|
44
|
-
*
|
|
45
|
-
* On @Computed() getters (stacked):
|
|
46
|
-
* @Debug({ label: "doubled" })
|
|
47
|
-
* @Computed()
|
|
48
|
-
* get doubled() { return this.count * 2; }
|
|
49
|
-
*
|
|
50
|
-
* On methods:
|
|
51
|
-
* @Debug()
|
|
52
|
-
* increment() { ... }
|
|
42
|
+
* Supports @State() fields, @Computed() getters, and methods.
|
|
53
43
|
*/
|
|
54
44
|
export function Debug(options: DebugOptions = {}): DebugDecorator {
|
|
55
45
|
const impl = function (
|
|
@@ -61,120 +51,83 @@ export function Debug(options: DebugOptions = {}): DebugDecorator {
|
|
|
61
51
|
) {
|
|
62
52
|
const label = options.label ?? (context.name as string);
|
|
63
53
|
|
|
64
|
-
// ── Method decorator ─────────────────────────────────────────────────
|
|
65
54
|
if (context.kind === "method") {
|
|
66
55
|
const original = value as (...args: unknown[]) => unknown;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
};
|
|
56
|
+
createMethodDecorator({
|
|
57
|
+
wrap(_original, _instance, _name) {
|
|
58
|
+
return function (this: object, ...args: unknown[]) {
|
|
59
|
+
const componentName = (this.constructor as { name: string }).name;
|
|
60
|
+
const start = performance.now();
|
|
61
|
+
let result: unknown;
|
|
62
|
+
let threw = false;
|
|
63
|
+
try {
|
|
64
|
+
result = original.apply(this, args);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
threw = true;
|
|
67
|
+
result = err;
|
|
68
|
+
throw err;
|
|
69
|
+
} finally {
|
|
70
|
+
const duration = performance.now() - start;
|
|
71
|
+
Registry.instance.recordMethodCall(
|
|
72
|
+
this, label, args,
|
|
73
|
+
threw ? `throw ${String(result)}` : result,
|
|
74
|
+
duration, componentName,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
})(original, context);
|
|
81
|
+
return;
|
|
94
82
|
}
|
|
95
83
|
|
|
96
|
-
// ── Getter decorator (@Computed() getters) ────────────────────────────
|
|
97
84
|
if (context.kind === "getter") {
|
|
98
85
|
const originalGetter = value as (this: object) => unknown;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
Registry.instance.registerSignal(instance, label, prevValue, componentName);
|
|
114
|
-
|
|
115
|
-
c.subscribe((newValue) => {
|
|
116
|
-
if (skipFirst) {
|
|
117
|
-
skipFirst = false;
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
Registry.instance.updateSignal(instance, label, newValue, prevValue);
|
|
121
|
-
prevValue = newValue;
|
|
86
|
+
createGetterObserverDecorator({
|
|
87
|
+
observe(getter, instance, _name) {
|
|
88
|
+
const componentName = (instance.constructor as { name: string }).name;
|
|
89
|
+
// Defer so field initializers (e.g. @State()) have run before we read them.
|
|
90
|
+
queueMicrotask(() => {
|
|
91
|
+
const c = computed(() => getter.call(instance));
|
|
92
|
+
let skipFirst = true;
|
|
93
|
+
let prevValue = c();
|
|
94
|
+
Registry.instance.registerSignal(instance, label, prevValue, componentName);
|
|
95
|
+
c.subscribe((newValue) => {
|
|
96
|
+
if (skipFirst) { skipFirst = false; return; }
|
|
97
|
+
Registry.instance.updateSignal(instance, label, newValue, prevValue);
|
|
98
|
+
prevValue = newValue;
|
|
99
|
+
});
|
|
122
100
|
});
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
|
|
101
|
+
},
|
|
102
|
+
})(originalGetter, context);
|
|
126
103
|
return;
|
|
127
104
|
}
|
|
128
105
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
) {
|
|
133
|
-
const instance = this as object & Record<string, unknown>;
|
|
134
|
-
const name = context.name as string;
|
|
135
|
-
const componentName = (
|
|
136
|
-
instance.constructor as { name: string }
|
|
137
|
-
).name;
|
|
138
|
-
|
|
139
|
-
// ── Wrapping @State() getter/setter ─────────────────────────────
|
|
140
|
-
// @State() initializer runs before @Debug() initializer (inner-first),
|
|
141
|
-
// so the getter/setter is already installed on the instance here.
|
|
142
|
-
const existingDesc = Object.getOwnPropertyDescriptor(instance, name);
|
|
106
|
+
createFieldDecorator({
|
|
107
|
+
bind(instance, name, _initialValue): FieldBinding {
|
|
108
|
+
const componentName = (instance.constructor as { name: string }).name;
|
|
143
109
|
|
|
110
|
+
// @State() runs inner-first, so its getter/setter is already on the instance.
|
|
111
|
+
const existingDesc = Object.getOwnPropertyDescriptor(instance, name);
|
|
144
112
|
if (existingDesc?.get && existingDesc.set) {
|
|
145
113
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
146
114
|
const originalGet = existingDesc.get;
|
|
147
115
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
148
116
|
const originalSet = existingDesc.set;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
get(this: object) {
|
|
159
|
-
return originalGet.call(this) as unknown;
|
|
160
|
-
},
|
|
161
|
-
set(this: object, newValue: unknown) {
|
|
162
|
-
const oldValue: unknown = originalGet.call(this);
|
|
163
|
-
originalSet.call(this, newValue);
|
|
164
|
-
Registry.instance.updateSignal(this, label, newValue, oldValue);
|
|
117
|
+
Registry.instance.registerSignal(instance, label, originalGet.call(instance), componentName);
|
|
118
|
+
return {
|
|
119
|
+
descriptor: {
|
|
120
|
+
get(this: object) { return originalGet.call(this) as unknown; },
|
|
121
|
+
set(this: object, newValue: unknown) {
|
|
122
|
+
const oldValue: unknown = originalGet.call(this);
|
|
123
|
+
originalSet.call(this, newValue);
|
|
124
|
+
Registry.instance.updateSignal(this, label, newValue, oldValue);
|
|
125
|
+
},
|
|
165
126
|
},
|
|
166
|
-
|
|
167
|
-
configurable: true,
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
return;
|
|
127
|
+
};
|
|
171
128
|
}
|
|
172
129
|
|
|
173
|
-
|
|
174
|
-
// The field initializer has already run, so read the assigned value.
|
|
175
|
-
const initialValue = instance[name];
|
|
176
|
-
Reflect.deleteProperty(instance, name);
|
|
177
|
-
|
|
130
|
+
const initialValue = _initialValue;
|
|
178
131
|
if (!isComputed(initialValue)) {
|
|
179
132
|
if (initialValue !== undefined) {
|
|
180
133
|
console.warn(
|
|
@@ -182,54 +135,45 @@ export function Debug(options: DebugOptions = {}): DebugDecorator {
|
|
|
182
135
|
`expected a computed() value but got ${typeof initialValue}. Skipping.`,
|
|
183
136
|
);
|
|
184
137
|
}
|
|
185
|
-
|
|
186
|
-
return;
|
|
138
|
+
return {};
|
|
187
139
|
}
|
|
188
140
|
|
|
189
|
-
// subscribe() calls the callback immediately (synchronously), so we
|
|
190
|
-
// use a flag to skip the first call and register via registerSignal instead.
|
|
191
141
|
let slot: ComputedSlot;
|
|
192
142
|
|
|
193
|
-
|
|
143
|
+
// subscribe() fires the callback immediately — skip that first call
|
|
144
|
+
// and register the initial value via registerSignal instead.
|
|
145
|
+
function subscribe(comp: TrackedComputed): () => void {
|
|
194
146
|
let skipFirst = true;
|
|
195
|
-
let prevValue =
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (skipFirst) {
|
|
199
|
-
skipFirst = false;
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
147
|
+
let prevValue = comp();
|
|
148
|
+
const unsub = comp.subscribe((newValue) => {
|
|
149
|
+
if (skipFirst) { skipFirst = false; return; }
|
|
202
150
|
Registry.instance.updateSignal(instance, label, newValue, prevValue);
|
|
203
151
|
prevValue = newValue;
|
|
204
152
|
});
|
|
205
|
-
|
|
206
153
|
Registry.instance.registerSignal(instance, label, prevValue, componentName);
|
|
207
154
|
return unsub;
|
|
208
155
|
}
|
|
209
156
|
|
|
210
157
|
slot = { computed: initialValue, unsub: subscribe(initialValue) };
|
|
211
158
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return slot.computed;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
slot = { computed: newValue, unsub: subscribe(newValue) };
|
|
159
|
+
return {
|
|
160
|
+
descriptor: {
|
|
161
|
+
get() { return slot.computed; },
|
|
162
|
+
set(newValue: unknown) {
|
|
163
|
+
slot.unsub();
|
|
164
|
+
if (!isComputed(newValue)) {
|
|
165
|
+
console.warn(
|
|
166
|
+
`[PraxisJS DevTools] @Debug() on "${componentName}.${name}": ` +
|
|
167
|
+
`expected a computed() value but got ${typeof newValue}. Skipping.`,
|
|
168
|
+
);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
slot = { computed: newValue, unsub: subscribe(newValue) };
|
|
172
|
+
},
|
|
228
173
|
},
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
});
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
})(undefined, context);
|
|
233
177
|
};
|
|
234
178
|
|
|
235
179
|
return impl as DebugDecorator;
|
package/src/decorators/trace.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import type { RootComponent } from "@praxisjs/core/internal";
|
|
2
|
+
import {
|
|
3
|
+
createClassDecorator,
|
|
4
|
+
ClassBehavior,
|
|
5
|
+
type ClassEnhancement,
|
|
6
|
+
} from "@praxisjs/decorators";
|
|
7
|
+
|
|
1
8
|
import { Registry } from "../core/registry";
|
|
2
9
|
|
|
3
10
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
11
|
type AnyConstructor = new (...args: any[]) => any;
|
|
5
12
|
|
|
13
|
+
const EXTRA_HOOKS = ["onBeforeUpdate", "onUpdate", "onAfterUpdate"] as const;
|
|
14
|
+
|
|
6
15
|
/**
|
|
7
16
|
* Instruments a component class to report renders and lifecycle events
|
|
8
17
|
* to the devtools panel.
|
|
@@ -11,58 +20,52 @@ type AnyConstructor = new (...args: any[]) => any;
|
|
|
11
20
|
* @Component()
|
|
12
21
|
* class MyComponent extends StatefulComponent { ... }
|
|
13
22
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const name = constructor.name;
|
|
23
|
+
class TraceBehavior extends ClassBehavior {
|
|
24
|
+
create(instance: RootComponent): ClassEnhancement {
|
|
17
25
|
const registry = Registry.instance;
|
|
18
|
-
const proto = constructor.prototype as Record<string, unknown>;
|
|
19
|
-
|
|
20
|
-
// ── render() ──────────────────────────────────────────────────────────
|
|
21
|
-
const originalRender = proto.render as
|
|
22
|
-
| ((...args: unknown[]) => unknown)
|
|
23
|
-
| undefined;
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
return {
|
|
28
|
+
onMount() {
|
|
29
|
+
registry.recordLifecycle(instance, "onMount");
|
|
30
|
+
},
|
|
31
|
+
onUnmount() {
|
|
32
|
+
registry.recordLifecycle(instance, "onUnmount");
|
|
33
|
+
},
|
|
34
|
+
render(originalRender) {
|
|
27
35
|
const start = performance.now();
|
|
28
|
-
const result = originalRender
|
|
36
|
+
const result = originalRender();
|
|
29
37
|
const duration = performance.now() - start;
|
|
30
|
-
registry.recordRender(
|
|
38
|
+
registry.recordRender(instance, duration);
|
|
31
39
|
return result;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
initialize(Enhanced: AnyConstructor, original: AnyConstructor): void {
|
|
45
|
+
const name = original.name;
|
|
46
|
+
const registry = Registry.instance;
|
|
47
|
+
const proto = Enhanced.prototype as Record<string, unknown>;
|
|
39
48
|
|
|
49
|
+
// onBeforeMount — also registers the component instance
|
|
50
|
+
const originalOnBeforeMount = proto.onBeforeMount as ((...args: unknown[]) => unknown) | undefined;
|
|
40
51
|
proto.onBeforeMount = function (this: object, ...args: unknown[]) {
|
|
41
52
|
registry.registerComponent(this, name);
|
|
42
53
|
registry.recordLifecycle(this, "onBeforeMount");
|
|
43
54
|
return originalOnBeforeMount?.call(this, ...args);
|
|
44
55
|
};
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"onMount",
|
|
49
|
-
"onUnmount",
|
|
50
|
-
"onBeforeUpdate",
|
|
51
|
-
"onUpdate",
|
|
52
|
-
"onAfterUpdate",
|
|
53
|
-
] as const;
|
|
54
|
-
|
|
55
|
-
for (const hook of hooks) {
|
|
56
|
-
const original = proto[hook] as
|
|
57
|
-
| ((...args: unknown[]) => unknown)
|
|
58
|
-
| undefined;
|
|
59
|
-
|
|
57
|
+
for (const hook of EXTRA_HOOKS) {
|
|
58
|
+
const orig = proto[hook] as ((...args: unknown[]) => unknown) | undefined;
|
|
60
59
|
proto[hook] = function (this: object, ...args: unknown[]) {
|
|
61
60
|
registry.recordLifecycle(this, hook);
|
|
62
|
-
return
|
|
61
|
+
return orig?.call(this, ...args);
|
|
63
62
|
};
|
|
64
63
|
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const traceBehavior = new TraceBehavior();
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
export function Trace() {
|
|
70
|
+
return createClassDecorator(traceBehavior);
|
|
68
71
|
}
|