@playfast/reform-profiler 1.0.2 → 1.2.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/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/noop.d.ts +23 -0
- package/dist/noop.d.ts.map +1 -0
- package/dist/noop.js +41 -0
- package/dist/noop.js.map +1 -0
- package/dist/profileScene.d.ts +12 -0
- package/dist/profileScene.d.ts.map +1 -0
- package/dist/profileScene.js +32 -0
- package/dist/profileScene.js.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/noop.d.ts +5 -0
- package/dist/react/noop.d.ts.map +1 -0
- package/dist/react/noop.js +3 -0
- package/dist/react/noop.js.map +1 -0
- package/dist/react/overlay.d.ts +5 -0
- package/dist/react/overlay.d.ts.map +1 -0
- package/dist/react/overlay.js +70 -0
- package/dist/react/overlay.js.map +1 -0
- package/dist/react/styles.d.ts +23 -0
- package/dist/react/styles.d.ts.map +1 -0
- package/dist/react/styles.js +91 -0
- package/dist/react/styles.js.map +1 -0
- package/dist/recorder.d.ts +38 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +82 -0
- package/dist/recorder.js.map +1 -0
- package/dist/registry.d.ts +12 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +57 -0
- package/dist/registry.js.map +1 -0
- package/dist/report.d.ts +3 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +48 -0
- package/dist/report.js.map +1 -0
- package/dist/trace.d.ts +36 -0
- package/dist/trace.d.ts.map +1 -0
- package/dist/trace.js +58 -0
- package/dist/trace.js.map +1 -0
- package/package.json +42 -24
- package/src/index.ts +1 -1
- package/src/noop.ts +48 -12
- package/src/profileScene.ts +85 -19
- package/src/profiler.test.ts +38 -10
- package/src/react/overlay.tsx +3 -1
- package/src/recorder.ts +2 -3
- package/src/registry.ts +4 -1
- package/src/trace.ts +19 -2
package/dist/registry.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { GlobalValue, Option } from 'effect';
|
|
2
|
+
const state = GlobalValue.globalValue(Symbol.for('reform-profiler/registry'), () => ({
|
|
3
|
+
entries: new Map(),
|
|
4
|
+
listeners: new Set(),
|
|
5
|
+
version: { current: 0 },
|
|
6
|
+
overlay: { owner: Option.none() },
|
|
7
|
+
}));
|
|
8
|
+
const notify = () => {
|
|
9
|
+
state.version.current += 1;
|
|
10
|
+
[...state.listeners].forEach((listener) => listener());
|
|
11
|
+
};
|
|
12
|
+
export const registerProfiler = (registration) => {
|
|
13
|
+
const existing = state.entries.get(registration.handle);
|
|
14
|
+
state.entries.set(registration.handle, {
|
|
15
|
+
registration,
|
|
16
|
+
count: existing === undefined ? 1 : existing.count + 1,
|
|
17
|
+
});
|
|
18
|
+
if (existing === undefined) {
|
|
19
|
+
notify();
|
|
20
|
+
}
|
|
21
|
+
return () => {
|
|
22
|
+
const current = state.entries.get(registration.handle);
|
|
23
|
+
if (current === undefined) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (current.count <= 1) {
|
|
27
|
+
state.entries.delete(registration.handle);
|
|
28
|
+
notify();
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
state.entries.set(registration.handle, { ...current, count: current.count - 1 });
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export const listProfilers = () => [...state.entries.values()].map((entry) => entry.registration);
|
|
36
|
+
export const subscribeProfilers = (listener) => {
|
|
37
|
+
state.listeners.add(listener);
|
|
38
|
+
return () => {
|
|
39
|
+
state.listeners.delete(listener);
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export const profilersVersion = () => state.version.current;
|
|
43
|
+
// First mounted overlay claims render; later instances no-op until claim released.
|
|
44
|
+
export const claimOverlay = (owner) => {
|
|
45
|
+
if (Option.isNone(state.overlay.owner)) {
|
|
46
|
+
state.overlay.owner = Option.some(owner);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return Option.contains(state.overlay.owner, owner);
|
|
50
|
+
};
|
|
51
|
+
export const releaseOverlay = (owner) => {
|
|
52
|
+
if (Option.contains(state.overlay.owner, owner)) {
|
|
53
|
+
state.overlay.owner = Option.none();
|
|
54
|
+
notify();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/report.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,YAAY,CAAA;AAiD7D,eAAO,MAAM,YAAY,GAAI,UAAU,cAAc,KAAG,MAqBvD,CAAA"}
|
package/dist/report.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const sortedRows = (stats) => [...stats.entries()]
|
|
2
|
+
.map(([name, stat]) => ({ name, stat }))
|
|
3
|
+
.toSorted((left, right) => right.stat.totalMs - left.stat.totalMs || right.stat.count - left.stat.count);
|
|
4
|
+
const MS_DECIMALS = 2;
|
|
5
|
+
const formatMs = (durationMs) => durationMs.toFixed(MS_DECIMALS);
|
|
6
|
+
const COUNT_WIDTH = 7;
|
|
7
|
+
const TOTAL_WIDTH = 9;
|
|
8
|
+
const AVG_WIDTH = 8;
|
|
9
|
+
const ruleOverhead = '── '.length + ' '.length;
|
|
10
|
+
const section = ({ title, rows, timed }) => {
|
|
11
|
+
if (rows.length === 0) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const nameWidth = Math.max('name'.length, ...rows.map((row) => row.name.length));
|
|
15
|
+
const header = timed
|
|
16
|
+
? `${'name'.padEnd(nameWidth)} ${'count'.padStart(COUNT_WIDTH)} ${'total ms'.padStart(TOTAL_WIDTH)} ${'avg ms'.padStart(AVG_WIDTH)}`
|
|
17
|
+
: `${'name'.padEnd(nameWidth)} ${'count'.padStart(COUNT_WIDTH)}`;
|
|
18
|
+
const lines = rows.map((row) => timed
|
|
19
|
+
? `${row.name.padEnd(nameWidth)} ${String(row.stat.count).padStart(COUNT_WIDTH)} ${formatMs(row.stat.totalMs).padStart(TOTAL_WIDTH)} ${formatMs(row.stat.count === 0 ? 0 : row.stat.totalMs / row.stat.count).padStart(AVG_WIDTH)}`
|
|
20
|
+
: `${row.name.padEnd(nameWidth)} ${String(row.stat.count).padStart(COUNT_WIDTH)}`);
|
|
21
|
+
return [
|
|
22
|
+
`── ${title} ${'─'.repeat(Math.max(0, header.length - title.length - ruleOverhead))}`,
|
|
23
|
+
header,
|
|
24
|
+
...lines,
|
|
25
|
+
'',
|
|
26
|
+
];
|
|
27
|
+
};
|
|
28
|
+
export const formatReport = (profiler) => {
|
|
29
|
+
const snapshot = profiler.snapshot();
|
|
30
|
+
const frameRows = snapshot.frames.count === 0 ? [] : [{ name: 'frame', stat: snapshot.frames }];
|
|
31
|
+
const dropped = snapshot.dropped === 0
|
|
32
|
+
? []
|
|
33
|
+
: [`(${snapshot.dropped} early records evicted from the ring buffer)`, ''];
|
|
34
|
+
return [
|
|
35
|
+
`reform profile — ${profiler.label}`,
|
|
36
|
+
'',
|
|
37
|
+
...section({ title: 'Events', rows: sortedRows(snapshot.events), timed: false }),
|
|
38
|
+
...section({ title: 'Reducers', rows: sortedRows(snapshot.reducers), timed: true }),
|
|
39
|
+
...section({ title: 'State updates', rows: sortedRows(snapshot.states), timed: false }),
|
|
40
|
+
...section({ title: 'Calcs', rows: sortedRows(snapshot.calcs), timed: true }),
|
|
41
|
+
...section({ title: 'Queries', rows: sortedRows(snapshot.queries), timed: true }),
|
|
42
|
+
...section({ title: 'Procedures', rows: sortedRows(snapshot.procedures), timed: true }),
|
|
43
|
+
...section({ title: 'Renders', rows: sortedRows(snapshot.renders), timed: true }),
|
|
44
|
+
...section({ title: 'Frames', rows: frameRows, timed: true }),
|
|
45
|
+
...dropped,
|
|
46
|
+
].join('\n');
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
|
package/dist/trace.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ProfilerHandle } from './recorder.js';
|
|
2
|
+
export type ChromeTraceEvent = {
|
|
3
|
+
readonly ph: 'X';
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly cat: string;
|
|
6
|
+
readonly ts: number;
|
|
7
|
+
readonly dur: number;
|
|
8
|
+
readonly pid: number;
|
|
9
|
+
readonly tid: number;
|
|
10
|
+
readonly args: Readonly<Record<string, string>>;
|
|
11
|
+
} | {
|
|
12
|
+
readonly ph: 'i';
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly cat: string;
|
|
15
|
+
readonly ts: number;
|
|
16
|
+
readonly pid: number;
|
|
17
|
+
readonly tid: number;
|
|
18
|
+
readonly s: 't';
|
|
19
|
+
readonly args: Readonly<Record<string, string>>;
|
|
20
|
+
} | {
|
|
21
|
+
readonly ph: 'M';
|
|
22
|
+
readonly name: 'process_name' | 'thread_name';
|
|
23
|
+
readonly ts: 0;
|
|
24
|
+
readonly pid: number;
|
|
25
|
+
readonly tid: number;
|
|
26
|
+
readonly args: {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export interface ChromeTrace {
|
|
31
|
+
readonly traceEvents: ReadonlyArray<ChromeTraceEvent>;
|
|
32
|
+
readonly displayTimeUnit: 'ms';
|
|
33
|
+
}
|
|
34
|
+
export declare const toChromeTrace: (profiler: ProfilerHandle) => ChromeTrace;
|
|
35
|
+
export declare const traceJson: (profiler: ProfilerHandle) => string;
|
|
36
|
+
//# sourceMappingURL=trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,YAAY,CAAA;AAE7D,MAAM,MAAM,gBAAgB,GACxB;IACE,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAA;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAChD,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAA;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAA;IACf,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAChD,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAA;IAChB,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,aAAa,CAAA;IAC7C,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAA;IACd,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CACzC,CAAA;AAEL,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAA;IACrD,QAAQ,CAAC,eAAe,EAAE,IAAI,CAAA;CAC/B;AAkBD,eAAO,MAAM,aAAa,GAAI,UAAU,cAAc,KAAG,WA0CxD,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,UAAU,cAAc,KAAG,MAEZ,CAAA"}
|
package/dist/trace.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const lanes = {
|
|
2
|
+
frame: { tid: 1, title: 'frames' },
|
|
3
|
+
event: { tid: 2, title: 'events' },
|
|
4
|
+
reducer: { tid: 3, title: 'reducers' },
|
|
5
|
+
state: { tid: 4, title: 'state updates' },
|
|
6
|
+
calc: { tid: 5, title: 'calcs' },
|
|
7
|
+
query: { tid: 6, title: 'queries' },
|
|
8
|
+
procedure: { tid: 7, title: 'procedures' },
|
|
9
|
+
render: { tid: 8, title: 'renders' },
|
|
10
|
+
};
|
|
11
|
+
const instantKinds = new Set(['event', 'state']);
|
|
12
|
+
const pid = 1;
|
|
13
|
+
const US_PER_MS = 1000;
|
|
14
|
+
export const toChromeTrace = (profiler) => {
|
|
15
|
+
const { records } = profiler.snapshot();
|
|
16
|
+
const meta = [
|
|
17
|
+
{
|
|
18
|
+
ph: 'M',
|
|
19
|
+
name: 'process_name',
|
|
20
|
+
ts: 0,
|
|
21
|
+
pid,
|
|
22
|
+
tid: 0,
|
|
23
|
+
args: { name: `reform: ${profiler.label}` },
|
|
24
|
+
},
|
|
25
|
+
...Object.values(lanes).map((lane) => ({
|
|
26
|
+
ph: 'M',
|
|
27
|
+
name: 'thread_name',
|
|
28
|
+
ts: 0,
|
|
29
|
+
pid,
|
|
30
|
+
tid: lane.tid,
|
|
31
|
+
args: { name: lane.title },
|
|
32
|
+
})),
|
|
33
|
+
];
|
|
34
|
+
const body = records.map((record) => {
|
|
35
|
+
const startUs = record.start * US_PER_MS;
|
|
36
|
+
const cat = record.kind;
|
|
37
|
+
const tid = lanes[record.kind].tid;
|
|
38
|
+
const args = record.detail === '' ? {} : { detail: record.detail };
|
|
39
|
+
const complete = {
|
|
40
|
+
ph: 'X',
|
|
41
|
+
name: record.name,
|
|
42
|
+
cat,
|
|
43
|
+
ts: startUs,
|
|
44
|
+
dur: record.dur * US_PER_MS,
|
|
45
|
+
pid,
|
|
46
|
+
tid,
|
|
47
|
+
args,
|
|
48
|
+
};
|
|
49
|
+
return instantKinds.has(record.kind)
|
|
50
|
+
? { ph: 'i', name: record.name, cat, ts: startUs, pid, tid, s: 't', args }
|
|
51
|
+
: complete;
|
|
52
|
+
});
|
|
53
|
+
return { traceEvents: [...meta, ...body], displayTimeUnit: 'ms' };
|
|
54
|
+
};
|
|
55
|
+
export const traceJson = (profiler) =>
|
|
56
|
+
// oxlint-disable-next-line reform-rules/no-json-parse-stringify -- Trace Event Format is a foreign wire format consumed by Chrome DevTools/Perfetto, not Schema-typed data
|
|
57
|
+
JSON.stringify(toChromeTrace(profiler));
|
|
58
|
+
//# sourceMappingURL=trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,55 +1,75 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playfast/reform-profiler",
|
|
3
|
-
"
|
|
4
|
-
"version": "1.0.2",
|
|
5
|
-
"type": "module",
|
|
3
|
+
"version": "1.2.0",
|
|
6
4
|
"description": "Performance profiler for reform scenes — a recording Instrumentation, Chrome DevTools trace export, terminal reports, and a React overlay. Dev builds get the real recorder; prod builds resolve to no-op stubs.",
|
|
7
5
|
"keywords": [
|
|
8
|
-
"
|
|
6
|
+
"devtools",
|
|
9
7
|
"effect",
|
|
10
|
-
"profiler",
|
|
11
8
|
"performance",
|
|
12
|
-
"
|
|
9
|
+
"profiler",
|
|
10
|
+
"reform"
|
|
13
11
|
],
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/playfast/reform/issues"
|
|
14
|
+
},
|
|
14
15
|
"license": "MIT",
|
|
15
16
|
"repository": {
|
|
16
17
|
"type": "git",
|
|
17
18
|
"url": "https://github.com/playfast/reform.git",
|
|
18
19
|
"directory": "packages/reform-profiler"
|
|
19
20
|
},
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"src",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
23
27
|
"sideEffects": false,
|
|
28
|
+
"main": "./dist/noop.js",
|
|
29
|
+
"types": "./dist/noop.d.ts",
|
|
24
30
|
"exports": {
|
|
25
31
|
"./package.json": "./package.json",
|
|
26
32
|
".": {
|
|
27
|
-
"
|
|
28
|
-
|
|
33
|
+
"playfast-src": {
|
|
34
|
+
"development": "./src/index.ts",
|
|
35
|
+
"default": "./src/noop.ts"
|
|
36
|
+
},
|
|
37
|
+
"development": "./dist/index.js",
|
|
38
|
+
"types": "./dist/noop.d.ts",
|
|
39
|
+
"default": "./dist/noop.js"
|
|
29
40
|
},
|
|
30
41
|
"./react": {
|
|
31
|
-
"
|
|
32
|
-
|
|
42
|
+
"playfast-src": {
|
|
43
|
+
"development": "./src/react/index.ts",
|
|
44
|
+
"default": "./src/react/noop.ts"
|
|
45
|
+
},
|
|
46
|
+
"development": "./dist/react/index.js",
|
|
47
|
+
"types": "./dist/react/noop.d.ts",
|
|
48
|
+
"default": "./dist/react/noop.js"
|
|
33
49
|
},
|
|
34
|
-
"./recorder":
|
|
50
|
+
"./recorder": {
|
|
51
|
+
"playfast-src": "./src/index.ts",
|
|
52
|
+
"types": "./dist/index.d.ts",
|
|
53
|
+
"default": "./dist/index.js"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
35
58
|
},
|
|
36
|
-
"files": [
|
|
37
|
-
"src",
|
|
38
|
-
"README.md"
|
|
39
|
-
],
|
|
40
59
|
"scripts": {
|
|
41
60
|
"clean": "rm -rf dist .tsbuildinfo",
|
|
42
61
|
"check": "tsc --noEmit",
|
|
43
|
-
"build": "tsc -p tsconfig.build.json",
|
|
62
|
+
"build": "rm -rf dist .tsbuildinfo && tsc -p tsconfig.build.json && bun ../../scripts/fix-esm-extensions.ts dist",
|
|
44
63
|
"test": "vitest run",
|
|
45
64
|
"test:watch": "vitest",
|
|
46
65
|
"coverage": "vitest run --coverage",
|
|
47
66
|
"lint": "oxlint src",
|
|
48
|
-
"lint:fix": "oxlint --fix src"
|
|
67
|
+
"lint:fix": "oxlint --fix src",
|
|
68
|
+
"prepack": "bun run build"
|
|
49
69
|
},
|
|
50
70
|
"peerDependencies": {
|
|
51
|
-
"effect": "*",
|
|
52
71
|
"@playfast/reform": "*",
|
|
72
|
+
"effect": "*",
|
|
53
73
|
"react": "^19.0.0"
|
|
54
74
|
},
|
|
55
75
|
"peerDependenciesMeta": {
|
|
@@ -57,7 +77,5 @@
|
|
|
57
77
|
"optional": true
|
|
58
78
|
}
|
|
59
79
|
},
|
|
60
|
-
"
|
|
61
|
-
"access": "public"
|
|
62
|
-
}
|
|
80
|
+
"playbook": "./playbook"
|
|
63
81
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ export {
|
|
|
7
7
|
type ProfileSnapshot,
|
|
8
8
|
type ProfileStat,
|
|
9
9
|
} from './recorder'
|
|
10
|
-
export { type ProfiledScene, profileScene } from './profileScene'
|
|
10
|
+
export { type CapturedProfiledScene, type ProfiledScene, profileScene } from './profileScene'
|
|
11
11
|
export { type ChromeTrace, type ChromeTraceEvent, toChromeTrace, traceJson } from './trace'
|
|
12
12
|
export { formatReport } from './report'
|
|
13
13
|
export {
|
package/src/noop.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
ProfilerOptionsExternalApi,
|
|
6
|
-
ProfileSnapshot,
|
|
7
|
-
} from './recorder'
|
|
1
|
+
import { type CapturedScene, type Scene, type UiContract } from '@playfast/reform'
|
|
2
|
+
import { noopInstrumentation } from '@playfast/reform/internal'
|
|
3
|
+
import type { CapturedProfiledScene, ProfiledScene } from './profileScene'
|
|
4
|
+
import type { ProfilerHandle, ProfilerOptionsExternalApi, ProfileSnapshot } from './recorder'
|
|
8
5
|
import type { ProfilerRegistration } from './registry'
|
|
9
6
|
import type { ChromeTrace } from './trace'
|
|
10
7
|
|
|
@@ -18,7 +15,7 @@ export type {
|
|
|
18
15
|
ProfileSnapshot,
|
|
19
16
|
ProfileStat,
|
|
20
17
|
} from './recorder'
|
|
21
|
-
export type { ProfiledScene } from './profileScene'
|
|
18
|
+
export type { CapturedProfiledScene, ProfiledScene } from './profileScene'
|
|
22
19
|
export type { ChromeTrace, ChromeTraceEvent } from './trace'
|
|
23
20
|
export type { ProfilerRegistration } from './registry'
|
|
24
21
|
|
|
@@ -47,10 +44,45 @@ export const noopProfiler: ProfilerHandle = {
|
|
|
47
44
|
export const makeProfiler = (_options: ProfilerOptionsExternalApi = {}): ProfilerHandle =>
|
|
48
45
|
noopProfiler
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
const profileCapturedScene = <
|
|
48
|
+
C extends UiContract,
|
|
49
|
+
S extends ReadonlyArray<unknown>,
|
|
50
|
+
Services,
|
|
51
|
+
P,
|
|
52
|
+
N extends string,
|
|
53
|
+
Identity,
|
|
54
|
+
>(
|
|
55
|
+
base: CapturedScene<C, S, Services, P, N, Identity>,
|
|
56
|
+
): CapturedProfiledScene<C, S, Services, P, N, Identity> => ({
|
|
57
|
+
scene: base,
|
|
58
|
+
profiler: noopProfiler,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
export function profileScene<
|
|
62
|
+
C extends UiContract,
|
|
63
|
+
S extends ReadonlyArray<unknown>,
|
|
64
|
+
Services,
|
|
65
|
+
P,
|
|
66
|
+
N extends string,
|
|
67
|
+
Identity,
|
|
68
|
+
>(
|
|
69
|
+
base: CapturedScene<C, S, Services, P, N, Identity>,
|
|
70
|
+
_options?: ProfilerOptionsExternalApi,
|
|
71
|
+
): CapturedProfiledScene<C, S, Services, P, N, Identity>
|
|
72
|
+
export function profileScene<C extends UiContract, S extends ReadonlyArray<unknown>>(
|
|
73
|
+
base: Scene<C, S>,
|
|
74
|
+
_options?: ProfilerOptionsExternalApi,
|
|
75
|
+
): ProfiledScene<C, S>
|
|
76
|
+
export function profileScene<C extends UiContract, S extends ReadonlyArray<unknown>>(
|
|
51
77
|
base: Scene<C, S>,
|
|
52
78
|
_options: ProfilerOptionsExternalApi = {},
|
|
53
|
-
): ProfiledScene<C, S>
|
|
79
|
+
): ProfiledScene<C, S> {
|
|
80
|
+
return base.capture<ProfiledScene<C, S>>(
|
|
81
|
+
<Services, P, N extends string, Identity>(
|
|
82
|
+
exactScene: CapturedScene<C, S, Services, P, N, Identity>,
|
|
83
|
+
): ProfiledScene<C, S> => profileCapturedScene(exactScene),
|
|
84
|
+
)
|
|
85
|
+
}
|
|
54
86
|
|
|
55
87
|
const emptyTrace: ChromeTrace = { traceEvents: [], displayTimeUnit: 'ms' }
|
|
56
88
|
|
|
@@ -61,11 +93,15 @@ export const traceJson = (_profiler: ProfilerHandle): string =>
|
|
|
61
93
|
|
|
62
94
|
export const formatReport = (_profiler: ProfilerHandle): string => ''
|
|
63
95
|
|
|
64
|
-
export const registerProfiler =
|
|
96
|
+
export const registerProfiler =
|
|
97
|
+
(_registration: ProfilerRegistration): (() => void) =>
|
|
98
|
+
() => {}
|
|
65
99
|
|
|
66
100
|
export const listProfilers = (): ReadonlyArray<ProfilerRegistration> => []
|
|
67
101
|
|
|
68
|
-
export const subscribeProfilers =
|
|
102
|
+
export const subscribeProfilers =
|
|
103
|
+
(_listener: () => void): (() => void) =>
|
|
104
|
+
() => {}
|
|
69
105
|
|
|
70
106
|
export const profilersVersion = (): number => 0
|
|
71
107
|
|
package/src/profileScene.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Effect, Layer } from 'effect'
|
|
2
|
-
import { profiledScene, type Scene, type UiContract } from '@playfast/reform'
|
|
2
|
+
import { type CapturedScene, profiledScene, type Scene, type UiContract } from '@playfast/reform'
|
|
3
|
+
import { type AnySceneCapture, type SceneCapture } from '@playfast/reform/internal'
|
|
3
4
|
import { makeProfiler, type ProfilerHandle, type ProfilerOptionsExternalApi } from './recorder'
|
|
4
5
|
import { registerProfiler } from './registry'
|
|
5
6
|
|
|
@@ -11,29 +12,94 @@ export interface ProfiledScene<
|
|
|
11
12
|
readonly profiler: ProfilerHandle
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export
|
|
15
|
+
export interface CapturedProfiledScene<
|
|
16
|
+
C extends UiContract,
|
|
17
|
+
S extends ReadonlyArray<unknown>,
|
|
18
|
+
Services,
|
|
19
|
+
P,
|
|
20
|
+
N extends string,
|
|
21
|
+
Identity,
|
|
22
|
+
> extends ProfiledScene<C, S> {
|
|
23
|
+
readonly scene: CapturedScene<C, S, Services, P, N, Identity>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const makeLifecycle = (profiler: ProfilerHandle) =>
|
|
27
|
+
Layer.scopedDiscard(
|
|
28
|
+
Effect.acquireRelease(
|
|
29
|
+
Effect.sync(() => registerProfiler({ label: profiler.label, handle: profiler })),
|
|
30
|
+
(unregister) => Effect.sync(unregister),
|
|
31
|
+
),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const withLifecycle = <
|
|
35
|
+
C extends UiContract,
|
|
36
|
+
S extends ReadonlyArray<unknown>,
|
|
37
|
+
Services,
|
|
38
|
+
P,
|
|
39
|
+
N extends string,
|
|
40
|
+
Identity,
|
|
41
|
+
>(
|
|
42
|
+
base: CapturedScene<C, S, Services, P, N, Identity>,
|
|
43
|
+
lifecycle: ReturnType<typeof makeLifecycle>,
|
|
44
|
+
): CapturedScene<C, S, Services, P, N, Identity> => {
|
|
45
|
+
const captured: CapturedScene<C, S, Services, P, N, Identity> = {
|
|
46
|
+
kind: 'Scene',
|
|
47
|
+
composition: base.composition,
|
|
48
|
+
// Lifecycle on the first provide layer tracks the runtime's build/dispose scope.
|
|
49
|
+
provide: base.provide.map((layer, index) =>
|
|
50
|
+
index === 0 ? Layer.merge(layer, lifecycle) : layer,
|
|
51
|
+
),
|
|
52
|
+
...(base.boot === undefined ? {} : { boot: base.boot }),
|
|
53
|
+
...(base.instrumentation === undefined ? {} : { instrumentation: base.instrumentation }),
|
|
54
|
+
capture: <Result>(capture: SceneCapture<C, S, Result>): Result => capture(captured),
|
|
55
|
+
captureAny: <Result>(capture: AnySceneCapture<Result>): Result => capture(captured),
|
|
56
|
+
}
|
|
57
|
+
return captured
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const profileCapturedScene = <
|
|
61
|
+
C extends UiContract,
|
|
62
|
+
S extends ReadonlyArray<unknown>,
|
|
63
|
+
Services,
|
|
64
|
+
P,
|
|
65
|
+
N extends string,
|
|
66
|
+
Identity,
|
|
67
|
+
>(
|
|
68
|
+
base: CapturedScene<C, S, Services, P, N, Identity>,
|
|
69
|
+
profiler: ProfilerHandle,
|
|
70
|
+
lifecycle: ReturnType<typeof makeLifecycle>,
|
|
71
|
+
): CapturedProfiledScene<C, S, Services, P, N, Identity> => ({
|
|
72
|
+
scene: withLifecycle(profiledScene(base, profiler), lifecycle),
|
|
73
|
+
profiler,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
export function profileScene<
|
|
77
|
+
C extends UiContract,
|
|
78
|
+
S extends ReadonlyArray<unknown>,
|
|
79
|
+
Services,
|
|
80
|
+
P,
|
|
81
|
+
N extends string,
|
|
82
|
+
Identity,
|
|
83
|
+
>(
|
|
84
|
+
base: CapturedScene<C, S, Services, P, N, Identity>,
|
|
85
|
+
options?: ProfilerOptionsExternalApi,
|
|
86
|
+
): CapturedProfiledScene<C, S, Services, P, N, Identity>
|
|
87
|
+
export function profileScene<C extends UiContract, S extends ReadonlyArray<unknown>>(
|
|
88
|
+
base: Scene<C, S>,
|
|
89
|
+
options?: ProfilerOptionsExternalApi,
|
|
90
|
+
): ProfiledScene<C, S>
|
|
91
|
+
export function profileScene<C extends UiContract, S extends ReadonlyArray<unknown>>(
|
|
15
92
|
base: Scene<C, S>,
|
|
16
93
|
options: ProfilerOptionsExternalApi = {},
|
|
17
|
-
): ProfiledScene<C, S>
|
|
94
|
+
): ProfiledScene<C, S> {
|
|
18
95
|
const profiler = makeProfiler({
|
|
19
96
|
label: options.label ?? base.composition.manifest.title,
|
|
20
97
|
...(options.capacity !== undefined ? { capacity: options.capacity } : {}),
|
|
21
98
|
})
|
|
22
|
-
const lifecycle =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
),
|
|
99
|
+
const lifecycle = makeLifecycle(profiler)
|
|
100
|
+
return base.capture<ProfiledScene<C, S>>(
|
|
101
|
+
<Services, P, N extends string, Identity>(
|
|
102
|
+
exactScene: CapturedScene<C, S, Services, P, N, Identity>,
|
|
103
|
+
): ProfiledScene<C, S> => profileCapturedScene(exactScene, profiler, lifecycle),
|
|
27
104
|
)
|
|
28
|
-
const wrapped = profiledScene(base, profiler)
|
|
29
|
-
return {
|
|
30
|
-
// Lifecycle on first provide layer so register/unregister track runtime build/dispose.
|
|
31
|
-
scene: {
|
|
32
|
-
...wrapped,
|
|
33
|
-
provide: wrapped.provide.map((layer, index) =>
|
|
34
|
-
index === 0 ? Layer.merge(layer, lifecycle) : layer,
|
|
35
|
-
),
|
|
36
|
-
},
|
|
37
|
-
profiler,
|
|
38
|
-
}
|
|
39
105
|
}
|