@typeonce/ritmo-devtools 0.6.0 → 0.7.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 (72) hide show
  1. package/README.md +105 -21
  2. package/dist/Determinism.d.ts +70 -0
  3. package/dist/Determinism.d.ts.map +1 -0
  4. package/dist/Determinism.js +137 -0
  5. package/dist/Determinism.js.map +1 -0
  6. package/dist/Format.d.ts +6 -1
  7. package/dist/Format.d.ts.map +1 -1
  8. package/dist/Format.js +24 -0
  9. package/dist/Format.js.map +1 -1
  10. package/dist/Invariant.d.ts +34 -12
  11. package/dist/Invariant.d.ts.map +1 -1
  12. package/dist/Invariant.js +6 -19
  13. package/dist/Invariant.js.map +1 -1
  14. package/dist/Lint.d.ts +60 -0
  15. package/dist/Lint.d.ts.map +1 -0
  16. package/dist/Lint.js +15 -0
  17. package/dist/Lint.js.map +1 -0
  18. package/dist/Monitor.d.ts +18 -4
  19. package/dist/Monitor.d.ts.map +1 -1
  20. package/dist/Monitor.js +31 -20
  21. package/dist/Monitor.js.map +1 -1
  22. package/dist/Replay.d.ts +271 -0
  23. package/dist/Replay.d.ts.map +1 -0
  24. package/dist/Replay.js +335 -0
  25. package/dist/Replay.js.map +1 -0
  26. package/dist/ReplayNodeSink.d.ts +11 -0
  27. package/dist/ReplayNodeSink.d.ts.map +1 -0
  28. package/dist/ReplayNodeSink.js +29 -0
  29. package/dist/ReplayNodeSink.js.map +1 -0
  30. package/dist/ReplayVitePlugin.d.ts +41 -0
  31. package/dist/ReplayVitePlugin.d.ts.map +1 -0
  32. package/dist/ReplayVitePlugin.js +41 -0
  33. package/dist/ReplayVitePlugin.js.map +1 -0
  34. package/dist/ReplayViteSink.d.ts +30 -0
  35. package/dist/ReplayViteSink.d.ts.map +1 -0
  36. package/dist/ReplayViteSink.js +9 -0
  37. package/dist/ReplayViteSink.js.map +1 -0
  38. package/dist/Session.d.ts +66 -14
  39. package/dist/Session.d.ts.map +1 -1
  40. package/dist/Session.js +92 -6
  41. package/dist/Session.js.map +1 -1
  42. package/dist/cli.d.ts +3 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +57 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/index.d.ts +3 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +3 -0
  49. package/dist/index.js.map +1 -1
  50. package/dist/internal/monitor.d.ts +4 -2
  51. package/dist/internal/monitor.d.ts.map +1 -1
  52. package/dist/internal/monitor.js +9 -3
  53. package/dist/internal/monitor.js.map +1 -1
  54. package/dist/internal/version.d.ts +7 -0
  55. package/dist/internal/version.d.ts.map +1 -0
  56. package/dist/internal/version.js +7 -0
  57. package/dist/internal/version.js.map +1 -0
  58. package/package.json +5 -2
  59. package/src/Determinism.ts +190 -0
  60. package/src/Format.ts +25 -0
  61. package/src/Invariant.ts +38 -12
  62. package/src/Lint.ts +72 -0
  63. package/src/Monitor.ts +48 -25
  64. package/src/Replay.ts +583 -0
  65. package/src/ReplayNodeSink.ts +38 -0
  66. package/src/ReplayVitePlugin.ts +74 -0
  67. package/src/ReplayViteSink.ts +33 -0
  68. package/src/Session.ts +153 -23
  69. package/src/cli.ts +55 -0
  70. package/src/index.ts +3 -0
  71. package/src/internal/monitor.ts +12 -5
  72. package/src/internal/version.ts +6 -0
