@ait-co/devtools 0.1.113 → 0.1.116

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 (71) hide show
  1. package/dist/bundle-KFs4t-wc.d.ts.map +1 -1
  2. package/dist/cli-BLBo0lkP.js +2466 -0
  3. package/dist/cli-BLBo0lkP.js.map +1 -0
  4. package/dist/debug-server-DcAKrbnq.js +921 -0
  5. package/dist/debug-server-DcAKrbnq.js.map +1 -0
  6. package/dist/debug-server-DlXJARIC.js +8171 -0
  7. package/dist/debug-server-DlXJARIC.js.map +1 -0
  8. package/dist/mcp/cli.js +3 -7771
  9. package/dist/mcp/cli.js.map +1 -1
  10. package/dist/mcp/server.js +11 -2
  11. package/dist/mcp/server.js.map +1 -1
  12. package/dist/panel/index.js +7 -1
  13. package/dist/panel/index.js.map +1 -1
  14. package/dist/{pool-mZlgCmNQ.d.ts → pool-htlVnEFl.d.ts} +2 -2
  15. package/dist/{pool-mZlgCmNQ.d.ts.map → pool-htlVnEFl.d.ts.map} +1 -1
  16. package/dist/{qr-http-server-BVS-HZjU.cjs → qr-http-server-BpTvfeku.cjs} +92 -8
  17. package/dist/qr-http-server-BpTvfeku.cjs.map +1 -0
  18. package/dist/{qr-http-server-BJJt3ush.js → qr-http-server-BsWlOA85.js} +92 -8
  19. package/dist/qr-http-server-BsWlOA85.js.map +1 -0
  20. package/dist/{qr-http-server-C1T4RNbq.cjs → qr-http-server-CrVKno9V.cjs} +92 -8
  21. package/dist/qr-http-server-CrVKno9V.cjs.map +1 -0
  22. package/dist/{qr-http-server-Cs93vEPH.js → qr-http-server-UBV2qUEd.js} +92 -8
  23. package/dist/qr-http-server-UBV2qUEd.js.map +1 -0
  24. package/dist/relay-secret-store-BHcOmaNK.js +3 -0
  25. package/dist/relay-secret-store-CkA7KNUb.js +154 -0
  26. package/dist/{relay-secret-store-DhzAnnj-.js.map → relay-secret-store-CkA7KNUb.js.map} +1 -1
  27. package/dist/{relay-secret-store-DhzAnnj-.js → relay-secret-store-CmqchhR5.js} +3 -3
  28. package/dist/relay-secret-store-CmqchhR5.js.map +1 -0
  29. package/dist/{relay-url-store-CwKT7i04.js → relay-url-store-C0qukm3R.js} +2 -2
  30. package/dist/{relay-url-store-CwKT7i04.js.map → relay-url-store-C0qukm3R.js.map} +1 -1
  31. package/dist/relay-url-store-NDtEcOE-.js +123 -0
  32. package/dist/relay-url-store-NDtEcOE-.js.map +1 -0
  33. package/dist/{relay-worker-Dppp2yZj.d.ts → relay-worker-DWW4vZxU.d.ts} +2 -2
  34. package/dist/{relay-worker-Dppp2yZj.d.ts.map → relay-worker-DWW4vZxU.d.ts.map} +1 -1
  35. package/dist/runtime-BU-iMHz6.d.ts +192 -0
  36. package/dist/runtime-BU-iMHz6.d.ts.map +1 -0
  37. package/dist/test-runner/bundle.js +72 -2
  38. package/dist/test-runner/bundle.js.map +1 -1
  39. package/dist/test-runner/cli.d.ts +19 -7
  40. package/dist/test-runner/cli.d.ts.map +1 -1
  41. package/dist/test-runner/cli.js +1 -550
  42. package/dist/test-runner/config.d.ts +1 -1
  43. package/dist/test-runner/pool.d.ts +1 -1
  44. package/dist/test-runner/relay-worker.d.ts +1 -1
  45. package/dist/test-runner/rpc.d.ts +1 -1
  46. package/dist/test-runner/runtime.d.ts +2 -2
  47. package/dist/test-runner/runtime.js +407 -21
  48. package/dist/test-runner/runtime.js.map +1 -1
  49. package/dist/test-runner/task-graph.d.ts +1 -1
  50. package/dist/totp-D104cJQN.js +3 -0
  51. package/dist/{totp-WY6l0ysP.js → totp-D70zD5tJ.js} +1 -1
  52. package/dist/{totp-WY6l0ysP.js.map → totp-D70zD5tJ.js.map} +1 -1
  53. package/dist/totp-DvOYkYim.js +212 -0
  54. package/dist/totp-DvOYkYim.js.map +1 -0
  55. package/dist/{tunnel-Cpn3mA4u.js → tunnel-B7U5k1xa.js} +2 -2
  56. package/dist/{tunnel-Cpn3mA4u.js.map → tunnel-B7U5k1xa.js.map} +1 -1
  57. package/dist/{tunnel-Dj8Kf2QS.cjs → tunnel-C-cgMnyY.cjs} +2 -2
  58. package/dist/{tunnel-Dj8Kf2QS.cjs.map → tunnel-C-cgMnyY.cjs.map} +1 -1
  59. package/dist/unplugin/index.cjs +1 -1
  60. package/dist/unplugin/index.js +1 -1
  61. package/dist/unplugin/tunnel.cjs +1 -1
  62. package/dist/unplugin/tunnel.js +1 -1
  63. package/package.json +1 -1
  64. package/dist/qr-http-server-BJJt3ush.js.map +0 -1
  65. package/dist/qr-http-server-BVS-HZjU.cjs.map +0 -1
  66. package/dist/qr-http-server-C1T4RNbq.cjs.map +0 -1
  67. package/dist/qr-http-server-Cs93vEPH.js.map +0 -1
  68. package/dist/runtime-C7uxh3Mf.d.ts +0 -62
  69. package/dist/runtime-C7uxh3Mf.d.ts.map +0 -1
  70. package/dist/test-runner/cli.js.map +0 -1
  71. package/dist/totp-D1pulXLa.js +0 -3
