@debuffjp/cli 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.
Files changed (90) hide show
  1. package/README.md +86 -2
  2. package/aseprite/new-font.lua +31 -0
  3. package/bin/debuff.js +3 -0
  4. package/client.d.ts +3 -0
  5. package/dist/agent-link.d.ts +28 -0
  6. package/dist/agent-link.d.ts.map +1 -0
  7. package/dist/agent-link.js +122 -0
  8. package/dist/agent-link.js.map +1 -0
  9. package/dist/assets/aseprite.d.ts +29 -0
  10. package/dist/assets/aseprite.d.ts.map +1 -0
  11. package/dist/assets/aseprite.js +107 -0
  12. package/dist/assets/aseprite.js.map +1 -0
  13. package/dist/assets/atlas.d.ts +58 -0
  14. package/dist/assets/atlas.d.ts.map +1 -0
  15. package/dist/assets/atlas.js +164 -0
  16. package/dist/assets/atlas.js.map +1 -0
  17. package/dist/assets/builtin.d.ts +5 -0
  18. package/dist/assets/builtin.d.ts.map +1 -0
  19. package/dist/assets/builtin.js +47 -0
  20. package/dist/assets/builtin.js.map +1 -0
  21. package/dist/assets/command.d.ts +4 -0
  22. package/dist/assets/command.d.ts.map +1 -0
  23. package/dist/assets/command.js +35 -0
  24. package/dist/assets/command.js.map +1 -0
  25. package/dist/assets/compile.d.ts +29 -0
  26. package/dist/assets/compile.d.ts.map +1 -0
  27. package/dist/assets/compile.js +209 -0
  28. package/dist/assets/compile.js.map +1 -0
  29. package/dist/assets/index.d.ts +6 -0
  30. package/dist/assets/index.d.ts.map +1 -0
  31. package/dist/assets/index.js +6 -0
  32. package/dist/assets/index.js.map +1 -0
  33. package/dist/cli.d.ts +3 -0
  34. package/dist/cli.d.ts.map +1 -0
  35. package/dist/cli.js +257 -0
  36. package/dist/cli.js.map +1 -0
  37. package/dist/config.d.ts +24 -0
  38. package/dist/config.d.ts.map +1 -0
  39. package/dist/config.js +53 -0
  40. package/dist/config.js.map +1 -0
  41. package/dist/fuzz.d.ts +43 -0
  42. package/dist/fuzz.d.ts.map +1 -0
  43. package/dist/fuzz.js +229 -0
  44. package/dist/fuzz.js.map +1 -0
  45. package/dist/index.d.ts +12 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +11 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/inspect.d.ts +35 -0
  50. package/dist/inspect.d.ts.map +1 -0
  51. package/dist/inspect.js +37 -0
  52. package/dist/inspect.js.map +1 -0
  53. package/dist/plugin.d.ts +146 -0
  54. package/dist/plugin.d.ts.map +1 -0
  55. package/dist/plugin.js +2 -0
  56. package/dist/plugin.js.map +1 -0
  57. package/dist/report.d.ts +26 -0
  58. package/dist/report.d.ts.map +1 -0
  59. package/dist/report.js +80 -0
  60. package/dist/report.js.map +1 -0
  61. package/dist/scenario/browser.d.ts +12 -0
  62. package/dist/scenario/browser.d.ts.map +1 -0
  63. package/dist/scenario/browser.js +59 -0
  64. package/dist/scenario/browser.js.map +1 -0
  65. package/dist/scenario/index.d.ts +5 -0
  66. package/dist/scenario/index.d.ts.map +1 -0
  67. package/dist/scenario/index.js +4 -0
  68. package/dist/scenario/index.js.map +1 -0
  69. package/dist/scenario/run.d.ts +14 -0
  70. package/dist/scenario/run.d.ts.map +1 -0
  71. package/dist/scenario/run.js +255 -0
  72. package/dist/scenario/run.js.map +1 -0
  73. package/dist/scenario/schema.d.ts +8 -0
  74. package/dist/scenario/schema.d.ts.map +1 -0
  75. package/dist/scenario/schema.js +23 -0
  76. package/dist/scenario/schema.js.map +1 -0
  77. package/dist/scenario/types.d.ts +103 -0
  78. package/dist/scenario/types.d.ts.map +1 -0
  79. package/dist/scenario/types.js +2 -0
  80. package/dist/scenario/types.js.map +1 -0
  81. package/dist/vite-plugin.d.ts +18 -0
  82. package/dist/vite-plugin.d.ts.map +1 -0
  83. package/dist/vite-plugin.js +122 -0
  84. package/dist/vite-plugin.js.map +1 -0
  85. package/dist/vite.d.ts +13 -0
  86. package/dist/vite.d.ts.map +1 -0
  87. package/dist/vite.js +25 -0
  88. package/dist/vite.js.map +1 -0
  89. package/package.json +71 -4
  90. package/schema/scenario.schema.json +120 -0