@@ -0,0 +1,11 @@
1
+ import type * as Replay from "./Replay.ts";
2
+ /** Where the last replay was written, and every path written so far. */
3
+ export interface NodeSink extends Replay.Sink {
4
+ readonly written: ReadonlyArray<string>;
5
+ }
6
+ /** The file name of a replay: its save time, then its tick count. */
7
+ export declare const fileName: (file: Replay.ReplayFile) => string;
8
+ export declare const make: (options: {
9
+ readonly directory: string;
10
+ }) => NodeSink;
11
+ //# sourceMappingURL=ReplayNodeSink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplayNodeSink.d.ts","sourceRoot":"","sources":["../src/ReplayNodeSink.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,KAAK,MAAM,MAAM,aAAa,CAAA;AAE1C,wEAAwE;AACxE,MAAM,WAAW,QAAS,SAAQ,MAAM,CAAC,IAAI;IAC3C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,qEAAqE;AACrE,eAAO,MAAM,QAAQ,SAAU,MAAM,CAAC,UAAU,KAAG,MACqE,CAAA;AAExH,eAAO,MAAM,IAAI,YAAa;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KAAG,QAW9D,CAAA"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * A `Replay.Sink` for Node: each saved replay becomes a JSON file in a
3
+ * directory. For headless runs (bots, scripts) that record replays.
4
+ *
5
+ * @module ReplayNodeSink
6
+ * @docGroup devtools
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const recorder = Replay.recorder(runtime, { ..., sink: ReplayNodeSink.make({ directory: ".replays" }) })
11
+ * ```
12
+ */
13
+ import { mkdirSync, writeFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ /** The file name of a replay: its save time, then its tick count. */
16
+ export const fileName = (file) => `${new Date(file.savedAt).toISOString().replaceAll(":", "-")}-tick-${file.ticks.at(-1)?.tick ?? file.start.tick}.json`;
17
+ export const make = (options) => {
18
+ const written = [];
19
+ return {
20
+ written,
21
+ save: (file) => {
22
+ mkdirSync(options.directory, { recursive: true });
23
+ const path = join(options.directory, fileName(file));
24
+ writeFileSync(path, `${JSON.stringify(file)}\n`);
25
+ written.push(path);
26
+ }
27
+ };
28
+ };
29
+ //# sourceMappingURL=ReplayNodeSink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplayNodeSink.js","sourceRoot":"","sources":["../src/ReplayNodeSink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAShC,qEAAqE;AACrE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAuB,EAAU,EAAE,CAC1D,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,OAAO,CAAA;AAExH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,OAAuC,EAAY,EAAE;IACxE,MAAM,OAAO,GAAkB,EAAE,CAAA;IACjC,OAAO;QACL,OAAO;QACP,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;YACb,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YACjD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;YACpD,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACpB,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * A Vite plugin (Node side) that receives the replays `ReplayViteSink`
3
+ * posts from the page and writes them into the game's repository as
4
+ * `.replays/<time>.json`, logging the path, the tick count, and the reason.
5
+ * Development only: it does nothing in a build.
6
+ *
7
+ * Add `.replays/` to the game's `.gitignore`.
8
+ *
9
+ * @module ReplayVitePlugin
10
+ * @docGroup devtools
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * // vite.config.ts
15
+ * import * as ReplayVitePlugin from "@typeonce/ritmo-devtools/ReplayVitePlugin"
16
+ * export default defineConfig({ plugins: [ReplayVitePlugin.plugin()] })
17
+ * ```
18
+ */
19
+ import type { IncomingMessage, ServerResponse } from "node:http";
20
+ /** The part of Vite's dev server the plugin uses. */
21
+ export interface DevServer {
22
+ readonly middlewares: {
23
+ readonly use: (path: string, handler: (request: IncomingMessage, response: ServerResponse, next: () => void) => void) => void;
24
+ };
25
+ readonly config: {
26
+ readonly root: string;
27
+ readonly logger: {
28
+ readonly info: (message: string) => void;
29
+ };
30
+ };
31
+ }
32
+ /** A Vite plugin object. */
33
+ export interface Plugin {
34
+ readonly name: string;
35
+ readonly apply: "serve";
36
+ readonly configureServer: (server: DevServer) => void;
37
+ }
38
+ export declare const plugin: (options?: {
39
+ readonly directory?: string;
40
+ }) => Plugin;
41
+ //# sourceMappingURL=ReplayVitePlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplayVitePlugin.d.ts","sourceRoot":"","sources":["../src/ReplayVitePlugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAMhE,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,CAAA;KAC9H,CAAA;IACD,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;SAAE,CAAA;KAAE,CAAA;CAC1G;AAED,4BAA4B;AAC5B,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;CACtD;AAED,eAAO,MAAM,MAAM,aAAa;IAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,KAAQ,MAkCrE,CAAA"}
@@ -0,0 +1,41 @@
1
+ import * as Replay from "./Replay.js";
2
+ import * as ReplayNodeSink from "./ReplayNodeSink.js";
3
+ import { endpoint } from "./ReplayViteSink.js";
4
+ export const plugin = (options = {}) => ({
5
+ name: "ritmo-replays",
6
+ apply: "serve",
7
+ configureServer: (server) => {
8
+ const sink = ReplayNodeSink.make({ directory: `${server.config.root}/${options.directory ?? ".replays"}` });
9
+ server.middlewares.use(endpoint, (request, response, next) => {
10
+ if (request.method !== "POST")
11
+ return next();
12
+ let body = "";
13
+ request.setEncoding("utf8");
14
+ request.on("data", (chunk) => {
15
+ body += chunk;
16
+ });
17
+ request.on("end", () => {
18
+ let data;
19
+ try {
20
+ data = JSON.parse(body);
21
+ }
22
+ catch {
23
+ data = undefined;
24
+ }
25
+ const parsed = Replay.parse(data);
26
+ if (!parsed.ok) {
27
+ response.statusCode = 400;
28
+ response.end(parsed.error._tag === "InvalidReplay" ? parsed.error.message : parsed.error._tag);
29
+ return;
30
+ }
31
+ sink.save(parsed.value);
32
+ const path = sink.written.at(-1).slice(server.config.root.length + 1);
33
+ const ticks = parsed.value.ticks.length;
34
+ server.config.logger.info(`[ritmo] replay saved: ${path} (${ticks} ticks): ${parsed.value.reason}`);
35
+ response.statusCode = 201;
36
+ response.end(path);
37
+ });
38
+ });
39
+ }
40
+ });
41
+ //# sourceMappingURL=ReplayVitePlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplayVitePlugin.js","sourceRoot":"","sources":["../src/ReplayVitePlugin.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAiB9C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,GAAoC,EAAE,EAAU,EAAE,CAAC,CAAC;IAChF,IAAI,EAAE,eAAe;IACrB,KAAK,EAAE,OAAO;IACd,eAAe,EAAE,CAAC,MAAM,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,IAAI,UAAU,EAAE,EAAE,CAAC,CAAA;QAC3G,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;YAC3D,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;gBAAE,OAAO,IAAI,EAAE,CAAA;YAC5C,IAAI,IAAI,GAAG,EAAE,CAAA;YACb,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAC3B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBACnC,IAAI,IAAI,KAAK,CAAA;YACf,CAAC,CAAC,CAAA;YACF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACrB,IAAI,IAAa,CAAA;gBACjB,IAAI,CAAC;oBACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBACzB,CAAC;gBAAC,MAAM,CAAC;oBACP,IAAI,GAAG,SAAS,CAAA;gBAClB,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBACjC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACf,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAA;oBACzB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;oBAC9F,OAAM;gBACR,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBACvB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;gBACtE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAA;gBACvC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,IAAI,KAAK,KAAK,YAAY,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;gBACnG,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAA;gBACzB,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * A `Replay.Sink` for a page served by Vite in development: each saved
3
+ * replay is posted to the dev server, where `ReplayVitePlugin` writes it
4
+ * into the game's repository (`.replays/`). The page's `fetch` is passed in.
5
+ *
6
+ * @module ReplayViteSink
7
+ * @docGroup devtools
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * if (import.meta.env.DEV) {
12
+ * const sink = ReplayViteSink.make({ fetch: (url, init) => fetch(url, init) })
13
+ * }
14
+ * ```
15
+ */
16
+ import type * as Replay from "./Replay.ts";
17
+ /** The dev server's endpoint for replays (what `ReplayVitePlugin` serves). */
18
+ export declare const endpoint = "/__ritmo/replays";
19
+ export interface Options {
20
+ /** The page's `fetch` (or any function with its shape for these two arguments). */
21
+ readonly fetch: (url: string, init: {
22
+ readonly method: "POST";
23
+ readonly headers: Readonly<Record<string, string>>;
24
+ readonly body: string;
25
+ }) => Promise<unknown>;
26
+ /** Called when a post fails (the dev server is down, the plugin is not installed). */
27
+ readonly onError?: (error: unknown) => void;
28
+ }
29
+ export declare const make: (options: Options) => Replay.Sink;
30
+ //# sourceMappingURL=ReplayViteSink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplayViteSink.d.ts","sourceRoot":"","sources":["../src/ReplayViteSink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,KAAK,MAAM,MAAM,aAAa,CAAA;AAE1C,8EAA8E;AAC9E,eAAO,MAAM,QAAQ,qBAAqB,CAAA;AAE1C,MAAM,WAAW,OAAO;IACtB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/J,sFAAsF;IACtF,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C;AAED,eAAO,MAAM,IAAI,YAAa,OAAO,KAAG,MAAM,CAAC,IAK7C,CAAA"}
@@ -0,0 +1,9 @@
1
+ /** The dev server's endpoint for replays (what `ReplayVitePlugin` serves). */
2
+ export const endpoint = "/__ritmo/replays";
3
+ export const make = (options) => ({
4
+ save: (file) => {
5
+ void options.fetch(endpoint, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(file) })
6
+ .catch((error) => options.onError?.(error));
7
+ }
8
+ });
9
+ //# sourceMappingURL=ReplayViteSink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplayViteSink.js","sourceRoot":"","sources":["../src/ReplayViteSink.ts"],"names":[],"mappings":"AAiBA,8EAA8E;AAC9E,MAAM,CAAC,MAAM,QAAQ,GAAG,kBAAkB,CAAA;AAS1C,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,OAAgB,EAAe,EAAE,CAAC,CAAC;IACtD,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;QACb,KAAK,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;aAC1H,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;IACxD,CAAC;CACF,CAAC,CAAA"}
package/dist/Session.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * A debug session around a runtime made with `debug: true`.
2
+ * A debug session around a runtime made with `debug: { hostClock }`.
3
3
  *
4
4
  * The session names the schedules it drives, keeps a bounded history of
5
5
  * trace events, and answers questions about it as text and data:
@@ -8,7 +8,8 @@
8
8
  * at the first system failure, thrown defect, or violated invariant.
9
9
  * - `journal(filter)` lists what happened, one fact per line, filtered by
10
10
  * frames, entity, component, resource, event, machine, system, or kind.
11
- * - `why(entity, component)` lists the latest changes to one component.
11
+ * - `why(entity, component)` lists the latest changes to one component, and
12
+ * the systems whose declared `spawns` could have spawned the entity.
12
13
  * - `system(name)` shows one system's declared access and recent activity.
13
14
  * - `describe()`, `dump(filter)`, `streams()` render the core debug handle.
14
15
  * - `report()` summarizes per-system timing and warnings accumulated while
@@ -34,25 +35,44 @@
34
35
  * console.log(session.journal({ frames: [180, 185], entity: 1 }))
35
36
  * ```