@@ -1 +1 @@
1
- {"version":3,"file":"relay-worker-Dppp2yZj.d.ts","names":[],"sources":["../src/test-runner/relay-worker.ts"],"mappings":";;;;;;UAuBiB,UAAA;EAcf;EAZA,IAAA;EAcA;EAZA,MAAA,EAAQ,SAAA;IAAc,KAAA;EAAA;AAAA;;UAIP,cAAA;EAiBA;EAff,SAAA;;EAEA,QAAA;EAiBA;EAfA,KAAA,EAAO,UAAA;EAoBP;EAlBA,MAAA;IACE,MAAA;IACA,MAAA;IACA,OAAA;IACA,KAAA;EAAA;AAAA;;UAKa,eAAA;EAqCP;;;EAjCR,aAAA,GAAgB,aAAA;EA+BhB;;;;EA1BA,SAAA;AAAA;;AAoFF;;;;;;;;;;;;;;;;;;;;iBA5DsB,qBAAA,CACpB,UAAA,EAAY,aAAA,EACZ,KAAA,YACA,IAAA,GAAO,eAAA,GACN,OAAA,CAAQ,cAAA;;;;;iBAwDK,cAAA,CAAe,MAAA,EAAQ,cAAA,GAAiB,KAAA,CAAM,UAAA;EAAe,IAAA;AAAA"}
1
+ {"version":3,"file":"relay-worker-DWW4vZxU.d.ts","names":[],"sources":["../src/test-runner/relay-worker.ts"],"mappings":";;;;;;UAuBiB,UAAA;EAcf;EAZA,IAAA;EAcA;EAZA,MAAA,EAAQ,SAAA;IAAc,KAAA;EAAA;AAAA;;UAIP,cAAA;EAiBA;EAff,SAAA;;EAEA,QAAA;EAiBA;EAfA,KAAA,EAAO,UAAA;EAoBP;EAlBA,MAAA;IACE,MAAA;IACA,MAAA;IACA,OAAA;IACA,KAAA;EAAA;AAAA;;UAKa,eAAA;EAqCP;;;EAjCR,aAAA,GAAgB,aAAA;EA+BhB;;;;EA1BA,SAAA;AAAA;;AAoFF;;;;;;;;;;;;;;;;;;;;iBA5DsB,qBAAA,CACpB,UAAA,EAAY,aAAA,EACZ,KAAA,YACA,IAAA,GAAO,eAAA,GACN,OAAA,CAAQ,cAAA;;;;;iBAwDK,cAAA,CAAe,MAAA,EAAQ,cAAA,GAAiB,KAAA,CAAM,UAAA;EAAe,IAAA;AAAA"}
@@ -0,0 +1,192 @@
1
+ //#region src/test-runner/runtime.d.ts
2
+ /**
3
+ * Thin test runtime for WebView execution.
4
+ *
5
+ * This file is bundled by `bundle.ts` together with the user's test file
6
+ * into a single IIFE injected into the WebView via `Runtime.evaluate`.
7
+ * It MUST stay browser-compatible — no Node.js APIs allowed.
8
+ *
9
+ * Design: rather than shipping @vitest/runner verbatim into a WebView (where
10
+ * its Node-side internals cause issues), this runtime provides a minimal
11
+ * compatible API surface — describe/it/test/expect globals — collects results
12
+ * into a plain JSON-safe object, and exports `runTestModule` as the entry point.
13
+ *
14
+ * @vitest/runner and @vitest/expect are listed as dependencies so that the
15
+ * package's type contracts are available and so the browser-compatible subsets
16
+ * can be referenced. The Vitest custom pool that drives this runtime through
17
+ * Vitest's `PoolRunnerInitializer` lives in `pool.ts`.
18
+ *
19
+ * NOTE: this file is imported by type from Node-side code (rpc.ts / relay-worker.ts)
20
+ * for the RunReport / TestResult type shapes. The runtime ITSELF is not imported
21
+ * at runtime on the Node side — only the types are used.
22
+ */
23
+ /**
24
+ * Result of a single test case.
25
+ * All fields are JSON-serialisable.
26
+ */
27
+ interface TestResult {
28
+ /** Full dot-joined test name including nested suite names. */
29
+ name: string;
30
+ /** `'pass'` or `'fail'`. `'skip'` for skipped tests. */
31
+ status: 'pass' | 'fail' | 'skip';
32
+ /** Duration in milliseconds. */
33
+ duration: number;
34
+ /** Error message (fail only). Does NOT include the expression/secret. */
35
+ error?: string;
36
+ }
37
+ /** Aggregate report returned by `runTestModule`. */
38
+ interface RunReport {
39
+ /** ISO timestamp of when `runTestModule` was called. */
40
+ startedAt: string;
41
+ /** Total elapsed milliseconds (wall-clock). */
42
+ duration: number;
43
+ passed: number;
44
+ failed: number;
45
+ skipped: number;
46
+ tests: TestResult[];
47
+ }
48
+ /**
49
+ * Brand property that tags asymmetric matcher markers (`expect.any(String)`,
50
+ * `expect.objectContaining(...)`, etc). A reserved string key is used as the
51
+ * brand so the guard stays a plain own-property check — markers are only ever
52
+ * compared in-process by reference, so no cross-realm Symbol coordination is
53
+ * needed.
54
+ */
55
+ declare const ASYMMETRIC_BRAND: "$$asymmetricMatch";
56
+ /**
57
+ * An asymmetric matcher: a placeholder that, when found on the `expected` side
58
+ * of a deep comparison, runs its own `asymmetricMatch(actual)` predicate
59
+ * instead of being structurally compared as a plain object. This is what makes
60
+ * `toMatchObject({ hash: expect.any(String) })` match any string `hash`.
61
+ */
62
+ interface AsymmetricMatcher {
63
+ /** Brand discriminator — present only on asymmetric markers. */
64
+ [ASYMMETRIC_BRAND]: true;
65
+ /** Human-readable label used in failure messages. */
66
+ toString(): string;
67
+ /** Returns true when `actual` satisfies this matcher. */
68
+ asymmetricMatch(actual: unknown): boolean;
69
+ }
70
+ /** The widest constructor type TypeScript allows without `any` (see toBeInstanceOf). */
71
+ type AnyCtor = abstract new (...args: never) => unknown;
72
+ /** Minimal expect builder compatible with Vitest's jest-style API. */
73
+ declare class Expectation {
74
+ #private;
75
+ constructor(received: unknown);
76
+ get not(): this;
77
+ toBe(expected: unknown): void;
78
+ toEqual(expected: unknown): void;
79
+ toBeTruthy(): void;
80
+ toBeFalsy(): void;
81
+ toBeNull(): void;
82
+ toBeUndefined(): void;
83
+ toBeGreaterThan(n: number): void;
84
+ toBeLessThan(n: number): void;
85
+ toContain(sub: string): void;
86
+ toThrow(msgFragment?: string): void;
87
+ toMatchObject(expected: Record<string, unknown>): void;
88
+ toHaveProperty(dotPath: string, ...rest: unknown[]): void;
89
+ toBeInstanceOf(ctor: abstract new (...args: never) => unknown): void;
90
+ toBeTypeOf(typeStr: string): void;
91
+ }
92
+ /**
93
+ * The `expect` callable plus its asymmetric-matcher static surface
94
+ * (`expect.any`, `expect.objectContaining`, …). The user file accesses these as
95
+ * `expect.any(String)`; the vitest redirect (`bundle.ts`) re-exports `expect` by
96
+ * reading `globalThis.expect`, so attaching the statics to this function object
97
+ * makes them reachable through the getter (a function's own properties survive
98
+ * the getter that returns the function itself).
99
+ */
100
+ interface ExpectStatic {
101
+ (received: unknown): Expectation;
102
+ any(ctor: AnyCtor): AsymmetricMatcher;
103
+ anything(): AsymmetricMatcher;
104
+ objectContaining(expected: Record<string, unknown>): AsymmetricMatcher;
105
+ arrayContaining(expected: unknown[]): AsymmetricMatcher;
106
+ stringContaining(sub: string): AsymmetricMatcher;
107
+ stringMatching(re: string | RegExp): AsymmetricMatcher;
108
+ }
109
+ /** Registers a suite scope; calls `fn` synchronously to collect inner tests. */
110
+ declare function describe(name: string, fn: () => void): void;
111
+ declare namespace describe {
112
+ var skip: (name: string, _fn: () => void) => void;
113
+ var skipIf: (cond: unknown) => DescribeRegistrar;
114
+ var runIf: (cond: unknown) => DescribeRegistrar;
115
+ }
116
+ /** Registers a test. */
117
+ declare function it(name: string, fn: () => void | Promise<void>): void;
118
+ declare namespace it {
119
+ var skip: (name: string, _fn?: () => void | Promise<void>) => void;
120
+ var skipIf: (cond: unknown) => ItRegistrar;
121
+ var runIf: (cond: unknown) => ItRegistrar;
122
+ }
123
+ /**
124
+ * `it.skipIf(cond)(name, fn)` / `it.runIf(cond)(name, fn)` — conditional test
125
+ * registration. `skipIf` skips when `cond` is truthy; `runIf` runs only when
126
+ * `cond` is truthy (skips otherwise). sdk-example uses
127
+ * `it.skipIf(cell.platform === 'mock')(...)` to skip real-SDK-only cases in env1.
128
+ */
129
+ type ItRegistrar = (name: string, fn: () => void | Promise<void>) => void;
130
+ /**
131
+ * `describe.skipIf(cond)(name, fn)` / `describe.runIf(cond)(name, fn)`.
132
+ * When skipped, the suite body still runs to register its tests but every test
133
+ * inside is marked skipped (collected via a temporary skip flag is overkill —
134
+ * a skipped describe simply does not invoke its body, mirroring `describe.skip`).
135
+ */
136
+ type DescribeRegistrar = (name: string, fn: () => void) => void;
137
+ declare function beforeAll(fn: () => void | Promise<void>): void;
138
+ declare function afterAll(fn: () => void | Promise<void>): void;
139
+ declare function beforeEach(fn: () => void | Promise<void>): void;
140
+ declare function afterEach(fn: () => void | Promise<void>): void;
141
+ interface SpyCall {
142
+ args: unknown[];
143
+ returnValue: unknown;
144
+ }
145
+ interface MockFn<T extends unknown[], R> {
146
+ (...args: T): R;
147
+ mock: {
148
+ calls: SpyCall[];
149
+ };
150
+ mockImplementation(fn: (...args: T) => R): this;
151
+ mockReturnValue(value: R): this;
152
+ mockRestore(): void;
153
+ }
154
+ /**
155
+ * Runtime globals object — exported for direct use in unit tests so that
156
+ * test factories can reference runtime's own `it`/`expect`/`beforeAll`/etc.
157
+ * without depending on `globalThis` injection.
158
+ *
159
+ * In a real WebView bundle these are accessed via globals installed by
160
+ * `runTestModule`; in Node tests, import from here directly.
161
+ */
162
+ declare const runtimeGlobals: {
163
+ readonly describe: typeof describe;
164
+ readonly it: typeof it;
165
+ readonly test: typeof it;
166
+ readonly expect: ExpectStatic;
167
+ readonly beforeAll: typeof beforeAll;
168
+ readonly afterAll: typeof afterAll;
169
+ readonly beforeEach: typeof beforeEach;
170
+ readonly afterEach: typeof afterEach;
171
+ readonly vi: {
172
+ /** Creates a spy on `obj[method]`, replacing it with a mock function. */spyOn<T extends Record<string, unknown>, K extends keyof T>(obj: T, method: K): MockFn<unknown[], unknown>; /** Creates a standalone mock function, optionally wrapping `impl`. */
173
+ fn<T extends unknown[], R>(impl?: (...args: T) => R): MockFn<T, R>; /** Restores all spies created via `vi.spyOn` to their original values. */
174
+ restoreAllMocks(): void;
175
+ };
176
+ };
177
+ /**
178
+ * Installs describe/it/test/expect/afterAll/afterEach/beforeAll/beforeEach/vi
179
+ * as globals, invokes `moduleFactory` to register the user's tests, then
180
+ * executes them and returns a RunReport.
181
+ *
182
+ * This function is exported as `__testBundle.runTestModule` by the IIFE wrapper
183
+ * that bundle.ts generates. The Node-side rpc.ts calls it via `Runtime.evaluate`.
184
+ *
185
+ * @param moduleFactory - A zero-argument function that contains the user's
186
+ * top-level test code (describe/it/test calls). The bundler wraps the entire
187
+ * test module so that its top-level statements become the body of this factory.
188
+ */
189
+ declare function runTestModule(moduleFactory?: () => void | Promise<void>): Promise<RunReport>;
190
+ //#endregion
191
+ export { runtimeGlobals as i, TestResult as n, runTestModule as r, RunReport as t };
192
+ //# sourceMappingURL=runtime-BU-iMHz6.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-BU-iMHz6.d.ts","names":[],"sources":["../src/test-runner/runtime.ts"],"mappings":";;AA8BA;;;;;;;;;;AAYA;;;;;;;;;;;;;AASC;UArBgB,UAAA;;EAEf,IAAA;EAgCmD;EA9BnD,MAAA;EAsCyB;EApCzB,QAAA;EAsCiB;EApCjB,KAAA;AAAA;;UAIe,SAAA;EAoCgB;EAlC/B,SAAA;EAgDG;EA9CH,QAAA;EACA,MAAA;EACA,MAAA;EACA,OAAA;EACA,KAAA,EAAO,UAAA;AAAA;;;;;;;;cAcH,gBAAA;;;;;;;UAQI,iBAAA;EA8RQ;EAAA,CA5Rf,gBAAA;EAmSY;EAjSb,QAAA;EAwSU;EAtSV,eAAA,CAAgB,MAAA;AAAA;;KAcb,OAAA,oBAA2B,IAAA;;cAoN1B,WAAA;EAAA;cAIQ,QAAA;EAAA,IAIR,GAAA,CAAA;EAaJ,IAAA,CAAK,QAAA;EAOL,OAAA,CAAQ,QAAA;EAOR,UAAA,CAAA;EAIA,SAAA,CAAA;EAIA,QAAA,CAAA;EAIA,aAAA,CAAA;EAOA,eAAA,CAAgB,CAAA;EAOhB,YAAA,CAAa,CAAA;EAOb,SAAA,CAAU,GAAA;EAOV,OAAA,CAAQ,WAAA;EAwBR,aAAA,CAAc,QAAA,EAAU,MAAA;EAOxB,cAAA,CAAe,OAAA,aAAoB,IAAA;EAiBnC,cAAA,CAAe,IAAA,mBAAuB,IAAA;EAOtC,UAAA,CAAW,OAAA;AAAA;;;;;;;;;UAgBH,YAAA;EAAA,CACP,QAAA,YAAoB,WAAA;EACrB,GAAA,CAAI,IAAA,EAAM,OAAA,GAAU,iBAAA;EACpB,QAAA,IAAY,iBAAA;EACZ,gBAAA,CAAiB,QAAA,EAAU,MAAA,oBAA0B,iBAAA;EACrD,eAAA,CAAgB,QAAA,cAAsB,iBAAA;EACtC,gBAAA,CAAiB,GAAA,WAAc,iBAAA;EAC/B,cAAA,CAAe,EAAA,WAAa,MAAA,GAAS,iBAAA;AAAA;;iBA8B9B,QAAA,CAAS,IAAA,UAAc,EAAA;AAAA,kBAAvB,QAAA;EAAA,yBAeoB,GAAA;EAAA,+BAgCM,iBAAA;EAAA,8BAED,iBAAA;AAAA;;iBA1CzB,EAAA,CAAG,IAAA,UAAc,EAAA,eAAiB,OAAA;AAAA,kBAAlC,EAAA;EAAA,yBAWc,GAAA,gBAAqB,OAAA;EAAA,+BAiBf,WAAA;EAAA,8BACD,WAAA;AAAA;;;;;AApCkB;;KA6BzC,WAAA,IAAe,IAAA,UAAc,EAAA,eAAiB,OAAA;;;;;;;KAiB9C,iBAAA,IAAqB,IAAA,UAAc,EAAA;AAAA,iBAuD/B,SAAA,CAAU,EAAA,eAAiB,OAAA;AAAA,iBAG3B,QAAA,CAAS,EAAA,eAAiB,OAAA;AAAA,iBAG1B,UAAA,CAAW,EAAA,eAAiB,OAAA;AAAA,iBAG5B,SAAA,CAAU,EAAA,eAAiB,OAAA;AAAA,UAQ1B,OAAA;EACR,IAAA;EACA,WAAA;AAAA;AAAA,UAGQ,MAAA;EAAA,IACJ,IAAA,EAAM,CAAA,GAAI,CAAA;EACd,IAAA;IAAQ,KAAA,EAAO,OAAA;EAAA;EACf,kBAAA,CAAmB,EAAA,MAAQ,IAAA,EAAM,CAAA,KAAM,CAAA;EACvC,eAAA,CAAgB,KAAA,EAAO,CAAA;EACvB,WAAA;AAAA;;;;;;;;;cAqFW,cAAA;EAAA;;;;;;;;;IAxLG,yFA4IE,MAAA,mCAAyC,CAAA,EAAC,GAAA,EACnD,CAAA,EAAC,MAAA,EACE,CAAA,GACP,MAAA,sBA/IqD;+BA+J/B,IAAA,OAAa,IAAA,EAAM,CAAA,KAAM,CAAA,GAAI,MAAA,CAAO,CAAA,EAAG,CAAA,GA/Jf;;;;AAAa;;;;;AAiBV;;;;;AAuDL;;AAxEe,iBAgN1C,aAAA,CACpB,aAAA,gBAA6B,OAAA,SAC5B,OAAA,CAAQ,SAAA"}
@@ -57,12 +57,31 @@ import { fileURLToPath } from "node:url";
57
57
  *