package/dist/fuzz.js ADDED
@@ -0,0 +1,229 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+ import { hooksReady } from "./scenario/browser.js";
4
+ import { launchBrowser } from "./scenario/run.js";
5
+ import { createDevServer } from "./vite.js";
6
+ /** Keys most small games use. Override with `keys` (KeyboardEvent.code values). */
7
+ export const DEFAULT_FUZZ_KEYS = [
8
+ "ArrowLeft",
9
+ "ArrowRight",
10
+ "ArrowUp",
11
+ "ArrowDown",
12
+ "KeyA",
13
+ "KeyD",
14
+ "KeyW",
15
+ "KeyS",
16
+ "Space",
17
+ "Enter",
18
+ "KeyZ",
19
+ "KeyX",
20
+ ];
21
+ const MOVE_KEYS = new Set(["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "KeyA", "KeyD", "KeyW", "KeyS"]);
22
+ /** Steps between state samples while fuzzing; loops shorter than this can slip through. */
23
+ const SAMPLE_STEPS = 10;
24
+ /** A field flipping between the same two values this many times with input unchanged counts as a loop. */
25
+ const LOOP_FLIPS = 4;
26
+ /** Small, fast, seeded PRNG (mulberry32): the same seed always gives the same inputs. */
27
+ function rng(seed) {
28
+ let a = seed >>> 0;
29
+ return () => {
30
+ a = (a + 0x6d2b79f5) >>> 0;
31
+ let t = a;
32
+ t = Math.imul(t ^ (t >>> 15), t | 1);
33
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
34
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
35
+ };
36
+ }
37
+ /**
38
+ * Call a dev hook. If the game throws, hand the error to the page's own error reporting (so the dev tools
39
+ * explain and source-map it, exactly as for a crash during play) and return it instead of throwing.
40
+ */
41
+ function callSafe(page, method, args) {
42
+ return page.evaluate(([m, a]) => {
43
+ const hooks = window.__DEBUFF_DEV__;
44
+ try {
45
+ return { ok: true, value: hooks[m]?.(...a) };
46
+ }
47
+ catch (err) {
48
+ reportError(err);
49
+ return { ok: false, error: err instanceof Error ? `${err.name}: ${err.message}` : String(err) };
50
+ }
51
+ }, [method, args]);
52
+ }
53
+ /**
54
+ * Play the game with random but plausible input: movement keys held for a while, action keys tapped.
55
+ * Finds runtime errors and stuck states, and writes each as a replayable scenario in `.debuff/fuzz/`.
56
+ * Deterministic: fixed steps plus a seeded game mean the recorded inputs reproduce the run exactly.
57
+ */
58
+ export async function runFuzz(options) {
59
+ const started = Date.now();
60
+ const seed = options.seed ?? Math.floor(Math.random() * 2 ** 31);
61
+ const runs = options.runs ?? 3;
62
+ const budget = Math.round((options.seconds ?? 60) * 60);
63
+ const keys = options.keys?.length ? options.keys : DEFAULT_FUZZ_KEYS;
64
+ const stuckSteps = Math.round((options.stuckSeconds ?? 15) * 60);
65
+ const outDir = join(options.root, ".debuff", "fuzz");
66
+ mkdirSync(outDir, { recursive: true });
67
+ const server = await createDevServer(options.root, { port: 0, logLevel: "error" });
68
+ await server.listen();
69
+ const base = server.resolvedUrls?.local[0];
70
+ if (!base)
71
+ throw new Error("Dev server did not report a URL");
72
+ const browser = await launchBrowser();
73
+ const findings = [];
74
+ let totalSteps = 0;
75
+ try {
76
+ for (let run = 1; run <= runs; run++) {
77
+ const random = rng(seed + run * 7919);
78
+ const page = await browser.newPage({ viewport: { width: 960, height: 540 }, deviceScaleFactor: 1 });
79
+ try {
80
+ const url = new URL(options.page ?? "/", base);
81
+ url.searchParams.set("debuff-scenario", "1");
82
+ await page.goto(url.toString());
83
+ await page.waitForFunction(hooksReady, undefined, { timeout: 15_000 });
84
+ const steps = [];
85
+ const held = new Set();
86
+ let played = 0;
87
+ let lastState = "";
88
+ let changedAt = 0;
89
+ let stuckReported = false;
90
+ let loopReported = false;
91
+ // Loop detection: per top-level state field, the values it took since input last changed.
92
+ let flips = new Map();
93
+ const inputChanged = () => {
94
+ flips = new Map();
95
+ };
96
+ const save = (label) => {
97
+ const file = join(outDir, `fuzz-${seed}-run${run}-${label}.json`);
98
+ // Release anything still held so the replay ends cleanly, then let the scenario runner judge it.
99
+ const replay = [...steps, ...[...held].map((code) => ({ release: code }))];
100
+ writeFileSync(file, `${JSON.stringify({ name: `fuzz ${seed} run ${run} (${label})`, ...(options.page ? { page: options.page } : {}), steps: replay }, null, 2)}\n`);
101
+ // Relative to the project root, so it reads well and works as an argument to `debuff scenario`.
102
+ return relative(options.root, file).split("\\").join("/");
103
+ };
104
+ /** Bouncing back and forth between two values with input unchanged, e.g. a door or stairs that send you straight back. */
105
+ const checkLoop = (state) => {
106
+ if (loopReported || held.size === 0)
107
+ return;
108
+ const fields = [["scene", state.scene]];
109
+ if (state.state && typeof state.state === "object")
110
+ fields.push(...Object.entries(state.state).map(([k, v]) => [`state.${k}`, v]));
111
+ for (const [path, value] of fields) {
112
+ // true/false phase flags (scrolling, attacking) flip all the time in normal play.
113
+ if (typeof value === "boolean")
114
+ continue;
115
+ const json = JSON.stringify(value) ?? "undefined";
116
+ const seen = flips.get(path) ?? [];
117
+ if (seen.at(-1) !== json)
118
+ seen.push(json);
119
+ flips.set(path, seen);
120
+ if (seen.length >= LOOP_FLIPS + 1 && new Set(seen).size === 2) {
121
+ loopReported = true;
122
+ findings.push({
123
+ kind: "loop",
124
+ run,
125
+ tick: state.tick,
126
+ message: `${path} kept flipping between ${seen[0]} and ${seen[1]} while ${[...held].join("+")} stayed held. Possible loop (e.g. a door or stairs that send the player straight back).`,
127
+ repro: save("loop"),
128
+ });
129
+ return;
130
+ }
131
+ }
132
+ };
133
+ const advance = async (n) => {
134
+ // Step in short chunks so loops shorter than one hold are seen.
135
+ let result = { ok: true, value: undefined };
136
+ for (let left = n; left > 0 && result.ok; left -= SAMPLE_STEPS) {
137
+ const chunk = Math.min(SAMPLE_STEPS, left);
138
+ steps.push({ wait: chunk });
139
+ played += chunk;
140
+ result = await callSafe(page, "step", [chunk]);
141
+ const value = result.ok ? result.value : undefined;
142
+ if (value && typeof value.tick === "number")
143
+ checkLoop(value);
144
+ }
145
+ return result;
146
+ };
147
+ while (played < budget) {
148
+ // Mostly hold a movement key for a while, sometimes tap an action key, sometimes do nothing.
149
+ const roll = random();
150
+ const key = keys[Math.floor(random() * keys.length)] ?? "Space";
151
+ let result;
152
+ if (roll < 0.15) {
153
+ result = await advance(1 + Math.floor(random() * 20));
154
+ }
155
+ else if (MOVE_KEYS.has(key) || roll < 0.35) {
156
+ inputChanged();
157
+ if (held.has(key)) {
158
+ held.delete(key);
159
+ steps.push({ release: key });
160
+ await callSafe(page, "release", [key]);
161
+ }
162
+ else {
163
+ held.add(key);
164
+ steps.push({ press: key });
165
+ await callSafe(page, "press", [key]);
166
+ }
167
+ // Mostly short holds; sometimes a long one, which is what walks into loops and softlocks.
168
+ result = await advance(random() < 0.1 ? 120 + Math.floor(random() * 180) : 6 + Math.floor(random() * 50));
169
+ }
170
+ else {
171
+ inputChanged();
172
+ steps.push({ press: key });
173
+ await callSafe(page, "press", [key]);
174
+ result = await advance(1 + Math.floor(random() * 4));
175
+ steps.push({ release: key });
176
+ await callSafe(page, "release", [key]);
177
+ inputChanged();
178
+ }
179
+ if (!result.ok) {
180
+ // Give the dev tools a moment to explain and source-map the error.
181
+ await page.waitForTimeout(250);
182
+ const explained = (await callSafe(page, "errors", []));
183
+ const state = (await callSafe(page, "state", []));
184
+ findings.push({
185
+ kind: "error",
186
+ run,
187
+ tick: state.ok ? state.value.tick : played,
188
+ message: result.error,
189
+ ...(explained.ok && explained.value.length ? { error: explained.value.at(-1) } : {}),
190
+ repro: save("error"),
191
+ });
192
+ break;
193
+ }
194
+ // Stuck: input keeps arriving but the scene's inspect() state hasn't changed for a long time.
195
+ const state = (await callSafe(page, "state", []));
196
+ if (state.ok && state.value.state !== undefined && !stuckReported) {
197
+ const now = JSON.stringify([state.value.scene, state.value.state]);
198
+ if (now !== lastState) {
199
+ lastState = now;
200
+ changedAt = played;
201
+ }
202
+ else if (played - changedAt >= stuckSteps) {
203
+ stuckReported = true;
204
+ findings.push({
205
+ kind: "stuck",
206
+ run,
207
+ tick: state.value.tick,
208
+ message: `State unchanged for ${Math.round((played - changedAt) / 60)} s of input (scene "${state.value.scene ?? "?"}"). Possible softlock, or a screen that ignores these keys.`,
209
+ repro: save("stuck"),
210
+ });
211
+ }
212
+ }
213
+ }
214
+ totalSteps += played;
215
+ }
216
+ finally {
217
+ await page.close();
218
+ }
219
+ }
220
+ }
221
+ finally {
222
+ await browser.close();
223
+ await server.close();
224
+ }
225
+ const report = { seed, runs, steps: totalSteps, findings, durationMs: Date.now() - started };
226
+ writeFileSync(join(outDir, `fuzz-${seed}-report.json`), `${JSON.stringify(report, null, 2)}\n`);
227
+ return report;
228
+ }
229
+ //# sourceMappingURL=fuzz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzz.js","sourceRoot":"","sources":["../src/fuzz.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export { AGENT_PATHS, type AgentStatus, DEV_SERVER_FILE, type DevServerInfo } from "./agent-link.js";
2
+ export * from "./assets/index.js";
3
+ export { CONFIG_FILES, type DebuffConfig, defineConfig, loadConfig, type ResolvedConfig, validatePlugins } from "./config.js";
4
+ export { DEFAULT_FUZZ_KEYS, type FuzzFinding, type FuzzOptions, type FuzzReport, runFuzz } from "./fuzz.js";
5
+ export { inspectProject } from "./inspect.js";
6
+ export type * from "./plugin.js";
7
+ export { TOOLING_API_VERSION } from "./plugin.js";
8
+ export { type BuildReport, buildReport, DEV_TOKENS, type FileReport, formatReport } from "./report.js";
9
+ export * from "./scenario/index.js";
10
+ export { createDevServer, viteConfig } from "./vite.js";
11
+ export { ASSETS_DIR, type DebuffViteOptions, debuff } from "./vite-plugin.js";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrG,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9H,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC5G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,mBAAmB,aAAa,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,KAAK,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvG,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export { AGENT_PATHS, DEV_SERVER_FILE } from "./agent-link.js";
2
+ export * from "./assets/index.js";
3
+ export { CONFIG_FILES, defineConfig, loadConfig, validatePlugins } from "./config.js";
4
+ export { DEFAULT_FUZZ_KEYS, runFuzz } from "./fuzz.js";
5
+ export { inspectProject } from "./inspect.js";
6
+ export { TOOLING_API_VERSION } from "./plugin.js";
7
+ export { buildReport, DEV_TOKENS, formatReport } from "./report.js";
8
+ export * from "./scenario/index.js";
9
+ export { createDevServer, viteConfig } from "./vite.js";
10
+ export { ASSETS_DIR, debuff } from "./vite-plugin.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAoB,eAAe,EAAsB,MAAM,iBAAiB,CAAC;AACrG,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,YAAY,EAAqB,YAAY,EAAE,UAAU,EAAuB,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9H,OAAO,EAAE,iBAAiB,EAAuD,OAAO,EAAE,MAAM,WAAW,CAAC;AAC5G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAoB,WAAW,EAAE,UAAU,EAAmB,YAAY,EAAE,MAAM,aAAa,CAAC;AACvG,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,UAAU,EAA0B,MAAM,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,35 @@
1
+ /** JSON summary of a debuff project for agents and tools (`debuff inspect`, MCP `inspect_project`). */
2
+ export declare function inspectProject(root: string): Promise<{
3
+ name: unknown;
4
+ debuff: string;
5
+ dependencies: {};
6
+ config: string | null;
7
+ plugins: {
8
+ name: string;
9
+ apiVersion: 1;
10
+ importers: {
11
+ name: string;
12
+ extensions: string[];
13
+ kind: string;
14
+ }[];
15
+ commands: string[];
16
+ scenarioSteps: string[];
17
+ vite: boolean;
18
+ }[];
19
+ assets: {
20
+ count: number;
21
+ bytes: number;
22
+ byId: Record<string, import("./plugin.js").CompiledEntry>;
23
+ unused: string[];
24
+ warnings: string[];
25
+ };
26
+ build: {
27
+ totals: Record<"asset" | "code" | "html" | "map" | "other", {
28
+ bytes: number;
29
+ gzip: number;
30
+ brotli: number;
31
+ }>;
32
+ } | null;
33
+ runtime: string;
34
+ }>;
35
+ //# sourceMappingURL=inspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../src/inspect.ts"],"names":[],"mappings":"AAQA,uGAAuG;AACvG,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BhD"}
@@ -0,0 +1,37 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { compileAssets, findUnused } from "./assets/index.js";
4
+ import { loadConfig } from "./config.js";
5
+ import { buildReport } from "./report.js";
6
+ const version = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
7
+ /** JSON summary of a debuff project for agents and tools (`debuff inspect`, MCP `inspect_project`). */
8
+ export async function inspectProject(root) {
9
+ const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
10
+ const config = await loadConfig(root);
11
+ const { manifest, warnings } = await compileAssets(root, { plugins: config.plugins, atlas: config.atlas });
12
+ const dist = join(root, "dist");
13
+ return {
14
+ name: pkg.name,
15
+ debuff: version,
16
+ dependencies: pkg.dependencies ?? {},
17
+ config: config.file ?? null,
18
+ plugins: config.plugins.map((pl) => ({
19
+ name: pl.name,
20
+ apiVersion: pl.apiVersion,
21
+ importers: (pl.assets ?? []).map((i) => ({ name: i.name, extensions: i.extensions, kind: "command" in i ? "command" : "function" })),
22
+ commands: (pl.commands ?? []).map((c) => `${pl.name}:${c.name}`),
23
+ scenarioSteps: (pl.scenarioSteps ?? []).map((s) => `${pl.name}:${s.name}`),
24
+ vite: Boolean(pl.vite),
25
+ })),
26
+ assets: {
27
+ count: Object.keys(manifest.assets).length,
28
+ bytes: Object.values(manifest.assets).reduce((n, a) => n + a.bytes, 0),
29
+ byId: manifest.assets,
30
+ unused: findUnused(root, manifest),
31
+ warnings,
32
+ },
33
+ build: existsSync(dist) ? { totals: buildReport(dist).totals } : null,
34
+ runtime: "Live params and scene state: window.__DEBUFF_DEV__ in the dev server, `debuff scenario`, or the debuff MCP game_* tools.",
35
+ };
36
+ }
37
+ //# sourceMappingURL=inspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect.js","sourceRoot":"","sources":["../src/inspect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ import type { PluginOption } from "vite";
2
+ /** Manifest entry types the runtime loader understands. */
3
+ export type AssetType = "image" | "json" | "audio" | "text" | "sheet";
4
+ /** One frame of a sprite sheet, in source-image pixels. `duration` is in seconds. */
5
+ export interface SheetFrame {
6
+ x: number;
7
+ y: number;
8
+ w: number;
9
+ h: number;
10
+ duration?: number;
11
+ }
12
+ export interface SheetAnimation {
13
+ /** Indices into `frames`. */
14
+ frames: number[];
15
+ loop?: boolean;
16
+ }
17
+ /** A manifest entry as produced by an importer; the compiler adds `bytes` and `hash`. */
18
+ export interface AssetEntry {
19
+ type: AssetType;
20
+ /** URL relative to the site root, e.g. "assets/hero.png". */
21
+ src: string;
22
+ group?: string;
23
+ /** Uniform sprite grid (from the `name@WxH.png` convention). */
24
+ grid?: {
25
+ frameWidth: number;
26
+ frameHeight: number;
27
+ };
28
+ /** Region of `src` holding this image, when it was packed into an atlas. */
29
+ rect?: {
30
+ x: number;
31
+ y: number;
32
+ w: number;
33
+ h: number;
34
+ };
35
+ /** Explicit frames, for `sheet` entries. */
36
+ frames?: SheetFrame[];
37
+ animations?: Record<string, SheetAnimation>;
38
+ }
39
+ export interface CompiledEntry extends AssetEntry {
40
+ bytes: number;
41
+ hash: string;
42
+ }
43
+ /** What an importer is given. Serialised as JSON on stdin for command importers. */
44
+ export interface SourceFile {
45
+ /** Absolute path on disk. */
46
+ path: string;
47
+ /** Path relative to `assets/`, forward slashes. */
48
+ rel: string;
49
+ /** Default asset id derived from the path (group prefix, no extension). */
50
+ id: string;
51
+ /** Preload group (first folder), if any. */
52
+ group?: string;
53
+ /** Lower-case extension including the dot, e.g. ".json". For multi-part matches, the matched suffix. */
54
+ ext: string;
55
+ }
56
+ /** A generated file, served in dev and emitted into the build under `assets/`. */
57
+ export interface GeneratedFile {
58
+ /** Path relative to `assets/`, e.g. "hero.png". */
59
+ path: string;
60
+ contents: string;
61
+ encoding?: "utf8" | "base64";
62
+ }
63
+ /** What an importer returns. Command importers print this as JSON on stdout. */
64
+ export interface ImportResult {
65
+ /** Manifest entries keyed by asset id. Entries without `src` bytes on disk must reference a generated file. */
66
+ assets: Record<string, AssetEntry>;
67
+ /** Files to serve and emit (atlases, transcoded audio…). Sources are never modified. */
68
+ outputs?: GeneratedFile[];
69
+ /** Other source files (relative to `assets/`) this importer handled, so built-ins skip them. */
70
+ consumes?: string[];
71
+ warnings?: string[];
72
+ }
73
+ interface ImporterBase {
74
+ /** Used in errors, cache keys and `debuff inspect`. */
75
+ name: string;
76
+ /** File suffixes this importer claims, matched case-insensitively against the end of the path, e.g. [".aseprite.json"]. */
77
+ extensions: string[];
78
+ }
79
+ /** Runs inside the CLI process. Must be a pure function of the file contents and options. */
80
+ export interface FunctionImporter extends ImporterBase {
81
+ import(file: SourceFile): ImportResult | Promise<ImportResult>;
82
+ }
83
+ /** Runs an external program (any language): `SourceFile` JSON on stdin, `ImportResult` JSON on stdout. */
84
+ export interface CommandImporter extends ImporterBase {
85
+ command: [string, ...string[]];
86
+ }
87
+ export type AssetImporter = FunctionImporter | CommandImporter;
88
+ export interface CommandOption {
89
+ /** cac-style flags, e.g. "--out <dir>" or "-w, --watch". */
90
+ flags: string;
91
+ description: string;
92
+ default?: unknown;
93
+ }
94
+ export interface CommandContext {
95
+ /** Project root. */
96
+ root: string;
97
+ /** Positional arguments after the command name. */
98
+ args: string[];
99
+ /** Parsed options (camelCased). */
100
+ options: Record<string, unknown>;
101
+ /** Emit machine-readable output when true (`--json`). */
102
+ json: boolean;
103
+ }
104
+ /** A CLI command, exposed as `debuff <plugin>:<name>`. */
105
+ export interface CommandDef {
106
+ /** kebab-case; the full command is `<plugin name>:<name>`. */
107
+ name: string;
108
+ description: string;
109
+ options?: CommandOption[];
110
+ /** Return an exit code (0 = success). */
111
+ run(ctx: CommandContext): number | Promise<number>;
112
+ }
113
+ /** What a scenario step handler can do in the running game. */
114
+ export interface StepContext {
115
+ /** Call a `window.__DEBUFF_DEV__` method, e.g. `call("press", "Space")`. */
116
+ call<R = unknown>(method: string, ...args: unknown[]): Promise<R>;
117
+ /** Evaluate a function in the page (Playwright semantics). */
118
+ evaluate<R, A>(fn: (arg: A) => R | Promise<R>, arg: A): Promise<R>;
119
+ }
120
+ /** A scenario step type, used in JSON as `{ "<plugin>:<name>": value }`. */
121
+ export interface StepDef<V = unknown> {
122
+ /** kebab-case; the step key is `<plugin name>:<name>`. */
123
+ name: string;
124
+ description: string;
125
+ /** JSON Schema for the step's value; merged into the scenario schema for editors and agents. */
126
+ schema: Record<string, unknown>;
127
+ /** Return a failure message, or undefined if the step passed. */
128
+ run(value: V, ctx: StepContext): string | undefined | Promise<string | undefined>;
129
+ }
130
+ /** Tooling plugin contract (ADR 0004). */
131
+ export interface ToolingPlugin {
132
+ /** Unique kebab-case id. */
133
+ name: string;
134
+ /** Contract version this plugin targets. */
135
+ apiVersion: 1;
136
+ assets?: AssetImporter[];
137
+ /** CLI commands, namespaced as `debuff <name>:<command>`. */
138
+ commands?: CommandDef[];
139
+ /** Scenario step types, namespaced as `"<name>:<step>"`. */
140
+ scenarioSteps?: StepDef[];
141
+ /** Extra Vite plugins (escape hatch). */
142
+ vite?: PluginOption;
143
+ }
144
+ export declare const TOOLING_API_VERSION = 1;
145
+ export {};
146
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/dist/plugin.js ADDED
@@ -0,0 +1,2 @@
1
+ export const TOOLING_API_VERSION = 1;
2
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AA0JA,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ export interface FileReport {
2
+ path: string;
3
+ kind: "code" | "html" | "asset" | "map" | "other";
4
+ bytes: number;
5
+ gzip: number;
6
+ brotli: number;
7
+ }
8
+ export interface BuildReport {
9
+ dir: string;
10
+ files: FileReport[];
11
+ totals: Record<FileReport["kind"], {
12
+ bytes: number;
13
+ gzip: number;
14
+ brotli: number;
15
+ }>;
16
+ largestAsset: FileReport | undefined;
17
+ leaks: {
18
+ path: string;
19
+ token: string;
20
+ }[];
21
+ }
22
+ /** Tokens that only exist in dev builds. Kept short so false positives are unlikely. */
23
+ export declare const DEV_TOKENS: string[];
24
+ export declare function buildReport(dir: string): BuildReport;
25
+ export declare function formatReport(r: BuildReport): string;
26
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpF,YAAY,EAAE,UAAU,GAAG,SAAS,CAAC;IACrC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAED,wFAAwF;AACxF,eAAO,MAAM,UAAU,UAAqB,CAAC;AAkB7C,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAyBpD;AAID,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CA2BnD"}
package/dist/report.js ADDED
@@ -0,0 +1,80 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { basename, extname, join, relative, sep } from "node:path";
3
+ import { brotliCompressSync, gzipSync } from "node:zlib";
4
+ import { lime, paint, pink, sky } from "create-debuff";
5
+ import pc from "picocolors";
6
+ /** Tokens that only exist in dev builds. Kept short so false positives are unlikely. */
7
+ export const DEV_TOKENS = ["__DEBUFF_DEV__"];
8
+ function walk(dir) {
9
+ return readdirSync(dir).flatMap((name) => {
10
+ const path = join(dir, name);
11
+ return statSync(path).isDirectory() ? walk(path) : [path];
12
+ });
13
+ }
14
+ function kindOf(path) {
15
+ const ext = extname(path);
16
+ if (ext === ".map")
17
+ return "map";
18
+ if (ext === ".js" || ext === ".mjs" || ext === ".css")
19
+ return "code";
20
+ if (ext === ".html")
21
+ return "html";
22
+ if (/\.(png|jpe?g|webp|gif|ogg|mp3|wav|m4a|json|txt|woff2?)$/.test(path))
23
+ return "asset";
24
+ return "other";
25
+ }
26
+ export function buildReport(dir) {
27
+ if (!existsSync(dir))
28
+ throw new Error(`No build output at ${dir}. Run \`debuff build\` first.`);
29
+ const files = [];
30
+ const leaks = [];
31
+ for (const file of walk(dir).sort()) {
32
+ if (basename(file).startsWith(".") || basename(file) === "debuff-report.json")
33
+ continue;
34
+ const data = readFileSync(file);
35
+ const path = relative(dir, file).split(sep).join("/");
36
+ const kind = kindOf(path);
37
+ files.push({ path, kind, bytes: data.length, gzip: gzipSync(data).length, brotli: brotliCompressSync(data).length });
38
+ if (kind === "code" || kind === "html") {
39
+ const text = data.toString("utf8");
40
+ for (const token of DEV_TOKENS)
41
+ if (text.includes(token))
42
+ leaks.push({ path, token });
43
+ }
44
+ }
45
+ const zero = () => ({ bytes: 0, gzip: 0, brotli: 0 });
46
+ const totals = { code: zero(), html: zero(), asset: zero(), map: zero(), other: zero() };
47
+ for (const f of files) {
48
+ const t = totals[f.kind];
49
+ t.bytes += f.bytes;
50
+ t.gzip += f.gzip;
51
+ t.brotli += f.brotli;
52
+ }
53
+ const largestAsset = files.filter((f) => f.kind === "asset").sort((a, b) => b.bytes - a.bytes)[0];
54
+ return { dir, files, totals, largestAsset, leaks };
55
+ }
56
+ const kb = (n) => `${(n / 1024).toFixed(1)} kB`;
57
+ export function formatReport(r) {
58
+ const w = Math.max(...r.files.map((f) => f.path.length), 4);
59
+ const kindColor = {
60
+ code: sky,
61
+ html: (s) => paint("violet", s),
62
+ asset: pink,
63
+ map: pc.dim,
64
+ other: pc.dim,
65
+ };
66
+ const lines = [pc.dim(`${"file".padEnd(w)} ${"raw".padStart(9)} ${"gzip".padStart(9)} ${"brotli".padStart(9)}`)];
67
+ for (const f of r.files) {
68
+ lines.push(`${kindColor[f.kind](f.path.padEnd(w))} ${pc.dim(kb(f.bytes).padStart(9))} ${kb(f.gzip).padStart(9)} ${pc.bold(kb(f.brotli).padStart(9))}`);
69
+ }
70
+ lines.push("");
71
+ for (const [kind, t] of Object.entries(r.totals)) {
72
+ if (t.bytes)
73
+ lines.push(`${kindColor[kind](kind.padEnd(6))} ${pc.bold(kb(t.brotli))} brotli ${pc.dim(`${kb(t.gzip)} gzip · ${kb(t.bytes)} raw`)}`);
74
+ }
75
+ if (r.largestAsset)
76
+ lines.push(pc.dim(`largest asset: ${r.largestAsset.path} (${kb(r.largestAsset.bytes)})`));
77
+ lines.push(r.leaks.length ? pc.red(`✖ dev leaks: ${r.leaks.map((l) => `${l.token} in ${l.path}`).join(", ")}`) : lime("✔ dev boundary clean"));
78
+ return lines.join("\n");
79
+ }
80
+ //# sourceMappingURL=report.js.map