36
37
  */
38
+ import { HostClock } from "@typeonce/ritmo";
37
39
  import type * as Debug from "@typeonce/ritmo/Debug";
40
+ import type * as Inspector from "@typeonce/ritmo/Inspector";
38
41
  import type * as Machine from "@typeonce/ritmo/Machine";
39
42
  import type * as Result from "@typeonce/ritmo/Result";
40
- import type { ExecutableScheduleDefinition } from "@typeonce/ritmo/Schedule";
43
+ import type * as Runtime from "@typeonce/ritmo/Runtime";
44
+ import type { AnyScheduleOrder, ExecutableScheduleDefinition } from "@typeonce/ritmo/Schedule";
41
45
  import type { Schema } from "@typeonce/ritmo/Schema";
42
46
  import * as Format from "./Format.ts";
43
47
  import type { Invariant } from "./Invariant.ts";
44
48
  import * as Rendered from "./Rendered.ts";
45
49
  /**
46
- * What a session needs from a runtime: its debug handle and the dynamic
47
- * `tryTick`, which reports missing requirements as data.
50
+ * What a session needs from a runtime: its debug handle, the dynamic
51
+ * `tryTick`, which reports missing requirements as data, `inspect` for
52
+ * invariants, and what it was provided with, which invariants are checked
53
+ * against.
48
54
  */