58
58
  * SECRET-HANDLING: the returned bundle code is caller-managed; never log it.
59
59
  */
60
+ /** The SDK package name that mini-app test code imports from. */
61
+ const SDK_PACKAGE = "@apps-in-toss/web-framework";
62
+ /**
63
+ * Names the runtime installs as globals before invoking the user factory.
64
+ * The `vitest` virtual module re-exports each as a lazy getter that reads from
65
+ * `globalThis` at access time. Keep in sync with the globals installed in
66
+ * `runtime.ts#runTestModule`.
67
+ */
68
+ const VITEST_GLOBAL_NAMES = [
69
+ "describe",
70
+ "it",
71
+ "test",
72
+ "expect",
73
+ "beforeAll",
74
+ "afterAll",
75
+ "beforeEach",
76
+ "afterEach",
77
+ "vi"
78
+ ];
60
79
  /**
61
80
  * Matches the bare SDK package and any sub-path import
62
81
  * (`@apps-in-toss/web-framework`, `@apps-in-toss/web-framework/foo`).
63
82
  * Built from {@link SDK_PACKAGE} so the package name has a single source.
64
83
  */
65
- const SDK_IMPORT_FILTER = new RegExp(`^${"@apps-in-toss/web-framework".replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`);
84
+ const SDK_IMPORT_FILTER = new RegExp(`^${SDK_PACKAGE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`);
66
85
  /**
67
86
  * esbuild plugin that intercepts SDK imports and redirects them to the
68
87
  * `window.__sdk` proxy that `src/in-app/auto.ts` installs at runtime.
@@ -103,6 +122,53 @@ module.exports = __proxy;
103
122
  };
104
123
  }
105
124
  /**
125
+ * esbuild plugin that intercepts `import … from 'vitest'` and replaces it with
126
+ * a virtual module that delegates every named import to `globalThis` at ACCESS
127
+ * time (not at bundle-evaluation time).
128
+ *
129
+ * The runtime installs `describe/it/test/expect/beforeAll/afterAll/beforeEach/
130
+ * afterEach/vi` as globals inside `runTestModule`, which runs AFTER the bundle
131
+ * IIFE is evaluated. A value-copy redirect (`export var describe =
132
+ * globalThis.describe`) would therefore capture `undefined` at evaluation time
133
+ * and the user's `describe(...)` calls would be no-ops — registering zero tests.
134
+ *
135
+ * The fix defers the lookup to call time using per-name **getter** exports.
136
+ * We emit a CommonJS module that:
137
+ * 1. sets `__esModule = true` so esbuild's `__toESM` interop maps each named
138
+ * import directly to a property access on the module (NOT wrapped under a
139
+ * `default` shim — which is what happens for a bare Proxy whose own-keys
140
+ * are empty, leaving every named import `undefined`);
141
+ * 2. defines each global name as a getter that reads `globalThis[name]` on
142
+ * every access. So `import { describe } from 'vitest'` compiles to
143
+ * `import_vitest.describe`, whose getter returns the real `describe` only
144
+ * when the factory calls it — after `runTestModule` installs the globals.
145
+ *
146
+ * A plain `module.exports = new Proxy(...)` does NOT work here: esbuild routes
147
+ * the virtual module through `__toESM`, which enumerates own-keys (none on an
148
+ * empty Proxy target) and therefore exposes zero named exports. Explicit getter
149
+ * properties give `__toESM` real keys to map while keeping access lazy.
150
+ */
151
+ function vitestRedirectPlugin() {
152
+ return {
153
+ name: "vitest-redirect",
154
+ setup(build) {
155
+ build.onResolve({ filter: /^vitest$/ }, () => ({
156
+ path: "vitest",
157
+ namespace: "vitest-redirect"
158
+ }));
159
+ build.onLoad({
160
+ filter: /^vitest$/,
161
+ namespace: "vitest-redirect"
162
+ }, () => {
163
+ return {
164
+ contents: `Object.defineProperty(exports, '__esModule', { value: true });\n${VITEST_GLOBAL_NAMES.map((name) => `Object.defineProperty(exports, ${JSON.stringify(name)}, { enumerable: true, get: function() { return globalThis[${JSON.stringify(name)}]; } });`).join("\n")}\n`,
165
+ loader: "js"
166
+ };
167
+ });
168
+ }
169
+ };
170
+ }
171
+ /**
106
172
  * esbuild plugin that transforms the user test file into a module that exports
107
173
  * an async `__userFactory` function. The factory defers the user's top-level
108
174
  * test registration code (describe/it/test calls) so it only runs when
@@ -249,7 +315,11 @@ async function bundleTestFile(absPath, opts) {
249
315
  platform: "browser",
250
316
  target: "es2022",
251
317
  write: false,
252
- plugins: [userFactoryPlugin(absPath), sdkRedirectPlugin()],
318
+ plugins: [
319
+ userFactoryPlugin(absPath),
320
+ vitestRedirectPlugin(),
321
+ sdkRedirectPlugin()
322
+ ],
253
323
  external: extraExternals,
254
324
  treeShaking: true,
255
325
  footer: { js: `globalThis[${JSON.stringify(globalName)}] = ${globalName};` }
@@ -1 +1 @@
1
- {"version":3,"file":"bundle.js","names":[],"sources":["../../src/test-runner/bundle.ts"],"sourcesContent":["/**\n * esbuild-based bundler for user test files.\n *\n * Bundles a single test file into a self-contained IIFE string that can be\n * injected into a WebView via `Runtime.evaluate`. The bundle includes the\n * test runtime (`runtime.ts`), which provides `describe/it/test/expect` and\n * the `runTestModule(factory)` entry point.\n *\n * ## How the wiring works\n *\n * The bundle exposes two exports on `globalThis.__testBundle`:\n * - `runTestModule` — the runtime's entry function.\n * - `__userFactory` — an async function whose body is the user's top-level\n * test registration code (describe/it/test calls).\n *\n * The Node-side RPC (`rpc.ts`) calls:\n * `globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory)`\n *\n * `runTestModule` then installs `describe/it/test/expect` as globals, invokes\n * the factory (which registers all tests), runs them, and returns a `RunReport`.\n *\n * ## Why a factory wrapper is needed\n *\n * Naively adding the runtime to `entryPoints` and bundling the user file would\n * fail for two reasons:\n * 1. `describe/it/test/expect` from the runtime are module-local in the IIFE\n * scope. The user's top-level `describe(...)` calls expect them as globals —\n * they are not globals until `runTestModule` installs them.\n * 2. Even with globals pre-installed, the user file runs at IIFE-evaluation\n * time, before the RPC layer calls `runTestModule` to reset state and start\n * the test clock.\n *\n * The factory approach solves both: the user's registration code is deferred\n * into a function that `runTestModule` calls AFTER installing the globals.\n *\n * ## Factory extraction algorithm\n *\n * The `userFactoryPlugin` reads the user file and splits lines into:\n * - **top-level**: `import …` and re-export lines — kept at module scope\n * (the only valid position for static `import` in ESM).\n * - **body**: all other statements — moved into the body of the exported\n * `__userFactory` async function.\n *\n * esbuild processes the re-generated module, following each static import\n * through the normal dependency graph (including the SDK-redirect plugin).\n *\n * ## SDK redirect\n *\n * Imports of `@apps-in-toss/web-framework` (and sub-paths) are intercepted via\n * the `sdkRedirectPlugin` and replaced with a virtual `window.__sdk` proxy that\n * `src/in-app/auto.ts` installs at runtime. This works for both 2.x and 3.x SDK.\n *\n * SECRET-HANDLING: the returned bundle code is caller-managed; never log it.\n */\n\nimport { accessSync } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n// esbuild is imported for TYPES only at module scope; the runtime module is\n// loaded lazily inside `bundleTestFile` via dynamic import. esbuild runs a\n// startup invariant check (`TextEncoder().encode('') instanceof Uint8Array`)\n// that fails in a jsdom realm — a static import would break every MCP/test\n// module that merely *imports* this file's transitive graph (e.g. debug-server →\n// run_tests). Lazy load keeps esbuild off the import graph until a bundle is\n// actually built, and mirrors the cloudflared/chii dynamic-import precedent.\nimport type * as esbuild from 'esbuild';\n\n/** Options accepted by `bundleTestFile`. */\nexport interface BundleOptions {\n /**\n * Additional esbuild `external` patterns. The SDK package\n * (`@apps-in-toss/web-framework` and `@apps-in-toss/web-framework/*`) is\n * always handled by the SDK redirect plugin — callers may add more patterns\n * to be left as globals.\n */\n extraExternals?: string[];\n /**\n * Global name for the IIFE output object. Defaults to `__testBundle`.\n * The runtime entry uses this to call `__testBundle.runTestModule(__userFactory)`.\n */\n globalName?: string;\n}\n\n/**\n * The result of bundling a test file.\n * `code` is a self-contained IIFE string ready for `Runtime.evaluate`.\n */\nexport interface BundleResult {\n code: string;\n warnings: string[];\n}\n\n/** The SDK package name that mini-app test code imports from. */\nconst SDK_PACKAGE = '@apps-in-toss/web-framework';\n\n/**\n * Matches the bare SDK package and any sub-path import\n * (`@apps-in-toss/web-framework`, `@apps-in-toss/web-framework/foo`).\n * Built from {@link SDK_PACKAGE} so the package name has a single source.\n */\nconst SDK_IMPORT_FILTER = new RegExp(`^${SDK_PACKAGE.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}`);\n\n/**\n * esbuild plugin that intercepts SDK imports and redirects them to the\n * `window.__sdk` proxy that `src/in-app/auto.ts` installs at runtime.\n *\n * Strategy: for every import of `@apps-in-toss/web-framework` (or sub-paths),\n * esbuild resolves it to a virtual module that re-exports all named exports\n * via `window.__sdk[name]`. This avoids bundling the real SDK (which may not\n * be available in the test environment) while still making named imports work.\n *\n * If `window.__sdk` is absent (non-dog-food build), every access throws a\n * descriptive error rather than returning `undefined` silently.\n */\nfunction sdkRedirectPlugin(): esbuild.Plugin {\n return {\n name: 'sdk-redirect',\n setup(build) {\n // Match the bare package and any sub-path imports\n build.onResolve({ filter: SDK_IMPORT_FILTER }, (args) => ({\n path: args.path,\n namespace: 'sdk-redirect',\n }));\n\n build.onLoad({ filter: /.*/, namespace: 'sdk-redirect' }, () => ({\n // Generate a virtual CommonJS-style module so that esbuild does NOT perform\n // strict named-export matching. When `format:'iife'` bundles a CJS module,\n // it wraps it with its own __toCommonJS helper and satisfies named imports\n // via property access on the module.exports object — which is our Proxy.\n // This means `import { getPlatformOS } from '...'` becomes\n // `__proxy.getPlatformOS` at runtime, which correctly reads from window.__sdk.\n contents: `\nvar __proxy = (typeof window !== 'undefined' && window.__sdk)\n ? window.__sdk\n : new Proxy({}, {\n get: function(_t, p) {\n throw new Error('window.__sdk is not installed — run in a dog-food build. Missing: ' + String(p));\n }\n });\nmodule.exports = __proxy;\n`,\n loader: 'js',\n }));\n },\n };\n}\n\n/**\n * esbuild plugin that transforms the user test file into a module that exports\n * an async `__userFactory` function. The factory defers the user's top-level\n * test registration code (describe/it/test calls) so it only runs when\n * `runTestModule(__userFactory)` explicitly invokes it — AFTER the runtime has\n * installed describe/it/test/expect as globals.\n *\n * Algorithm:\n * - Import declarations and re-export statements are kept at module top-level\n * (the only valid ESM position for static `import`). A statement that spans\n * multiple lines — e.g. a named import with one member per line:\n * import {\n * appLogin,\n * getAnonymousKey,\n * } from '@apps-in-toss/web-framework';\n * is tracked as a single block: every line from the opening `import {` /\n * `export {` through the closing `from '…'` (or side-effect `'…'`) line is\n * kept together at top-level. This prevents the member lines and the\n * closing `} from '…'` line from leaking into the factory body, which would\n * leave an unterminated `import {` at module scope (the #678 env3 failure:\n * esbuild threw `Expected \"as\" but found \"{\"` on multi-line SDK imports).\n * - All other lines (describe/it/test calls, local declarations, etc.) are\n * moved into the body of the exported async factory function.\n *\n * This preserves SDK import resolution (the sdk-redirect plugin processes\n * top-level imports normally) while deferring test registration to the factory.\n */\nfunction userFactoryPlugin(absPath: string): esbuild.Plugin {\n const NAMESPACE = 'user-test-factory';\n return {\n name: 'user-test-factory',\n setup(build) {\n // Resolve the virtual \"user-test-factory\" specifier to our namespace.\n build.onResolve({ filter: /^user-test-factory$/ }, () => ({\n path: absPath,\n namespace: NAMESPACE,\n }));\n\n // Load the user file, split imports from body, wrap body in the factory.\n build.onLoad({ filter: /.*/, namespace: NAMESPACE }, async (args) => {\n const source = await fs.readFile(args.path, 'utf8');\n const lines = source.split('\\n');\n\n const topLevelLines: string[] = [];\n const bodyLines: string[] = [];\n\n // Matches `export` value declarations that cannot appear inside a\n // function body. We strip the `export` keyword so they become plain\n // declarations inside the factory.\n const EXPORT_DECLARATION_RE =\n /^(export\\s+)(default\\s+|async\\s+function\\s+|function\\s+|class\\s+|const\\s+|let\\s+|var\\s+)/;\n\n // True when `trimmed` begins a static `import` statement.\n const isImportStart = (trimmed: string): boolean =>\n trimmed.startsWith('import ') ||\n trimmed.startsWith('import{') ||\n trimmed.startsWith(\"import'\") ||\n trimmed.startsWith('import\"');\n\n // A module-scope `import`/`export … from` statement is \"complete\" on a\n // single line when it ends with a quoted module specifier (optionally\n // followed by `;`/whitespace), e.g. `… from '@x';` or side-effect\n // `import './x';`. A line ending in `{` or `,` (the common multi-line\n // named-import shape) is therefore NOT complete and must accumulate\n // further lines until the closing `from '…'` line.\n const endsStatement = (trimmed: string): boolean =>\n /['\"]\\s*;?\\s*$/.test(trimmed.replace(/\\/\\/.*$/, '').trimEnd());\n\n // When set, we are inside an unterminated multi-line import/re-export\n // block: every subsequent line stays at top level until the block ends.\n let inImportBlock = false;\n\n for (const line of lines) {\n const trimmed = line.trimStart();\n const indent = line.slice(0, line.length - trimmed.length);\n\n // Continuation of a multi-line import / re-export block — keep at\n // top level. The block ends on the line that terminates the\n // statement (closing `from '…'` / side-effect `'…'`).\n if (inImportBlock) {\n topLevelLines.push(line);\n if (endsStatement(trimmed)) {\n inImportBlock = false;\n }\n continue;\n }\n\n // Static import declarations must stay at module top level\n // (the ESM spec forbids `import` inside a function body). If the\n // statement does not terminate on this line, open an import block so\n // the member lines and closing `} from '…'` line stay top-level too.\n if (isImportStart(trimmed)) {\n topLevelLines.push(line);\n if (!endsStatement(trimmed)) {\n inImportBlock = true;\n }\n } else if (trimmed.startsWith('export ')) {\n // Determine whether this is a re-export (stays top-level) or a value\n // declaration (goes into the factory, export keyword stripped).\n const m = trimmed.match(EXPORT_DECLARATION_RE);\n if (m) {\n // Value declaration — strip `export ` and move into factory body.\n // e.g. `export function hello()` → `function hello()`\n // `export const x = 1` → `const x = 1`\n bodyLines.push(indent + trimmed.slice('export '.length));\n } else {\n // Re-export or `export type { … }` — stays at top level. A\n // multi-line `export { … } from '…'` opens an import block too.\n topLevelLines.push(line);\n if (/\\bfrom\\b/.test(trimmed) ? !endsStatement(trimmed) : trimmed.endsWith('{')) {\n inImportBlock = true;\n }\n }\n } else {\n bodyLines.push(line);\n }\n }\n\n const factoryContent = [\n ...topLevelLines,\n '',\n '// biome-ignore lint: generated factory wrapper',\n 'export default async function __userFactory(): Promise<void> {',\n ...bodyLines.map((l) => ` ${l}`),\n '}',\n ].join('\\n');\n\n return {\n contents: factoryContent,\n loader: 'ts',\n resolveDir: path.dirname(absPath),\n };\n });\n },\n };\n}\n\n/**\n * Returns the absolute path to the test-runner runtime module.\n *\n * Searches candidates in priority order:\n * 1. Co-located `runtime.ts` / `runtime.js` — covers the source tree\n * (tsx / ts-node) and the `dist/test-runner/` entry.\n * 2. `../test-runner/runtime.js` — covers the `dist/mcp/cli.js` entry,\n * where `import.meta.url` resolves to `dist/mcp/` (a sibling directory\n * of `dist/test-runner/`). Without this second candidate the MCP entry\n * point would look for `dist/mcp/runtime.js`, which does not exist, and\n * every `run_tests` call would fail with an esbuild \"Could not resolve\"\n * error (#678).\n *\n * Returns the first candidate that exists on disk. Falls back to the\n * co-located `runtime.js` path so esbuild produces a clear \"file not found\"\n * error rather than a cryptic failure.\n */\nfunction getRuntimePath(): string {\n const dir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.join(dir, 'runtime.ts'),\n path.join(dir, 'runtime.js'),\n path.join(dir, '..', 'test-runner', 'runtime.js'),\n ];\n for (const candidate of candidates) {\n try {\n accessSync(candidate);\n return candidate;\n } catch {\n // try next candidate\n }\n }\n // Let esbuild produce a \"file not found\" error with a clear path.\n return path.join(dir, 'runtime.js');\n}\n\n/**\n * Bundles `absPath` into a single IIFE string suitable for `Runtime.evaluate`.\n *\n * The IIFE installs `window.__testBundle` (or the custom `globalName`) with:\n * - `runTestModule` — the runtime entry (from `runtime.ts`).\n * - `__userFactory` — an async function wrapping the user's test registration\n * code so it runs AFTER `runTestModule` installs the globals.\n *\n * Callers (rpc.ts) invoke:\n * `globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory)`\n *\n * @param absPath - Absolute path to the user test file.\n * @param opts - Optional bundling overrides.\n */\nexport async function bundleTestFile(absPath: string, opts?: BundleOptions): Promise<BundleResult> {\n const globalName = opts?.globalName ?? '__testBundle';\n const extraExternals = opts?.extraExternals ?? [];\n\n // Lazy load esbuild at call time (see the module-scope import note).\n const esbuild = await import('esbuild');\n const runtimePath = getRuntimePath();\n\n // Stdin wrapper: import the runtime and the user factory, re-export both.\n // esbuild follows the static imports to include runtime.ts and the user file\n // (via the userFactoryPlugin) in the single IIFE output.\n const wrapperContent = [\n `import { runTestModule } from ${JSON.stringify(runtimePath)};`,\n `import __userFactory from \"user-test-factory\";`,\n `export { runTestModule, __userFactory };`,\n ].join('\\n');\n\n const result = await esbuild.build({\n stdin: {\n contents: wrapperContent,\n loader: 'ts',\n // resolveDir is used for relative imports from the wrapper. Since the\n // wrapper only imports absolute paths (runtimePath) and the virtual\n // \"user-test-factory\" specifier (resolved by plugin), the directory\n // doesn't matter — but we still provide a sensible default.\n resolveDir: path.dirname(absPath),\n },\n bundle: true,\n format: 'iife',\n globalName,\n platform: 'browser',\n target: 'es2022',\n write: false,\n plugins: [userFactoryPlugin(absPath), sdkRedirectPlugin()],\n external: extraExternals,\n treeShaking: true,\n // Ensure the IIFE result is always reachable via globalThis regardless of\n // the evaluation context. esbuild's `globalName` emits:\n // var __testBundle = (() => { ... })();\n // When `Runtime.evaluate` runs this bundle code inside an outer wrapper\n // (rpc.ts's async IIFE), `var` creates a local variable — NOT a global\n // property — so `globalThis.__testBundle` stays `undefined`. The footer\n // explicitly assigns the local variable to `globalThis` to close that gap.\n footer: {\n js: `globalThis[${JSON.stringify(globalName)}] = ${globalName};`,\n },\n });\n\n const warnings = result.warnings.map(\n (w) =>\n `${path.relative(process.cwd(), w.location?.file ?? '')}:${w.location?.line ?? '?'}: ${w.text}`,\n );\n\n const outputFile = result.outputFiles?.[0];\n if (!outputFile) {\n throw new Error('bundleTestFile: esbuild produced no output — check entryPoints');\n }\n\n return { code: outputFile.text, warnings };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqGA,MAAM,oBAAoB,IAAI,OAAO,IAPjB,8BAOiC,QAAQ,uBAAuB,OAAO,GAAG;;;;;;;;;;;;;AAc9F,SAAS,oBAAoC;AAC3C,QAAO;EACL,MAAM;EACN,MAAM,OAAO;AAEX,SAAM,UAAU,EAAE,QAAQ,mBAAmB,GAAG,UAAU;IACxD,MAAM,KAAK;IACX,WAAW;IACZ,EAAE;AAEH,SAAM,OAAO;IAAE,QAAQ;IAAM,WAAW;IAAgB,SAAS;IAO/D,UAAU;;;;;;;;;;IAUV,QAAQ;IACT,EAAE;;EAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BH,SAAS,kBAAkB,SAAiC;CAC1D,MAAM,YAAY;AAClB,QAAO;EACL,MAAM;EACN,MAAM,OAAO;AAEX,SAAM,UAAU,EAAE,QAAQ,uBAAuB,SAAS;IACxD,MAAM;IACN,WAAW;IACZ,EAAE;AAGH,SAAM,OAAO;IAAE,QAAQ;IAAM,WAAW;IAAW,EAAE,OAAO,SAAS;IAEnE,MAAM,SADS,MAAM,GAAG,SAAS,KAAK,MAAM,OAAO,EAC9B,MAAM,KAAK;IAEhC,MAAM,gBAA0B,EAAE;IAClC,MAAM,YAAsB,EAAE;IAK9B,MAAM,wBACJ;IAGF,MAAM,iBAAiB,YACrB,QAAQ,WAAW,UAAU,IAC7B,QAAQ,WAAW,UAAU,IAC7B,QAAQ,WAAW,UAAU,IAC7B,QAAQ,WAAW,WAAU;IAQ/B,MAAM,iBAAiB,YACrB,gBAAgB,KAAK,QAAQ,QAAQ,WAAW,GAAG,CAAC,SAAS,CAAC;IAIhE,IAAI,gBAAgB;AAEpB,SAAK,MAAM,QAAQ,OAAO;KACxB,MAAM,UAAU,KAAK,WAAW;KAChC,MAAM,SAAS,KAAK,MAAM,GAAG,KAAK,SAAS,QAAQ,OAAO;AAK1D,SAAI,eAAe;AACjB,oBAAc,KAAK,KAAK;AACxB,UAAI,cAAc,QAAQ,CACxB,iBAAgB;AAElB;;AAOF,SAAI,cAAc,QAAQ,EAAE;AAC1B,oBAAc,KAAK,KAAK;AACxB,UAAI,CAAC,cAAc,QAAQ,CACzB,iBAAgB;gBAET,QAAQ,WAAW,UAAU,CAItC,KADU,QAAQ,MAAM,sBAAsB,CAK5C,WAAU,KAAK,SAAS,QAAQ,MAAM,EAAiB,CAAC;UACnD;AAGL,oBAAc,KAAK,KAAK;AACxB,UAAI,WAAW,KAAK,QAAQ,GAAG,CAAC,cAAc,QAAQ,GAAG,QAAQ,SAAS,IAAI,CAC5E,iBAAgB;;SAIpB,WAAU,KAAK,KAAK;;AAaxB,WAAO;KACL,UAVqB;MACrB,GAAG;MACH;MACA;MACA;MACA,GAAG,UAAU,KAAK,MAAM,KAAK,IAAI;MACjC;MACD,CAAC,KAAK,KAAK;KAIV,QAAQ;KACR,YAAY,KAAK,QAAQ,QAAQ;KAClC;KACD;;EAEL;;;;;;;;;;;;;;;;;;;AAoBH,SAAS,iBAAyB;CAChC,MAAM,MAAM,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;CACxD,MAAM,aAAa;EACjB,KAAK,KAAK,KAAK,aAAa;EAC5B,KAAK,KAAK,KAAK,aAAa;EAC5B,KAAK,KAAK,KAAK,MAAM,eAAe,aAAa;EAClD;AACD,MAAK,MAAM,aAAa,WACtB,KAAI;AACF,aAAW,UAAU;AACrB,SAAO;SACD;AAKV,QAAO,KAAK,KAAK,KAAK,aAAa;;;;;;;;;;;;;;;;AAiBrC,eAAsB,eAAe,SAAiB,MAA6C;CACjG,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,iBAAiB,MAAM,kBAAkB,EAAE;CAGjD,MAAM,UAAU,MAAM,OAAO;CAC7B,MAAM,cAAc,gBAAgB;CAKpC,MAAM,iBAAiB;EACrB,iCAAiC,KAAK,UAAU,YAAY,CAAC;EAC7D;EACA;EACD,CAAC,KAAK,KAAK;CAEZ,MAAM,SAAS,MAAM,QAAQ,MAAM;EACjC,OAAO;GACL,UAAU;GACV,QAAQ;GAKR,YAAY,KAAK,QAAQ,QAAQ;GAClC;EACD,QAAQ;EACR,QAAQ;EACR;EACA,UAAU;EACV,QAAQ;EACR,OAAO;EACP,SAAS,CAAC,kBAAkB,QAAQ,EAAE,mBAAmB,CAAC;EAC1D,UAAU;EACV,aAAa;EAQb,QAAQ,EACN,IAAI,cAAc,KAAK,UAAU,WAAW,CAAC,MAAM,WAAW,IAC/D;EACF,CAAC;CAEF,MAAM,WAAW,OAAO,SAAS,KAC9B,MACC,GAAG,KAAK,SAAS,QAAQ,KAAK,EAAE,EAAE,UAAU,QAAQ,GAAG,CAAC,GAAG,EAAE,UAAU,QAAQ,IAAI,IAAI,EAAE,OAC5F;CAED,MAAM,aAAa,OAAO,cAAc;AACxC,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,iEAAiE;AAGnF,QAAO;EAAE,MAAM,WAAW;EAAM;EAAU"}
1
+ {"version":3,"file":"bundle.js","names":[],"sources":["../../src/test-runner/bundle.ts"],"sourcesContent":["/**\n * esbuild-based bundler for user test files.\n *\n * Bundles a single test file into a self-contained IIFE string that can be\n * injected into a WebView via `Runtime.evaluate`. The bundle includes the\n * test runtime (`runtime.ts`), which provides `describe/it/test/expect` and\n * the `runTestModule(factory)` entry point.\n *\n * ## How the wiring works\n *\n * The bundle exposes two exports on `globalThis.__testBundle`:\n * - `runTestModule` — the runtime's entry function.\n * - `__userFactory` — an async function whose body is the user's top-level\n * test registration code (describe/it/test calls).\n *\n * The Node-side RPC (`rpc.ts`) calls:\n * `globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory)`\n *\n * `runTestModule` then installs `describe/it/test/expect` as globals, invokes\n * the factory (which registers all tests), runs them, and returns a `RunReport`.\n *\n * ## Why a factory wrapper is needed\n *\n * Naively adding the runtime to `entryPoints` and bundling the user file would\n * fail for two reasons:\n * 1. `describe/it/test/expect` from the runtime are module-local in the IIFE\n * scope. The user's top-level `describe(...)` calls expect them as globals —\n * they are not globals until `runTestModule` installs them.\n * 2. Even with globals pre-installed, the user file runs at IIFE-evaluation\n * time, before the RPC layer calls `runTestModule` to reset state and start\n * the test clock.\n *\n * The factory approach solves both: the user's registration code is deferred\n * into a function that `runTestModule` calls AFTER installing the globals.\n *\n * ## Factory extraction algorithm\n *\n * The `userFactoryPlugin` reads the user file and splits lines into:\n * - **top-level**: `import …` and re-export lines — kept at module scope\n * (the only valid position for static `import` in ESM).\n * - **body**: all other statements — moved into the body of the exported\n * `__userFactory` async function.\n *\n * esbuild processes the re-generated module, following each static import\n * through the normal dependency graph (including the SDK-redirect plugin).\n *\n * ## SDK redirect\n *\n * Imports of `@apps-in-toss/web-framework` (and sub-paths) are intercepted via\n * the `sdkRedirectPlugin` and replaced with a virtual `window.__sdk` proxy that\n * `src/in-app/auto.ts` installs at runtime. This works for both 2.x and 3.x SDK.\n *\n * SECRET-HANDLING: the returned bundle code is caller-managed; never log it.\n */\n\nimport { accessSync } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n// esbuild is imported for TYPES only at module scope; the runtime module is\n// loaded lazily inside `bundleTestFile` via dynamic import. esbuild runs a\n// startup invariant check (`TextEncoder().encode('') instanceof Uint8Array`)\n// that fails in a jsdom realm — a static import would break every MCP/test\n// module that merely *imports* this file's transitive graph (e.g. debug-server →\n// run_tests). Lazy load keeps esbuild off the import graph until a bundle is\n// actually built, and mirrors the cloudflared/chii dynamic-import precedent.\nimport type * as esbuild from 'esbuild';\n\n/** Options accepted by `bundleTestFile`. */\nexport interface BundleOptions {\n /**\n * Additional esbuild `external` patterns. The SDK package\n * (`@apps-in-toss/web-framework` and `@apps-in-toss/web-framework/*`) is\n * always handled by the SDK redirect plugin — callers may add more patterns\n * to be left as globals.\n */\n extraExternals?: string[];\n /**\n * Global name for the IIFE output object. Defaults to `__testBundle`.\n * The runtime entry uses this to call `__testBundle.runTestModule(__userFactory)`.\n */\n globalName?: string;\n}\n\n/**\n * The result of bundling a test file.\n * `code` is a self-contained IIFE string ready for `Runtime.evaluate`.\n */\nexport interface BundleResult {\n code: string;\n warnings: string[];\n}\n\n/** The SDK package name that mini-app test code imports from. */\nconst SDK_PACKAGE = '@apps-in-toss/web-framework';\n\n/**\n * Names the runtime installs as globals before invoking the user factory.\n * The `vitest` virtual module re-exports each as a lazy getter that reads from\n * `globalThis` at access time. Keep in sync with the globals installed in\n * `runtime.ts#runTestModule`.\n */\nconst VITEST_GLOBAL_NAMES = [\n 'describe',\n 'it',\n 'test',\n 'expect',\n 'beforeAll',\n 'afterAll',\n 'beforeEach',\n 'afterEach',\n 'vi',\n] as const;\n\n/**\n * Matches the bare SDK package and any sub-path import\n * (`@apps-in-toss/web-framework`, `@apps-in-toss/web-framework/foo`).\n * Built from {@link SDK_PACKAGE} so the package name has a single source.\n */\nconst SDK_IMPORT_FILTER = new RegExp(`^${SDK_PACKAGE.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}`);\n\n/**\n * esbuild plugin that intercepts SDK imports and redirects them to the\n * `window.__sdk` proxy that `src/in-app/auto.ts` installs at runtime.\n *\n * Strategy: for every import of `@apps-in-toss/web-framework` (or sub-paths),\n * esbuild resolves it to a virtual module that re-exports all named exports\n * via `window.__sdk[name]`. This avoids bundling the real SDK (which may not\n * be available in the test environment) while still making named imports work.\n *\n * If `window.__sdk` is absent (non-dog-food build), every access throws a\n * descriptive error rather than returning `undefined` silently.\n */\nfunction sdkRedirectPlugin(): esbuild.Plugin {\n return {\n name: 'sdk-redirect',\n setup(build) {\n // Match the bare package and any sub-path imports\n build.onResolve({ filter: SDK_IMPORT_FILTER }, (args) => ({\n path: args.path,\n namespace: 'sdk-redirect',\n }));\n\n build.onLoad({ filter: /.*/, namespace: 'sdk-redirect' }, () => ({\n // Generate a virtual CommonJS-style module so that esbuild does NOT perform\n // strict named-export matching. When `format:'iife'` bundles a CJS module,\n // it wraps it with its own __toCommonJS helper and satisfies named imports\n // via property access on the module.exports object — which is our Proxy.\n // This means `import { getPlatformOS } from '...'` becomes\n // `__proxy.getPlatformOS` at runtime, which correctly reads from window.__sdk.\n contents: `\nvar __proxy = (typeof window !== 'undefined' && window.__sdk)\n ? window.__sdk\n : new Proxy({}, {\n get: function(_t, p) {\n throw new Error('window.__sdk is not installed — run in a dog-food build. Missing: ' + String(p));\n }\n });\nmodule.exports = __proxy;\n`,\n loader: 'js',\n }));\n },\n };\n}\n\n/**\n * esbuild plugin that intercepts `import … from 'vitest'` and replaces it with\n * a virtual module that delegates every named import to `globalThis` at ACCESS\n * time (not at bundle-evaluation time).\n *\n * The runtime installs `describe/it/test/expect/beforeAll/afterAll/beforeEach/\n * afterEach/vi` as globals inside `runTestModule`, which runs AFTER the bundle\n * IIFE is evaluated. A value-copy redirect (`export var describe =\n * globalThis.describe`) would therefore capture `undefined` at evaluation time\n * and the user's `describe(...)` calls would be no-ops — registering zero tests.\n *\n * The fix defers the lookup to call time using per-name **getter** exports.\n * We emit a CommonJS module that:\n * 1. sets `__esModule = true` so esbuild's `__toESM` interop maps each named\n * import directly to a property access on the module (NOT wrapped under a\n * `default` shim — which is what happens for a bare Proxy whose own-keys\n * are empty, leaving every named import `undefined`);\n * 2. defines each global name as a getter that reads `globalThis[name]` on\n * every access. So `import { describe } from 'vitest'` compiles to\n * `import_vitest.describe`, whose getter returns the real `describe` only\n * when the factory calls it — after `runTestModule` installs the globals.\n *\n * A plain `module.exports = new Proxy(...)` does NOT work here: esbuild routes\n * the virtual module through `__toESM`, which enumerates own-keys (none on an\n * empty Proxy target) and therefore exposes zero named exports. Explicit getter\n * properties give `__toESM` real keys to map while keeping access lazy.\n */\nfunction vitestRedirectPlugin(): esbuild.Plugin {\n return {\n name: 'vitest-redirect',\n setup(build) {\n build.onResolve({ filter: /^vitest$/ }, () => ({\n path: 'vitest',\n namespace: 'vitest-redirect',\n }));\n\n build.onLoad({ filter: /^vitest$/, namespace: 'vitest-redirect' }, () => {\n const getters = VITEST_GLOBAL_NAMES.map(\n (name) =>\n `Object.defineProperty(exports, ${JSON.stringify(name)}, { enumerable: true, get: function() { return globalThis[${JSON.stringify(name)}]; } });`,\n ).join('\\n');\n // __esModule lets esbuild __toESM map named imports to these getters\n // directly. Each getter reads globalThis lazily so the value resolves at\n // call time — after runTestModule installs the globals.\n return {\n contents: `Object.defineProperty(exports, '__esModule', { value: true });\\n${getters}\\n`,\n loader: 'js',\n };\n });\n },\n };\n}\n\n/**\n * esbuild plugin that transforms the user test file into a module that exports\n * an async `__userFactory` function. The factory defers the user's top-level\n * test registration code (describe/it/test calls) so it only runs when\n * `runTestModule(__userFactory)` explicitly invokes it — AFTER the runtime has\n * installed describe/it/test/expect as globals.\n *\n * Algorithm:\n * - Import declarations and re-export statements are kept at module top-level\n * (the only valid ESM position for static `import`). A statement that spans\n * multiple lines — e.g. a named import with one member per line:\n * import {\n * appLogin,\n * getAnonymousKey,\n * } from '@apps-in-toss/web-framework';\n * is tracked as a single block: every line from the opening `import {` /\n * `export {` through the closing `from '…'` (or side-effect `'…'`) line is\n * kept together at top-level. This prevents the member lines and the\n * closing `} from '…'` line from leaking into the factory body, which would\n * leave an unterminated `import {` at module scope (the #678 env3 failure:\n * esbuild threw `Expected \"as\" but found \"{\"` on multi-line SDK imports).\n * - All other lines (describe/it/test calls, local declarations, etc.) are\n * moved into the body of the exported async factory function.\n *\n * This preserves SDK import resolution (the sdk-redirect plugin processes\n * top-level imports normally) while deferring test registration to the factory.\n */\nfunction userFactoryPlugin(absPath: string): esbuild.Plugin {\n const NAMESPACE = 'user-test-factory';\n return {\n name: 'user-test-factory',\n setup(build) {\n // Resolve the virtual \"user-test-factory\" specifier to our namespace.\n build.onResolve({ filter: /^user-test-factory$/ }, () => ({\n path: absPath,\n namespace: NAMESPACE,\n }));\n\n // Load the user file, split imports from body, wrap body in the factory.\n build.onLoad({ filter: /.*/, namespace: NAMESPACE }, async (args) => {\n const source = await fs.readFile(args.path, 'utf8');\n const lines = source.split('\\n');\n\n const topLevelLines: string[] = [];\n const bodyLines: string[] = [];\n\n // Matches `export` value declarations that cannot appear inside a\n // function body. We strip the `export` keyword so they become plain\n // declarations inside the factory.\n const EXPORT_DECLARATION_RE =\n /^(export\\s+)(default\\s+|async\\s+function\\s+|function\\s+|class\\s+|const\\s+|let\\s+|var\\s+)/;\n\n // True when `trimmed` begins a static `import` statement.\n const isImportStart = (trimmed: string): boolean =>\n trimmed.startsWith('import ') ||\n trimmed.startsWith('import{') ||\n trimmed.startsWith(\"import'\") ||\n trimmed.startsWith('import\"');\n\n // A module-scope `import`/`export … from` statement is \"complete\" on a\n // single line when it ends with a quoted module specifier (optionally\n // followed by `;`/whitespace), e.g. `… from '@x';` or side-effect\n // `import './x';`. A line ending in `{` or `,` (the common multi-line\n // named-import shape) is therefore NOT complete and must accumulate\n // further lines until the closing `from '…'` line.\n const endsStatement = (trimmed: string): boolean =>\n /['\"]\\s*;?\\s*$/.test(trimmed.replace(/\\/\\/.*$/, '').trimEnd());\n\n // When set, we are inside an unterminated multi-line import/re-export\n // block: every subsequent line stays at top level until the block ends.\n let inImportBlock = false;\n\n for (const line of lines) {\n const trimmed = line.trimStart();\n const indent = line.slice(0, line.length - trimmed.length);\n\n // Continuation of a multi-line import / re-export block — keep at\n // top level. The block ends on the line that terminates the\n // statement (closing `from '…'` / side-effect `'…'`).\n if (inImportBlock) {\n topLevelLines.push(line);\n if (endsStatement(trimmed)) {\n inImportBlock = false;\n }\n continue;\n }\n\n // Static import declarations must stay at module top level\n // (the ESM spec forbids `import` inside a function body). If the\n // statement does not terminate on this line, open an import block so\n // the member lines and closing `} from '…'` line stay top-level too.\n if (isImportStart(trimmed)) {\n topLevelLines.push(line);\n if (!endsStatement(trimmed)) {\n inImportBlock = true;\n }\n } else if (trimmed.startsWith('export ')) {\n // Determine whether this is a re-export (stays top-level) or a value\n // declaration (goes into the factory, export keyword stripped).\n const m = trimmed.match(EXPORT_DECLARATION_RE);\n if (m) {\n // Value declaration — strip `export ` and move into factory body.\n // e.g. `export function hello()` → `function hello()`\n // `export const x = 1` → `const x = 1`\n bodyLines.push(indent + trimmed.slice('export '.length));\n } else {\n // Re-export or `export type { … }` — stays at top level. A\n // multi-line `export { … } from '…'` opens an import block too.\n topLevelLines.push(line);\n if (/\\bfrom\\b/.test(trimmed) ? !endsStatement(trimmed) : trimmed.endsWith('{')) {\n inImportBlock = true;\n }\n }\n } else {\n bodyLines.push(line);\n }\n }\n\n const factoryContent = [\n ...topLevelLines,\n '',\n '// biome-ignore lint: generated factory wrapper',\n 'export default async function __userFactory(): Promise<void> {',\n ...bodyLines.map((l) => ` ${l}`),\n '}',\n ].join('\\n');\n\n return {\n contents: factoryContent,\n loader: 'ts',\n resolveDir: path.dirname(absPath),\n };\n });\n },\n };\n}\n\n/**\n * Returns the absolute path to the test-runner runtime module.\n *\n * Searches candidates in priority order:\n * 1. Co-located `runtime.ts` / `runtime.js` — covers the source tree\n * (tsx / ts-node) and the `dist/test-runner/` entry.\n * 2. `../test-runner/runtime.js` — covers the `dist/mcp/cli.js` entry,\n * where `import.meta.url` resolves to `dist/mcp/` (a sibling directory\n * of `dist/test-runner/`). Without this second candidate the MCP entry\n * point would look for `dist/mcp/runtime.js`, which does not exist, and\n * every `run_tests` call would fail with an esbuild \"Could not resolve\"\n * error (#678).\n *\n * Returns the first candidate that exists on disk. Falls back to the\n * co-located `runtime.js` path so esbuild produces a clear \"file not found\"\n * error rather than a cryptic failure.\n */\nfunction getRuntimePath(): string {\n const dir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.join(dir, 'runtime.ts'),\n path.join(dir, 'runtime.js'),\n path.join(dir, '..', 'test-runner', 'runtime.js'),\n ];\n for (const candidate of candidates) {\n try {\n accessSync(candidate);\n return candidate;\n } catch {\n // try next candidate\n }\n }\n // Let esbuild produce a \"file not found\" error with a clear path.\n return path.join(dir, 'runtime.js');\n}\n\n/**\n * Bundles `absPath` into a single IIFE string suitable for `Runtime.evaluate`.\n *\n * The IIFE installs `window.__testBundle` (or the custom `globalName`) with:\n * - `runTestModule` — the runtime entry (from `runtime.ts`).\n * - `__userFactory` — an async function wrapping the user's test registration\n * code so it runs AFTER `runTestModule` installs the globals.\n *\n * Callers (rpc.ts) invoke:\n * `globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory)`\n *\n * @param absPath - Absolute path to the user test file.\n * @param opts - Optional bundling overrides.\n */\nexport async function bundleTestFile(absPath: string, opts?: BundleOptions): Promise<BundleResult> {\n const globalName = opts?.globalName ?? '__testBundle';\n const extraExternals = opts?.extraExternals ?? [];\n\n // Lazy load esbuild at call time (see the module-scope import note).\n const esbuild = await import('esbuild');\n const runtimePath = getRuntimePath();\n\n // Stdin wrapper: import the runtime and the user factory, re-export both.\n // esbuild follows the static imports to include runtime.ts and the user file\n // (via the userFactoryPlugin) in the single IIFE output.\n const wrapperContent = [\n `import { runTestModule } from ${JSON.stringify(runtimePath)};`,\n `import __userFactory from \"user-test-factory\";`,\n `export { runTestModule, __userFactory };`,\n ].join('\\n');\n\n const result = await esbuild.build({\n stdin: {\n contents: wrapperContent,\n loader: 'ts',\n // resolveDir is used for relative imports from the wrapper. Since the\n // wrapper only imports absolute paths (runtimePath) and the virtual\n // \"user-test-factory\" specifier (resolved by plugin), the directory\n // doesn't matter — but we still provide a sensible default.\n resolveDir: path.dirname(absPath),\n },\n bundle: true,\n format: 'iife',\n globalName,\n platform: 'browser',\n target: 'es2022',\n write: false,\n plugins: [userFactoryPlugin(absPath), vitestRedirectPlugin(), sdkRedirectPlugin()],\n external: extraExternals,\n treeShaking: true,\n // Ensure the IIFE result is always reachable via globalThis regardless of\n // the evaluation context. esbuild's `globalName` emits:\n // var __testBundle = (() => { ... })();\n // When `Runtime.evaluate` runs this bundle code inside an outer wrapper\n // (rpc.ts's async IIFE), `var` creates a local variable — NOT a global\n // property — so `globalThis.__testBundle` stays `undefined`. The footer\n // explicitly assigns the local variable to `globalThis` to close that gap.\n footer: {\n js: `globalThis[${JSON.stringify(globalName)}] = ${globalName};`,\n },\n });\n\n const warnings = result.warnings.map(\n (w) =>\n `${path.relative(process.cwd(), w.location?.file ?? '')}:${w.location?.line ?? '?'}: ${w.text}`,\n );\n\n const outputFile = result.outputFiles?.[0];\n if (!outputFile) {\n throw new Error('bundleTestFile: esbuild produced no output — check entryPoints');\n }\n\n return { code: outputFile.text, warnings };\n}\n"],"mappings":"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"}
@@ -443,18 +443,30 @@ interface RunWithConnectionOptions extends RelayRunOptions {
443
443
  * This pure function is the testable core of the CLI (and is what the
444
444
  * `run_tests` MCP tool calls against the daemon's attached connection); it is
445
445
  * separate from `main()` so tests can call it without spawning a subprocess.
446
- *
447
- * A standalone CLI relay attach/detach lifecycle (connect via Chii relay URL,
448
- * `enableDomains`, run, then close) is not wired into `main()` yet.
449
446
  */
450
447
  declare function runWithConnection(connection: CdpConnection, files: string[], opts?: RunWithConnectionOptions): Promise<RelayRunReport>;
451
448
  /**
452
449
  * CLI entry point.
453
450
  *
454
- * Resolves the matched test files and prints a "relay attach required" notice:
455
- * the CLI's own standalone relay attach (resolve CDP URL, attach, run, close) is
456
- * not wired yet, so today these files run via the `run_tests` MCP tool against
457
- * the daemon's attached page.
451
+ * Performs a standalone relay attach run lifecycle:
452
+ *
453
+ * 1. Parse args: globs, --timeout, --cell-sdk-line, --cell-platform,
454
+ * --scheme-url (required for env3), --headless, --project-root.
455
+ * 2. Discover test files; exit 1 if none.
456
+ * 3. Load .ait_relay secret into AIT_DEBUG_TOTP_SECRET, then boot relay family.
457
+ * 4. Assemble AttachDeps (no qrHttpServer → text QR).
458
+ * 5. prepareAttach(deps, 'relay-dev', { scheme_url }, conn).
459
+ * 6. renderAndMaybeWait(deps, prep, true, timeoutMs, conn) — text QR + wait.
460
+ * 7. If cell flags present, injectGlobals({ __AIT_CELL__: cell }) before run.
461
+ * 8. runWithConnection(conn, files, { timeoutMs, printSummary: true }).
462
+ * 9. family.stop(); process.exitCode = failed > 0 ? 1 : 0.
463
+ *
464
+ * The CLI is not a daemon — no lock, router, SSE, or tools_list is needed.
465
+ * Attach timeout exits with code 1; test failures exit with code 1.
466
+ *
467
+ * SECRET-HANDLING: scheme_url / relay wssUrl / TOTP codes are never written to
468
+ * stdout/stderr directly. text QR renders via renderAndMaybeWait which encodes
469
+ * the TOTP `at=` code inside the QR payload (not in plain log lines).
458
470
  */
459
471
  declare function main(argv?: string[]): Promise<void>;
460
472
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","names":[],"sources":["../../src/mcp/cdp-connection.ts","../../src/test-runner/bundle.ts","../../src/test-runner/runtime.ts","../../src/test-runner/relay-worker.ts","../../src/test-runner/cli.ts"],"mappings":"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"}
1
+ {"version":3,"file":"cli.d.ts","names":[],"sources":["../../src/mcp/cdp-connection.ts","../../src/test-runner/bundle.ts","../../src/test-runner/runtime.ts","../../src/test-runner/relay-worker.ts","../../src/test-runner/cli.ts"],"mappings":"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"}