@flighthq/debug 0.5.1-next.3308.79ae053 → 0.5.1-next.4304.322dc96
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/README.md +2 -2
- package/dist/debug.d.ts +21 -7
- package/dist/debug.d.ts.map +1 -1
- package/dist/debug.js +75 -61
- package/dist/debug.js.map +1 -1
- package/dist/debugTiming.d.ts +6 -4
- package/dist/debugTiming.d.ts.map +1 -1
- package/dist/debugTiming.js +23 -24
- package/dist/debugTiming.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/src/debug.test.ts +157 -96
- package/src/debugTiming.test.ts +56 -34
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/debug`. The `@flight
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/debug@0.5.1-next.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/debug@0.5.1-next.4304.322dc96](https://github.com/flighthq/flight/tree/322dc963fc5a404a31bc0cc612e906de504ea024/packages/debug)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/322dc963fc5a404a31bc0cc612e906de504ea024/LICENSE.md)
|
package/dist/debug.d.ts
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import type { EntityConstruction } from '@flighthq/entity/contract';
|
|
2
|
+
import type { LogState } from '@flighthq/log/contract';
|
|
3
|
+
import type { DebugOptions, DebugSubsystemHooks, DebugSubsystemName, Entity, LogSink, RenderState } from '@flighthq/types/contract';
|
|
4
|
+
import { LogLevel } from '@flighthq/types/contract';
|
|
5
|
+
export interface DebugState extends Entity {
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
enabledSubsystems: Readonly<DebugSubsystemHooks>[];
|
|
8
|
+
frameNumber: number;
|
|
9
|
+
installedSink: LogSink | null;
|
|
10
|
+
savedChannelLevels: Map<string, LogLevel | null>;
|
|
11
|
+
savedGlobalLevel: LogLevel;
|
|
12
|
+
subsystems: Map<string, Readonly<DebugSubsystemHooks>>;
|
|
13
|
+
}
|
|
14
|
+
export declare function createDebugState(): DebugState;
|
|
15
|
+
export declare function disableDebug(state: DebugState, logState: LogState): void;
|
|
16
|
+
export declare function enableDebug(state: DebugState, logState: LogState, options?: Readonly<DebugOptions>): void;
|
|
17
|
+
export declare function enableFlightDiagnostics(debugState: DebugState, logState: LogState, state: RenderState): void;
|
|
18
|
+
export declare function initializeDebugState(out: EntityConstruction<DebugState>): void;
|
|
19
|
+
export declare function isDebugEnabled(state: Readonly<DebugState>): boolean;
|
|
20
|
+
export declare function registerDebugSubsystem(state: DebugState, name: DebugSubsystemName, hooks: Readonly<DebugSubsystemHooks>): void;
|
|
21
|
+
export declare function unregisterDebugSubsystem(state: DebugState, name: DebugSubsystemName): boolean;
|
|
8
22
|
//# sourceMappingURL=debug.d.ts.map
|
package/dist/debug.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAYvD,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,MAAM,EACN,OAAO,EACP,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAIpD,MAAM,WAAW,UAAW,SAAQ,MAAM;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC,CAAC;IACjD,gBAAgB,EAAE,QAAQ,CAAC;IAC3B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC;CACxD;AAGD,wBAAgB,gBAAgB,IAAI,UAAU,CAI7C;AAMD,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAOxE;AASD,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,GAAE,QAAQ,CAAC,YAAY,CAAM,GAAG,IAAI,CA8C7G;AAID,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAI5G;AAID,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,kBAAkB,CAAC,UAAU,CAAC,GAAG,IAAI,CAQ9E;AAGD,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAEnE;AAOD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,kBAAkB,EACxB,KAAK,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GACnC,IAAI,CAEN;AAID,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAE7F"}
|
package/dist/debug.js
CHANGED
|
@@ -1,55 +1,62 @@
|
|
|
1
|
+
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
1
2
|
import { addLogSink, clearLogChannelLevel, createConsoleLogSink, getLogChannelLevel, getLogLevel, removeLogSink, setLogChannelLevel, setLogLevel, } from '@flighthq/log/contract';
|
|
2
3
|
import { enableColorAdjustmentGuards, enableRenderRegistriesGuards } from '@flighthq/render/contract';
|
|
3
4
|
import { LogLevel } from '@flighthq/types/contract';
|
|
5
|
+
// Creates an empty debug state: no registered subsystems and no active session.
|
|
6
|
+
export function createDebugState() {
|
|
7
|
+
const out = allocateEntity();
|
|
8
|
+
initializeDebugState(out);
|
|
9
|
+
return finishEntity(out);
|
|
10
|
+
}
|
|
4
11
|
// The one call that returns Flight to the zero-debug baseline: removes the sink enableDebug
|
|
5
12
|
// installed, restores the log levels it raised, and runs the disable-guards binding of every
|
|
6
13
|
// subsystem it switched on. No-op when debug is not enabled (idempotent, sentinel — not a throw).
|
|
7
14
|
// After this, importing @flighthq/debug costs nothing and no debug output is produced.
|
|
8
|
-
export function disableDebug() {
|
|
9
|
-
if (!
|
|
15
|
+
export function disableDebug(state, logState) {
|
|
16
|
+
if (!state.enabled)
|
|
10
17
|
return;
|
|
11
|
-
for (const hooks of
|
|
18
|
+
for (const hooks of state.enabledSubsystems)
|
|
12
19
|
hooks.disableGuards?.();
|
|
13
|
-
|
|
14
|
-
_removeDebugSink();
|
|
15
|
-
_restoreDebugLevels();
|
|
16
|
-
|
|
20
|
+
state.enabledSubsystems.length = 0;
|
|
21
|
+
_removeDebugSink(state, logState);
|
|
22
|
+
_restoreDebugLevels(state, logState);
|
|
23
|
+
state.enabled = false;
|
|
17
24
|
}
|
|
18
|
-
// The friendly entry point to Flight's diagnostics. Installs a dev console LogSink (a
|
|
19
|
-
// one by default; override with `options.sink`), raises the global log level (default
|
|
25
|
+
// The friendly entry point to Flight's diagnostics. Installs a dev console LogSink on `logState` (a
|
|
26
|
+
// text-formatted one by default; override with `options.sink`), raises the global log level (default
|
|
20
27
|
// LogLevel.Debug) and the per-channel levels of the selected subsystems plus any explicit
|
|
21
28
|
// `options.channels`, and runs each selected subsystem's registered guard-enabler. `options.subsystems`
|
|
22
29
|
// selects which registered subsystems to switch on; omit it to switch on every registered subsystem.
|
|
23
30
|
// Idempotent: a second call while already enabled is a no-op (disableDebug first to re-enable with
|
|
24
31
|
// different options).
|
|
25
|
-
export function enableDebug(options = {}) {
|
|
26
|
-
if (
|
|
32
|
+
export function enableDebug(state, logState, options = {}) {
|
|
33
|
+
if (state.enabled)
|
|
27
34
|
return;
|
|
28
35
|
const level = options.level ?? LogLevel.Debug;
|
|
29
|
-
const subsystems = _resolveDebugSubsystems(options.subsystems);
|
|
36
|
+
const subsystems = _resolveDebugSubsystems(state, options.subsystems);
|
|
30
37
|
const channels = _collectDebugChannels(subsystems, options.channels);
|
|
31
|
-
|
|
32
|
-
const savedChannelLevels = new Map(channels.map((channel) => [channel, getLogChannelLevel(channel)]));
|
|
38
|
+
state.savedGlobalLevel = getLogLevel(logState);
|
|
39
|
+
const savedChannelLevels = new Map(channels.map((channel) => [channel, getLogChannelLevel(logState, channel)]));
|
|
33
40
|
try {
|
|
34
|
-
_applyDebugLevels(level, channels);
|
|
35
|
-
_installDebugSink(options.sink ?? createConsoleLogSink());
|
|
41
|
+
_applyDebugLevels(logState, level, channels);
|
|
42
|
+
_installDebugSink(state, logState, options.sink ?? createConsoleLogSink(logState));
|
|
36
43
|
for (const hooks of subsystems) {
|
|
37
|
-
|
|
44
|
+
state.enabledSubsystems.push(hooks);
|
|
38
45
|
hooks.enableGuards?.();
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
catch (error) {
|
|
42
|
-
for (let index =
|
|
49
|
+
for (let index = state.enabledSubsystems.length - 1; index >= 0; index--) {
|
|
43
50
|
try {
|
|
44
|
-
|
|
51
|
+
state.enabledSubsystems[index].disableGuards?.();
|
|
45
52
|
}
|
|
46
53
|
catch {
|
|
47
54
|
// Keep unwinding; the original enable error owns this failed transaction.
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
|
-
|
|
57
|
+
state.enabledSubsystems.length = 0;
|
|
51
58
|
try {
|
|
52
|
-
_removeDebugSink();
|
|
59
|
+
_removeDebugSink(state, logState);
|
|
53
60
|
}
|
|
54
61
|
catch {
|
|
55
62
|
// Keep restoring levels; the original enable error must not be masked.
|
|
@@ -57,58 +64,65 @@ export function enableDebug(options = {}) {
|
|
|
57
64
|
for (const [channel, savedLevel] of savedChannelLevels) {
|
|
58
65
|
try {
|
|
59
66
|
if (savedLevel === null)
|
|
60
|
-
clearLogChannelLevel(channel);
|
|
67
|
+
clearLogChannelLevel(logState, channel);
|
|
61
68
|
else
|
|
62
|
-
setLogChannelLevel(channel, savedLevel);
|
|
69
|
+
setLogChannelLevel(logState, channel, savedLevel);
|
|
63
70
|
}
|
|
64
71
|
catch {
|
|
65
72
|
// Keep restoring the remaining levels; the original enable error must survive.
|
|
66
73
|
}
|
|
67
74
|
}
|
|
68
75
|
try {
|
|
69
|
-
setLogLevel(
|
|
76
|
+
setLogLevel(logState, state.savedGlobalLevel);
|
|
70
77
|
}
|
|
71
78
|
catch {
|
|
72
79
|
// The original enable error remains the public failure even if rollback also fails.
|
|
73
80
|
}
|
|
74
81
|
throw error;
|
|
75
82
|
}
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
state.savedChannelLevels = savedChannelLevels;
|
|
84
|
+
state.enabled = true;
|
|
85
|
+
}
|
|
86
|
+
// Enables a debug session with the default options, then the generic render guards for one render
|
|
87
|
+
// state, reporting through the same caller-owned log state.
|
|
88
|
+
export function enableFlightDiagnostics(debugState, logState, state) {
|
|
89
|
+
enableDebug(debugState, logState);
|
|
90
|
+
enableColorAdjustmentGuards(state, logState);
|
|
91
|
+
enableRenderRegistriesGuards(state, logState);
|
|
78
92
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
// Resets a debug state to no registered subsystems and no active session (the construction
|
|
94
|
+
// initializer of createDebugState).
|
|
95
|
+
export function initializeDebugState(out) {
|
|
96
|
+
out.enabled = false;
|
|
97
|
+
out.enabledSubsystems = [];
|
|
98
|
+
out.frameNumber = 0;
|
|
99
|
+
out.installedSink = null;
|
|
100
|
+
out.savedChannelLevels = new Map();
|
|
101
|
+
out.savedGlobalLevel = LogLevel.Verbose;
|
|
102
|
+
out.subsystems = new Map();
|
|
83
103
|
}
|
|
84
104
|
// Whether enableDebug is currently in effect (a sink is installed and levels/guards are raised).
|
|
85
|
-
export function isDebugEnabled() {
|
|
86
|
-
return
|
|
105
|
+
export function isDebugEnabled(state) {
|
|
106
|
+
return state.enabled;
|
|
87
107
|
}
|
|
88
108
|
// Registers a diagnostic subsystem's wiring — the log channels whose verbosity enableDebug should
|
|
89
109
|
// raise and the guard-enabler/-disabler bindings it should run for `name`. A package's thin debug
|
|
90
110
|
// adapter (or the app) calls this so enableDebug(['render']) can drive the subsystem without
|
|
91
111
|
// @flighthq/debug importing it. Last-write-wins; vendor-prefix custom subsystem names ('acme.Foo')
|
|
92
112
|
// to avoid colliding with built-ins.
|
|
93
|
-
export function registerDebugSubsystem(name, hooks) {
|
|
94
|
-
|
|
113
|
+
export function registerDebugSubsystem(state, name, hooks) {
|
|
114
|
+
state.subsystems.set(name, hooks);
|
|
95
115
|
}
|
|
96
116
|
// Removes a subsystem registration. Returns false when nothing was registered under `name`
|
|
97
117
|
// (sentinel, not a throw). Does not affect an already-active debug session.
|
|
98
|
-
export function unregisterDebugSubsystem(name) {
|
|
99
|
-
return
|
|
118
|
+
export function unregisterDebugSubsystem(state, name) {
|
|
119
|
+
return state.subsystems.delete(name);
|
|
100
120
|
}
|
|
101
|
-
const _subsystems = new Map();
|
|
102
|
-
const _enabledSubsystems = [];
|
|
103
|
-
let _enabled = false;
|
|
104
|
-
let _installedSink = null;
|
|
105
|
-
let _savedGlobalLevel = LogLevel.Verbose;
|
|
106
|
-
let _savedChannelLevels = new Map();
|
|
107
121
|
// Raises the global level and every selected channel's level to `level` for the session.
|
|
108
|
-
function _applyDebugLevels(level, channels) {
|
|
109
|
-
setLogLevel(level);
|
|
122
|
+
function _applyDebugLevels(logState, level, channels) {
|
|
123
|
+
setLogLevel(logState, level);
|
|
110
124
|
for (const channel of channels)
|
|
111
|
-
setLogChannelLevel(channel, level);
|
|
125
|
+
setLogChannelLevel(logState, channel, level);
|
|
112
126
|
}
|
|
113
127
|
// Gathers the channels to raise: every selected subsystem's channels plus any explicit extras.
|
|
114
128
|
function _collectDebugChannels(subsystems, extra) {
|
|
@@ -122,25 +136,25 @@ function _collectDebugChannels(subsystems, extra) {
|
|
|
122
136
|
return channels;
|
|
123
137
|
}
|
|
124
138
|
// Installs the debug sink and records it so disableDebug can remove exactly this one.
|
|
125
|
-
function _installDebugSink(sink) {
|
|
126
|
-
|
|
127
|
-
addLogSink(sink);
|
|
139
|
+
function _installDebugSink(state, logState, sink) {
|
|
140
|
+
state.installedSink = sink;
|
|
141
|
+
addLogSink(logState, sink);
|
|
128
142
|
}
|
|
129
143
|
// Removes the installed debug sink, if any.
|
|
130
|
-
function _removeDebugSink() {
|
|
131
|
-
if (
|
|
144
|
+
function _removeDebugSink(state, logState) {
|
|
145
|
+
if (state.installedSink === null)
|
|
132
146
|
return;
|
|
133
|
-
removeLogSink(
|
|
134
|
-
|
|
147
|
+
removeLogSink(logState, state.installedSink);
|
|
148
|
+
state.installedSink = null;
|
|
135
149
|
}
|
|
136
150
|
// Resolves the subsystem hooks to drive: the named ones when `names` is given (unregistered names
|
|
137
151
|
// are skipped), otherwise every registered subsystem.
|
|
138
|
-
function _resolveDebugSubsystems(names) {
|
|
152
|
+
function _resolveDebugSubsystems(state, names) {
|
|
139
153
|
if (names === undefined)
|
|
140
|
-
return [...
|
|
154
|
+
return [...state.subsystems.values()];
|
|
141
155
|
const resolved = [];
|
|
142
156
|
for (const name of names) {
|
|
143
|
-
const hooks =
|
|
157
|
+
const hooks = state.subsystems.get(name);
|
|
144
158
|
if (hooks !== undefined)
|
|
145
159
|
resolved.push(hooks);
|
|
146
160
|
}
|
|
@@ -148,14 +162,14 @@ function _resolveDebugSubsystems(names) {
|
|
|
148
162
|
}
|
|
149
163
|
// Restores the global level and per-channel overrides enableDebug saved. Only the channels debug
|
|
150
164
|
// raised are touched; channel overrides the caller set before the session are preserved.
|
|
151
|
-
function _restoreDebugLevels() {
|
|
152
|
-
setLogLevel(
|
|
153
|
-
for (const [channel, savedLevel] of
|
|
165
|
+
function _restoreDebugLevels(state, logState) {
|
|
166
|
+
setLogLevel(logState, state.savedGlobalLevel);
|
|
167
|
+
for (const [channel, savedLevel] of state.savedChannelLevels) {
|
|
154
168
|
if (savedLevel === null)
|
|
155
|
-
clearLogChannelLevel(channel);
|
|
169
|
+
clearLogChannelLevel(logState, channel);
|
|
156
170
|
else
|
|
157
|
-
setLogChannelLevel(channel, savedLevel);
|
|
171
|
+
setLogChannelLevel(logState, channel, savedLevel);
|
|
158
172
|
}
|
|
159
|
-
|
|
173
|
+
state.savedChannelLevels = new Map();
|
|
160
174
|
}
|
|
161
175
|
//# sourceMappingURL=debug.js.map
|
package/dist/debug.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,WAAW,GACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,2BAA2B,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"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"}
|
package/dist/debugTiming.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { LogState } from '@flighthq/log/contract';
|
|
1
2
|
import type { LogTimer } from '@flighthq/types/contract';
|
|
2
|
-
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
3
|
+
import type { DebugState } from './debug.ts';
|
|
4
|
+
export declare function beginDebugSpan(state: Readonly<DebugState>, nowMs: number, name: string, channel?: string | null): LogTimer | null;
|
|
5
|
+
export declare function endDebugSpan(logState: LogState, nowMs: number, timer: Readonly<LogTimer> | null): number;
|
|
6
|
+
export declare function markDebugFrame(state: DebugState, logState: LogState, nowMs: number, label?: string, channel?: string | null): void;
|
|
7
|
+
export declare function measureDebugSpan<T>(state: Readonly<DebugState>, logState: LogState, clock: () => number, name: string, fn: () => T, channel?: string | null): T;
|
|
6
8
|
//# sourceMappingURL=debugTiming.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debugTiming.d.ts","sourceRoot":"","sources":["../src/debugTiming.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"debugTiming.d.ts","sourceRoot":"","sources":["../src/debugTiming.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAQ7C,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC3B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAM,GAAG,IAAW,GAC5B,QAAQ,GAAG,IAAI,CAEjB;AAKD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,GAAG,IAAI,GAAG,MAAM,CAExG;AAOD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,GAAE,MAAM,GAAG,IAAW,GAC5B,IAAI,CAGN;AAMD,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC3B,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,CAAC,EACX,OAAO,GAAE,MAAM,GAAG,IAAW,GAC5B,CAAC,CAOH"}
|
package/dist/debugTiming.js
CHANGED
|
@@ -1,42 +1,41 @@
|
|
|
1
1
|
import { endLogTimer, logDebug, startLogTimer } from '@flighthq/log/contract';
|
|
2
2
|
import { isDebugEnabled } from "./debug.js";
|
|
3
|
-
// Opens a named timing span
|
|
4
|
-
// endDebugSpan, or null when debug is disabled (production, or
|
|
5
|
-
// point — with debug off, opening a span is one boolean check
|
|
6
|
-
// instrumentation left in shipping code costs effectively nothing. Pair
|
|
7
|
-
// endDebugSpan (measureDebugSpan brackets both for a single synchronous call).
|
|
8
|
-
export function beginDebugSpan(name, channel = null) {
|
|
9
|
-
return isDebugEnabled() ? startLogTimer(name, channel) : null;
|
|
3
|
+
// Opens a named timing span at the caller's clock reading `nowMs`, but only while a debug session is
|
|
4
|
+
// active: returns a LogTimer to hand to endDebugSpan, or null when debug is disabled (production, or
|
|
5
|
+
// before enableDebug). Gating here is the point — with debug off, opening a span is one boolean check
|
|
6
|
+
// and allocates nothing, so span instrumentation left in shipping code costs effectively nothing. Pair
|
|
7
|
+
// every non-null result with endDebugSpan (measureDebugSpan brackets both for a single synchronous call).
|
|
8
|
+
export function beginDebugSpan(state, nowMs, name, channel = null) {
|
|
9
|
+
return isDebugEnabled(state) ? startLogTimer(name, channel, nowMs) : null;
|
|
10
10
|
}
|
|
11
|
-
// Closes a span opened by beginDebugSpan, emitting its elapsed-time Debug entry through
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
export function endDebugSpan(timer) {
|
|
15
|
-
return timer === null ? -1 : endLogTimer(timer);
|
|
11
|
+
// Closes a span opened by beginDebugSpan, emitting its elapsed-time Debug entry through `logState` and
|
|
12
|
+
// returning the elapsed milliseconds. A null timer — debug was disabled when the span opened — is a
|
|
13
|
+
// no-op returning -1, the sentinel for "not measured".
|
|
14
|
+
export function endDebugSpan(logState, nowMs, timer) {
|
|
15
|
+
return timer === null ? -1 : endLogTimer(logState, nowMs, timer);
|
|
16
16
|
}
|
|
17
17
|
// Emits a frame-boundary marker while a debug session is active, so the per-frame span entries between
|
|
18
18
|
// two markers can be read against the frame they fell in. `label` names the frame; omit it to tag the
|
|
19
|
-
// marker with
|
|
20
|
-
// so a per-frame call in the app loop costs a single boolean
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// marker with the state's ascending frame counter, which a new session does not reset. No-op (and no
|
|
20
|
+
// counter advance) when debug is disabled, so a per-frame call in the app loop costs a single boolean
|
|
21
|
+
// check in production.
|
|
22
|
+
export function markDebugFrame(state, logState, nowMs, label, channel = null) {
|
|
23
|
+
if (!isDebugEnabled(state))
|
|
23
24
|
return;
|
|
24
|
-
logDebug({ frame: label ?? ++
|
|
25
|
+
logDebug(logState, nowMs, { frame: label ?? ++state.frameNumber }, channel);
|
|
25
26
|
}
|
|
26
27
|
// Brackets a synchronous function as a timing span: times `fn` while a debug session is active,
|
|
27
28
|
// emitting one elapsed-time entry, and returns `fn`'s result. When debug is disabled `fn` still runs —
|
|
28
29
|
// only the timing is skipped — so wrapping a call never changes behavior, only observability. The span
|
|
29
|
-
// is closed even if `fn` throws.
|
|
30
|
-
export function measureDebugSpan(name, fn, channel = null) {
|
|
31
|
-
const timer = beginDebugSpan(name, channel);
|
|
30
|
+
// is closed even if `fn` throws. `clock` is read once when the span opens and once when it closes.
|
|
31
|
+
export function measureDebugSpan(state, logState, clock, name, fn, channel = null) {
|
|
32
|
+
const timer = beginDebugSpan(state, clock(), name, channel);
|
|
32
33
|
try {
|
|
33
34
|
return fn();
|
|
34
35
|
}
|
|
35
36
|
finally {
|
|
36
|
-
|
|
37
|
+
if (timer !== null)
|
|
38
|
+
endDebugSpan(logState, clock(), timer);
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
|
-
// Monotonic id handed to unlabeled frame markers. Not reset between sessions — a marker's job is to
|
|
40
|
-
// correlate span entries with a frame boundary, for which a unique ascending id suffices.
|
|
41
|
-
let _debugFrameNumber = 0;
|
|
42
41
|
//# sourceMappingURL=debugTiming.js.map
|
package/dist/debugTiming.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debugTiming.js","sourceRoot":"","sources":["../src/debugTiming.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"debugTiming.js","sourceRoot":"","sources":["../src/debugTiming.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAI9E,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,qGAAqG;AACrG,qGAAqG;AACrG,sGAAsG;AACtG,uGAAuG;AACvG,0GAA0G;AAC1G,MAAM,UAAU,cAAc,CAC5B,KAA2B,EAC3B,KAAa,EACb,IAAY,EACZ,UAAyB,IAAI;IAE7B,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5E,CAAC;AAED,uGAAuG;AACvG,oGAAoG;AACpG,uDAAuD;AACvD,MAAM,UAAU,YAAY,CAAC,QAAkB,EAAE,KAAa,EAAE,KAAgC;IAC9F,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,uGAAuG;AACvG,sGAAsG;AACtG,qGAAqG;AACrG,sGAAsG;AACtG,uBAAuB;AACvB,MAAM,UAAU,cAAc,CAC5B,KAAiB,EACjB,QAAkB,EAClB,KAAa,EACb,KAAc,EACd,UAAyB,IAAI;IAE7B,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;QAAE,OAAO;IACnC,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC;AAC9E,CAAC;AAED,gGAAgG;AAChG,uGAAuG;AACvG,uGAAuG;AACvG,mGAAmG;AACnG,MAAM,UAAU,gBAAgB,CAC9B,KAA2B,EAC3B,QAAkB,EAClB,KAAmB,EACnB,IAAY,EACZ,EAAW,EACX,UAAyB,IAAI;IAE7B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5D,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAC;IACd,CAAC;YAAS,CAAC;QACT,IAAI,KAAK,KAAK,IAAI;YAAE,YAAY,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { beginDebugSpan, disableDebug, enableDebug, enableFlightDiagnostics, endDebugSpan, isDebugEnabled, markDebugFrame, measureDebugSpan, registerDebugSubsystem, unregisterDebugSubsystem, } from './contract.ts';
|
|
1
|
+
export { beginDebugSpan, createDebugState, disableDebug, enableDebug, enableFlightDiagnostics, endDebugSpan, isDebugEnabled, markDebugFrame, measureDebugSpan, registerDebugSubsystem, unregisterDebugSubsystem, } from './contract.ts';
|
|
2
|
+
export type { DebugState } from './contract.ts';
|
|
2
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,YAAY,EACZ,WAAW,EACX,uBAAuB,EACvB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,uBAAuB,EACvB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { beginDebugSpan, disableDebug, enableDebug, enableFlightDiagnostics, endDebugSpan, isDebugEnabled, markDebugFrame, measureDebugSpan, registerDebugSubsystem, unregisterDebugSubsystem, } from "./contract.js";
|
|
1
|
+
export { beginDebugSpan, createDebugState, disableDebug, enableDebug, enableFlightDiagnostics, endDebugSpan, isDebugEnabled, markDebugFrame, measureDebugSpan, registerDebugSubsystem, unregisterDebugSubsystem, } from "./contract.js";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,YAAY,EACZ,WAAW,EACX,uBAAuB,EACvB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,uBAAuB,EACvB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,eAAe,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/debug",
|
|
3
|
-
"version": "0.5.1-next.
|
|
3
|
+
"version": "0.5.1-next.4304.322dc96",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,9 +38,10 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/
|
|
42
|
-
"@flighthq/
|
|
43
|
-
"@flighthq/
|
|
41
|
+
"@flighthq/entity": "0.5.1-next.4304.322dc96",
|
|
42
|
+
"@flighthq/log": "0.5.1-next.4304.322dc96",
|
|
43
|
+
"@flighthq/render": "0.5.1-next.4304.322dc96",
|
|
44
|
+
"@flighthq/types": "0.5.1-next.4304.322dc96"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"typescript": "^5.3.0"
|
package/src/debug.test.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { LogState } from '@flighthq/log/contract';
|
|
1
2
|
import {
|
|
2
3
|
clearLogChannelLevels,
|
|
3
4
|
clearLogSinks,
|
|
5
|
+
createLogState,
|
|
4
6
|
createMemoryLogSink,
|
|
5
7
|
getLogChannelLevel,
|
|
6
8
|
getLogLevel,
|
|
@@ -14,14 +16,17 @@ import {
|
|
|
14
16
|
areRenderRegistriesGuardsEnabled,
|
|
15
17
|
createRenderState,
|
|
16
18
|
} from '@flighthq/render/contract';
|
|
17
|
-
import type { MemoryLogSink } from '@flighthq/types/contract';
|
|
19
|
+
import type { LogEntry, MemoryLogSink } from '@flighthq/types/contract';
|
|
18
20
|
import { LogLevel } from '@flighthq/types/contract';
|
|
19
21
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
20
22
|
|
|
23
|
+
import type { DebugState } from './debug.ts';
|
|
21
24
|
import {
|
|
25
|
+
createDebugState,
|
|
22
26
|
disableDebug,
|
|
23
27
|
enableDebug,
|
|
24
28
|
enableFlightDiagnostics,
|
|
29
|
+
initializeDebugState,
|
|
25
30
|
isDebugEnabled,
|
|
26
31
|
registerDebugSubsystem,
|
|
27
32
|
unregisterDebugSubsystem,
|
|
@@ -32,132 +37,170 @@ import {
|
|
|
32
37
|
const testSubsystemNames = ['render', 'input', 'audio', 'acme.custom'];
|
|
33
38
|
|
|
34
39
|
function readMemory(sink: MemoryLogSink): readonly unknown[] {
|
|
35
|
-
return
|
|
40
|
+
return readLogEntries(sink);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Monotonic test clock: each read advances one millisecond, so spans measure a positive duration.
|
|
44
|
+
function clock(): number {
|
|
45
|
+
return ++clockMs;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readLogEntries(sink: Readonly<MemoryLogSink>): LogEntry[] {
|
|
49
|
+
const entries: LogEntry[] = [];
|
|
50
|
+
getMemoryLogSinkEntries(entries, sink);
|
|
51
|
+
return entries;
|
|
36
52
|
}
|
|
37
53
|
|
|
38
54
|
beforeEach(() => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
clockMs = 0;
|
|
56
|
+
debugState = createDebugState();
|
|
57
|
+
logState = createLogState();
|
|
58
|
+
if (isDebugEnabled(debugState)) disableDebug(debugState, logState);
|
|
59
|
+
for (const name of testSubsystemNames) unregisterDebugSubsystem(debugState, name);
|
|
60
|
+
clearLogSinks(logState);
|
|
61
|
+
clearLogChannelLevels(logState);
|
|
62
|
+
setLogLevel(logState, LogLevel.Verbose);
|
|
44
63
|
});
|
|
45
64
|
|
|
46
65
|
afterEach(() => {
|
|
47
|
-
if (isDebugEnabled()) disableDebug();
|
|
48
|
-
for (const name of testSubsystemNames) unregisterDebugSubsystem(name);
|
|
49
|
-
clearLogSinks();
|
|
50
|
-
clearLogChannelLevels();
|
|
51
|
-
setLogLevel(LogLevel.Verbose);
|
|
66
|
+
if (isDebugEnabled(debugState)) disableDebug(debugState, logState);
|
|
67
|
+
for (const name of testSubsystemNames) unregisterDebugSubsystem(debugState, name);
|
|
68
|
+
clearLogSinks(logState);
|
|
69
|
+
clearLogChannelLevels(logState);
|
|
70
|
+
setLogLevel(logState, LogLevel.Verbose);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('createDebugState', () => {
|
|
74
|
+
it('starts disabled with no frame, sink, saved levels, or subsystems', () => {
|
|
75
|
+
const state = createDebugState();
|
|
76
|
+
expect(state.enabled).toBe(false);
|
|
77
|
+
expect(state.enabledSubsystems).toEqual([]);
|
|
78
|
+
expect(state.frameNumber).toBe(0);
|
|
79
|
+
expect(state.installedSink).toBeNull();
|
|
80
|
+
expect(state.savedChannelLevels.size).toBe(0);
|
|
81
|
+
expect(state.subsystems.size).toBe(0);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('returns independent states', () => {
|
|
85
|
+
expect(createDebugState().subsystems).not.toBe(createDebugState().subsystems);
|
|
86
|
+
});
|
|
52
87
|
});
|
|
53
88
|
|
|
54
89
|
describe('disableDebug', () => {
|
|
55
90
|
it('removes the installed sink so a later log does not reach it', () => {
|
|
56
91
|
const memory = createMemoryLogSink(16);
|
|
57
|
-
registerDebugSubsystem('render', { channels: ['render'] });
|
|
58
|
-
enableDebug({ subsystems: ['render'], sink: memory.sink });
|
|
92
|
+
registerDebugSubsystem(debugState, 'render', { channels: ['render'] });
|
|
93
|
+
enableDebug(debugState, logState, { subsystems: ['render'], sink: memory.sink });
|
|
59
94
|
|
|
60
|
-
log(LogLevel.Debug, 'before', 'render');
|
|
95
|
+
log(logState, 0, LogLevel.Debug, 'before', 'render');
|
|
61
96
|
expect(readMemory(memory)).toHaveLength(1);
|
|
62
97
|
|
|
63
|
-
disableDebug();
|
|
64
|
-
log(LogLevel.Debug, 'after', 'render');
|
|
98
|
+
disableDebug(debugState, logState);
|
|
99
|
+
log(logState, 0, LogLevel.Debug, 'after', 'render');
|
|
65
100
|
expect(readMemory(memory)).toHaveLength(1);
|
|
66
101
|
});
|
|
67
102
|
|
|
68
103
|
it('runs each enabled subsystem disableGuards binding', () => {
|
|
69
104
|
const disableGuards = vi.fn();
|
|
70
|
-
registerDebugSubsystem('render', { disableGuards });
|
|
71
|
-
enableDebug({ subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
105
|
+
registerDebugSubsystem(debugState, 'render', { disableGuards });
|
|
106
|
+
enableDebug(debugState, logState, { subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
72
107
|
|
|
73
108
|
expect(disableGuards).not.toHaveBeenCalled();
|
|
74
|
-
disableDebug();
|
|
109
|
+
disableDebug(debugState, logState);
|
|
75
110
|
expect(disableGuards).toHaveBeenCalledTimes(1);
|
|
76
111
|
});
|
|
77
112
|
|
|
78
113
|
it('restores the global level and clears the channel overrides it raised', () => {
|
|
79
|
-
setLogLevel(LogLevel.Warn);
|
|
80
|
-
registerDebugSubsystem('render', { channels: ['render'] });
|
|
81
|
-
enableDebug(
|
|
114
|
+
setLogLevel(logState, LogLevel.Warn);
|
|
115
|
+
registerDebugSubsystem(debugState, 'render', { channels: ['render'] });
|
|
116
|
+
enableDebug(debugState, logState, {
|
|
117
|
+
subsystems: ['render'],
|
|
118
|
+
level: LogLevel.Verbose,
|
|
119
|
+
sink: createMemoryLogSink(1).sink,
|
|
120
|
+
});
|
|
82
121
|
|
|
83
|
-
disableDebug();
|
|
84
|
-
expect(getLogLevel()).toBe(LogLevel.Warn);
|
|
85
|
-
expect(getLogChannelLevel('render')).toBeNull();
|
|
122
|
+
disableDebug(debugState, logState);
|
|
123
|
+
expect(getLogLevel(logState)).toBe(LogLevel.Warn);
|
|
124
|
+
expect(getLogChannelLevel(logState, 'render')).toBeNull();
|
|
86
125
|
});
|
|
87
126
|
|
|
88
127
|
it('preserves channel overrides the caller set before the session', () => {
|
|
89
|
-
setLogChannelLevel('unrelated', LogLevel.Error);
|
|
90
|
-
registerDebugSubsystem('render', { channels: ['render'] });
|
|
91
|
-
enableDebug({ subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
128
|
+
setLogChannelLevel(logState, 'unrelated', LogLevel.Error);
|
|
129
|
+
registerDebugSubsystem(debugState, 'render', { channels: ['render'] });
|
|
130
|
+
enableDebug(debugState, logState, { subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
92
131
|
|
|
93
|
-
expect(getLogChannelLevel('render')).toBe(LogLevel.Debug);
|
|
132
|
+
expect(getLogChannelLevel(logState, 'render')).toBe(LogLevel.Debug);
|
|
94
133
|
|
|
95
|
-
disableDebug();
|
|
96
|
-
expect(getLogChannelLevel('render')).toBeNull();
|
|
97
|
-
expect(getLogChannelLevel('unrelated')).toBe(LogLevel.Error);
|
|
134
|
+
disableDebug(debugState, logState);
|
|
135
|
+
expect(getLogChannelLevel(logState, 'render')).toBeNull();
|
|
136
|
+
expect(getLogChannelLevel(logState, 'unrelated')).toBe(LogLevel.Error);
|
|
98
137
|
});
|
|
99
138
|
|
|
100
139
|
it('restores a pre-existing override on a channel debug also raised', () => {
|
|
101
|
-
setLogChannelLevel('render', LogLevel.Warn);
|
|
102
|
-
registerDebugSubsystem('render', { channels: ['render'] });
|
|
103
|
-
enableDebug(
|
|
140
|
+
setLogChannelLevel(logState, 'render', LogLevel.Warn);
|
|
141
|
+
registerDebugSubsystem(debugState, 'render', { channels: ['render'] });
|
|
142
|
+
enableDebug(debugState, logState, {
|
|
143
|
+
subsystems: ['render'],
|
|
144
|
+
level: LogLevel.Verbose,
|
|
145
|
+
sink: createMemoryLogSink(1).sink,
|
|
146
|
+
});
|
|
104
147
|
|
|
105
|
-
expect(getLogChannelLevel('render')).toBe(LogLevel.Verbose);
|
|
148
|
+
expect(getLogChannelLevel(logState, 'render')).toBe(LogLevel.Verbose);
|
|
106
149
|
|
|
107
|
-
disableDebug();
|
|
108
|
-
expect(getLogChannelLevel('render')).toBe(LogLevel.Warn);
|
|
150
|
+
disableDebug(debugState, logState);
|
|
151
|
+
expect(getLogChannelLevel(logState, 'render')).toBe(LogLevel.Warn);
|
|
109
152
|
});
|
|
110
153
|
|
|
111
154
|
it('is a no-op when debug is not enabled', () => {
|
|
112
|
-
expect(isDebugEnabled()).toBe(false);
|
|
113
|
-
expect(() => disableDebug()).not.toThrow();
|
|
114
|
-
expect(isDebugEnabled()).toBe(false);
|
|
155
|
+
expect(isDebugEnabled(debugState)).toBe(false);
|
|
156
|
+
expect(() => disableDebug(debugState, logState)).not.toThrow();
|
|
157
|
+
expect(isDebugEnabled(debugState)).toBe(false);
|
|
115
158
|
});
|
|
116
159
|
});
|
|
117
160
|
|
|
118
161
|
describe('enableDebug', () => {
|
|
119
162
|
it('runs a selected subsystem enableGuards and raises its channel levels', () => {
|
|
120
163
|
const enableGuards = vi.fn();
|
|
121
|
-
registerDebugSubsystem('render', { channels: ['render', 'render.batch'], enableGuards });
|
|
164
|
+
registerDebugSubsystem(debugState, 'render', { channels: ['render', 'render.batch'], enableGuards });
|
|
122
165
|
|
|
123
|
-
enableDebug({ subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
166
|
+
enableDebug(debugState, logState, { subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
124
167
|
|
|
125
168
|
expect(enableGuards).toHaveBeenCalledTimes(1);
|
|
126
|
-
expect(getLogChannelLevel('render')).toBe(LogLevel.Debug);
|
|
127
|
-
expect(getLogChannelLevel('render.batch')).toBe(LogLevel.Debug);
|
|
128
|
-
expect(isDebugEnabled()).toBe(true);
|
|
169
|
+
expect(getLogChannelLevel(logState, 'render')).toBe(LogLevel.Debug);
|
|
170
|
+
expect(getLogChannelLevel(logState, 'render.batch')).toBe(LogLevel.Debug);
|
|
171
|
+
expect(isDebugEnabled(debugState)).toBe(true);
|
|
129
172
|
});
|
|
130
173
|
|
|
131
174
|
it('routes a log on a raised channel to the installed sink', () => {
|
|
132
175
|
const memory = createMemoryLogSink(16);
|
|
133
|
-
registerDebugSubsystem('render', { channels: ['render'] });
|
|
134
|
-
enableDebug({ subsystems: ['render'], sink: memory.sink });
|
|
176
|
+
registerDebugSubsystem(debugState, 'render', { channels: ['render'] });
|
|
177
|
+
enableDebug(debugState, logState, { subsystems: ['render'], sink: memory.sink });
|
|
135
178
|
|
|
136
|
-
log(LogLevel.Debug, { msg: 'draw' }, 'render');
|
|
179
|
+
log(logState, 0, LogLevel.Debug, { msg: 'draw' }, 'render');
|
|
137
180
|
expect(readMemory(memory)).toHaveLength(1);
|
|
138
181
|
});
|
|
139
182
|
|
|
140
183
|
it('honors an explicit level and extra channels', () => {
|
|
141
|
-
registerDebugSubsystem('render', { channels: ['render'] });
|
|
142
|
-
enableDebug({
|
|
184
|
+
registerDebugSubsystem(debugState, 'render', { channels: ['render'] });
|
|
185
|
+
enableDebug(debugState, logState, {
|
|
143
186
|
subsystems: ['render'],
|
|
144
187
|
level: LogLevel.Verbose,
|
|
145
188
|
channels: ['app'],
|
|
146
189
|
sink: createMemoryLogSink(1).sink,
|
|
147
190
|
});
|
|
148
191
|
|
|
149
|
-
expect(getLogLevel()).toBe(LogLevel.Verbose);
|
|
150
|
-
expect(getLogChannelLevel('render')).toBe(LogLevel.Verbose);
|
|
151
|
-
expect(getLogChannelLevel('app')).toBe(LogLevel.Verbose);
|
|
192
|
+
expect(getLogLevel(logState)).toBe(LogLevel.Verbose);
|
|
193
|
+
expect(getLogChannelLevel(logState, 'render')).toBe(LogLevel.Verbose);
|
|
194
|
+
expect(getLogChannelLevel(logState, 'app')).toBe(LogLevel.Verbose);
|
|
152
195
|
});
|
|
153
196
|
|
|
154
197
|
it('enables every registered subsystem when none are named', () => {
|
|
155
198
|
const renderGuards = vi.fn();
|
|
156
199
|
const inputGuards = vi.fn();
|
|
157
|
-
registerDebugSubsystem('render', { enableGuards: renderGuards });
|
|
158
|
-
registerDebugSubsystem('input', { enableGuards: inputGuards });
|
|
200
|
+
registerDebugSubsystem(debugState, 'render', { enableGuards: renderGuards });
|
|
201
|
+
registerDebugSubsystem(debugState, 'input', { enableGuards: inputGuards });
|
|
159
202
|
|
|
160
|
-
enableDebug({ sink: createMemoryLogSink(1).sink });
|
|
203
|
+
enableDebug(debugState, logState, { sink: createMemoryLogSink(1).sink });
|
|
161
204
|
|
|
162
205
|
expect(renderGuards).toHaveBeenCalledTimes(1);
|
|
163
206
|
expect(inputGuards).toHaveBeenCalledTimes(1);
|
|
@@ -165,20 +208,20 @@ describe('enableDebug', () => {
|
|
|
165
208
|
|
|
166
209
|
it('skips an unregistered subsystem name without throwing', () => {
|
|
167
210
|
const memory = createMemoryLogSink(1);
|
|
168
|
-
expect(() => enableDebug({ subsystems: ['audio'], sink: memory.sink })).not.toThrow();
|
|
169
|
-
expect(isDebugEnabled()).toBe(true);
|
|
211
|
+
expect(() => enableDebug(debugState, logState, { subsystems: ['audio'], sink: memory.sink })).not.toThrow();
|
|
212
|
+
expect(isDebugEnabled(debugState)).toBe(true);
|
|
170
213
|
});
|
|
171
214
|
|
|
172
215
|
it('is idempotent — a second enable while active does not re-run guards or re-install', () => {
|
|
173
216
|
const enableGuards = vi.fn();
|
|
174
217
|
const memory = createMemoryLogSink(16);
|
|
175
|
-
registerDebugSubsystem('render', { channels: ['render'], enableGuards });
|
|
218
|
+
registerDebugSubsystem(debugState, 'render', { channels: ['render'], enableGuards });
|
|
176
219
|
|
|
177
|
-
enableDebug({ subsystems: ['render'], sink: memory.sink });
|
|
178
|
-
enableDebug({ subsystems: ['render'], sink: memory.sink });
|
|
220
|
+
enableDebug(debugState, logState, { subsystems: ['render'], sink: memory.sink });
|
|
221
|
+
enableDebug(debugState, logState, { subsystems: ['render'], sink: memory.sink });
|
|
179
222
|
|
|
180
223
|
expect(enableGuards).toHaveBeenCalledTimes(1);
|
|
181
|
-
log(LogLevel.Debug, 'once', 'render');
|
|
224
|
+
log(logState, 0, LogLevel.Debug, 'once', 'render');
|
|
182
225
|
expect(readMemory(memory)).toHaveLength(1);
|
|
183
226
|
});
|
|
184
227
|
|
|
@@ -200,31 +243,31 @@ describe('enableDebug', () => {
|
|
|
200
243
|
if (failEnable) throw originalError;
|
|
201
244
|
},
|
|
202
245
|
});
|
|
203
|
-
registerDebugSubsystem('render', hooks('render'));
|
|
204
|
-
registerDebugSubsystem('input', hooks('input', false, true));
|
|
205
|
-
registerDebugSubsystem('audio', hooks('audio', true));
|
|
206
|
-
setLogLevel(LogLevel.Warn);
|
|
207
|
-
setLogChannelLevel('render', LogLevel.Error);
|
|
208
|
-
setLogChannelLevel('input', LogLevel.Info);
|
|
209
|
-
setLogChannelLevel('unrelated', LogLevel.Verbose);
|
|
246
|
+
registerDebugSubsystem(debugState, 'render', hooks('render'));
|
|
247
|
+
registerDebugSubsystem(debugState, 'input', hooks('input', false, true));
|
|
248
|
+
registerDebugSubsystem(debugState, 'audio', hooks('audio', true));
|
|
249
|
+
setLogLevel(logState, LogLevel.Warn);
|
|
250
|
+
setLogChannelLevel(logState, 'render', LogLevel.Error);
|
|
251
|
+
setLogChannelLevel(logState, 'input', LogLevel.Info);
|
|
252
|
+
setLogChannelLevel(logState, 'unrelated', LogLevel.Verbose);
|
|
210
253
|
|
|
211
254
|
let thrown: unknown;
|
|
212
255
|
try {
|
|
213
|
-
enableDebug({ subsystems: ['render', 'input', 'audio'], sink: memory.sink });
|
|
256
|
+
enableDebug(debugState, logState, { subsystems: ['render', 'input', 'audio'], sink: memory.sink });
|
|
214
257
|
} catch (error) {
|
|
215
258
|
thrown = error;
|
|
216
259
|
}
|
|
217
260
|
|
|
218
261
|
expect(thrown).toBe(originalError);
|
|
219
|
-
expect(isDebugEnabled()).toBe(false);
|
|
262
|
+
expect(isDebugEnabled(debugState)).toBe(false);
|
|
220
263
|
expect([...activeGuards]).toEqual([]);
|
|
221
264
|
expect(rollbackOrder).toEqual(['audio', 'input', 'render']);
|
|
222
|
-
expect(getLogLevel()).toBe(LogLevel.Warn);
|
|
223
|
-
expect(getLogChannelLevel('render')).toBe(LogLevel.Error);
|
|
224
|
-
expect(getLogChannelLevel('input')).toBe(LogLevel.Info);
|
|
225
|
-
expect(getLogChannelLevel('audio')).toBeNull();
|
|
226
|
-
expect(getLogChannelLevel('unrelated')).toBe(LogLevel.Verbose);
|
|
227
|
-
log(LogLevel.Error, 'after failed enable');
|
|
265
|
+
expect(getLogLevel(logState)).toBe(LogLevel.Warn);
|
|
266
|
+
expect(getLogChannelLevel(logState, 'render')).toBe(LogLevel.Error);
|
|
267
|
+
expect(getLogChannelLevel(logState, 'input')).toBe(LogLevel.Info);
|
|
268
|
+
expect(getLogChannelLevel(logState, 'audio')).toBeNull();
|
|
269
|
+
expect(getLogChannelLevel(logState, 'unrelated')).toBe(LogLevel.Verbose);
|
|
270
|
+
log(logState, 0, LogLevel.Error, 'after failed enable');
|
|
228
271
|
expect(readMemory(memory)).toEqual([]);
|
|
229
272
|
});
|
|
230
273
|
});
|
|
@@ -232,41 +275,55 @@ describe('enableDebug', () => {
|
|
|
232
275
|
describe('enableFlightDiagnostics', () => {
|
|
233
276
|
it('enables the console diagnostics session and every generic render guard for the state', () => {
|
|
234
277
|
const state = createRenderState();
|
|
235
|
-
enableFlightDiagnostics(state);
|
|
236
|
-
expect(isDebugEnabled()).toBe(true);
|
|
278
|
+
enableFlightDiagnostics(debugState, logState, state);
|
|
279
|
+
expect(isDebugEnabled(debugState)).toBe(true);
|
|
237
280
|
expect(areColorAdjustmentGuardsEnabled(state)).toBe(true);
|
|
238
281
|
expect(areRenderRegistriesGuardsEnabled(state)).toBe(true);
|
|
239
282
|
});
|
|
240
283
|
});
|
|
241
284
|
|
|
285
|
+
describe('initializeDebugState', () => {
|
|
286
|
+
it('resets a used state to the fresh baseline', () => {
|
|
287
|
+
const state = createDebugState();
|
|
288
|
+
state.enabled = true;
|
|
289
|
+
state.frameNumber = 12;
|
|
290
|
+
state.savedChannelLevels.set('render', null);
|
|
291
|
+
initializeDebugState(state);
|
|
292
|
+
expect(state.enabled).toBe(false);
|
|
293
|
+
expect(state.frameNumber).toBe(0);
|
|
294
|
+
expect(state.savedChannelLevels.size).toBe(0);
|
|
295
|
+
expect(state.savedGlobalLevel).toBe(LogLevel.Verbose);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
242
299
|
describe('isDebugEnabled', () => {
|
|
243
300
|
it('reports the enable/disable lifecycle', () => {
|
|
244
|
-
expect(isDebugEnabled()).toBe(false);
|
|
245
|
-
enableDebug({ sink: createMemoryLogSink(1).sink });
|
|
246
|
-
expect(isDebugEnabled()).toBe(true);
|
|
247
|
-
disableDebug();
|
|
248
|
-
expect(isDebugEnabled()).toBe(false);
|
|
301
|
+
expect(isDebugEnabled(debugState)).toBe(false);
|
|
302
|
+
enableDebug(debugState, logState, { sink: createMemoryLogSink(1).sink });
|
|
303
|
+
expect(isDebugEnabled(debugState)).toBe(true);
|
|
304
|
+
disableDebug(debugState, logState);
|
|
305
|
+
expect(isDebugEnabled(debugState)).toBe(false);
|
|
249
306
|
});
|
|
250
307
|
});
|
|
251
308
|
|
|
252
309
|
describe('registerDebugSubsystem', () => {
|
|
253
310
|
it('drives a newly registered subsystem through enableDebug', () => {
|
|
254
311
|
const enableGuards = vi.fn();
|
|
255
|
-
registerDebugSubsystem('acme.custom', { channels: ['acme.custom'], enableGuards });
|
|
312
|
+
registerDebugSubsystem(debugState, 'acme.custom', { channels: ['acme.custom'], enableGuards });
|
|
256
313
|
|
|
257
|
-
enableDebug({ subsystems: ['acme.custom'], sink: createMemoryLogSink(1).sink });
|
|
314
|
+
enableDebug(debugState, logState, { subsystems: ['acme.custom'], sink: createMemoryLogSink(1).sink });
|
|
258
315
|
|
|
259
316
|
expect(enableGuards).toHaveBeenCalledTimes(1);
|
|
260
|
-
expect(getLogChannelLevel('acme.custom')).toBe(LogLevel.Debug);
|
|
317
|
+
expect(getLogChannelLevel(logState, 'acme.custom')).toBe(LogLevel.Debug);
|
|
261
318
|
});
|
|
262
319
|
|
|
263
320
|
it('is last-write-wins for a repeated name', () => {
|
|
264
321
|
const first = vi.fn();
|
|
265
322
|
const second = vi.fn();
|
|
266
|
-
registerDebugSubsystem('render', { enableGuards: first });
|
|
267
|
-
registerDebugSubsystem('render', { enableGuards: second });
|
|
323
|
+
registerDebugSubsystem(debugState, 'render', { enableGuards: first });
|
|
324
|
+
registerDebugSubsystem(debugState, 'render', { enableGuards: second });
|
|
268
325
|
|
|
269
|
-
enableDebug({ subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
326
|
+
enableDebug(debugState, logState, { subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
270
327
|
|
|
271
328
|
expect(first).not.toHaveBeenCalled();
|
|
272
329
|
expect(second).toHaveBeenCalledTimes(1);
|
|
@@ -276,14 +333,18 @@ describe('registerDebugSubsystem', () => {
|
|
|
276
333
|
describe('unregisterDebugSubsystem', () => {
|
|
277
334
|
it('returns true and stops the subsystem from being driven', () => {
|
|
278
335
|
const enableGuards = vi.fn();
|
|
279
|
-
registerDebugSubsystem('render', { enableGuards });
|
|
336
|
+
registerDebugSubsystem(debugState, 'render', { enableGuards });
|
|
280
337
|
|
|
281
|
-
expect(unregisterDebugSubsystem('render')).toBe(true);
|
|
282
|
-
enableDebug({ subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
338
|
+
expect(unregisterDebugSubsystem(debugState, 'render')).toBe(true);
|
|
339
|
+
enableDebug(debugState, logState, { subsystems: ['render'], sink: createMemoryLogSink(1).sink });
|
|
283
340
|
expect(enableGuards).not.toHaveBeenCalled();
|
|
284
341
|
});
|
|
285
342
|
|
|
286
343
|
it('returns false for a name that was never registered', () => {
|
|
287
|
-
expect(unregisterDebugSubsystem('audio')).toBe(false);
|
|
344
|
+
expect(unregisterDebugSubsystem(debugState, 'audio')).toBe(false);
|
|
288
345
|
});
|
|
289
346
|
});
|
|
347
|
+
|
|
348
|
+
let clockMs = 0;
|
|
349
|
+
let debugState: DebugState = createDebugState();
|
|
350
|
+
let logState: LogState = createLogState();
|
package/src/debugTiming.test.ts
CHANGED
|
@@ -1,44 +1,61 @@
|
|
|
1
|
+
import type { LogState } from '@flighthq/log/contract';
|
|
1
2
|
import {
|
|
2
3
|
addLogSink,
|
|
3
4
|
clearLogChannelLevels,
|
|
4
5
|
clearLogSinks,
|
|
6
|
+
createLogState,
|
|
5
7
|
createMemoryLogSink,
|
|
6
8
|
getMemoryLogSinkEntries,
|
|
7
9
|
setLogLevel,
|
|
8
10
|
} from '@flighthq/log/contract';
|
|
9
|
-
import type { MemoryLogSink } from '@flighthq/types/contract';
|
|
11
|
+
import type { LogEntry, MemoryLogSink } from '@flighthq/types/contract';
|
|
10
12
|
import { LogLevel } from '@flighthq/types/contract';
|
|
11
13
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
12
14
|
|
|
13
|
-
import {
|
|
15
|
+
import type { DebugState } from './debug.ts';
|
|
16
|
+
import { createDebugState, disableDebug, enableDebug, isDebugEnabled } from './debug.ts';
|
|
14
17
|
import { beginDebugSpan, endDebugSpan, markDebugFrame, measureDebugSpan } from './debugTiming.ts';
|
|
15
18
|
|
|
16
19
|
function entryData(sink: MemoryLogSink): Readonly<Record<string, unknown>>[] {
|
|
17
|
-
return
|
|
20
|
+
return readLogEntries(sink).map((entry) => (entry as { data: Record<string, unknown> }).data);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Monotonic test clock: each read advances one millisecond, so spans measure a positive duration.
|
|
24
|
+
function clock(): number {
|
|
25
|
+
return ++clockMs;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readLogEntries(sink: Readonly<MemoryLogSink>): LogEntry[] {
|
|
29
|
+
const entries: LogEntry[] = [];
|
|
30
|
+
getMemoryLogSinkEntries(entries, sink);
|
|
31
|
+
return entries;
|
|
18
32
|
}
|
|
19
33
|
|
|
20
34
|
beforeEach(() => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
clockMs = 0;
|
|
36
|
+
debugState = createDebugState();
|
|
37
|
+
logState = createLogState();
|
|
38
|
+
if (isDebugEnabled(debugState)) disableDebug(debugState, logState);
|
|
39
|
+
clearLogSinks(logState);
|
|
40
|
+
clearLogChannelLevels(logState);
|
|
41
|
+
setLogLevel(logState, LogLevel.Verbose);
|
|
25
42
|
});
|
|
26
43
|
|
|
27
44
|
afterEach(() => {
|
|
28
|
-
if (isDebugEnabled()) disableDebug();
|
|
29
|
-
clearLogSinks();
|
|
30
|
-
clearLogChannelLevels();
|
|
31
|
-
setLogLevel(LogLevel.Verbose);
|
|
45
|
+
if (isDebugEnabled(debugState)) disableDebug(debugState, logState);
|
|
46
|
+
clearLogSinks(logState);
|
|
47
|
+
clearLogChannelLevels(logState);
|
|
48
|
+
setLogLevel(logState, LogLevel.Verbose);
|
|
32
49
|
});
|
|
33
50
|
|
|
34
51
|
describe('beginDebugSpan', () => {
|
|
35
52
|
it('returns null when debug is disabled', () => {
|
|
36
|
-
expect(beginDebugSpan('draw')).toBeNull();
|
|
53
|
+
expect(beginDebugSpan(debugState, clock(), 'draw')).toBeNull();
|
|
37
54
|
});
|
|
38
55
|
|
|
39
56
|
it('returns a running timer when debug is enabled', () => {
|
|
40
|
-
enableDebug({ sink: createMemoryLogSink(1).sink });
|
|
41
|
-
const timer = beginDebugSpan('draw');
|
|
57
|
+
enableDebug(debugState, logState, { sink: createMemoryLogSink(1).sink });
|
|
58
|
+
const timer = beginDebugSpan(debugState, clock(), 'draw');
|
|
42
59
|
expect(timer).not.toBeNull();
|
|
43
60
|
expect(timer!.label).toBe('draw');
|
|
44
61
|
});
|
|
@@ -46,14 +63,15 @@ describe('beginDebugSpan', () => {
|
|
|
46
63
|
|
|
47
64
|
describe('endDebugSpan', () => {
|
|
48
65
|
it('is a no-op returning -1 for a null timer', () => {
|
|
49
|
-
expect(endDebugSpan(null)).toBe(-1);
|
|
66
|
+
expect(endDebugSpan(logState, clock(), null)).toBe(-1);
|
|
50
67
|
});
|
|
51
68
|
|
|
52
69
|
it('emits an elapsed-time entry and returns the elapsed milliseconds', () => {
|
|
53
70
|
const memory = createMemoryLogSink(16);
|
|
54
|
-
enableDebug({ sink: memory.sink });
|
|
55
|
-
const
|
|
56
|
-
|
|
71
|
+
enableDebug(debugState, logState, { sink: memory.sink });
|
|
72
|
+
const timer = beginDebugSpan(debugState, clock(), 'draw');
|
|
73
|
+
const elapsed = endDebugSpan(logState, clock(), timer);
|
|
74
|
+
expect(elapsed).toBe(1);
|
|
57
75
|
const data = entryData(memory);
|
|
58
76
|
expect(data).toHaveLength(1);
|
|
59
77
|
expect(data[0].label).toBe('draw');
|
|
@@ -64,23 +82,23 @@ describe('endDebugSpan', () => {
|
|
|
64
82
|
describe('markDebugFrame', () => {
|
|
65
83
|
it('is a no-op when debug is disabled', () => {
|
|
66
84
|
const memory = createMemoryLogSink(4);
|
|
67
|
-
addLogSink(memory.sink);
|
|
68
|
-
markDebugFrame();
|
|
69
|
-
expect(
|
|
85
|
+
addLogSink(logState, memory.sink);
|
|
86
|
+
markDebugFrame(debugState, logState, clock());
|
|
87
|
+
expect(readLogEntries(memory)).toHaveLength(0);
|
|
70
88
|
});
|
|
71
89
|
|
|
72
90
|
it('emits a labeled frame marker when debug is enabled', () => {
|
|
73
91
|
const memory = createMemoryLogSink(16);
|
|
74
|
-
enableDebug({ sink: memory.sink });
|
|
75
|
-
markDebugFrame('level-load');
|
|
92
|
+
enableDebug(debugState, logState, { sink: memory.sink });
|
|
93
|
+
markDebugFrame(debugState, logState, clock(), 'level-load');
|
|
76
94
|
expect(entryData(memory)[0].frame).toBe('level-load');
|
|
77
95
|
});
|
|
78
96
|
|
|
79
97
|
it('tags an unlabeled marker with an ascending counter', () => {
|
|
80
98
|
const memory = createMemoryLogSink(16);
|
|
81
|
-
enableDebug({ sink: memory.sink });
|
|
82
|
-
markDebugFrame();
|
|
83
|
-
markDebugFrame();
|
|
99
|
+
enableDebug(debugState, logState, { sink: memory.sink });
|
|
100
|
+
markDebugFrame(debugState, logState, clock());
|
|
101
|
+
markDebugFrame(debugState, logState, clock());
|
|
84
102
|
const [a, b] = entryData(memory);
|
|
85
103
|
expect(typeof a.frame).toBe('number');
|
|
86
104
|
expect(b.frame as number).toBeGreaterThan(a.frame as number);
|
|
@@ -89,24 +107,24 @@ describe('markDebugFrame', () => {
|
|
|
89
107
|
|
|
90
108
|
describe('measureDebugSpan', () => {
|
|
91
109
|
it('returns the wrapped function result', () => {
|
|
92
|
-
expect(measureDebugSpan('compute', () => 42)).toBe(42);
|
|
110
|
+
expect(measureDebugSpan(debugState, logState, clock, 'compute', () => 42)).toBe(42);
|
|
93
111
|
});
|
|
94
112
|
|
|
95
113
|
it('runs the function but emits nothing when debug is disabled', () => {
|
|
96
114
|
const memory = createMemoryLogSink(4);
|
|
97
|
-
addLogSink(memory.sink);
|
|
115
|
+
addLogSink(logState, memory.sink);
|
|
98
116
|
let ran = false;
|
|
99
|
-
measureDebugSpan('compute', () => {
|
|
117
|
+
measureDebugSpan(debugState, logState, clock, 'compute', () => {
|
|
100
118
|
ran = true;
|
|
101
119
|
});
|
|
102
120
|
expect(ran).toBe(true);
|
|
103
|
-
expect(
|
|
121
|
+
expect(readLogEntries(memory)).toHaveLength(0);
|
|
104
122
|
});
|
|
105
123
|
|
|
106
124
|
it('emits one timing entry named for the span when debug is enabled', () => {
|
|
107
125
|
const memory = createMemoryLogSink(16);
|
|
108
|
-
enableDebug({ sink: memory.sink });
|
|
109
|
-
measureDebugSpan('compute', () => 1);
|
|
126
|
+
enableDebug(debugState, logState, { sink: memory.sink });
|
|
127
|
+
measureDebugSpan(debugState, logState, clock, 'compute', () => 1);
|
|
110
128
|
const data = entryData(memory);
|
|
111
129
|
expect(data).toHaveLength(1);
|
|
112
130
|
expect(data[0].label).toBe('compute');
|
|
@@ -114,12 +132,16 @@ describe('measureDebugSpan', () => {
|
|
|
114
132
|
|
|
115
133
|
it('closes the span even when the function throws', () => {
|
|
116
134
|
const memory = createMemoryLogSink(16);
|
|
117
|
-
enableDebug({ sink: memory.sink });
|
|
135
|
+
enableDebug(debugState, logState, { sink: memory.sink });
|
|
118
136
|
expect(() =>
|
|
119
|
-
measureDebugSpan('boom', () => {
|
|
137
|
+
measureDebugSpan(debugState, logState, clock, 'boom', () => {
|
|
120
138
|
throw new Error('nope');
|
|
121
139
|
}),
|
|
122
140
|
).toThrow('nope');
|
|
123
141
|
expect(entryData(memory)).toHaveLength(1);
|
|
124
142
|
});
|
|
125
143
|
});
|
|
144
|
+
|
|
145
|
+
let clockMs = 0;
|
|
146
|
+
let debugState: DebugState = createDebugState();
|
|
147
|
+
let logState: LogState = createLogState();
|