49
- export interface Target<S extends Schema.Any, Root> {
55
+ export interface Target<S extends Schema.Any, Root, Services extends Record<string, unknown> = Record<string, unknown>, Resources extends object = object, Machines extends Record<string, unknown> = Record<string, unknown>> {
50
56
  readonly debug: Debug.Handle<S, Root>;
51
- readonly tryTick: (schedule: ExecutableScheduleDefinition<S, any, any, any, any>) => Result.Result<void, unknown>;
57
+ readonly tryTick: (schedule: ExecutableScheduleDefinition<S, any, any, any, any, AnyScheduleOrder>) => Result.Result<void, unknown>;
58
+ readonly inspect: (inspector: never) => unknown;
59
+ readonly services: Services;
60
+ readonly resourceValues: Resources;
61
+ readonly machineValues: Machines;
52
62
  }
53
- export interface Options<S extends Schema.Any, Root, Names extends string> {
63
+ type InvariantRequirementErrors<Invariants extends ReadonlyArray<Invariant<Invariant.CheckInspector>>, Services extends Record<string, unknown>, Resources extends object, Machines extends Record<string, unknown>> = Runtime.ValidateInspector<Invariants[number]["inspector"], Services, Resources, Machines> extends {
64
+ readonly __fixRuntimeRequirements__: infer Errors;
65
+ } ? Errors : never;
66
+ /**
67
+ * Rejects invariants whose inspectors read a resource, service, or machine
68
+ * the runtime does not provide, naming each one.
69
+ */
70
+ export type ValidateInvariants<Invariants extends ReadonlyArray<Invariant<Invariant.CheckInspector>>, Services extends Record<string, unknown>, Resources extends object, Machines extends Record<string, unknown>> = [InvariantRequirementErrors<Invariants, Services, Resources, Machines>] extends [never] ? unknown : {
71
+ readonly __fixInvariantRequirements__: InvariantRequirementErrors<Invariants, Services, Resources, Machines>;
72
+ };
73
+ export interface Options<S extends Schema.Any, Root, Names extends string, Invariants extends ReadonlyArray<Invariant<Invariant.CheckInspector>> = readonly []> {
54
74
  /** The schedules `run` can drive, by name. Also names them in traces. */
55
- readonly schedules: Readonly<Record<Names, ExecutableScheduleDefinition<S, any, Root, any, any>>>;
75
+ readonly schedules: Readonly<Record<Names, ExecutableScheduleDefinition<S, any, Root, any, any, AnyScheduleOrder>>>;
56
76
  /**
57
77
  * More schedules to include in `describe()`, its lints, and the access
58
78
  * maps, without making them runnable: typically a render schedule that
@@ -60,11 +80,22 @@ export interface Options<S extends Schema.Any, Root, Names extends string> {
60
80
  * and components only those schedules read are reported as never read.
61
81
  * Their services show as not provided, which is not a lint.
62
82
  */
63
- readonly describe?: Readonly<Record<string, ExecutableScheduleDefinition<S, any, Root, any, any>>>;
64
- /** Checked after every frame of every run. */
65
- readonly invariants?: ReadonlyArray<Invariant>;
83
+ readonly describe?: Readonly<Record<string, ExecutableScheduleDefinition<S, any, Root, any, any, AnyScheduleOrder>>>;
84
+ /** Events the host publishes with `runtime.send`: written, for the lints (see `Debug.Handle.declareHostEvents`). */
85
+ readonly hostEvents?: ReadonlyArray<Schema.EventDescriptor<S>>;
86
+ /**
87
+ * Checked after every frame of every run, in order; the first violation
88
+ * stops the run. Built with `Invariant.fromInspector(Game.Inspector(...))`.
89
+ */
90
+ readonly invariants?: Invariants;
66
91
  /** Frames of trace history kept for `journal` and `why`. Defaults to 600. */
67
92
  readonly history?: number;
93
+ /**
94
+ * Times runs (`RunResult.ms`). `HostClock.test()` makes every timing 0,
95
+ * so text output is identical across runs (give the runtime's
96
+ * `debug: { hostClock }` a test clock too).
97
+ */
98
+ readonly hostClock: HostClock.HostClock;
68
99
  /** Value formatting for all text output. */
69
100
  readonly format?: Format.ValueOptions;
70
101
  }
@@ -89,6 +120,12 @@ export type Stop = {
89
120
  readonly reason: "missingRequirements";
90
121
  readonly frame: number;
91
122
  readonly requirements: unknown;
123
+ }
124
+ /** A `Game.System.thisTick` read whose writer runs after it, or that has none; nothing ran. */
125
+ | {
126
+ readonly reason: "tickOrder";
127
+ readonly frame: number;
128
+ readonly issues: ReadonlyArray<string>;
92
129
  } | {
93
130
  readonly reason: "defect";
94
131
  readonly frame: number;
@@ -98,6 +135,20 @@ export type Stop = {
98
135
  readonly frame: number;
99
136
  readonly invariant: string;
100
137
  readonly message: string;
138
+ }
139
+ /**
140
+ * A failed tick result nobody read (for example a setup tick the game ran
141
+ * before this run): `frame` is the failed tick's, `skipped` the systems
142
+ * that did not run then.
143
+ */
144
+ | {
145
+ readonly reason: "ignored-failure";
146
+ readonly frame: number;
147
+ readonly schedule: string;
148
+ readonly system: string | undefined;
149
+ readonly error: unknown;
150
+ readonly skipped: ReadonlyArray<string>;
151
+ readonly when: "next-tick" | "session-end";
101
152
  };
102
153
  export interface RunResult {
103
154
  readonly schedule: string;
@@ -163,7 +214,7 @@ export interface SystemStats {
163
214
  readonly maxMs: number;
164
215
  }
165
216
  export interface Warning {
166
- readonly code: "missed-read" | "pending-commands" | "pending-next-state" | "transition-failed" | "discarded-messages" | "sent-event-unread" | "system-failed" | "non-finite-value" | "population-growing";
217
+ readonly code: "missed-read" | "pending-commands" | "pending-next-state" | "transition-failed" | "discarded-messages" | "sent-event-unread" | "system-failed" | "non-finite-value" | "population-growing" | "ignored-failure";
167
218
  readonly message: string;
168
219
  readonly count: number;
169
220
  readonly firstFrame: number;
@@ -208,5 +259,6 @@ export interface Session<S extends Schema.Any, Names extends string> {
208
259
  /** Stops tracing. Queries keep working on the history recorded so far. */
209
260
  readonly close: () => void;
210
261
  }
211
- export declare const make: <S extends Schema.Any, Root, const Names extends string>(runtime: Target<S, Root>, options: Options<S, Root, Names>) => Session<S, Names>;
262
+ export declare const make: <S extends Schema.Any, Root, Services extends Record<string, unknown>, Resources extends object, Machines extends Record<string, unknown>, const Names extends string, const Invariants extends ReadonlyArray<Invariant<Inspector.InspectorDefinition<any, string | undefined, Root, any, any>>> = readonly []>(runtime: Target<S, Root, Services, Resources, Machines>, options: Options<S, Root, Names, Invariants> & ValidateInvariants<Invariants, Services, Resources, Machines>) => Session<S, Names>;
263
+ export {};
212
264
  //# sourceMappingURL=Session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Session.d.ts","sourceRoot":"","sources":["../src/Session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Session.d.ts","sourceRoot":"","sources":["../src/Session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,KAAK,KAAK,MAAM,uBAAuB,CAAA;AACnD,OAAO,KAAK,KAAK,SAAS,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,KAAK,OAAO,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,KAAK,MAAM,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,KAAK,OAAO,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAA;AAC9F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAEpD,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAGzC;;;;;GAKG;AACH,MAAM,WAAW,MAAM,CACrB,CAAC,SAAS,MAAM,CAAC,GAAG,EACpB,IAAI,EACJ,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,SAAS,SAAS,MAAM,GAAG,MAAM,EACjC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAElE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;IACrC,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,4BAA4B,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACnI,QAAQ,CAAC,OAAO,EAAE,CAAC,SAAS,EAAE,KAAK,KAAK,OAAO,CAAA;IAC/C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,cAAc,EAAE,SAAS,CAAA;IAClC,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAA;CACjC;AAED,KAAK,0BAA0B,CAC7B,UAAU,SAAS,aAAa,CAAC,SAAS,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,EACrE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,SAAS,MAAM,EACxB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACtC,OAAO,CAAC,iBAAiB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS;IACpG,QAAQ,CAAC,0BAA0B,EAAE,MAAM,MAAM,CAAA;CAClD,GACG,MAAM,GACN,KAAK,CAAA;AAET;;;GAGG;AACH,MAAM,MAAM,kBAAkB,CAC5B,UAAU,SAAS,aAAa,CAAC,SAAS,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,EACrE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,SAAS,MAAM,EACxB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACtC,CAAC,0BAA0B,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACvF,OAAO,GACP;IAAE,QAAQ,CAAC,4BAA4B,EAAE,0BAA0B,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;CAAE,CAAA;AAEpH,MAAM,WAAW,OAAO,CACtB,CAAC,SAAS,MAAM,CAAC,GAAG,EACpB,IAAI,EACJ,KAAK,SAAS,MAAM,EACpB,UAAU,SAAS,aAAa,CAAC,SAAS,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,GAAG,SAAS,EAAE;IAEnF,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IACnH;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,4BAA4B,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IACpH,oHAAoH;IACpH,QAAQ,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9D;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAA;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,SAAS,CAAA;IACvC,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,YAAY,CAAA;CACtC;AAED,MAAM,WAAW,UAAU;IACzB,oCAAoC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;CAC5C;AAED,0EAA0E;AAC1E,MAAM,MAAM,IAAI,GACZ;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAChC;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACxG;IAAE,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;CAAE;AACpG,+FAA+F;GAC7F;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,GAChG;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC9E;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAChH;;;;GAIG;GACD;IACE,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACvC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,aAAa,CAAA;CAC3C,CAAA;AAEL,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IACrD,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IACrE,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;IAClC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,eAAe,GAAG;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAClF;AAED,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG;IACjD,oDAAoD;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpD,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAA;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAA;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,CAAA;IAC3C,mBAAmB;IACnB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC/C,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EACT,aAAa,GACb,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,oBAAoB,GACpB,mBAAmB,GACnB,eAAe,GACf,kBAAkB,GAClB,oBAAoB,GACpB,iBAAiB,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IACvD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;IAC5C,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IACzC,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IACnD,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACzC,oDAAoD;IACpD,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAA;CACtC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,iBAAiB,GAAG,SAAS,CAAA;IACzD,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;CAC5C;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,KAAK,SAAS,MAAM;IACjE,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;IACjF,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IAC9F,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IAChK,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IACtJ,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;IAC3G,QAAQ,CAAC,QAAQ,EAAE,MAAM,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IAC7D,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACnF,QAAQ,CAAC,OAAO,EAAE,MAAM,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAA;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAChD,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,MAAM,CAAA;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAA;CAC3B;AAmCD,eAAO,MAAM,IAAI,GACf,CAAC,SAAS,MAAM,CAAC,GAAG,EACpB,IAAI,EACJ,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,SAAS,MAAM,EACxB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,CAAC,KAAK,SAAS,MAAM,EAC1B,KAAK,CAAC,UAAU,SAAS,aAAa,CAAC,SAAS,CAAC,SAAS,CAAC,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,WAE9H,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,WAC9C,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,kBAAkB,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,KAC3G,OAAO,CAAC,CAAC,EAAE,KAAK,CAgclB,CAAA"}
package/dist/Session.js CHANGED
@@ -1,3 +1,41 @@
1
+ /**
2
+ * A debug session around a runtime made with `debug: { hostClock }`.
3
+ *
4
+ * The session names the schedules it drives, keeps a bounded history of
5
+ * trace events, and answers questions about it as text and data:
6
+ *
7
+ * - `run(name, { frames, until })` ticks a schedule frame by frame and stops
8
+ * at the first system failure, thrown defect, or violated invariant.
9
+ * - `journal(filter)` lists what happened, one fact per line, filtered by
10
+ * frames, entity, component, resource, event, machine, system, or kind.
11
+ * - `why(entity, component)` lists the latest changes to one component, and
12
+ * the systems whose declared `spawns` could have spawned the entity.
13
+ * - `system(name)` shows one system's declared access and recent activity.
14
+ * - `describe()`, `dump(filter)`, `streams()` render the core debug handle.
15
+ * - `report()` summarizes per-system timing and warnings accumulated while
16
+ * tracing: missed reads, commands still pending at the end of a frame,
17
+ * failed transitions, skipped systems that discarded messages, NaN or
18
+ * infinite numbers written anywhere, and entity counts that keep growing
19
+ * through a run.
20
+ *
21
+ * Everything returns a `Rendered` value that prints as text.
22
+ *
23
+ * @module Session
24
+ * @docGroup devtools
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const session = Session.make(runtime, {
29
+ * schedules: { setup: setupSchedule, update: updateSchedule },
30
+ * invariants: [PlayerInsideWorld]
31
+ * })
32
+ * session.run("setup")
33
+ * console.log(session.run("update", { frames: 300 }))
34
+ * console.log(session.why(1, Position))
35
+ * console.log(session.journal({ frames: [180, 185], entity: 1 }))
36
+ * ```
37
+ */
38
+ import { HostClock } from "@typeonce/ritmo";
1
39
  import * as Format from "./Format.js";
2
40
  import * as Rendered from "./Rendered.js";
3
41
  import { nonFinitePath } from "./internal/values.js";
@@ -25,6 +63,7 @@ const growsSteadily = (samples) => {
25
63
  };
26
64
  const describeError = (error) => typeof error === "object" && error !== null ? error : {};
27
65
  export const make = (runtime, options) => {
66
+ const inspect = runtime.inspect;
28
67
  const debug = runtime.debug;
29
68
  const capacity = options.history ?? 600;
30
69
  const format = options.format ?? {};
@@ -35,6 +74,7 @@ export const make = (runtime, options) => {
35
74
  throw new Error(`Session: "${name}" is both a runnable and a described-only schedule`);
36
75
  }
37
76
  debug.nameSchedules({ ...described, ...schedules });
77
+ debug.declareHostEvents(options.hostEvents ?? []);
38
78
  const history = [];
39
79
  let current = { frame: debug.frame(), events: [] };
40
80
  history.push(current);
@@ -158,6 +198,10 @@ export const make = (runtime, options) => {
158
198
  }
159
199
  }
160
200
  break;
201
+ case "failure.ignored":
202
+ ignored = event;
203
+ warn("ignored-failure", `ignored:${event.frame}`, Format.ignoredFailure(event, format), event.frame);
204
+ break;
161
205
  case "transition":
162
206
  if (event.outcome !== "failed")
163
207
  pendingStates.delete(event.machine);
@@ -167,6 +211,13 @@ export const make = (runtime, options) => {
167
211
  break;
168
212
  }
169
213
  };
214
+ /** The last `failure.ignored` event seen, for `run` to stop on. */
215
+ let ignored;
216
+ const takeIgnored = () => {
217
+ const found = ignored;
218
+ ignored = undefined;
219
+ return found;
220
+ };
170
221
  const stopObserving = debug.observe(record);
171
222
  const historyRange = () => {
172
223
  const first = history.find((entry) => entry.events.length > 0);
@@ -175,7 +226,7 @@ export const make = (runtime, options) => {
175
226
  const run = (name, runOptions = {}) => {
176
227
  const schedule = schedules[name];
177
228
  const frames = runOptions.frames ?? 1;
178
- const started = performance.now();
229
+ const started = options.hostClock.now();
179
230
  const warningsBefore = new Map([...warnings].map(([key, warning]) => [key, warning.count]));
180
231
  const initial = debug.population();
181
232
  const entityCounts = [];
@@ -189,6 +240,7 @@ export const make = (runtime, options) => {
189
240
  let first;
190
241
  let last;
191
242
  let stop = { reason: "completed" };
243
+ takeIgnored();
192
244
  for (let index = 0; index < frames; index++) {
193
245
  let result;
194
246
  try {
@@ -205,17 +257,25 @@ export const make = (runtime, options) => {
205
257
  first ??= frame;
206
258
  last = frame;
207
259
  sample();
260
+ const unread = takeIgnored();
261
+ if (unread !== undefined) {
262
+ const { type: _type, ...found } = unread;
263
+ stop = { reason: "ignored-failure", ...found };
264
+ break;
265
+ }
208
266
  if (!result.ok) {
209
267
  const error = describeError(result.error);
210
268
  stop = error.kind === "MissingRuntimeRequirements"
211
269
  ? { reason: "missingRequirements", frame, requirements: error.requirements }
212
- : { reason: "failure", frame, system: typeof error.system === "string" ? error.system : "(unknown)", error: error.error };
270
+ : error.kind === "TickOrderViolation"
271
+ ? { reason: "tickOrder", frame, issues: (error.issues ?? []).map((issue) => issue.message) }
272
+ : { reason: "failure", frame, system: typeof error.system === "string" ? error.system : "(unknown)", error: error.error };
213
273
  break;
214
274
  }
215
275
  const violation = (options.invariants ?? []).reduce((found, invariant) => {
216
276
  if (found)
217
277
  return found;
218
- const message = invariant.check();
278
+ const message = inspect(invariant.inspector);
219
279
  return message === undefined ? undefined : { invariant: invariant.name, message };
220
280
  }, undefined);
221
281
  if (violation) {
@@ -254,7 +314,7 @@ export const make = (runtime, options) => {
254
314
  frames: first === undefined || last === undefined ? 0 : last - first + 1,
255
315
  range: first === undefined || last === undefined ? undefined : [first, last],
256
316
  stop,
257
- ms: performance.now() - started,
317
+ ms: HostClock.elapsed(started, options.hostClock.now()),
258
318
  lintWarnings: lintWarnings.length,
259
319
  warnings: raised,
260
320
  entities: range(initial.entities, entityCounts),
@@ -319,11 +379,28 @@ export const make = (runtime, options) => {
319
379
  : component.name in currentValue.components
320
380
  ? `e${entity} ${component.name} is now ${Format.value(currentValue.components[component.name], format)}`
321
381
  : `e${entity} has no ${component.name} now`;
322
- const header = lines.length === 0
382
+ const changes = lines.length === 0
323
383
  ? `${now}; no change to it in ${rangeText()}`
324
384
  : `${now}; last ${lines.length} changes (oldest first):`;
385
+ const spawners = spawnersOf(currentValue === undefined ? [component.name] : Object.keys(currentValue.components), component.name);
386
+ const header = `${changes}\n${spawners.length === 0
387
+ ? `no described system declares spawns of ${component.name}`
388
+ : `could have spawned it: ${spawners.join(", ")}`}`;
325
389
  return Rendered.make(lines, renderLines(header, lines));
326
390
  };
391
+ /**
392
+ * The described systems whose declared `spawns` include `component` and
393
+ * cover every component the entity has that no described system may
394
+ * insert (so it can only have come from the spawn).
395
+ */
396
+ const spawnersOf = (has, component) => {
397
+ const systems = debug.describe().systems;
398
+ const insertable = new Set(systems.flatMap((system) => system.commands.inserts));
399
+ const fromSpawn = has.filter((name) => !insertable.has(name));
400
+ return systems
401
+ .filter((system) => system.commands.spawns.includes(component) && fromSpawn.every((name) => system.commands.spawns.includes(name)))
402
+ .map((system) => system.name);
403
+ };
327
404
  const whyResource = (resource, whyOptions = {}) => {
328
405
  const lines = collectLines({ resource, kinds: ["resource"], limit: whyOptions.limit ?? 5 });
329
406
  const resources = debug.dump({ limit: 0 }).resources;
@@ -359,6 +436,8 @@ export const make = (runtime, options) => {
359
436
  return Rendered.make(data, lines.join("\n"));
360
437
  };
361
438
  const report = () => {
439
+ // The report is where a session ends: a failure still unread shows here.
440
+ debug.checkIgnoredFailures();
362
441
  const systems = [...stats.keys()].map((name) => statsFor(name)).sort((left, right) => right.totalMs - left.totalMs);
363
442
  const data = {
364
443
  frames: debug.frame(),
@@ -406,7 +485,10 @@ export const make = (runtime, options) => {
406
485
  },
407
486
  report,
408
487
  frame: () => debug.frame(),
409
- close: stopObserving
488
+ close: () => {
489
+ debug.checkIgnoredFailures();
490
+ stopObserving();
491
+ }
410
492
  };
411
493
  };
412
494
  const renderRun = (result, format) => {
@@ -426,6 +508,10 @@ const renderRun = (result, format) => {
426
508
  return `${head}, DEFECT at f${stop.frame}: ${stop.error instanceof Error ? stop.error.stack ?? stop.error.message : Format.value(stop.error, format)}`;
427
509
  case "invariant":
428
510
  return `${head}, INVARIANT "${stop.invariant}" violated at f${stop.frame}: ${stop.message}`;
511
+ case "tickOrder":
512
+ return `${head}, NOT RUN after f${stop.frame}: ${stop.issues.join("; ")}`;
513
+ case "ignored-failure":
514
+ return `${head}, STOPPED: ${Format.ignoredFailure({ type: "failure.ignored", ...stop }, format)}, at f${stop.frame}`;
429
515
  }
430
516
  };
431
517
  //# sourceMappingURL=Session.js.map