@debuffjp/dev 0.0.0-stage → 0.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/README.md +45 -2
- package/dist/explain.d.ts +57 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +243 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts +114 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +157 -0
- package/dist/index.js.map +1 -0
- package/dist/overlay.d.ts +18 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +833 -0
- package/dist/overlay.js.map +1 -0
- package/dist/replay.d.ts +53 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +101 -0
- package/dist/replay.js.map +1 -0
- package/dist/session.d.ts +166 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +411 -0
- package/dist/session.js.map +1 -0
- package/package.json +47 -4
package/README.md
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @debuffjp/dev
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Dev-only inspection and control hooks that let browser tools, the `debuff` CLI and AI agents see and drive a running game.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { devTools } from "@debuffjp/dev";
|
|
7
|
+
|
|
8
|
+
if (import.meta.env.DEV) game.use(devTools({ input }));
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Bundlers drop the call from production builds, and `debuff build` fails if it leaks.
|
|
12
|
+
|
|
13
|
+
## `window.__DEBUFF_DEV__`
|
|
14
|
+
|
|
15
|
+
All arguments and return values are JSON-serialisable.
|
|
16
|
+
|
|
17
|
+
| Method | Returns |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `state()` | `{ scene, tick, time, frozen, stats, state, pluginNames, plugins }`. `state` is the active scene's `inspect()`, and `plugins` holds `game.inspect` snapshots |
|
|
20
|
+
| `step(n)` | pauses, advances `n` fixed steps, returns `state()` |
|
|
21
|
+
| `pause()` / `resume()` / `speed(scale)` | live control; `state().pausedBy` says who paused |
|
|
22
|
+
| `frames(n)` | the latest `n` frame breakdowns: interval, update and render ms, catch-up steps, quads, draw calls, long tasks |
|
|
23
|
+
| `notes()` | moments flagged in the overlay, with state and frames |
|
|
24
|
+
| `params()` / `setParam(group, key, value)` | typed parameter schemas and values |
|
|
25
|
+
| `press(code)` / `release(code)` | keyboard |
|
|
26
|
+
| `tap(x, y)` / `pointerDown` / `pointerMove` / `pointerUp` | pointer, in logical coordinates |
|
|
27
|
+
| `errors()` | runtime errors since load, explained: `kind` (`your-code`, `debuff-usage`, `debuff-bug`, `outside`), source-mapped `where`, `hint`, `count` |
|
|
28
|
+
| `capture()` | PNG data URL of the current frame |
|
|
29
|
+
| `replay(toTick?)` | everything played since load (keys, pointer, param changes) up to `toTick`, as a scenario that replays it exactly, plus `warnings` for anything it can't reproduce. Needs `devTools({ input })` |
|
|
30
|
+
|
|
31
|
+
## Overlay
|
|
32
|
+
|
|
33
|
+
`devOverlay()` adds a panel. The backquote key hides it, and the ▾ button (or double-clicking the header) collapses it to a single bar with fps, an error count and the agent badge. The panel remembers whether it's collapsed and which tab is open.
|
|
34
|
+
|
|
35
|
+
The toolbar has pause, step, speed (1×, ½×, ¼×), **snap** (download the current frame as a PNG) and **restart** (re-enter the current scene). Below it are four tabs:
|
|
36
|
+
|
|
37
|
+
- **perf**: a frame graph coloured by health: lime is on time, amber is a little late (normal display jitter) and red is a dropped frame. Update and render time are stacked inside each bar. A summary shows your headroom and whether recent dropped frames came from the game's own work or from outside it (GC, the browser, other tabs). Click a bar to pause and inspect that frame with a plain-words diagnosis, or tick **pause on dropped frame**. Phases also appear as `debuff:update` and `debuff:render` in the browser profiler.
|
|
38
|
+
- **tune**: sliders for every `game.params` group. Values you've changed from their defaults turn amber. Double-click a name to reset it, and **copy changes** copies just your changes, ready to paste into the param definitions.
|
|
39
|
+
- **state**: your scene's `inspect()` data and plugin snapshots, updating live.
|
|
40
|
+
- **log**: runtime errors, each labelled with whose problem it is (your code, a call debuff rejected, a debuff bug, or something outside the game), with a link that opens the exact source line in your editor, a hint, and a report link for debuff bugs. Also an activity log of who changed what (you, an agent or a scenario) with undo, and notes you can flag for agents. **save replay** and **copy replay** turn the session so far into a scenario that `debuff scenario` plays back exactly.
|
|
41
|
+
|
|
42
|
+
## Agent link
|
|
43
|
+
|
|
44
|
+
Under `debuff dev`, the page listens for commands from `@debuffjp/mcp` over the dev server, so agents work on the game you're looking at. Pass `agentLink: false` to opt out.
|
|
45
|
+
|
|
46
|
+
Open the page with `?debuff-scenario` to stop the loop so time advances only through `step()`. `debuff scenario` uses this mode.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Whose problem an error is, so the fix starts in the right place. */
|
|
2
|
+
export type ErrorKind =
|
|
3
|
+
/** Thrown from the game's own code. */
|
|
4
|
+
"your-code"
|
|
5
|
+
/** debuff rejected a call from the game's code (bad argument, unknown id…); the message says why. */
|
|
6
|
+
| "debuff-usage"
|
|
7
|
+
/** An unexpected failure inside debuff itself: please report it. */
|
|
8
|
+
| "debuff-bug"
|
|
9
|
+
/** No frame from this page: a browser extension, another script or the browser. */
|
|
10
|
+
| "outside";
|
|
11
|
+
export interface SourceLocation {
|
|
12
|
+
/** Path relative to the dev server, e.g. `src/main.ts`. */
|
|
13
|
+
file: string;
|
|
14
|
+
line: number;
|
|
15
|
+
column: number;
|
|
16
|
+
/** The function name, when the stack has one. */
|
|
17
|
+
fn?: string;
|
|
18
|
+
}
|
|
19
|
+
/** A runtime error explained for humans and agents. JSON-serialisable. */
|
|
20
|
+
export interface ExplainedError {
|
|
21
|
+
kind: ErrorKind;
|
|
22
|
+
name: string;
|
|
23
|
+
message: string;
|
|
24
|
+
/** Where in the game's code to look: the throwing frame, or the call into debuff that failed. */
|
|
25
|
+
where?: SourceLocation;
|
|
26
|
+
/** A plain-words next step. */
|
|
27
|
+
hint: string;
|
|
28
|
+
/** Loop phase and tick, when the game loop caught it. */
|
|
29
|
+
phase?: string;
|
|
30
|
+
scene?: string;
|
|
31
|
+
tick?: number;
|
|
32
|
+
/** How many times this same error happened. */
|
|
33
|
+
count: number;
|
|
34
|
+
stack: string;
|
|
35
|
+
/** For `debuff-bug`: a prefilled GitHub issue. */
|
|
36
|
+
reportUrl?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Explain an error from its stack: whose code threw, where to look, and what to try.
|
|
40
|
+
* Heuristic but conservative: when unsure it says so instead of guessing.
|
|
41
|
+
*/
|
|
42
|
+
export declare function explainError(error: unknown, context?: {
|
|
43
|
+
phase?: string;
|
|
44
|
+
scene?: string | undefined;
|
|
45
|
+
tick?: number;
|
|
46
|
+
}, origin?: string): ExplainedError;
|
|
47
|
+
/** Same error, same place: used to collapse repeats (a broken update throws every frame). */
|
|
48
|
+
export declare function errorKey(e: ExplainedError): string;
|
|
49
|
+
export declare function loc(l: SourceLocation): string;
|
|
50
|
+
/**
|
|
51
|
+
* Stacks point into the code the dev server serves (TypeScript already compiled). Map `e.where` back to
|
|
52
|
+
* the original source line via the module's source map. Resolves true if the location changed.
|
|
53
|
+
*/
|
|
54
|
+
export declare function mapLocation(e: ExplainedError, origin?: string): Promise<boolean>;
|
|
55
|
+
/** @internal Base64 VLQ source-map mappings → absolute segments per generated line. */
|
|
56
|
+
export declare function decodeMappings(mappings: string): string[][][];
|
|
57
|
+
//# sourceMappingURL=explain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../src/explain.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,MAAM,MAAM,SAAS;AACnB,uCAAuC;AACrC,WAAW;AACb,qGAAqG;GACnG,cAAc;AAChB,oEAAoE;GAClE,YAAY;AACd,mFAAmF;GACjF,SAAS,CAAC;AAEd,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,EAC3E,MAAM,SAAyD,GAC9D,cAAc,CAuEhB;AAED,6FAA6F;AAC7F,wBAAgB,QAAQ,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAElD;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAE7C;AAiDD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,SAAyD,GAAG,OAAO,CAAC,OAAO,CAAC,CAmBtI;AAwCD,uFAAuF;AACvF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,EAAE,CAsB7D"}
|
package/dist/explain.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
const ISSUES = "https://github.com/debuffjp/debuff/issues/new";
|
|
2
|
+
/**
|
|
3
|
+
* Explain an error from its stack: whose code threw, where to look, and what to try.
|
|
4
|
+
* Heuristic but conservative: when unsure it says so instead of guessing.
|
|
5
|
+
*/
|
|
6
|
+
export function explainError(error, context = {}, origin = typeof location === "undefined" ? "" : location.origin) {
|
|
7
|
+
const err = error instanceof Error ? error : undefined;
|
|
8
|
+
const name = err?.name ?? "Error";
|
|
9
|
+
const message = err?.message ?? String(error);
|
|
10
|
+
const stack = err?.stack ?? `${name}: ${message}`;
|
|
11
|
+
const frames = parseStack(stack, origin);
|
|
12
|
+
const top = frames.find((f) => f.sameOrigin);
|
|
13
|
+
const yours = frames.find((f) => f.sameOrigin && !f.debuff && !f.thirdParty);
|
|
14
|
+
const base = {
|
|
15
|
+
name,
|
|
16
|
+
message,
|
|
17
|
+
count: 1,
|
|
18
|
+
stack,
|
|
19
|
+
...(context.phase === undefined ? {} : { phase: context.phase }),
|
|
20
|
+
...(context.scene === undefined ? {} : { scene: context.scene }),
|
|
21
|
+
...(context.tick === undefined ? {} : { tick: context.tick }),
|
|
22
|
+
};
|
|
23
|
+
const at = (f) => (f ? { where: strip(f) } : {});
|
|
24
|
+
if (!top) {
|
|
25
|
+
return {
|
|
26
|
+
...base,
|
|
27
|
+
kind: "outside",
|
|
28
|
+
hint: "No frame from this game in the stack: likely a browser extension or another script. Try a private window.",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (!top.debuff) {
|
|
32
|
+
if (top.thirdParty) {
|
|
33
|
+
return {
|
|
34
|
+
...base,
|
|
35
|
+
kind: "your-code",
|
|
36
|
+
...at(yours ?? top),
|
|
37
|
+
hint: `Thrown inside a dependency (${top.file}). Check what your code passed to it${yours ? ` at ${loc(yours)}` : ""}.`,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { ...base, kind: "your-code", ...at(top), hint: hintFor(name, message, top) };
|
|
41
|
+
}
|
|
42
|
+
// Thrown inside debuff. Its deliberate errors are plain `Error`s with a message that says what to do;
|
|
43
|
+
// a TypeError/RangeError/ReferenceError from inside debuff is a bug in debuff.
|
|
44
|
+
const deliberate = name === "Error";
|
|
45
|
+
if (deliberate) {
|
|
46
|
+
return {
|
|
47
|
+
...base,
|
|
48
|
+
kind: "debuff-usage",
|
|
49
|
+
...at(yours),
|
|
50
|
+
hint: yours
|
|
51
|
+
? `debuff rejected a call from ${loc(yours)}. The message says what it expected.`
|
|
52
|
+
: "debuff rejected a call. The message says what it expected.",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const title = `${name}: ${message}`.slice(0, 120);
|
|
56
|
+
const body = [
|
|
57
|
+
"**What happened**",
|
|
58
|
+
"",
|
|
59
|
+
`\`${name}: ${message}\`${context.phase ? ` during \`${context.phase}\`` : ""}`,
|
|
60
|
+
"",
|
|
61
|
+
"**Stack**",
|
|
62
|
+
"",
|
|
63
|
+
"```",
|
|
64
|
+
stack.split("\n").slice(0, 15).join("\n"),
|
|
65
|
+
"```",
|
|
66
|
+
"",
|
|
67
|
+
`**Browser**: ${typeof navigator === "undefined" ? "unknown" : navigator.userAgent}`,
|
|
68
|
+
].join("\n");
|
|
69
|
+
return {
|
|
70
|
+
...base,
|
|
71
|
+
kind: "debuff-bug",
|
|
72
|
+
...at(yours ?? top),
|
|
73
|
+
hint: `This looks like a bug in debuff (${top.file}), not in your code. Please report it.`,
|
|
74
|
+
reportUrl: `${ISSUES}?${new URLSearchParams({ title, body, labels: "bug" })}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Same error, same place: used to collapse repeats (a broken update throws every frame). */
|
|
78
|
+
export function errorKey(e) {
|
|
79
|
+
return `${e.name}|${e.message}|${e.where ? loc(e.where) : ""}`;
|
|
80
|
+
}
|
|
81
|
+
export function loc(l) {
|
|
82
|
+
return `${l.file}:${l.line}:${l.column}`;
|
|
83
|
+
}
|
|
84
|
+
function strip(f) {
|
|
85
|
+
return { file: f.file, line: f.line, column: f.column, ...(f.fn ? { fn: f.fn } : {}) };
|
|
86
|
+
}
|
|
87
|
+
/** Parse V8 (`at fn (url:1:2)`) and Firefox/Safari (`fn@url:1:2`) stack lines. */
|
|
88
|
+
function parseStack(stack, origin) {
|
|
89
|
+
const frames = [];
|
|
90
|
+
for (const raw of stack.split("\n")) {
|
|
91
|
+
const lineText = raw.trim();
|
|
92
|
+
const m = /^at (?:(.*?) \()?(.+?):(\d+):(\d+)\)?$/.exec(lineText) ?? /^(?:(.*?)@)?(.+?):(\d+):(\d+)$/.exec(lineText);
|
|
93
|
+
if (!m?.[2])
|
|
94
|
+
continue;
|
|
95
|
+
const url = m[2];
|
|
96
|
+
let path = url;
|
|
97
|
+
let sameOrigin = false;
|
|
98
|
+
try {
|
|
99
|
+
const u = new URL(url);
|
|
100
|
+
sameOrigin = origin !== "" && u.origin === origin;
|
|
101
|
+
path = decodeURIComponent(u.pathname);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Not a URL (node paths in tests): keep as is.
|
|
105
|
+
sameOrigin = origin === "";
|
|
106
|
+
}
|
|
107
|
+
const file = path.replace(/^\/(@fs\/)?/, "");
|
|
108
|
+
const debuff = /(^|\/)@debuffjp[/_]|\.vite\/deps\/@debuffjp|\/packages\/(core|dev|input|audio|assets|tilemap|juice|ui)\//.test(path);
|
|
109
|
+
const thirdParty = !debuff && /node_modules\//.test(path);
|
|
110
|
+
frames.push({ file, line: Number(m[3]), column: Number(m[4]), debuff, thirdParty, sameOrigin, ...(m[1] ? { fn: m[1] } : {}) });
|
|
111
|
+
}
|
|
112
|
+
return frames;
|
|
113
|
+
}
|
|
114
|
+
function hintFor(name, message, where) {
|
|
115
|
+
const read = /(?:reading|setting) '([^']+)'|undefined \(evaluating '([^']+)'\)|can't access property "([^"]+)"/.exec(message);
|
|
116
|
+
if (name === "TypeError" && read) {
|
|
117
|
+
const prop = read[1] ?? read[2] ?? read[3];
|
|
118
|
+
return `Something at ${loc(where)} is undefined when its \`${prop}\` is used. Common causes: an asset, map object or scene field that doesn't exist (yet), or a typo in an id. Check the state tab when it happens.`;
|
|
119
|
+
}
|
|
120
|
+
if (name === "TypeError" && /is not a function/.test(message)) {
|
|
121
|
+
return `A value called as a function at ${loc(where)} isn't one. Check the name and that it's defined before this runs.`;
|
|
122
|
+
}
|
|
123
|
+
if (name === "ReferenceError")
|
|
124
|
+
return `A name used at ${loc(where)} isn't defined. Check for typos or a missing import.`;
|
|
125
|
+
if (name === "RangeError" && /call stack/i.test(message))
|
|
126
|
+
return `Infinite recursion at ${loc(where)}: a function keeps calling itself (or a setter sets itself).`;
|
|
127
|
+
return `Thrown from your code at ${loc(where)}.`;
|
|
128
|
+
}
|
|
129
|
+
const maps = new Map();
|
|
130
|
+
/**
|
|
131
|
+
* Stacks point into the code the dev server serves (TypeScript already compiled). Map `e.where` back to
|
|
132
|
+
* the original source line via the module's source map. Resolves true if the location changed.
|
|
133
|
+
*/
|
|
134
|
+
export async function mapLocation(e, origin = typeof location === "undefined" ? "" : location.origin) {
|
|
135
|
+
const where = e.where;
|
|
136
|
+
if (!where || !origin)
|
|
137
|
+
return false;
|
|
138
|
+
const frame = rawFrames(e.stack).find((f) => f.file === where.file && f.line === where.line && f.column === where.column);
|
|
139
|
+
if (!frame)
|
|
140
|
+
return false;
|
|
141
|
+
const mappings = await loadMappings(frame.url);
|
|
142
|
+
const segments = mappings?.[where.line - 1];
|
|
143
|
+
if (!segments?.length)
|
|
144
|
+
return false;
|
|
145
|
+
let best;
|
|
146
|
+
for (const seg of segments) {
|
|
147
|
+
if (Number(seg[0]) <= where.column - 1)
|
|
148
|
+
best = seg;
|
|
149
|
+
else
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
best ??= segments[0];
|
|
153
|
+
if (!best || best.length < 4)
|
|
154
|
+
return false;
|
|
155
|
+
const before = loc(where);
|
|
156
|
+
e.where = { ...where, line: Number(best[2]) + 1, column: Number(best[3]) + 1 };
|
|
157
|
+
e.hint = e.hint.split(before).join(loc(e.where));
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
function rawFrames(stack) {
|
|
161
|
+
const out = [];
|
|
162
|
+
for (const raw of stack.split("\n")) {
|
|
163
|
+
const t = raw.trim();
|
|
164
|
+
const m = /^at (?:(.*?) \()?(.+?):(\d+):(\d+)\)?$/.exec(t) ?? /^(?:(.*?)@)?(.+?):(\d+):(\d+)$/.exec(t);
|
|
165
|
+
if (!m?.[2])
|
|
166
|
+
continue;
|
|
167
|
+
try {
|
|
168
|
+
const u = new URL(m[2]);
|
|
169
|
+
out.push({ url: m[2], file: decodeURIComponent(u.pathname).replace(/^\/(@fs\/)?/, ""), line: Number(m[3]), column: Number(m[4]) });
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// not a URL
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
/** Decoded mappings as [genColumn, source, origLine, origColumn] per generated line (absolute values). */
|
|
178
|
+
function loadMappings(url) {
|
|
179
|
+
let p = maps.get(url);
|
|
180
|
+
if (!p) {
|
|
181
|
+
p = (async () => {
|
|
182
|
+
try {
|
|
183
|
+
const code = await (await fetch(url)).text();
|
|
184
|
+
const ref = /\/\/# sourceMappingURL=(\S+)\s*$/.exec(code)?.[1];
|
|
185
|
+
if (!ref)
|
|
186
|
+
return undefined;
|
|
187
|
+
const json = ref.startsWith("data:") ? atob(ref.slice(ref.indexOf(",") + 1)) : await (await fetch(new URL(ref, url))).text();
|
|
188
|
+
return decodeMappings(JSON.parse(json).mappings);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
})();
|
|
194
|
+
maps.set(url, p);
|
|
195
|
+
}
|
|
196
|
+
return p;
|
|
197
|
+
}
|
|
198
|
+
const B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
199
|
+
/** @internal Base64 VLQ source-map mappings → absolute segments per generated line. */
|
|
200
|
+
export function decodeMappings(mappings) {
|
|
201
|
+
const lines = [];
|
|
202
|
+
let src = 0;
|
|
203
|
+
let origLine = 0;
|
|
204
|
+
let origCol = 0;
|
|
205
|
+
for (const lineText of mappings.split(";")) {
|
|
206
|
+
let genCol = 0;
|
|
207
|
+
const segs = [];
|
|
208
|
+
for (const seg of lineText.split(",")) {
|
|
209
|
+
if (!seg)
|
|
210
|
+
continue;
|
|
211
|
+
const v = vlq(seg);
|
|
212
|
+
genCol += v[0] ?? 0;
|
|
213
|
+
if (v.length >= 4) {
|
|
214
|
+
src += v[1] ?? 0;
|
|
215
|
+
origLine += v[2] ?? 0;
|
|
216
|
+
origCol += v[3] ?? 0;
|
|
217
|
+
segs.push([String(genCol), String(src), String(origLine), String(origCol)]);
|
|
218
|
+
}
|
|
219
|
+
else
|
|
220
|
+
segs.push([String(genCol)]);
|
|
221
|
+
}
|
|
222
|
+
lines.push(segs);
|
|
223
|
+
}
|
|
224
|
+
return lines;
|
|
225
|
+
}
|
|
226
|
+
function vlq(s) {
|
|
227
|
+
const out = [];
|
|
228
|
+
let value = 0;
|
|
229
|
+
let shift = 0;
|
|
230
|
+
for (const ch of s) {
|
|
231
|
+
const digit = B64.indexOf(ch);
|
|
232
|
+
value += (digit & 31) << shift;
|
|
233
|
+
if (digit & 32)
|
|
234
|
+
shift += 5;
|
|
235
|
+
else {
|
|
236
|
+
out.push(value & 1 ? -(value >>> 1) : value >>> 1);
|
|
237
|
+
value = 0;
|
|
238
|
+
shift = 0;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../src/explain.ts"],"names":[],"mappings":"AAwCA,MAAM,MAAM,GAAG,+CAA+C,CAAC;AAQ/D;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAc,EACd,OAAO,GAAkE,EAAE,EAC3E,MAAM,GAAG,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM;IAE/D,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,MAAM,IAAI,GAAG,GAAG,EAAE,IAAI,IAAI,OAAO,CAAC;IAClC,MAAM,OAAO,GAAG,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC;IAClD,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAC7E,MAAM,IAAI,GAAG;QACX,IAAI;QACJ,OAAO;QACP,KAAK,EAAE,CAAC;QACR,KAAK;QACL,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QAChE,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QAChE,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;KAC9D,CAAC;IACF,MAAM,EAAE,GAAG,CAAC,CAAoB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEpE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO;YACL,GAAG,IAAI;YACP,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,2GAA2G;SAClH,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QAChB,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACnB,OAAO;gBACL,GAAG,IAAI;gBACP,IAAI,EAAE,WAAW;gBACjB,GAAG,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC;gBACnB,IAAI,EAAE,+BAA+B,GAAG,CAAC,IAAI,uCAAuC,KAAK,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG;aACxH,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;IACvF,CAAC;IACD,sGAAsG;IACtG,+EAA+E;IAC/E,MAAM,UAAU,GAAG,IAAI,KAAK,OAAO,CAAC;IACpC,IAAI,UAAU,EAAE,CAAC;QACf,OAAO;YACL,GAAG,IAAI;YACP,IAAI,EAAE,cAAc;YACpB,GAAG,EAAE,CAAC,KAAK,CAAC;YACZ,IAAI,EAAE,KAAK;gBACT,CAAC,CAAC,+BAA+B,GAAG,CAAC,KAAK,CAAC,sCAAsC;gBACjF,CAAC,CAAC,4DAA4D;SACjE,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG;QACX,mBAAmB;QACnB,EAAE;QACF,KAAK,IAAI,KAAK,OAAO,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QAC/E,EAAE;QACF,WAAW;QACX,EAAE;QACF,KAAK;QACL,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACzC,KAAK;QACL,EAAE;QACF,gBAAgB,OAAO,SAAS,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,EAAE;KACrF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,OAAO;QACL,GAAG,IAAI;QACP,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC;QACnB,IAAI,EAAE,oCAAoC,GAAG,CAAC,IAAI,wCAAwC;QAC1F,SAAS,EAAE,GAAG,MAAM,IAAI,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE;KAC9E,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,QAAQ,CAAC,CAAiB;IACxC,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,CAAiB;IACnC,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;AAC3C,CAAC;AAED,SAAS,KAAK,CAAC,CAAQ;IACrB,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACzF,CAAC;AAED,kFAAkF;AAClF,SAAS,UAAU,CAAC,KAAa,EAAE,MAAc;IAC/C,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,wCAAwC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,gCAAgC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrH,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAAE,SAAS;QACtB,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACjB,IAAI,IAAI,GAAG,GAAG,CAAC;QACf,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YACvB,UAAU,GAAG,MAAM,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC;YAClD,IAAI,GAAG,kBAAkB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;YAC/C,UAAU,GAAG,MAAM,KAAK,EAAE,CAAC;QAC7B,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,0GAA0G,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrI,MAAM,UAAU,GAAG,CAAC,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACjI,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,OAAe,EAAE,KAAY;IAC1D,MAAM,IAAI,GAAG,kGAAkG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9H,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO,gBAAgB,GAAG,CAAC,KAAK,CAAC,4BAA4B,IAAI,mJAAmJ,CAAC;IACvN,CAAC;IACD,IAAI,IAAI,KAAK,WAAW,IAAI,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9D,OAAO,mCAAmC,GAAG,CAAC,KAAK,CAAC,oEAAoE,CAAC;IAC3H,CAAC;IACD,IAAI,IAAI,KAAK,gBAAgB;QAAE,OAAO,kBAAkB,GAAG,CAAC,KAAK,CAAC,sDAAsD,CAAC;IACzH,IAAI,IAAI,KAAK,YAAY,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC;QACtD,OAAO,yBAAyB,GAAG,CAAC,KAAK,CAAC,8DAA8D,CAAC;IAC3G,OAAO,4BAA4B,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;AACnD,CAAC;AAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAA6C,CAAC;AAElE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,CAAiB,EAAE,MAAM,GAAG,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM;IAClH,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;IACtB,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACpC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1H,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ,EAAE,MAAM;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,IAA0B,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,GAAG,GAAG,CAAC;;YAC9C,MAAM;IACb,CAAC;IACD,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;IACrB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAC/E,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,GAAG,GAAkE,EAAE,CAAC;IAC9E,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,wCAAwC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,gCAAgC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAAE,SAAS;QACtB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACxB,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACrI,CAAC;QAAC,MAAM,CAAC;YACP,YAAY;QACd,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0GAA0G;AAC1G,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;YACd,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC7C,MAAM,GAAG,GAAG,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC/D,IAAI,CAAC,GAAG;oBAAE,OAAO,SAAS,CAAC;gBAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC7H,OAAO,cAAc,CAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAA0B,CAAC,QAAQ,CAAC,CAAC;YAC7E,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,GAAG,GAAG,kEAAkE,CAAC;AAE/E,uFAAuF;AACvF,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3C,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,MAAM,IAAI,GAAe,EAAE,CAAC;QAC5B,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,GAAG;gBAAE,SAAS;YACnB,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACnB,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACpB,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBAClB,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACjB,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACtB,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACrB,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC9E,CAAC;;gBAAM,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,GAAG,CAAC,CAAS;IACpB,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9B,KAAK,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,KAAK,CAAC;QAC/B,IAAI,KAAK,GAAG,EAAE;YAAE,KAAK,IAAI,CAAC,CAAC;aACtB,CAAC;YACJ,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC;YACnD,KAAK,GAAG,CAAC,CAAC;YACV,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Game, ParamDescription, ParamValue, Plugin } from "@debuffjp/core";
|
|
2
|
+
import type { ExplainedError } from "./explain.js";
|
|
3
|
+
import type { Replay } from "./replay.js";
|
|
4
|
+
import { type FrameSample, type Note } from "./session.js";
|
|
5
|
+
/** The parts of `@debuffjp/input`'s `Input` the hooks drive. Structural, so `dev` does not depend on `input`. */
|
|
6
|
+
export interface DrivableInput {
|
|
7
|
+
keys: {
|
|
8
|
+
press(code: string): void;
|
|
9
|
+
release(code: string): void;
|
|
10
|
+
};
|
|
11
|
+
pointer: {
|
|
12
|
+
press(x: number, y: number): void;
|
|
13
|
+
move(x: number, y: number): void;
|
|
14
|
+
release(): void;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface DevState {
|
|
18
|
+
scene: string | undefined;
|
|
19
|
+
tick: number;
|
|
20
|
+
time: number;
|
|
21
|
+
frozen: boolean;
|
|
22
|
+
/** Who paused the game ("you", "agent", "spike"…), or null while running. */
|
|
23
|
+
pausedBy: string | null;
|
|
24
|
+
timeScale: number;
|
|
25
|
+
/** Active rendering backend. */
|
|
26
|
+
renderer: "webgl2" | "2d";
|
|
27
|
+
stats: ReturnType<Game["stats"]["summary"]>;
|
|
28
|
+
/** Whatever the active scene's `inspect()` returns. */
|
|
29
|
+
state: unknown;
|
|
30
|
+
/** Installed plugin names, in install order. */
|
|
31
|
+
pluginNames: readonly string[];
|
|
32
|
+
/** Snapshots from `game.inspect` providers, keyed by name. */
|
|
33
|
+
plugins: Record<string, unknown>;
|
|
34
|
+
/** Number of notes the human has flagged (read them with `notes()`). */
|
|
35
|
+
notes: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The dev inspection and control surface, available as `window.__DEBUFF_DEV__` in dev builds.
|
|
39
|
+
* Every method takes and returns JSON-serialisable values so browsers, CLIs and agents can call it.
|
|
40
|
+
*/
|
|
41
|
+
export interface DevHooks {
|
|
42
|
+
readonly version: 2;
|
|
43
|
+
/** True when the page was opened with `?debuff-scenario`: the loop is stopped and time only advances via `step`. */
|
|
44
|
+
readonly controlled: boolean;
|
|
45
|
+
/** Advance `steps` fixed simulation steps (pausing first) and return the new state. */
|
|
46
|
+
step(steps?: number): DevState;
|
|
47
|
+
state(): DevState;
|
|
48
|
+
pause(): DevState;
|
|
49
|
+
resume(): DevState;
|
|
50
|
+
/** Simulation speed multiplier (1 = normal). */
|
|
51
|
+
speed(scale: number): void;
|
|
52
|
+
press(code: string): void;
|
|
53
|
+
release(code: string): void;
|
|
54
|
+
/** Press and release the primary pointer at logical coordinates across one step. */
|
|
55
|
+
tap(x: number, y: number): void;
|
|
56
|
+
pointerDown(x: number, y: number): void;
|
|
57
|
+
pointerMove(x: number, y: number): void;
|
|
58
|
+
pointerUp(): void;
|
|
59
|
+
params(): Record<string, ParamDescription>;
|
|
60
|
+
setParam(group: string, key: string, value: ParamValue): void;
|
|
61
|
+
/** The latest `n` frame breakdowns (update/render ms, steps, quads, draw calls, long tasks), oldest first. */
|
|
62
|
+
frames(n?: number): FrameSample[];
|
|
63
|
+
/**
|
|
64
|
+
* Everything played since page load (keys, pointer, param changes) up to `toTick` (default: now), as a
|
|
65
|
+
* scenario that replays it exactly. Pass a note's `tick` to get the session leading up to that note.
|
|
66
|
+
*/
|
|
67
|
+
replay(toTick?: number): Replay;
|
|
68
|
+
/** Moments the human flagged in the overlay, with the game state at that tick. */
|
|
69
|
+
notes(): Note[];
|
|
70
|
+
/**
|
|
71
|
+
* Runtime errors since load, explained: `kind` says whose problem it is ("your-code", "debuff-usage",
|
|
72
|
+
* "debuff-bug", "outside"), `where` points at the game's source, `hint` suggests a next step. Repeats are counted.
|
|
73
|
+
*/
|
|
74
|
+
errors(): ExplainedError[];
|
|
75
|
+
/** PNG data URL of the current frame. */
|
|
76
|
+
capture(): string;
|
|
77
|
+
/** Names of the commands the game registered with `devTools({ commands })`. */
|
|
78
|
+
commands(): string[];
|
|
79
|
+
/** Run a game command, e.g. to start a scenario at a checkpoint. Recorded in replays. */
|
|
80
|
+
command(name: string, ...args: unknown[]): unknown;
|
|
81
|
+
}
|
|
82
|
+
declare global {
|
|
83
|
+
interface Window {
|
|
84
|
+
__DEBUFF_DEV__?: DevHooks;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export interface DevHooksOptions {
|
|
88
|
+
input?: DrivableInput;
|
|
89
|
+
/**
|
|
90
|
+
* Accept commands from agents through the dev server (`debuff dev` + `@debuffjp/mcp`), so an agent
|
|
91
|
+
* works on this live game and its actions show in the overlay. Default true.
|
|
92
|
+
*/
|
|
93
|
+
agentLink?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Game-defined commands that scenarios (`{ "command": "warp", "args": ["hollow"] }`) and agents can run,
|
|
96
|
+
* e.g. jumping to a checkpoint or granting an item. Keeps test shortcuts out of the game's own code paths.
|
|
97
|
+
*/
|
|
98
|
+
commands?: Record<string, (...args: never[]) => unknown>;
|
|
99
|
+
}
|
|
100
|
+
/** Dev-server endpoints for the agent link (see `@debuffjp/cli`). */
|
|
101
|
+
export declare const AGENT_EVENTS = "/__debuff/agent/events";
|
|
102
|
+
export declare const AGENT_RESULT = "/__debuff/agent/result";
|
|
103
|
+
/**
|
|
104
|
+
* The dev tools as a runtime plugin. Installs `window.__DEBUFF_DEV__` and removes it on `game.dispose()`.
|
|
105
|
+
* Use inside `if (import.meta.env.DEV)` so bundlers drop it; `debuff build` fails if it leaks.
|
|
106
|
+
*/
|
|
107
|
+
export declare function devTools(options?: DevHooksOptions): Plugin;
|
|
108
|
+
/** Shorthand for `game.use(devTools(options))`; returns the installed hooks. */
|
|
109
|
+
export declare function exposeDevHooks(game: Game, options?: DevHooksOptions): DevHooks;
|
|
110
|
+
export { type ErrorKind, type ExplainedError, explainError, type SourceLocation } from "./explain.js";
|
|
111
|
+
export { type DevOverlayOptions, devOverlay } from "./overlay.js";
|
|
112
|
+
export { InputRecording, type Replay, type ReplayStep } from "./replay.js";
|
|
113
|
+
export { type Activity, DevSession, diagnose, FrameRecorder, type FrameSample, type FrameSummary, HITCH, type Note, summarize, } from "./session.js";
|
|
114
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,IAAI,EAAE,MAAM,cAAc,CAAC;AAEvE,iHAAiH;AACjH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACjE,OAAO,EAAE;QAAE,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,IAAI,IAAI,CAAA;KAAE,CAAC;CACnG;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5C,uDAAuD;IACvD,KAAK,EAAE,OAAO,CAAC;IACf,gDAAgD;IAChD,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,oHAAoH;IACpH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,uFAAuF;IACvF,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,KAAK,IAAI,QAAQ,CAAC;IAClB,KAAK,IAAI,QAAQ,CAAC;IAClB,MAAM,IAAI,QAAQ,CAAC;IACnB,gDAAgD;IAChD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oFAAoF;IACpF,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,SAAS,IAAI,IAAI,CAAC;IAClB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9D,8GAA8G;IAC9G,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;IAClC;;;OAGG;IACH,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,kFAAkF;IAClF,KAAK,IAAI,IAAI,EAAE,CAAC;IAChB;;;OAGG;IACH,MAAM,IAAI,cAAc,EAAE,CAAC;IAC3B,yCAAyC;IACzC,OAAO,IAAI,MAAM,CAAC;IAClB,+EAA+E;IAC/E,QAAQ,IAAI,MAAM,EAAE,CAAC;IACrB,yFAAyF;IACzF,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;CACpD;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,cAAc,CAAC,EAAE,QAAQ,CAAC;KAC3B;CACF;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,CAAC;CAC1D;AAED,qEAAqE;AACrE,eAAO,MAAM,YAAY,2BAA2B,CAAC;AACrD,eAAO,MAAM,YAAY,2BAA2B,CAAC;AAErD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,MAAM,CAiB9D;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAIlF;AAoHD,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACtG,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EACL,KAAK,QAAQ,EACb,UAAU,EACV,QAAQ,EACR,aAAa,EACb,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,EACL,KAAK,IAAI,EACT,SAAS,GACV,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { DevSession } from "./session.js";
|
|
2
|
+
/** Dev-server endpoints for the agent link (see `@debuffjp/cli`). */
|
|
3
|
+
export const AGENT_EVENTS = "/__debuff/agent/events";
|
|
4
|
+
export const AGENT_RESULT = "/__debuff/agent/result";
|
|
5
|
+
/**
|
|
6
|
+
* The dev tools as a runtime plugin. Installs `window.__DEBUFF_DEV__` and removes it on `game.dispose()`.
|
|
7
|
+
* Use inside `if (import.meta.env.DEV)` so bundlers drop it; `debuff build` fails if it leaks.
|
|
8
|
+
*/
|
|
9
|
+
export function devTools(options = {}) {
|
|
10
|
+
return {
|
|
11
|
+
name: "dev-tools",
|
|
12
|
+
install(game) {
|
|
13
|
+
const session = DevSession.acquire(game);
|
|
14
|
+
if (options.input)
|
|
15
|
+
session.attachInput(options.input);
|
|
16
|
+
const errors = session.errors;
|
|
17
|
+
const hooks = createHooks(game, session, options, errors, () => (hooks.controlled ? "scenario" : "agent"));
|
|
18
|
+
window.__DEBUFF_DEV__ = hooks;
|
|
19
|
+
const unlink = options.agentLink === false || hooks.controlled ? undefined : linkAgent(game, session, options, errors);
|
|
20
|
+
return () => {
|
|
21
|
+
unlink?.();
|
|
22
|
+
delete window.__DEBUFF_DEV__;
|
|
23
|
+
session.release();
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Shorthand for `game.use(devTools(options))`; returns the installed hooks. */
|
|
29
|
+
export function exposeDevHooks(game, options = {}) {
|
|
30
|
+
game.use(devTools(options));
|
|
31
|
+
if (!window.__DEBUFF_DEV__)
|
|
32
|
+
throw new Error("dev-tools plugin did not install");
|
|
33
|
+
return window.__DEBUFF_DEV__;
|
|
34
|
+
}
|
|
35
|
+
function createHooks(game, session, options, errors, who) {
|
|
36
|
+
const controlled = new URLSearchParams(location.search).has("debuff-scenario");
|
|
37
|
+
if (controlled) {
|
|
38
|
+
game.pauseWhenHidden = false;
|
|
39
|
+
game.stop();
|
|
40
|
+
}
|
|
41
|
+
const input = () => {
|
|
42
|
+
if (!options.input)
|
|
43
|
+
throw new Error("exposeDevHooks: pass { input } to drive input");
|
|
44
|
+
return options.input;
|
|
45
|
+
};
|
|
46
|
+
const state = () => ({
|
|
47
|
+
scene: game.scene?.id,
|
|
48
|
+
tick: game.clock.tick,
|
|
49
|
+
time: game.clock.time,
|
|
50
|
+
frozen: game.frozen,
|
|
51
|
+
pausedBy: session.pausedBy ?? null,
|
|
52
|
+
timeScale: game.timeScale,
|
|
53
|
+
renderer: game.draw.kind,
|
|
54
|
+
stats: game.stats.summary(),
|
|
55
|
+
state: game.scene?.inspect?.(),
|
|
56
|
+
pluginNames: game.plugins,
|
|
57
|
+
plugins: game.inspect.snapshot(),
|
|
58
|
+
notes: session.notes.length,
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
version: 2,
|
|
62
|
+
controlled,
|
|
63
|
+
step(steps = 1) {
|
|
64
|
+
// Scenarios own the clock already; elsewhere, stepping pauses the live loop first.
|
|
65
|
+
if (controlled)
|
|
66
|
+
game.step(steps);
|
|
67
|
+
else
|
|
68
|
+
session.step(steps, who());
|
|
69
|
+
return state();
|
|
70
|
+
},
|
|
71
|
+
state,
|
|
72
|
+
pause() {
|
|
73
|
+
session.pause(who());
|
|
74
|
+
return state();
|
|
75
|
+
},
|
|
76
|
+
resume() {
|
|
77
|
+
session.resume(who());
|
|
78
|
+
return state();
|
|
79
|
+
},
|
|
80
|
+
speed: (scale) => session.setSpeed(scale, who()),
|
|
81
|
+
press: (code) => input().keys.press(code),
|
|
82
|
+
release: (code) => input().keys.release(code),
|
|
83
|
+
tap(x, y) {
|
|
84
|
+
input().pointer.press(x, y);
|
|
85
|
+
game.step(1);
|
|
86
|
+
input().pointer.release();
|
|
87
|
+
},
|
|
88
|
+
pointerDown: (x, y) => input().pointer.press(x, y),
|
|
89
|
+
pointerMove: (x, y) => input().pointer.move(x, y),
|
|
90
|
+
pointerUp: () => input().pointer.release(),
|
|
91
|
+
params: () => game.params.describe(),
|
|
92
|
+
setParam: (group, key, value) => session.setParam(group, key, value, who()),
|
|
93
|
+
frames: (n = 60) => session.recorder.recent(n),
|
|
94
|
+
replay: (toTick) => {
|
|
95
|
+
if (!session.recording)
|
|
96
|
+
throw new Error("Replays need input: use devTools({ input })");
|
|
97
|
+
return session.recording.toReplay(toTick);
|
|
98
|
+
},
|
|
99
|
+
notes: () => session.notes.map((n) => ({ ...n })),
|
|
100
|
+
errors: () => errors.map((e) => ({ ...e })),
|
|
101
|
+
commands: () => Object.keys(options.commands ?? {}),
|
|
102
|
+
command: (name, ...args) => {
|
|
103
|
+
const fn = options.commands?.[name];
|
|
104
|
+
if (!fn)
|
|
105
|
+
throw new Error(`Unknown command "${name}". Registered: ${Object.keys(options.commands ?? {}).join(", ") || "none"}`);
|
|
106
|
+
session.recording?.command(name, args);
|
|
107
|
+
session.log(who(), "command", name);
|
|
108
|
+
return fn(...args);
|
|
109
|
+
},
|
|
110
|
+
capture: () => {
|
|
111
|
+
// Re-render without advancing time: WebGL clears its buffer after presenting, so read in the same task.
|
|
112
|
+
game.redraw();
|
|
113
|
+
return game.canvas.toDataURL("image/png");
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Agent link (page side): listen for commands from the dev server over Server-Sent Events, run them
|
|
119
|
+
* on the dev hooks as "agent", and post results back. Silent when the page isn't served by `debuff dev`.
|
|
120
|
+
*/
|
|
121
|
+
function linkAgent(game, session, options, errors) {
|
|
122
|
+
if (typeof EventSource === "undefined" || !/^https?:$/.test(location.protocol))
|
|
123
|
+
return undefined;
|
|
124
|
+
const hooks = createHooks(game, session, options, errors, () => "agent");
|
|
125
|
+
const events = new EventSource(AGENT_EVENTS);
|
|
126
|
+
events.addEventListener("error", () => {
|
|
127
|
+
// 404 (not served by debuff dev) closes the stream for good; nothing else to do.
|
|
128
|
+
if (events.readyState === EventSource.CLOSED)
|
|
129
|
+
events.close();
|
|
130
|
+
});
|
|
131
|
+
events.addEventListener("presence", (e) => session.setAgentsAttached(JSON.parse(e.data).agents));
|
|
132
|
+
events.addEventListener("command", (e) => {
|
|
133
|
+
const { id, method, args } = JSON.parse(e.data);
|
|
134
|
+
session.agentSeen();
|
|
135
|
+
let body;
|
|
136
|
+
try {
|
|
137
|
+
const fn = hooks[method];
|
|
138
|
+
if (typeof fn !== "function")
|
|
139
|
+
throw new Error(`Unknown dev hook "${String(method)}"`);
|
|
140
|
+
body = { id, ok: true, result: fn(...args) };
|
|
141
|
+
if (method === "capture")
|
|
142
|
+
session.log("agent", "captured a frame");
|
|
143
|
+
else if (method === "press" || method === "release" || method === "tap")
|
|
144
|
+
session.log("agent", method, String(args[0] ?? ""));
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
body = { id, ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
148
|
+
}
|
|
149
|
+
void fetch(AGENT_RESULT, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
|
|
150
|
+
});
|
|
151
|
+
return () => events.close();
|
|
152
|
+
}
|
|
153
|
+
export { explainError } from "./explain.js";
|
|
154
|
+
export { devOverlay } from "./overlay.js";
|
|
155
|
+
export { InputRecording } from "./replay.js";
|
|
156
|
+
export { DevSession, diagnose, FrameRecorder, HITCH, summarize, } from "./session.js";
|
|
157
|
+
//# sourceMappingURL=index.js.map
|