@forgeax/engine-profiler 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +164 -0
- package/bin/forgeax-engine-profiler.mjs +5 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/browser-profile-detail-owner.test-d.d.ts +2 -0
- package/dist/__tests__/browser-profile-detail-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/browser-user-timing.test.d.ts +2 -0
- package/dist/__tests__/browser-user-timing.test.d.ts.map +1 -0
- package/dist/__tests__/cli-consumer.integration.test.d.ts +2 -0
- package/dist/__tests__/cli-consumer.integration.test.d.ts.map +1 -0
- package/dist/__tests__/cli-contract.test.d.ts +2 -0
- package/dist/__tests__/cli-contract.test.d.ts.map +1 -0
- package/dist/__tests__/cli-input-errors.test.d.ts +2 -0
- package/dist/__tests__/cli-input-errors.test.d.ts.map +1 -0
- package/dist/__tests__/cli-process.integration.test.d.ts +2 -0
- package/dist/__tests__/cli-process.integration.test.d.ts.map +1 -0
- package/dist/__tests__/compare.test.d.ts +2 -0
- package/dist/__tests__/compare.test.d.ts.map +1 -0
- package/dist/__tests__/comparison-side-owner.test-d.d.ts +2 -0
- package/dist/__tests__/comparison-side-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/consumer-smoke.integration.test.d.ts +2 -0
- package/dist/__tests__/consumer-smoke.integration.test.d.ts.map +1 -0
- package/dist/__tests__/generated-freshness.test.d.ts +2 -0
- package/dist/__tests__/generated-freshness.test.d.ts.map +1 -0
- package/dist/__tests__/model-determinism.test.d.ts +2 -0
- package/dist/__tests__/model-determinism.test.d.ts.map +1 -0
- package/dist/__tests__/model-query.test.d.ts +2 -0
- package/dist/__tests__/model-query.test.d.ts.map +1 -0
- package/dist/__tests__/overflow-long-tail.test.d.ts +2 -0
- package/dist/__tests__/overflow-long-tail.test.d.ts.map +1 -0
- package/dist/__tests__/profile-source-owner.test-d.d.ts +2 -0
- package/dist/__tests__/profile-source-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/profiler-error-owner.test-d.d.ts +2 -0
- package/dist/__tests__/profiler-error-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/published-entry.browser.test.d.ts +2 -0
- package/dist/__tests__/published-entry.browser.test.d.ts.map +1 -0
- package/dist/__tests__/recorder-contract.test.d.ts +2 -0
- package/dist/__tests__/recorder-contract.test.d.ts.map +1 -0
- package/dist/__tests__/schema-contract.test.d.ts +2 -0
- package/dist/__tests__/schema-contract.test.d.ts.map +1 -0
- package/dist/__tests__/schema-derived-union-owner.test.d.ts +2 -0
- package/dist/__tests__/schema-derived-union-owner.test.d.ts.map +1 -0
- package/dist/browser-user-timing.d.ts +24 -0
- package/dist/browser-user-timing.d.ts.map +1 -0
- package/dist/browser-user-timing.mjs +140 -0
- package/dist/browser-user-timing.mjs.map +1 -0
- package/dist/cli.d.ts +21 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.mjs +7783 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/clock.d.ts +5 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/compare.d.ts +42 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/errors.d.ts +64 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/generated/profile-capture.d.ts +44 -0
- package/dist/generated/profile-capture.d.ts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +8111 -0
- package/dist/index.mjs.map +1 -0
- package/dist/model.d.ts +44 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/profiler.d.ts +24 -0
- package/dist/profiler.d.ts.map +1 -0
- package/dist/recorder.d.ts +26 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/schema.d.ts +13 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/tool-preview-profile.d.ts +33 -0
- package/dist/tool-preview-profile.d.ts.map +1 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +76 -0
- package/schema/profile-capture.schema.json +71 -0
- package/scripts/consume-smoke.mjs +17 -0
- package/scripts/generate-profile-types.mjs +75 -0
- package/src/__tests__/browser-profile-detail-owner.test-d.ts +26 -0
- package/src/__tests__/browser-user-timing.test.ts +127 -0
- package/src/__tests__/cli-consumer.integration.test.ts +113 -0
- package/src/__tests__/cli-contract.test.ts +115 -0
- package/src/__tests__/cli-input-errors.test.ts +210 -0
- package/src/__tests__/cli-process.integration.test.ts +154 -0
- package/src/__tests__/compare.test.ts +223 -0
- package/src/__tests__/comparison-side-owner.test-d.ts +28 -0
- package/src/__tests__/consumer-smoke.integration.test.ts +37 -0
- package/src/__tests__/fixtures/cli/invalid-version.json +18 -0
- package/src/__tests__/fixtures/cli/partial-capture.json +28 -0
- package/src/__tests__/fixtures/cli/valid-capture.json +43 -0
- package/src/__tests__/fixtures/profile-capture/invalid-boundary.json +144 -0
- package/src/__tests__/fixtures/profile-capture/model-input.json +54 -0
- package/src/__tests__/fixtures/profile-capture/sink-failure.json +30 -0
- package/src/__tests__/fixtures/profile-capture/valid-complete.json +43 -0
- package/src/__tests__/fixtures/profile-capture/valid-overflow.json +45 -0
- package/src/__tests__/generated-freshness.test.ts +14 -0
- package/src/__tests__/model-determinism.test.ts +70 -0
- package/src/__tests__/model-query.test.ts +128 -0
- package/src/__tests__/overflow-long-tail.test.ts +131 -0
- package/src/__tests__/profile-source-owner.test-d.ts +45 -0
- package/src/__tests__/profiler-error-owner.test-d.ts +64 -0
- package/src/__tests__/published-entry.browser.test.ts +27 -0
- package/src/__tests__/recorder-contract.test.ts +163 -0
- package/src/__tests__/schema-contract.test.ts +42 -0
- package/src/__tests__/schema-derived-union-owner.test.ts +57 -0
- package/src/browser-user-timing.ts +194 -0
- package/src/cli.ts +425 -0
- package/src/clock.ts +18 -0
- package/src/compare.ts +159 -0
- package/src/errors.ts +78 -0
- package/src/generated/profile-capture.ts +48 -0
- package/src/index.ts +54 -0
- package/src/model.ts +161 -0
- package/src/profiler.ts +201 -0
- package/src/recorder.ts +417 -0
- package/src/schema.ts +168 -0
- package/src/tool-preview-profile.ts +67 -0
- package/src/types.ts +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/compare.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison-side-owner.test-d.d.ts","sourceRoot":"","sources":["../../src/__tests__/comparison-side-owner.test-d.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer-smoke.integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/consumer-smoke.integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generated-freshness.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/generated-freshness.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-determinism.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/model-determinism.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-query.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/model-query.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overflow-long-tail.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/overflow-long-tail.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-source-owner.test-d.d.ts","sourceRoot":"","sources":["../../src/__tests__/profile-source-owner.test-d.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiler-error-owner.test-d.d.ts","sourceRoot":"","sources":["../../src/__tests__/profiler-error-owner.test-d.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"published-entry.browser.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/published-entry.browser.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder-contract.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/recorder-contract.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-contract.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/schema-contract.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-derived-union-owner.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/schema-derived-union-owner.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Profiler } from './profiler.js';
|
|
2
|
+
import type { ProfileSource } from './types.js';
|
|
3
|
+
/** Configuration shared by editor and standalone Play browser hosts. */
|
|
4
|
+
export interface UserTimingProfilerOptions {
|
|
5
|
+
/** Global flag that enables marks. Defaults to the harness diagnostic flag. */
|
|
6
|
+
readonly diagnosticsKey?: string;
|
|
7
|
+
/** Stable id written into the optional Profiler capture projection. */
|
|
8
|
+
readonly captureId?: string;
|
|
9
|
+
/** Host callback invoked after an owner phase closes. */
|
|
10
|
+
readonly onPhaseEnd?: (phase: {
|
|
11
|
+
readonly source: ProfileSource;
|
|
12
|
+
readonly phase: string;
|
|
13
|
+
}) => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a zero-cost-while-disabled browser projection of the engine Profiler.
|
|
17
|
+
*
|
|
18
|
+
* The engine frame loop remains the phase owner. This adapter only translates
|
|
19
|
+
* the already-open phases to User Timing marks when a host explicitly enables
|
|
20
|
+
* the diagnostic flag before app creation. Normal game/editor frames therefore
|
|
21
|
+
* do not allocate a profiler or call performance.mark().
|
|
22
|
+
*/
|
|
23
|
+
export declare function createUserTimingProfiler(options?: UserTimingProfilerOptions): Profiler | undefined;
|
|
24
|
+
//# sourceMappingURL=browser-user-timing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-user-timing.d.ts","sourceRoot":"","sources":["../src/browser-user-timing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,KAAK,EAAqC,aAAa,EAAE,MAAM,YAAY,CAAC;AAKnF,wEAAwE;AACxE,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,uEAAuE;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACnG;AAgDD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,yBAA8B,GACtC,QAAQ,GAAG,SAAS,CAwHtB"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/browser-user-timing.ts
|
|
2
|
+
var DEFAULT_CAPTURE_ID = "capture-0001";
|
|
3
|
+
var CAPTURE_ID_PATTERN = /^capture-[0-9]{4,}$/;
|
|
4
|
+
function resolveCaptureId(captureId) {
|
|
5
|
+
return captureId === void 0 || CAPTURE_ID_PATTERN.test(captureId) ? captureId ?? DEFAULT_CAPTURE_ID : DEFAULT_CAPTURE_ID;
|
|
6
|
+
}
|
|
7
|
+
function partialCapture(captureId, phaseCatalog) {
|
|
8
|
+
return {
|
|
9
|
+
schemaVersion: "1.0",
|
|
10
|
+
captureId,
|
|
11
|
+
timeUnit: "microseconds",
|
|
12
|
+
frameLimit: 1,
|
|
13
|
+
eventLimit: 1,
|
|
14
|
+
phaseCatalog,
|
|
15
|
+
records: [],
|
|
16
|
+
completeness: {
|
|
17
|
+
status: "partial",
|
|
18
|
+
retainedEventCount: 0,
|
|
19
|
+
droppedEventCount: 0,
|
|
20
|
+
incompleteReason: "user-timing-transport"
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function diagnosticsEnabled(key) {
|
|
25
|
+
const value = globalThis[key];
|
|
26
|
+
return typeof value === "object" && value !== null && value.enabled === true;
|
|
27
|
+
}
|
|
28
|
+
function diagnosticsDetail(key) {
|
|
29
|
+
const value = globalThis[key];
|
|
30
|
+
if (typeof value !== "object" || value === null) return "owner";
|
|
31
|
+
const detail = value.detail;
|
|
32
|
+
return detail === "nested" || detail === "passes" ? detail : "owner";
|
|
33
|
+
}
|
|
34
|
+
function createUserTimingProfiler(options = {}) {
|
|
35
|
+
const diagnosticsKey = options.diagnosticsKey ?? "__forgeaxFramePhaseDiagnostics";
|
|
36
|
+
const captureId = resolveCaptureId(options.captureId);
|
|
37
|
+
const timingEnabled = diagnosticsEnabled(diagnosticsKey);
|
|
38
|
+
const detail = diagnosticsDetail(diagnosticsKey);
|
|
39
|
+
const performanceApi = timingEnabled ? globalThis.performance : void 0;
|
|
40
|
+
if (!timingEnabled && options.onPhaseEnd === void 0) return void 0;
|
|
41
|
+
if (timingEnabled && (performanceApi === void 0 || typeof performanceApi.mark !== "function")) {
|
|
42
|
+
if (options.onPhaseEnd === void 0) return void 0;
|
|
43
|
+
}
|
|
44
|
+
let phaseCatalog = { app: [], render: [] };
|
|
45
|
+
let frameId;
|
|
46
|
+
let active = true;
|
|
47
|
+
let latest;
|
|
48
|
+
const openPhases = [];
|
|
49
|
+
function mark(name) {
|
|
50
|
+
if (performanceApi === void 0) return;
|
|
51
|
+
try {
|
|
52
|
+
performanceApi.mark(name);
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function timingSource(source) {
|
|
57
|
+
return source === "app" ? "frame" : "render";
|
|
58
|
+
}
|
|
59
|
+
const session = {
|
|
60
|
+
captureId,
|
|
61
|
+
detail,
|
|
62
|
+
beginFrame(nextFrameId) {
|
|
63
|
+
frameId = nextFrameId;
|
|
64
|
+
openPhases.length = 0;
|
|
65
|
+
return { ok: true, value: void 0 };
|
|
66
|
+
},
|
|
67
|
+
beginPhase(inputOrSource, phaseName) {
|
|
68
|
+
if (frameId === void 0) return { ok: true, value: void 0 };
|
|
69
|
+
const input = typeof inputOrSource === "string" ? { source: inputOrSource, phase: phaseName } : inputOrSource;
|
|
70
|
+
openPhases.push(input);
|
|
71
|
+
mark(`forgeax.${timingSource(input.source)}.phase.${frameId}.${input.phase}.begin`);
|
|
72
|
+
return { ok: true, value: void 0 };
|
|
73
|
+
},
|
|
74
|
+
endPhase() {
|
|
75
|
+
const phase = openPhases.pop();
|
|
76
|
+
if (phase !== void 0) options.onPhaseEnd?.(phase);
|
|
77
|
+
if (frameId !== void 0 && phase !== void 0) {
|
|
78
|
+
mark(`forgeax.${timingSource(phase.source)}.phase.${frameId}.${phase.phase}.end`);
|
|
79
|
+
}
|
|
80
|
+
return { ok: true, value: void 0 };
|
|
81
|
+
},
|
|
82
|
+
recordSkip(input) {
|
|
83
|
+
if (frameId !== void 0) {
|
|
84
|
+
mark(
|
|
85
|
+
`forgeax.${timingSource(input.source)}.phase.${frameId}.${input.phase}.skip.${input.reason}`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return { ok: true, value: void 0 };
|
|
89
|
+
},
|
|
90
|
+
endFrame() {
|
|
91
|
+
frameId = void 0;
|
|
92
|
+
openPhases.length = 0;
|
|
93
|
+
return { ok: true, value: void 0 };
|
|
94
|
+
},
|
|
95
|
+
finish() {
|
|
96
|
+
latest = partialCapture(captureId, phaseCatalog);
|
|
97
|
+
active = false;
|
|
98
|
+
frameId = void 0;
|
|
99
|
+
openPhases.length = 0;
|
|
100
|
+
return { ok: true, value: latest };
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
return {
|
|
104
|
+
registerPhaseCatalog(source, phases) {
|
|
105
|
+
const definition = [...phases];
|
|
106
|
+
phaseCatalog = { ...phaseCatalog, [source]: definition };
|
|
107
|
+
let registered = true;
|
|
108
|
+
return {
|
|
109
|
+
ok: true,
|
|
110
|
+
value: () => {
|
|
111
|
+
if (!registered) return;
|
|
112
|
+
registered = false;
|
|
113
|
+
if (phaseCatalog[source] !== definition) return;
|
|
114
|
+
phaseCatalog = { ...phaseCatalog, [source]: [] };
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
startCapture() {
|
|
119
|
+
active = true;
|
|
120
|
+
latest = void 0;
|
|
121
|
+
return { ok: true, value: session };
|
|
122
|
+
},
|
|
123
|
+
activeCaptureId() {
|
|
124
|
+
return active ? captureId : void 0;
|
|
125
|
+
},
|
|
126
|
+
activeSession() {
|
|
127
|
+
return active ? session : void 0;
|
|
128
|
+
},
|
|
129
|
+
latestCapture() {
|
|
130
|
+
return latest;
|
|
131
|
+
},
|
|
132
|
+
get phaseCatalog() {
|
|
133
|
+
return phaseCatalog;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { createUserTimingProfiler };
|
|
139
|
+
//# sourceMappingURL=browser-user-timing.mjs.map
|
|
140
|
+
//# sourceMappingURL=browser-user-timing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/browser-user-timing.ts"],"names":[],"mappings":"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","file":"browser-user-timing.mjs","sourcesContent":["import type { Profiler } from './profiler.js';\nimport type { ProfileDetail, RecorderSession } from './recorder.js';\nimport type { ProfileCapture, ProfilePhaseStart, ProfileSource } from './types.js';\n\n/** The small browser API surface used by the opt-in User Timing adapter. */\ntype UserTiming = { readonly mark: (name: string) => void };\n\n/** Configuration shared by editor and standalone Play browser hosts. */\nexport interface UserTimingProfilerOptions {\n /** Global flag that enables marks. Defaults to the harness diagnostic flag. */\n readonly diagnosticsKey?: string;\n /** Stable id written into the optional Profiler capture projection. */\n readonly captureId?: string;\n /** Host callback invoked after an owner phase closes. */\n readonly onPhaseEnd?: (phase: { readonly source: ProfileSource; readonly phase: string }) => void;\n}\n\ntype DiagnosticGlobal = Record<string, unknown>;\ntype PhaseCatalog = ProfileCapture['phaseCatalog'];\ntype BrowserProfileDetail = ProfileDetail;\nconst DEFAULT_CAPTURE_ID = 'capture-0001';\nconst CAPTURE_ID_PATTERN = /^capture-[0-9]{4,}$/;\n\nfunction resolveCaptureId(captureId: string | undefined): string {\n return captureId === undefined || CAPTURE_ID_PATTERN.test(captureId)\n ? (captureId ?? DEFAULT_CAPTURE_ID)\n : DEFAULT_CAPTURE_ID;\n}\n\nfunction partialCapture(captureId: string, phaseCatalog: PhaseCatalog): ProfileCapture {\n return {\n schemaVersion: '1.0',\n captureId,\n timeUnit: 'microseconds',\n frameLimit: 1,\n eventLimit: 1,\n phaseCatalog,\n records: [],\n completeness: {\n status: 'partial',\n retainedEventCount: 0,\n droppedEventCount: 0,\n incompleteReason: 'user-timing-transport',\n },\n };\n}\n\nfunction diagnosticsEnabled(key: string): boolean {\n const value = (globalThis as unknown as DiagnosticGlobal)[key];\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { readonly enabled?: unknown }).enabled === true\n );\n}\n\nfunction diagnosticsDetail(key: string): BrowserProfileDetail {\n const value = (globalThis as unknown as DiagnosticGlobal)[key];\n if (typeof value !== 'object' || value === null) return 'owner';\n const detail = (value as { readonly detail?: unknown }).detail;\n return detail === 'nested' || detail === 'passes' ? detail : 'owner';\n}\n\n/**\n * Creates a zero-cost-while-disabled browser projection of the engine Profiler.\n *\n * The engine frame loop remains the phase owner. This adapter only translates\n * the already-open phases to User Timing marks when a host explicitly enables\n * the diagnostic flag before app creation. Normal game/editor frames therefore\n * do not allocate a profiler or call performance.mark().\n */\nexport function createUserTimingProfiler(\n options: UserTimingProfilerOptions = {},\n): Profiler | undefined {\n const diagnosticsKey = options.diagnosticsKey ?? '__forgeaxFramePhaseDiagnostics';\n const captureId = resolveCaptureId(options.captureId);\n const timingEnabled = diagnosticsEnabled(diagnosticsKey);\n const detail = diagnosticsDetail(diagnosticsKey);\n const performanceApi = timingEnabled\n ? (globalThis as unknown as { readonly performance?: UserTiming }).performance\n : undefined;\n if (!timingEnabled && options.onPhaseEnd === undefined) return undefined;\n if (\n timingEnabled &&\n (performanceApi === undefined || typeof performanceApi.mark !== 'function')\n ) {\n if (options.onPhaseEnd === undefined) return undefined;\n }\n\n let phaseCatalog: PhaseCatalog = { app: [], render: [] };\n let frameId: number | undefined;\n let active = true;\n let latest: ProfileCapture | undefined;\n const openPhases: Array<{ readonly source: ProfileSource; readonly phase: string }> = [];\n\n function mark(name: string): void {\n if (performanceApi === undefined) return;\n try {\n performanceApi.mark(name);\n } catch {\n // Diagnostics must not change the host frame-loop behavior.\n }\n }\n\n function timingSource(source: ProfileSource): 'frame' | 'render' {\n return source === 'app' ? 'frame' : 'render';\n }\n\n const session = {\n captureId,\n detail,\n beginFrame(nextFrameId: number) {\n frameId = nextFrameId;\n openPhases.length = 0;\n return { ok: true as const, value: undefined };\n },\n beginPhase(inputOrSource: ProfilePhaseStart | ProfileSource, phaseName?: string) {\n if (frameId === undefined) return { ok: true as const, value: undefined };\n const input =\n typeof inputOrSource === 'string'\n ? { source: inputOrSource, phase: phaseName as string }\n : inputOrSource;\n openPhases.push(input);\n mark(`forgeax.${timingSource(input.source)}.phase.${frameId}.${input.phase}.begin`);\n return { ok: true as const, value: undefined };\n },\n endPhase() {\n const phase = openPhases.pop();\n if (phase !== undefined) options.onPhaseEnd?.(phase);\n if (frameId !== undefined && phase !== undefined) {\n mark(`forgeax.${timingSource(phase.source)}.phase.${frameId}.${phase.phase}.end`);\n }\n return { ok: true as const, value: undefined };\n },\n recordSkip(input: {\n readonly source: ProfileSource;\n readonly phase: string;\n readonly reason: string;\n }) {\n if (frameId !== undefined) {\n mark(\n `forgeax.${timingSource(input.source)}.phase.${frameId}.${input.phase}.skip.${input.reason}`,\n );\n }\n return { ok: true as const, value: undefined };\n },\n endFrame() {\n frameId = undefined;\n openPhases.length = 0;\n return { ok: true as const, value: undefined };\n },\n finish() {\n latest = partialCapture(captureId, phaseCatalog);\n active = false;\n frameId = undefined;\n openPhases.length = 0;\n return { ok: true as const, value: latest };\n },\n } satisfies RecorderSession;\n\n return {\n registerPhaseCatalog(source, phases) {\n const definition = [...phases];\n phaseCatalog = { ...phaseCatalog, [source]: definition };\n let registered = true;\n return {\n ok: true,\n value: () => {\n if (!registered) return;\n registered = false;\n if (phaseCatalog[source] !== definition) return;\n phaseCatalog = { ...phaseCatalog, [source]: [] };\n },\n };\n },\n startCapture() {\n active = true;\n latest = undefined;\n return { ok: true, value: session };\n },\n activeCaptureId() {\n return active ? captureId : undefined;\n },\n activeSession() {\n return active ? session : undefined;\n },\n latestCapture() {\n return latest;\n },\n get phaseCatalog() {\n return phaseCatalog;\n },\n };\n}\n"]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface ProfilerCliResult {
|
|
2
|
+
readonly stdout: string;
|
|
3
|
+
readonly stderr: string;
|
|
4
|
+
readonly exitCode: number;
|
|
5
|
+
}
|
|
6
|
+
type CliError = {
|
|
7
|
+
readonly code: 'cli-arguments-invalid' | 'cli-input-empty' | 'cli-input-file-read-failed' | 'cli-input-invalid-json' | 'cli-input-stdin-read-failed' | 'cli-query-invalid';
|
|
8
|
+
readonly expected: string;
|
|
9
|
+
readonly hint: string;
|
|
10
|
+
readonly detail: {
|
|
11
|
+
readonly argument?: string;
|
|
12
|
+
readonly path?: string;
|
|
13
|
+
readonly side?: 'left' | 'right';
|
|
14
|
+
readonly message: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare function runProfilerCli(args: readonly string[], stdin: string | CliError): ProfilerCliResult;
|
|
18
|
+
export declare function readCliInput(args: readonly string[], readStdin: () => string): string | CliError;
|
|
19
|
+
export declare function main(): void;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAeD,KAAK,QAAQ,GAAG;IACd,QAAQ,CAAC,IAAI,EACT,uBAAuB,GACvB,iBAAiB,GACjB,4BAA4B,GAC5B,wBAAwB,GACxB,6BAA6B,GAC7B,mBAAmB,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AAuTF,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,KAAK,EAAE,MAAM,GAAG,QAAQ,GACvB,iBAAiB,CAiCnB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,MAAM,GAAG,MAAM,GAAG,QAAQ,CAchG;AAED,wBAAgB,IAAI,IAAI,IAAI,CAS3B"}
|