@cimplify/cli 0.15.3 → 0.17.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 (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -2
  3. package/dist/add-C3DPTVH3.mjs +140 -0
  4. package/dist/{add-KRIIKAS4.mjs → add-JX2GXWXU.mjs} +4 -4
  5. package/dist/ai-HUZTVJIQ.mjs +17 -0
  6. package/dist/{assets-CWB72PZG.mjs → assets-MPESJSUV.mjs} +12 -7
  7. package/dist/{auth-step-up-Z6KOTTV7.mjs → auth-step-up-VJAFGSW6.mjs} +5 -5
  8. package/dist/build-E7RRQWVF.mjs +616 -0
  9. package/dist/{cancel-ZHVVILOU.mjs → cancel-UMKR274A.mjs} +4 -4
  10. package/dist/capture-TK4NIUYM.mjs +69 -0
  11. package/dist/chunk-25ZPFHLF.mjs +51 -0
  12. package/dist/{chunk-OLZKNUMK.mjs → chunk-3LJG22FS.mjs} +6 -6
  13. package/dist/chunk-454RQ6NR.mjs +10614 -0
  14. package/dist/{chunk-O2TO2J4I.mjs → chunk-6CQMRZ2W.mjs} +2 -2
  15. package/dist/chunk-6KKLF5KG.mjs +32 -0
  16. package/dist/chunk-74QZQSCL.mjs +84 -0
  17. package/dist/chunk-BGM7CH5G.mjs +81 -0
  18. package/dist/chunk-BTNVYAC4.mjs +5557 -0
  19. package/dist/{chunk-I2UP7YTG.mjs → chunk-ECGDRU56.mjs} +1 -1
  20. package/dist/{chunk-UTD34C2H.mjs → chunk-EOOOXVHR.mjs} +2 -4
  21. package/dist/chunk-EYEMPIMP.mjs +74 -0
  22. package/dist/{chunk-3RPR33MO.mjs → chunk-FV273ECI.mjs} +11 -7
  23. package/dist/{chunk-MLE4MVYC.mjs → chunk-GGPN4QYQ.mjs} +2 -2
  24. package/dist/chunk-HQPZQIWW.mjs +1456 -0
  25. package/dist/{chunk-3QUK5BPY.mjs → chunk-I7GEHLDD.mjs} +16 -1
  26. package/dist/chunk-ITDAICBY.mjs +363 -0
  27. package/dist/chunk-JKECUAAV.mjs +2169 -0
  28. package/dist/chunk-JNCEKW2Y.mjs +41 -0
  29. package/dist/chunk-JTYVGORV.mjs +101 -0
  30. package/dist/chunk-KQNZKV6E.mjs +535 -0
  31. package/dist/chunk-LJDIQ4PF.mjs +6463 -0
  32. package/dist/{chunk-2MKZKUVH.mjs → chunk-LLRUOIMU.mjs} +1 -2
  33. package/dist/chunk-MJMRIMR7.mjs +86 -0
  34. package/dist/chunk-N2WZJX5P.mjs +1989 -0
  35. package/dist/chunk-NEB372WD.mjs +1152 -0
  36. package/dist/chunk-OPX7KPOU.mjs +68 -0
  37. package/dist/chunk-PHUBMRAS.mjs +623 -0
  38. package/dist/chunk-PQBALZUH.mjs +1941 -0
  39. package/dist/chunk-PVW5D5AW.mjs +42 -0
  40. package/dist/{chunk-LZEG3FEY.mjs → chunk-QQWYQ2HS.mjs} +7 -7
  41. package/dist/chunk-QVR6V6QP.mjs +46 -0
  42. package/dist/chunk-SGT5IJVW.mjs +28 -0
  43. package/dist/chunk-SZXO6YSX.mjs +502 -0
  44. package/dist/chunk-THATLMEX.mjs +1227 -0
  45. package/dist/chunk-TYR4Q37L.mjs +391 -0
  46. package/dist/{chunk-DMLQBAGC.mjs → chunk-V3R2NYQR.mjs} +1 -1
  47. package/dist/chunk-VHK7WBO3.mjs +189 -0
  48. package/dist/{chunk-TH4XENKH.mjs → chunk-XASDB7YS.mjs} +1 -1
  49. package/dist/{chunk-NYXA6X56.mjs → chunk-XBQ4LO2N.mjs} +31 -11
  50. package/dist/chunk-XF3MGADK.mjs +2946 -0
  51. package/dist/{chunk-5KYR6WJB.mjs → chunk-XQAHREKO.mjs} +17 -1
  52. package/dist/chunk-XZG3DTXV.mjs +696 -0
  53. package/dist/chunk-Y4YGR2NY.mjs +52 -0
  54. package/dist/{chunk-LJEKKFC7.mjs → chunk-YS2JUNSQ.mjs} +19 -11
  55. package/dist/chunk-ZG7NJLXN.mjs +104 -0
  56. package/dist/create-3TGG7B3X.mjs +10 -0
  57. package/dist/credits-MEG7V5DI.mjs +33 -0
  58. package/dist/{deploy-STWZ3OSM.mjs → deploy-CNRORHJ7.mjs} +11 -11
  59. package/dist/dev-RXSBY7U4.mjs +8 -0
  60. package/dist/dev-YLYC6B7R.mjs +401 -0
  61. package/dist/dispatcher.mjs +131 -30
  62. package/dist/{doctor-3RW3OGZJ.mjs → doctor-XRTNG2IT.mjs} +13 -21
  63. package/dist/{domains-FTNK4IKY.mjs → domains-JIHFKDWR.mjs} +6 -6
  64. package/dist/engine-runtime-fetch-QVKJVC3W.mjs +10 -0
  65. package/dist/{env-TNCYUAMT.mjs → env-LJ3DCAA5.mjs} +7 -7
  66. package/dist/env-R72AHBT2.mjs +19 -0
  67. package/dist/explain-32RSXEXC.mjs +251 -0
  68. package/dist/inspect-PYLYN45K.mjs +86 -0
  69. package/dist/{inspect-X3M7WSTL.mjs → inspect-ZOLZHNSZ.mjs} +28 -13
  70. package/dist/introspect-27MMTACH.mjs +9 -0
  71. package/dist/kernel-IC4ZC54Z.mjs +662 -0
  72. package/dist/kernel-plugin.js +6862 -0
  73. package/dist/keys-WKOQ4KQE.mjs +71 -0
  74. package/dist/lines-I6ZB2IOU.mjs +56 -0
  75. package/dist/{link-76Z6V6CJ.mjs → link-TE2FUX74.mjs} +6 -6
  76. package/dist/{lint-DDZ7HUQN.mjs → lint-735IPMC6.mjs} +2 -2
  77. package/dist/{list-2MFOQKKQ.mjs → list-CHYY2P4T.mjs} +7 -11
  78. package/dist/lock-XVR3RRZ6.mjs +57 -0
  79. package/dist/{login-RLMDEWAL.mjs → login-LW7CHZDZ.mjs} +10 -8
  80. package/dist/{logout-Z57X6HPE.mjs → logout-6U6OP5DM.mjs} +2 -2
  81. package/dist/{logs-TNDM4RFE.mjs → logs-HKH4H6PT.mjs} +5 -5
  82. package/dist/logs-SCQC5HNO.mjs +44 -0
  83. package/dist/mcp-VRHQOEGG.mjs +1077 -0
  84. package/dist/{mock-EVER34IX.mjs → mock-5H3CVPHC.mjs} +2 -2
  85. package/dist/outdated-T7QW6F3Z.mjs +58 -0
  86. package/dist/port-24R7ZNP4.mjs +2408 -0
  87. package/dist/{projects-YL4T24VV.mjs → projects-HYITVEXS.mjs} +6 -6
  88. package/dist/publish-DZRR5A5X.mjs +20 -0
  89. package/dist/remix-4HVXRIEB.mjs +125 -0
  90. package/dist/repo-FG3CMZQS.mjs +9 -0
  91. package/dist/report-M7F4ZYP7.mjs +2 -0
  92. package/dist/rollback-H7Q4XYGX.mjs +43 -0
  93. package/dist/{rollback-47VQ4HJK.mjs → rollback-OM64O67F.mjs} +6 -6
  94. package/dist/scene-QTHT3ZPL.mjs +324 -0
  95. package/dist/{status-TWPOMWNA.mjs → status-5D4RR3MM.mjs} +5 -5
  96. package/dist/status-AKI6E3JK.mjs +18 -0
  97. package/dist/test-W6MURUGZ.mjs +233 -0
  98. package/dist/testers-EI32JB5F.mjs +49 -0
  99. package/dist/{unlink-H3BO3DOQ.mjs → unlink-BND47IWW.mjs} +2 -2
  100. package/dist/{update-FLY46XI3.mjs → update-XKKFREKH.mjs} +4 -4
  101. package/dist/{whoami-CQPPARRS.mjs → whoami-WHLGCVRC.mjs} +3 -3
  102. package/dist/workshop-F2T4IDOU.mjs +1703 -0
  103. package/package.json +11 -7
  104. package/templates/storefront-auto/app/products/[slug]/page.tsx +1 -1
  105. package/templates/storefront-auto/package.json +1 -1
  106. package/templates/storefront-bakery/package.json +1 -1
  107. package/templates/storefront-fashion/app/products/[slug]/page.tsx +1 -1
  108. package/templates/storefront-fashion/package.json +1 -1
  109. package/templates/storefront-grocery/package.json +1 -1
  110. package/templates/storefront-pharmacy/app/products/[slug]/page.tsx +1 -1
  111. package/templates/storefront-pharmacy/package.json +1 -1
  112. package/templates/storefront-restaurant/package.json +1 -1
  113. package/templates/storefront-retail/app/products/[slug]/page.tsx +1 -1
  114. package/templates/storefront-retail/package.json +1 -1
  115. package/templates/storefront-services/package.json +1 -1
  116. package/dist/chunk-ARIDYKLO.mjs +0 -6463
  117. package/dist/dev-7O65HY6C.mjs +0 -8
  118. package/dist/explain-DLRUUCNS.mjs +0 -251
  119. package/dist/introspect-RC265ACF.mjs +0 -9
  120. package/dist/repo-UUT3AZ3L.mjs +0 -9
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import { framesFor } from './chunk-JTYVGORV.mjs';
3
+ import { loadScript, DEFAULT_DEVICE, BACKENDS, DEFAULT_SEED, DEFAULT_FRAMES, parseDevice, parseInsets, withInsets } from './chunk-SZXO6YSX.mjs';
4
+ import { flagString } from './chunk-XQAHREKO.mjs';
5
+ import { CliError, CLI_ERROR_CODE } from './chunk-I7GEHLDD.mjs';
6
+ import path from 'path';
7
+
8
+ function invalid(message, remediation) {
9
+ return new CliError(CLI_ERROR_CODE.INVALID_INPUT, message, remediation === void 0 ? void 0 : { remediation });
10
+ }
11
+ function deviceFrom(spec, safe) {
12
+ const parsed = parseDevice(spec);
13
+ if (typeof parsed === "string") throw invalid(parsed);
14
+ if (safe === void 0) return parsed;
15
+ const insets = parseInsets(safe);
16
+ if (typeof insets === "string") throw invalid(insets);
17
+ return withInsets(parsed, insets);
18
+ }
19
+ async function commonOptions(args, root) {
20
+ const scriptFlag = flagString(args, "script");
21
+ let script = null;
22
+ let scriptPath = null;
23
+ if (scriptFlag !== void 0) {
24
+ scriptPath = path.relative(root, path.resolve(root, scriptFlag)) || scriptFlag;
25
+ try {
26
+ script = await loadScript(path.resolve(root, scriptFlag));
27
+ } catch (error) {
28
+ throw invalid(error.message, 'An input script is JSON or TS: { steps: [{ wait: 30 }, { tap: [195, 600] }, { capture: "menu" }] }');
29
+ }
30
+ }
31
+ const device = deviceFrom(flagString(args, "device") ?? script?.devices?.[0] ?? DEFAULT_DEVICE, flagString(args, "safe"));
32
+ const backendFlag = flagString(args, "backend") ?? "swiftshader";
33
+ if (!BACKENDS.includes(backendFlag)) throw invalid(`--backend is one of ${BACKENDS.join(", ")}; got "${backendFlag}"`);
34
+ const seedFlag = flagString(args, "seed");
35
+ const seed = seedFlag === void 0 ? script?.seed ?? DEFAULT_SEED : Number(seedFlag);
36
+ if (!Number.isInteger(seed)) throw invalid(`--seed takes an integer; got "${seedFlag}"`);
37
+ const frame = framesFor(flagString(args, "time"), flagString(args, "frame"), DEFAULT_FRAMES);
38
+ if (typeof frame === "string") throw invalid(frame);
39
+ return { device, backend: backendFlag, seed, scene: flagString(args, "scene") ?? script?.scene ?? null, frame, script, scriptPath };
40
+ }
41
+
42
+ export { commonOptions };
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { promptYesNo } from './chunk-TH4XENKH.mjs';
3
- import { gitRemoteHead, managedProviderForHost } from './chunk-UTD34C2H.mjs';
4
- import { TOKEN_PURPOSE, REPO_PROVIDER, isManagedRepoProvider, REPO_PROVIDER_VALUES } from './chunk-2MKZKUVH.mjs';
5
- import { parseArgs, flagString, flagBool } from './chunk-5KYR6WJB.mjs';
6
- import { ApiClient } from './chunk-NYXA6X56.mjs';
7
- import { readAuth, readProjectLink } from './chunk-MLE4MVYC.mjs';
8
- import { CliError, CLI_ERROR_CODE, isJsonMode, result, dim, success, info, bold } from './chunk-3QUK5BPY.mjs';
2
+ import { promptYesNo } from './chunk-XASDB7YS.mjs';
3
+ import { gitRemoteHead, managedProviderForHost } from './chunk-EOOOXVHR.mjs';
4
+ import { TOKEN_PURPOSE, REPO_PROVIDER, isManagedRepoProvider, REPO_PROVIDER_VALUES } from './chunk-LLRUOIMU.mjs';
5
+ import { parseArgs, flagString, flagBool } from './chunk-XQAHREKO.mjs';
6
+ import { ApiClient } from './chunk-XBQ4LO2N.mjs';
7
+ import { readAuth, readProjectLink } from './chunk-GGPN4QYQ.mjs';
8
+ import { CliError, CLI_ERROR_CODE, isJsonMode, result, dim, success, info, bold } from './chunk-I7GEHLDD.mjs';
9
9
 
10
10
  // src/commands/repo.ts
11
11
  var SUB_PROVISION = "provision";
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import { locateToolchain, localEngineRuntimeDir, validManifest, engineCacheRoot, cachedVersion, storeRuntime, cachedEngineRuntimeDir } from './chunk-N2WZJX5P.mjs';
3
+ import { ApiClient } from './chunk-XBQ4LO2N.mjs';
4
+ import { readAuthOrNull } from './chunk-GGPN4QYQ.mjs';
5
+ import { isCliError, CLI_ERROR_CODE, info, dim, warn } from './chunk-I7GEHLDD.mjs';
6
+
7
+ // src/interactives/build/engine-runtime-fetch.ts
8
+ var ENGINE_RUNTIME_ROUTE = "/v1/interactives/engine/runtime";
9
+ var ENGINE_RUNTIME_FILES_ROUTE = `${ENGINE_RUNTIME_ROUTE}/files`;
10
+ var DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
11
+ async function signedInClient() {
12
+ const auth = await readAuthOrNull();
13
+ return auth === null ? null : ApiClient.fromAuth(auth);
14
+ }
15
+ async function ensureEngineRuntime(options) {
16
+ const sdkDir = locateToolchain(options.projectRoot)?.sdkDir ?? null;
17
+ const local = sdkDir === null ? null : localEngineRuntimeDir(sdkDir);
18
+ if (local !== null) return { source: "local", dir: local, version: null };
19
+ const client = options.client === void 0 ? await signedInClient() : options.client;
20
+ if (client === null) return { source: "signed_out", dir: null, version: null };
21
+ let manifest;
22
+ try {
23
+ manifest = await client.get(ENGINE_RUNTIME_ROUTE);
24
+ } catch (error) {
25
+ if (isCliError(error) && error.code === CLI_ERROR_CODE.NOT_FOUND) return { source: "unpublished", dir: null, version: null };
26
+ throw error;
27
+ }
28
+ if (!validManifest(manifest)) throw new Error("the registry returned an engine runtime manifest this CLI cannot use");
29
+ const root = options.root ?? engineCacheRoot();
30
+ const cached = cachedVersion(root, manifest.version);
31
+ if (cached !== null) return { source: "cached", dir: cached, version: manifest.version };
32
+ const download = (file) => client.getBytes(`${ENGINE_RUNTIME_FILES_ROUTE}/${file.sha256}`, { timeoutMs: DOWNLOAD_TIMEOUT_MS });
33
+ const dir = await storeRuntime(root, manifest, download);
34
+ return { source: "fetched", dir, version: manifest.version };
35
+ }
36
+ async function refreshEngineRuntime(projectRoot) {
37
+ try {
38
+ const fetched = await ensureEngineRuntime({ projectRoot });
39
+ if (fetched.source === "fetched") info(dim(`Downloaded the Cimplify Engine runtime ${fetched.version ?? ""}`));
40
+ } catch (error) {
41
+ if (cachedEngineRuntimeDir() !== null) return;
42
+ warn(`Could not update the Cimplify Engine runtime: ${error instanceof Error ? error.message : String(error)}`);
43
+ }
44
+ }
45
+
46
+ export { ENGINE_RUNTIME_FILES_ROUTE, ENGINE_RUNTIME_ROUTE, ensureEngineRuntime, refreshEngineRuntime };
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ // src/interactives/diagnostics.ts
3
+ function diagnostic(code, message, file, at, severity = "error") {
4
+ return { code, severity, message, file, line: at ? at.line : null, column: at ? at.column : null };
5
+ }
6
+ function hasErrors(diagnostics) {
7
+ for (let i = 0; i < diagnostics.length; i++) if (diagnostics[i].severity === "error") return true;
8
+ return false;
9
+ }
10
+ function formatDiagnostic(d) {
11
+ const where = d.file === null ? "" : d.line === null ? `${d.file}: ` : `${d.file}:${d.line}:${d.column}: `;
12
+ return `${where}${d.severity} ${d.code}: ${d.message}`;
13
+ }
14
+ function compareDiagnostics(a, b) {
15
+ const fa = a.file ?? "";
16
+ const fb = b.file ?? "";
17
+ if (fa !== fb) return fa < fb ? -1 : 1;
18
+ const la = a.line ?? 0;
19
+ const lb = b.line ?? 0;
20
+ if (la !== lb) return la - lb;
21
+ const ca = a.column ?? 0;
22
+ const cb = b.column ?? 0;
23
+ if (ca !== cb) return ca - cb;
24
+ if (a.code !== b.code) return a.code < b.code ? -1 : 1;
25
+ return a.message < b.message ? -1 : a.message > b.message ? 1 : 0;
26
+ }
27
+
28
+ export { compareDiagnostics, diagnostic, formatDiagnostic, hasErrors };
@@ -0,0 +1,502 @@
1
+ #!/usr/bin/env node
2
+ import { locateChromium, startDevServer, HARDWARE_GPU_ARGS, BROWSER_ARGS, probeScript, SILENT_PARAM, EMPTY_READINESS } from './chunk-XF3MGADK.mjs';
3
+ import { inspectSnapshot } from './chunk-THATLMEX.mjs';
4
+ import { inflateSync, deflateSync } from 'zlib';
5
+ import { promises } from 'fs';
6
+ import path from 'path';
7
+ import vm from 'vm';
8
+ import ts from 'typescript';
9
+
10
+ var GOLDEN = { mean: 1.5, outliers: 5e-3 };
11
+ var OUTLIER_DELTA = 24;
12
+ var SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
13
+ var CHANNELS = { 0: 1, 2: 3, 4: 2, 6: 4 };
14
+ var CRC_TABLE = (() => {
15
+ const table = new Uint32Array(256);
16
+ for (let n = 0; n < 256; n++) {
17
+ let c = n;
18
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
19
+ table[n] = c >>> 0;
20
+ }
21
+ return table;
22
+ })();
23
+ function crc32(bytes) {
24
+ let c = 4294967295;
25
+ for (let i = 0; i < bytes.length; i++) c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
26
+ return (c ^ 4294967295) >>> 0;
27
+ }
28
+ function paeth(a, b, c) {
29
+ const p = a + b - c;
30
+ const pa = Math.abs(p - a);
31
+ const pb = Math.abs(p - b);
32
+ const pc = Math.abs(p - c);
33
+ return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
34
+ }
35
+ function decodePng(data) {
36
+ const bytes = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
37
+ if (bytes.length < SIGNATURE.length || !bytes.subarray(0, SIGNATURE.length).equals(SIGNATURE)) throw new Error("not a PNG file");
38
+ let at = SIGNATURE.length;
39
+ let width = 0;
40
+ let height = 0;
41
+ let depth = 0;
42
+ let type = 0;
43
+ let interlace = 0;
44
+ const idat = [];
45
+ while (at + 8 <= bytes.length) {
46
+ const length = bytes.readUInt32BE(at);
47
+ const kind = bytes.toString("latin1", at + 4, at + 8);
48
+ const body = bytes.subarray(at + 8, at + 8 + length);
49
+ if (kind === "IHDR") {
50
+ width = body.readUInt32BE(0);
51
+ height = body.readUInt32BE(4);
52
+ depth = body[8];
53
+ type = body[9];
54
+ interlace = body[12];
55
+ } else if (kind === "IDAT") idat.push(body);
56
+ else if (kind === "IEND") break;
57
+ at += 12 + length;
58
+ }
59
+ const channels = CHANNELS[type];
60
+ if (depth !== 8 || channels === void 0 || interlace !== 0) throw new Error(`unsupported PNG (bit depth ${depth}, colour type ${type}, interlace ${interlace})`);
61
+ const raw = inflateSync(Buffer.concat(idat));
62
+ const stride = width * channels;
63
+ const rows = new Uint8Array(height * stride);
64
+ for (let y = 0; y < height; y++) {
65
+ const filter = raw[y * (stride + 1)];
66
+ const line = raw.subarray(y * (stride + 1) + 1, (y + 1) * (stride + 1));
67
+ for (let x = 0; x < stride; x++) {
68
+ const left = x >= channels ? rows[y * stride + x - channels] : 0;
69
+ const up = y > 0 ? rows[(y - 1) * stride + x] : 0;
70
+ const corner = x >= channels && y > 0 ? rows[(y - 1) * stride + x - channels] : 0;
71
+ const value = line[x];
72
+ const predicted = filter === 1 ? left : filter === 2 ? up : filter === 3 ? left + up >> 1 : filter === 4 ? paeth(left, up, corner) : 0;
73
+ rows[y * stride + x] = value + predicted & 255;
74
+ }
75
+ }
76
+ const pixels = new Uint8Array(width * height * 4);
77
+ for (let i = 0; i < width * height; i++) {
78
+ const s = i * channels;
79
+ const d = i * 4;
80
+ if (channels >= 3) {
81
+ pixels[d] = rows[s];
82
+ pixels[d + 1] = rows[s + 1];
83
+ pixels[d + 2] = rows[s + 2];
84
+ pixels[d + 3] = channels === 4 ? rows[s + 3] : 255;
85
+ } else {
86
+ pixels[d] = pixels[d + 1] = pixels[d + 2] = rows[s];
87
+ pixels[d + 3] = channels === 2 ? rows[s + 1] : 255;
88
+ }
89
+ }
90
+ return { width, height, pixels };
91
+ }
92
+ function chunk(kind, body) {
93
+ const head = Buffer.alloc(8);
94
+ head.writeUInt32BE(body.length, 0);
95
+ head.write(kind, 4, "latin1");
96
+ const tail = Buffer.alloc(4);
97
+ tail.writeUInt32BE(crc32(Buffer.concat([head.subarray(4), body])), 0);
98
+ return Buffer.concat([head, body, tail]);
99
+ }
100
+ function encodePng(image) {
101
+ const header = Buffer.alloc(13);
102
+ header.writeUInt32BE(image.width, 0);
103
+ header.writeUInt32BE(image.height, 4);
104
+ header[8] = 8;
105
+ header[9] = 6;
106
+ const stride = image.width * 4;
107
+ const raw = Buffer.alloc((stride + 1) * image.height);
108
+ for (let y = 0; y < image.height; y++) Buffer.from(image.pixels.buffer, image.pixels.byteOffset + y * stride, stride).copy(raw, y * (stride + 1) + 1);
109
+ return Buffer.concat([SIGNATURE, chunk("IHDR", header), chunk("IDAT", deflateSync(raw)), chunk("IEND", new Uint8Array(0))]);
110
+ }
111
+ var DIM = 0.25;
112
+ function compareImages(actual, expected, tolerance = GOLDEN) {
113
+ if (actual.width !== expected.width || actual.height !== expected.height) return { mean: Number.POSITIVE_INFINITY, outliers: 1, passed: false, size_matches: false, diff: null };
114
+ const texels = actual.width * actual.height;
115
+ const diff = new Uint8Array(texels * 4);
116
+ let total = 0;
117
+ let outliers = 0;
118
+ for (let i = 0; i < texels; i++) {
119
+ const at = i * 4;
120
+ let worst = 0;
121
+ for (let c = 0; c < 3; c++) {
122
+ const delta = Math.abs(actual.pixels[at + c] - expected.pixels[at + c]);
123
+ total += delta;
124
+ if (delta > worst) worst = delta;
125
+ }
126
+ const outlier = worst > OUTLIER_DELTA;
127
+ if (outlier) outliers++;
128
+ const grey = Math.round((expected.pixels[at] + expected.pixels[at + 1] + expected.pixels[at + 2]) / 3 * DIM);
129
+ diff[at] = outlier ? 255 : grey;
130
+ diff[at + 1] = outlier ? 0 : grey;
131
+ diff[at + 2] = outlier ? 255 : grey;
132
+ diff[at + 3] = 255;
133
+ }
134
+ const mean = total / (texels * 3);
135
+ const fraction = outliers / texels;
136
+ return { mean, outliers: fraction, passed: mean <= tolerance.mean && fraction <= tolerance.outliers, size_matches: true, diff: { width: actual.width, height: actual.height, pixels: diff } };
137
+ }
138
+ var SCRIPT_SCHEMA = "cimplify.input/1";
139
+ var DEFAULT_DRAG_FRAMES = 12;
140
+ var DEFAULT_HOLD_FRAMES = 1;
141
+ function isRecord(value) {
142
+ return typeof value === "object" && value !== null && !Array.isArray(value);
143
+ }
144
+ function frames(value, where) {
145
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) throw new Error(`${where} must be a whole number of frames, 0 or more`);
146
+ return value;
147
+ }
148
+ function isPoint(value) {
149
+ return Array.isArray(value) && value.length === 2 && value.every((v) => typeof v === "number" && Number.isFinite(v));
150
+ }
151
+ function point(value, where) {
152
+ if (isPoint(value)) return [value[0], value[1]];
153
+ throw new Error(`${where} must be [x, y] in points`);
154
+ }
155
+ function target(value, where) {
156
+ if (isPoint(value)) return [value[0], value[1]];
157
+ if (isRecord(value)) {
158
+ if (typeof value.node === "number" && Number.isInteger(value.node)) return { node: value.node };
159
+ if (typeof value.name === "string" && value.name.length > 0) return { name: value.name };
160
+ if (typeof value.text === "string" && value.text.length > 0) return { text: value.text };
161
+ }
162
+ throw new Error(`${where} must be [x, y] in points, { node: id }, { name: "variable" } or { text: "shown text" }`);
163
+ }
164
+ function step(value, index) {
165
+ const where = `steps[${index}]`;
166
+ if (!isRecord(value)) throw new Error(`${where} must be an object`);
167
+ const keys = Object.keys(value);
168
+ if ("wait" in value) return { wait: frames(value.wait, `${where}.wait`) };
169
+ if ("wait_ms" in value) {
170
+ if (typeof value.wait_ms !== "number" || value.wait_ms < 0) throw new Error(`${where}.wait_ms must be 0 or more milliseconds`);
171
+ return { wait_ms: value.wait_ms };
172
+ }
173
+ if ("tap" in value) return { tap: target(value.tap, `${where}.tap`), hold: value.hold === void 0 ? DEFAULT_HOLD_FRAMES : frames(value.hold, `${where}.hold`) };
174
+ if ("drag" in value) {
175
+ const drag = value.drag;
176
+ if (!isRecord(drag)) throw new Error(`${where}.drag must be { from, to, frames?, path? }`);
177
+ const from = target(drag.from, `${where}.drag.from`);
178
+ const to = target(drag.to, `${where}.drag.to`);
179
+ if (drag.path !== void 0) {
180
+ if (!Array.isArray(drag.path) || drag.path.length === 0) throw new Error(`${where}.drag.path must be a non-empty array of [x, y] points, one per frame`);
181
+ const path2 = drag.path.map((p, i) => point(p, `${where}.drag.path[${i}]`));
182
+ if (drag.frames !== void 0 && drag.frames !== path2.length) throw new Error(`${where}.drag.frames must equal the path length (${path2.length})`);
183
+ return { drag: { from, to, frames: path2.length, path: path2 } };
184
+ }
185
+ return { drag: { from, to, frames: drag.frames === void 0 ? DEFAULT_DRAG_FRAMES : Math.max(1, frames(drag.frames, `${where}.drag.frames`)) } };
186
+ }
187
+ if ("key" in value) {
188
+ if (typeof value.key !== "string" || value.key.length === 0) throw new Error(`${where}.key must be a key name such as "Space" or "ArrowLeft"`);
189
+ return { key: value.key, hold: value.hold === void 0 ? DEFAULT_HOLD_FRAMES : frames(value.hold, `${where}.hold`) };
190
+ }
191
+ if ("down" in value) return { down: point(value.down, `${where}.down`) };
192
+ if ("move" in value) return { move: point(value.move, `${where}.move`) };
193
+ if ("up" in value) {
194
+ if (value.up !== true) throw new Error(`${where}.up must be true`);
195
+ return { up: true };
196
+ }
197
+ if ("key_down" in value || "key_up" in value) {
198
+ const down = "key_down" in value;
199
+ const key = down ? value.key_down : value.key_up;
200
+ if (typeof key !== "string" || key.length === 0) throw new Error(`${where}.${down ? "key_down" : "key_up"} must be a key name such as "Space" or "KeyW"`);
201
+ return down ? { key_down: key } : { key_up: key };
202
+ }
203
+ if ("text" in value) {
204
+ if (typeof value.text !== "string") throw new Error(`${where}.text must be the text to type`);
205
+ return { text: value.text };
206
+ }
207
+ if ("type" in value) {
208
+ if (typeof value.type !== "string") throw new Error(`${where}.type must be the text to type`);
209
+ return { type: value.type };
210
+ }
211
+ if ("capture" in value) {
212
+ if (typeof value.capture !== "string" || !/^[a-z0-9][a-z0-9_-]*$/.test(value.capture)) throw new Error(`${where}.capture must be a name of lowercase letters, digits, - and _`);
213
+ return { capture: value.capture };
214
+ }
215
+ throw new Error(`${where} has no known action (${keys.join(", ")}); use wait, wait_ms, tap, drag, key, type, capture, down, move, up, key_down, key_up or text`);
216
+ }
217
+ function parseScript(value) {
218
+ const body = Array.isArray(value) ? { steps: value } : value;
219
+ if (!isRecord(body)) throw new Error("an input script is an array of steps or { steps: [...] }");
220
+ if (!Array.isArray(body.steps)) throw new Error("an input script needs a steps array");
221
+ const devices = body.devices ?? body.device;
222
+ const list = devices === void 0 ? null : Array.isArray(devices) ? devices : [devices];
223
+ if (list !== null && !list.every((d) => typeof d === "string")) throw new Error("devices must be device names");
224
+ if (body.seed !== void 0 && (typeof body.seed !== "number" || !Number.isInteger(body.seed))) throw new Error("seed must be an integer");
225
+ if (body.scene !== void 0 && typeof body.scene !== "string") throw new Error("scene must be a scene name");
226
+ if (body.max_stalls !== void 0 && (typeof body.max_stalls !== "number" || !Number.isInteger(body.max_stalls) || body.max_stalls < 0)) {
227
+ throw new Error("max_stalls must be a whole number of frames, 0 or more");
228
+ }
229
+ return {
230
+ devices: list,
231
+ seed: body.seed ?? null,
232
+ scene: body.scene ?? null,
233
+ steps: body.steps.map(step),
234
+ max_stalls: body.max_stalls ?? null
235
+ };
236
+ }
237
+ function evaluateModule(source, file) {
238
+ const js = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 }, fileName: file }).outputText;
239
+ const module = { exports: {} };
240
+ const context = vm.createContext({ module, exports: module.exports });
241
+ vm.runInContext(js, context, { filename: file, timeout: 1e3 });
242
+ const exported = module.exports;
243
+ return "default" in exported ? exported.default : exported;
244
+ }
245
+ async function loadScript(file) {
246
+ const text = await promises.readFile(file, "utf8");
247
+ const ext = path.extname(file);
248
+ try {
249
+ const value = ext === ".json" ? JSON.parse(text) : evaluateModule(text, file);
250
+ return parseScript(value);
251
+ } catch (error) {
252
+ throw new Error(`${file}: ${error.message}`);
253
+ }
254
+ }
255
+
256
+ // src/interactives/inspect/session.ts
257
+ var FRAME_MS = 1e3 / 60;
258
+ var DEFAULT_SEED = 1;
259
+ var DEFAULT_EPOCH_MS = Date.UTC(2026, 0, 1);
260
+ var DEFAULT_FRAMES = 60;
261
+ var BACKENDS = ["swiftshader", "hardware"];
262
+ var READY_TIMEOUT_MS = 6e4;
263
+ var POLL_MS = 100;
264
+ var LOCAL_HOST = "127.0.0.1";
265
+ var ANY_PORT = 0;
266
+ var MAX_NODES = 4096;
267
+ var ERRORS_SELECTOR = "#errors";
268
+ async function playStep(step2, driver) {
269
+ const mouse = driver.mouse;
270
+ const keyboard = driver.keyboard;
271
+ if ("wait" in step2) await driver.step(step2.wait);
272
+ else if ("wait_ms" in step2) await driver.step(Math.round(step2.wait_ms / FRAME_MS));
273
+ else if ("tap" in step2) {
274
+ const [x, y] = await driver.resolve(step2.tap);
275
+ await mouse.move(x, y);
276
+ await mouse.down();
277
+ await driver.step(step2.hold ?? DEFAULT_HOLD_FRAMES);
278
+ await mouse.up();
279
+ await driver.step(1);
280
+ } else if ("drag" in step2) {
281
+ const from = await driver.resolve(step2.drag.from);
282
+ const to = await driver.resolve(step2.drag.to);
283
+ const path2 = step2.drag.path;
284
+ const frames2 = path2?.length ?? step2.drag.frames ?? DEFAULT_DRAG_FRAMES;
285
+ await mouse.move(from[0], from[1]);
286
+ await mouse.down();
287
+ await driver.step(1);
288
+ for (let i = 1; i <= frames2; i++) {
289
+ if (path2 !== void 0) await mouse.move(path2[i - 1][0], path2[i - 1][1]);
290
+ else await mouse.move(from[0] + (to[0] - from[0]) * i / frames2, from[1] + (to[1] - from[1]) * i / frames2);
291
+ await driver.step(1);
292
+ }
293
+ await mouse.up();
294
+ await driver.step(1);
295
+ } else if ("key" in step2) {
296
+ await keyboard.down(step2.key);
297
+ await driver.step(step2.hold ?? DEFAULT_HOLD_FRAMES);
298
+ await keyboard.up(step2.key);
299
+ await driver.step(1);
300
+ } else if ("type" in step2) {
301
+ await keyboard.type(step2.type);
302
+ await driver.step(1);
303
+ } else if ("down" in step2) {
304
+ await mouse.move(step2.down[0], step2.down[1]);
305
+ await mouse.down();
306
+ } else if ("move" in step2) await mouse.move(step2.move[0], step2.move[1]);
307
+ else if ("up" in step2) await mouse.up();
308
+ else if ("key_down" in step2) await keyboard.down(step2.key_down);
309
+ else if ("key_up" in step2) await keyboard.up(step2.key_up);
310
+ else if ("text" in step2) await keyboard.type(step2.text);
311
+ else await driver.capture(step2.capture);
312
+ }
313
+ var SessionError = class extends Error {
314
+ };
315
+ var CaptureSession = class _CaptureSession {
316
+ constructor(options, server, browser, context, page) {
317
+ this.options = options;
318
+ this.server = server;
319
+ this.browser = browser;
320
+ this.context = context;
321
+ this.page = page;
322
+ }
323
+ options;
324
+ server;
325
+ browser;
326
+ context;
327
+ page;
328
+ guest = null;
329
+ frame = 0;
330
+ cssSafeArea = false;
331
+ static async open(setup, options) {
332
+ const chromium = locateChromium([setup.toolchain.base, setup.toolchain.sdkDir]);
333
+ if (typeof chromium === "string") throw new SessionError(chromium);
334
+ const server = await startDevServer({ root: setup.root, host: LOCAL_HOST, port: ANY_PORT, watch: false, build: setup.build, packagesRoot: setup.toolchain.base });
335
+ let browser = null;
336
+ try {
337
+ browser = await chromium.launch({ headless: true, args: options.backend === "hardware" ? HARDWARE_GPU_ARGS : BROWSER_ARGS });
338
+ const device = options.device;
339
+ const context = await browser.newContext({ viewport: { width: device.width, height: device.height }, deviceScaleFactor: device.scale, hasTouch: device.touch });
340
+ await context.addInitScript({
341
+ content: probeScript({ seed: options.seed, epoch: DEFAULT_EPOCH_MS, frame_ms: FRAME_MS, insets: device.insets, bare: true, scene: options.scene, max_nodes: MAX_NODES })
342
+ });
343
+ const page = await context.newPage();
344
+ const session = new _CaptureSession(options, server, browser, context, page);
345
+ await session.overrideSafeArea();
346
+ await page.goto(`${server.url}/?${SILENT_PARAM}`);
347
+ await session.waitReady();
348
+ return session;
349
+ } catch (error) {
350
+ await browser?.close();
351
+ await server.close();
352
+ throw error;
353
+ }
354
+ }
355
+ async overrideSafeArea() {
356
+ const insets = this.options.device.insets;
357
+ try {
358
+ const cdp = await this.context.newCDPSession(this.page);
359
+ await cdp.send("Emulation.setSafeAreaInsetsOverride", {
360
+ insets: { top: insets.top, topMax: insets.top, right: insets.right, rightMax: insets.right, bottom: insets.bottom, bottomMax: insets.bottom, left: insets.left, leftMax: insets.left }
361
+ });
362
+ this.cssSafeArea = true;
363
+ } catch {
364
+ this.cssSafeArea = false;
365
+ }
366
+ }
367
+ async pageErrors() {
368
+ return (await this.page.locator(ERRORS_SELECTOR).textContent() ?? "").trim();
369
+ }
370
+ async waitReady() {
371
+ const deadline = Date.now() + READY_TIMEOUT_MS;
372
+ while (Date.now() < deadline) {
373
+ for (const frame of this.page.frames()) {
374
+ const state = await frame.evaluate(() => {
375
+ const probe = globalThis.__cimplify_probe;
376
+ if (probe === void 0 || !probe.guest) return { ready: false, refused: null };
377
+ return { ready: probe.ready(), refused: probe.refused() };
378
+ }, void 0).catch(() => ({ ready: false, refused: null }));
379
+ if (state.refused !== null) throw new SessionError(state.refused);
380
+ if (state.ready) {
381
+ this.guest = frame;
382
+ return;
383
+ }
384
+ }
385
+ const errors = await this.pageErrors();
386
+ if (errors.length > 0) throw new SessionError(`the game failed to start: ${errors}`);
387
+ await new Promise((resolve) => setTimeout(resolve, POLL_MS));
388
+ }
389
+ throw new SessionError(`the game did not start its engine within ${READY_TIMEOUT_MS / 1e3} s`);
390
+ }
391
+ get frameHandle() {
392
+ if (this.guest === null) throw new SessionError("the game frame is not ready");
393
+ return this.guest;
394
+ }
395
+ async step(frames2) {
396
+ if (frames2 <= 0) return;
397
+ const frame = this.frameHandle;
398
+ try {
399
+ this.frame = await frame.evaluate((n) => globalThis.__cimplify_probe.step(n), frames2);
400
+ } catch (error) {
401
+ const refused = await frame.evaluate(() => globalThis.__cimplify_probe.refused(), void 0).catch(() => null);
402
+ if (refused !== null) throw new SessionError(refused);
403
+ throw error;
404
+ }
405
+ }
406
+ async advanceTo(frame) {
407
+ await this.step(frame - this.frame);
408
+ }
409
+ async loopSamples() {
410
+ return this.frameHandle.evaluate(() => {
411
+ const probe = globalThis.__cimplify_loop_probe;
412
+ return probe?.samples?.() ?? {};
413
+ }, void 0).catch(() => ({}));
414
+ }
415
+ async readiness() {
416
+ return this.frameHandle.evaluate(() => {
417
+ const probe = globalThis.__cimplify_readiness_probe;
418
+ return probe?.report?.() ?? null;
419
+ }, void 0).then((report) => report ?? EMPTY_READINESS).catch(() => EMPTY_READINESS);
420
+ }
421
+ async snapshot() {
422
+ return this.frameHandle.evaluate(() => globalThis.__cimplify_probe.snapshot(), void 0);
423
+ }
424
+ driver() {
425
+ const frame = this.frameHandle;
426
+ return {
427
+ open: () => frame.evaluate(() => globalThis.__cimplify_probe.shadowOpen(), void 0),
428
+ apply: (patch) => frame.evaluate((p) => globalThis.__cimplify_probe.shadowApply(p), patch),
429
+ frame: (dt, full) => frame.evaluate(([d, f]) => globalThis.__cimplify_probe.shadowFrame(d, f), [dt, full]),
430
+ close: () => frame.evaluate(() => globalThis.__cimplify_probe.shadowClose(), void 0)
431
+ };
432
+ }
433
+ async screenshot() {
434
+ await this.page.mainFrame().evaluate(
435
+ () => new Promise((resolve) => {
436
+ const scope = globalThis;
437
+ scope.requestAnimationFrame(() => scope.requestAnimationFrame(() => resolve()));
438
+ }),
439
+ void 0
440
+ );
441
+ return this.page.screenshot({ type: "png" });
442
+ }
443
+ async inspect(png) {
444
+ const screenshot = png === null ? null : decodePng(png);
445
+ return inspectSnapshot(await this.snapshot(), this.driver(), this.options.device, screenshot);
446
+ }
447
+ async resolve(target2) {
448
+ if (Array.isArray(target2)) return target2;
449
+ const { report } = await this.inspect(null);
450
+ const visible = report.nodes.filter((n) => n.visible && n.rect !== null);
451
+ const t = target2;
452
+ const match = t.node !== void 0 ? visible.find((n) => n.id === t.node) : t.name !== void 0 ? visible.find((n) => n.name === t.name) : visible.find((n) => n.text === t.text) ?? visible.find((n) => n.text !== null && t.text !== void 0 && n.text.includes(t.text));
453
+ if (match === void 0 || match.rect === null) throw new SessionError(`no visible node matches ${JSON.stringify(target2)} at frame ${this.frame}`);
454
+ return [match.rect.x + match.rect.width / 2, match.rect.y + match.rect.height / 2];
455
+ }
456
+ async perform(step2, onCapture) {
457
+ await playStep(step2, { mouse: this.page.mouse, keyboard: this.page.keyboard, step: (frames2) => this.step(frames2), resolve: (target2) => this.resolve(target2), capture: onCapture });
458
+ }
459
+ async run(script, onCapture) {
460
+ for (const step2 of script.steps) await this.perform(step2, onCapture);
461
+ }
462
+ async close() {
463
+ await this.page.close().catch(() => {
464
+ });
465
+ await this.context.close().catch(() => {
466
+ });
467
+ await this.browser.close().catch(() => {
468
+ });
469
+ await this.server.close();
470
+ }
471
+ };
472
+
473
+ // src/interactives/inspect/devices.ts
474
+ var NO_INSETS = { top: 0, right: 0, bottom: 0, left: 0 };
475
+ var DEVICES = {
476
+ "phone-portrait": { name: "phone-portrait", width: 390, height: 844, scale: 3, insets: { top: 47, right: 0, bottom: 34, left: 0 }, touch: true },
477
+ "phone-landscape": { name: "phone-landscape", width: 844, height: 390, scale: 3, insets: { top: 0, right: 47, bottom: 21, left: 47 }, touch: true },
478
+ "phone-small": { name: "phone-small", width: 375, height: 667, scale: 2, insets: { top: 20, right: 0, bottom: 0, left: 0 }, touch: true },
479
+ tablet: { name: "tablet", width: 820, height: 1180, scale: 2, insets: { top: 24, right: 0, bottom: 20, left: 0 }, touch: true },
480
+ "tablet-landscape": { name: "tablet-landscape", width: 1180, height: 820, scale: 2, insets: { top: 24, right: 0, bottom: 20, left: 0 }, touch: true }
481
+ };
482
+ var DEFAULT_DEVICE = "phone-portrait";
483
+ var CHECK_DEVICES = ["phone-portrait", "phone-landscape", "tablet"];
484
+ var CUSTOM = /^(\d{2,5})x(\d{2,5})(?:@(\d(?:\.\d+)?))?$/;
485
+ function parseDevice(spec) {
486
+ const known = DEVICES[spec];
487
+ if (known !== void 0) return known;
488
+ const match = CUSTOM.exec(spec);
489
+ if (match === null) return `unknown device "${spec}"; use one of ${Object.keys(DEVICES).join(", ")} or WIDTHxHEIGHT[@SCALE]`;
490
+ const scale = match[3] === void 0 ? 1 : Number(match[3]);
491
+ return { name: spec, width: Number(match[1]), height: Number(match[2]), scale, insets: NO_INSETS, touch: false };
492
+ }
493
+ function withInsets(device, insets) {
494
+ return { ...device, insets };
495
+ }
496
+ function parseInsets(spec) {
497
+ const parts = spec.split(",").map((p) => Number(p.trim()));
498
+ if (parts.length !== 4 || parts.some((p) => !Number.isFinite(p) || p < 0)) return `--safe takes four non-negative numbers, top,right,bottom,left; got "${spec}"`;
499
+ return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[3] };
500
+ }
501
+
502
+ export { BACKENDS, CHECK_DEVICES, CaptureSession, DEFAULT_DEVICE, DEFAULT_DRAG_FRAMES, DEFAULT_EPOCH_MS, DEFAULT_FRAMES, DEFAULT_HOLD_FRAMES, DEFAULT_SEED, DEVICES, FRAME_MS, GOLDEN, SCRIPT_SCHEMA, SessionError, compareImages, decodePng, encodePng, loadScript, parseDevice, parseInsets, withInsets };