@isentinel/jest-roblox 0.3.7 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,10 +15,13 @@ Run your roblox-ts and Luau tests inside Roblox, get results in your terminal.
15
15
  - roblox-ts and pure Luau
16
16
  - Source-mapped errors (Luau line numbers back to `.ts` files)
17
17
  - Code coverage (via [Lute](https://github.com/luau-lang/lute) instrumentation)
18
- - Two backends: Open Cloud (remote) and Studio (local)
18
+ - Three backends: Open Cloud (remote), Studio (attached, local), and Studio CLI
19
+ (self-launched headless Studio, local)
19
20
  - Multiple output formatters (human, agent, JSON, GitHub Actions)
20
21
 
21
- > [!NOTE] roblox-ts projects currently require
22
+ <!-- prettier-ignore -->
23
+ > [!NOTE]
24
+ > roblox-ts projects currently require
22
25
  > [@isentinel/roblox-ts](https://npmx.dev/package/@isentinel/roblox-ts) for
23
26
  > source maps and coverage support.
24
27
 
@@ -68,6 +71,15 @@ Then run:
68
71
  jest-roblox
69
72
  ```
70
73
 
74
+ <!-- prettier-ignore -->
75
+ > [!NOTE]
76
+ > `projects` is optional. With it omitted, the CLI derives one project per
77
+ > `luauRoots` mount (the compiled-output dirs your Rojo project mounts,
78
+ > auto-detected from your tsconfig `outDir`), generating each project's
79
+ > `jest.config` stub for you. Set `projects` explicitly when you need
80
+ > per-project overrides (a distinct `displayName`, `setupFiles`, `include`,
81
+ > etc.) or a tighter project layout than the luau roots imply.
82
+
71
83
  ## Usage
72
84
 
73
85
  ```bash
@@ -88,6 +100,7 @@ jest-roblox --testPathPattern="modifiers|define\\.spec|triggers"
88
100
  # Use a specific backend (default "auto" picks Studio if the plugin is
89
101
  # connected, else Open Cloud if credentials are set — see Backends below)
90
102
  jest-roblox --backend studio
103
+ jest-roblox --backend studio-cli
91
104
  jest-roblox --backend open-cloud
92
105
 
93
106
  # Collect coverage
@@ -133,7 +146,7 @@ per-package declarations error loudly.
133
146
 
134
147
  | Field | What it does | Default |
135
148
  | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
136
- | `backend` | `"auto"`, `"open-cloud"`, or `"studio"` | `"auto"` |
149
+ | `backend` | `"auto"`, `"open-cloud"`, `"studio"`, or `"studio-cli"` | `"auto"` |
137
150
  | `color` | Use ANSI colors in console output | `true` |
138
151
  | `formatters` | Output formatters (`"default"`, `"agent"`, `"json"`, `"github-actions"`) | `["default"]` |
139
152
  | `gameOutput` | Write Game Output to a file — a path, or `true` for `game-output.log` under the root. In `--workspace` mode this is one grouped aggregate file across every package | — |
@@ -144,6 +157,7 @@ per-package declarations error loudly.
144
157
  | `placeId` | Open Cloud place ID | — |
145
158
  | `port` | WebSocket port for Studio backend | `3001` |
146
159
  | `silent` | Suppress console output | `false` |
160
+ | `studioPath` | Roblox Studio executable for the `studio-cli` backend (auto-detected if unset; also `--studioPath` / `JEST_ROBLOX_STUDIO_PATH`) | — |
147
161
  | `universeId` | Open Cloud universe ID | — |
148
162
 
149
163
  #### Per-package fields
@@ -169,7 +183,7 @@ Put these under `test: { ... }`.
169
183
 
170
184
  | Field | What it does | Default |
171
185
  | ------------------------ | ------------------------------------------------ | ------------------------------------ |
172
- | `projects` | Where to look for tests in the DataModel | **required** |
186
+ | `projects` | Where to look for tests in the DataModel | one project per `luauRoots` mount |
173
187
  | `testMatch` | Glob patterns that find test files | `**/*.spec.ts`, `**/*.test.ts`, etc. |
174
188
  | `testPathIgnorePatterns` | Patterns to skip | `/node_modules/`, `/dist/`, `/out/` |
175
189
  | `setupFiles` | Scripts to run before the test environment loads | — |
@@ -181,7 +195,9 @@ Put these under `test: { ... }`.
181
195
 
182
196
  Put these under `test: { ... }`.
183
197
 
184
- > [!IMPORTANT] Coverage requires [Lute](https://github.com/luau-lang/lute) to be
198
+ <!-- prettier-ignore -->
199
+ > [!IMPORTANT]
200
+ > Coverage requires [Lute](https://github.com/luau-lang/lute) to be
185
201
  > installed and on your `PATH`. Lute parses Luau ASTs so the CLI can insert
186
202
  > coverage probes.
187
203
 
@@ -194,7 +210,9 @@ Put these under `test: { ... }`.
194
210
  | `coveragePathIgnorePatterns` | Files to leave out of coverage | test files, `node_modules`, `rbxts_include` |
195
211
  | `collectCoverageFrom` | Globs for files to include in coverage | — |
196
212
 
197
- > [!NOTE] Coverage uses vitest `all` / Istanbul semantics: every instrumented
213
+ <!-- prettier-ignore -->
214
+ > [!NOTE]
215
+ > Coverage uses vitest `all` / Istanbul semantics: every instrumented
198
216
  > file matching the include globs is reported, so a source file with no test
199
217
  > shows **0%** (and fails `coverageThreshold`) instead of being silently
200
218
  > omitted. When `collectCoverageFrom` is unset for a multi-project run, the
@@ -258,7 +276,7 @@ export default defineConfig({
258
276
 
259
277
  ## Backends
260
278
 
261
- Two ways to run tests, plus an auto-pick:
279
+ Three ways to run tests, plus an auto-pick:
262
280
 
263
281
  ### Auto (default)
264
282
 
@@ -320,7 +338,9 @@ Connects to Roblox Studio over WebSocket. Faster than Open Cloud (no upload
320
338
  step), but Studio must be open with the plugin running. Studio doesn't expose
321
339
  which place is open, so multiple concurrent projects aren't supported yet.
322
340
 
323
- > [!NOTE] For `--coverage`, prefer `--backend open-cloud` since the coverage
341
+ <!-- prettier-ignore -->
342
+ > [!NOTE]
343
+ > For `--coverage`, prefer `--backend open-cloud` since the coverage
324
344
  > output is built to a separate output under `.jest-roblox/coverage/` that is
325
345
  > likely not the studio place being served.
326
346
 
@@ -332,7 +352,7 @@ Create a file named drillbit.toml in your project's directory.
332
352
 
333
353
  ```toml
334
354
  [plugins.jest_roblox]
335
- github = "https://github.com/christopher-buss/jest-roblox-cli/releases/download/v0.2.7/JestRobloxRunner.rbxm"
355
+ github = "https://github.com/christopher-buss/jest-roblox-cli/releases/download/v0.3.8/JestRobloxRunner.rbxm"
336
356
  ```
337
357
 
338
358
  Then run `drillbit` and it will download the plugin and install it in Studio for
@@ -342,12 +362,54 @@ Or download `JestRobloxRunner.rbxm` from the
342
362
  [latest release](https://github.com/christopher-buss/jest-roblox-cli/releases)
343
363
  and drop it into your Studio plugins folder.
344
364
 
365
+ ### Studio CLI (self-launched, local)
366
+
367
+ `--backend studio-cli` owns the whole Studio lifecycle: it builds its own place,
368
+ launches Roblox Studio headless via Studio's `--task RunScript` interface,
369
+ drives the installed plugin's Run mode, reads the result from Studio's output
370
+ log, and quits Studio. No API key, no upload, no pre-opened editor — you just
371
+ need Studio installed (logged in) with the jest plugin. It spawns its own
372
+ isolated Studio instance, so any editor you already have open is untouched.
373
+
374
+ It is selected only when you ask for it explicitly — `auto` never launches a
375
+ Studio process on its own. Studio is auto-discovered per-OS; override the
376
+ executable with `studioPath` (config key), `--studioPath`, or
377
+ `JEST_ROBLOX_STUDIO_PATH`. The backend is serial: `--parallel > 1` errors.
378
+
379
+ Pass `--headed` to show the Studio window during the run instead of the default
380
+ hidden one — useful for watching a slow run or a hang (Studio still self-quits
381
+ when tests finish, so a fast run just flashes). It is a per-run debugging flag,
382
+ CLI-only and inert on every other backend.
383
+
384
+ Once the result lands, Studio is shut down gracefully: it is allowed to close
385
+ the place — running any edit-mode plugin `BindToClose` handlers and freeing the
386
+ place lock — and is then killed the instant the lock releases, skipping Studio's
387
+ slow telemetry teardown. The shutdown is decoupled from the result, so pass/fail
388
+ prints immediately and the process exits once teardown finishes.
389
+
390
+ Unlike the attached `studio` backend, `--coverage` works here: studio-cli opens
391
+ the Coverage-Instrumented Place instead of the Clean Place, so the report
392
+ universe, thresholds, reporters, and exclusions behave identically to the
393
+ open-cloud backend (including the all-files semantics where an untested included
394
+ file reports **0%** and fails `coverageThreshold`).
395
+
396
+ <!-- prettier-ignore -->
397
+ > [!NOTE]
398
+ > studio-cli is a local-developer convenience backend (it needs a logged-in
399
+ > Studio and the installed plugin), not a CI path.
400
+
345
401
  ## Workspace mode
346
402
 
347
403
  Run tests across multiple packages in a pnpm workspace in a single invocation.
348
- Open Cloud only Studio backend is not supported.
349
-
350
- > [!NOTE] Package discovery uses one of two sources. By default it reads
404
+ Works on every backend: Open Cloud (fans packages across parallel tasks),
405
+ `studio-cli` (one self-launched Studio process drives every package, no
406
+ sharding), and the attached `studio` backend (runs the workspace inside an open
407
+ Studio — handy for debugging the flow). `studio-cli` is serial, so
408
+ `--parallel > 1` is rejected with `--workspace`.
409
+
410
+ <!-- prettier-ignore -->
411
+ > [!NOTE]
412
+ > Package discovery uses one of two sources. By default it reads
351
413
  > `pnpm-workspace.yaml` at the workspace root. Alternatively, declare a
352
414
  > `workspace` block in your jest config (see
353
415
  > [Workspaces without pnpm](#workspaces-without-pnpm)) to enumerate packages by
@@ -440,8 +502,10 @@ project) under `.jest-roblox/output/`.
440
502
 
441
503
  | Flag | What it does |
442
504
  | -------------------------------- | ----------------------------------------------------------------------------------------------------------- |
443
- | `--backend <type>` | Choose `auto`, `open-cloud`, or `studio` |
505
+ | `--backend <type>` | Choose `auto`, `open-cloud`, `studio`, or `studio-cli` |
444
506
  | `--port <n>` | WebSocket port for Studio |
507
+ | `--studioPath <path>` | Roblox Studio executable for `studio-cli` (auto-detected if unset) |
508
+ | `--headed` | Show the Studio window during the run (`studio-cli` only; default: hidden) |
445
509
  | `--config <path>` | Path to config file |
446
510
  | `--testPathPattern <regex>` | Filter test files by path |
447
511
  | `-t, --testNamePattern <regex>` | Filter tests by name |
@@ -486,7 +550,9 @@ project) under `.jest-roblox/output/`.
486
550
  5. Maps Luau line numbers to TypeScript via source maps (roblox-ts only)
487
551
  6. Prints results
488
552
 
489
- > [!NOTE] Coverage adds extra steps: copy Luau files, insert tracking probes,
553
+ <!-- prettier-ignore -->
554
+ > [!NOTE]
555
+ > Coverage adds extra steps: copy Luau files, insert tracking probes,
490
556
  > build a separate place file, then map hit counts back to source. For
491
557
  > roblox-ts, this goes through source maps to report TypeScript lines.
492
558
 
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync } from "node:fs";
3
+ import { registerHooks } from "node:module";
3
4
  import { dirname, resolve } from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
 
7
+ import { load, resolve as resolveLuau } from "../loaders/luau-raw.mjs";
8
+
6
9
  const sourceEntry = resolve(dirname(fileURLToPath(import.meta.url)), "../src/cli.ts");
7
10
 
8
- const { register } = await import("node:module");
9
- register("../loaders/luau-raw.mjs", import.meta.url);
11
+ registerHooks({ load, resolve: resolveLuau });
10
12
 
11
13
  if (existsSync(sourceEntry)) {
12
14
  const { main } = await import("../src/cli.ts");
package/dist/cli.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as CliOptions } from "./schema-CCF2E3k_.mjs";
1
+ import { t as CliOptions } from "./schema-i-pZhLCV.mjs";
2
2
 
3
3
  //#region src/cli.d.ts
4
4
  declare function parseArgs(args: Array<string>): CliOptions;
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as loadConfig, D as outputMultiResult, H as formatBanner, O as outputSingleResult, P as parseGameOutput, Q as mergeCliWithConfig, Y as LuauScriptError, dt as version, f as formatMissingScopes, lt as isValidBackend, n as runJestRoblox, ot as VALID_BACKENDS, p as walkErrorChain, ut as ConfigError } from "./run-Bh8G33J1.mjs";
1
+ import { A as outputSingleResult, I as parseGameOutput, W as formatBanner, Z as LuauScriptError, ct as VALID_BACKENDS, dt as isValidBackend, et as mergeCliWithConfig, ft as ConfigError, h as walkErrorChain, k as outputMultiResult, m as formatMissingScopes, n as runJestRoblox, pt as version, tt as loadConfig } from "./run-Cirdb_CB.mjs";
2
2
  import { OpenCloudError } from "@bedrock-rbx/ocale";
3
3
  import process from "node:process";
4
4
  import { parseArgs as parseArgs$1 } from "node:util";
@@ -9,8 +9,13 @@ const HELP_TEXT = `
9
9
  Usage: jest-roblox [options] [files...]
10
10
 
11
11
  Options:
12
- --backend <type> Backend: "auto", "open-cloud", or "studio" (default: auto)
12
+ --backend <type> Backend: "auto", "open-cloud", "studio", or
13
+ "studio-cli" (default: auto)
13
14
  --port <number> WebSocket port for studio backend (default: 3001)
15
+ --studioPath <path> Roblox Studio executable for the studio-cli
16
+ backend (auto-detected if not set)
17
+ --headed Show the Studio window during the run
18
+ (studio-cli backend only; default: hidden)
14
19
  --config <path> Path to config file
15
20
  --testPathPattern <regex> Filter test files by path pattern
16
21
  -t, --testNamePattern <regex> Filter tests by name pattern
@@ -96,6 +101,7 @@ function parseArgs(args) {
96
101
  type: "string"
97
102
  },
98
103
  "gameOutput": { type: "string" },
104
+ "headed": { type: "boolean" },
99
105
  "help": {
100
106
  default: false,
101
107
  type: "boolean"
@@ -126,6 +132,7 @@ function parseArgs(args) {
126
132
  "showLuau": { type: "boolean" },
127
133
  "silent": { type: "boolean" },
128
134
  "sourceMap": { type: "boolean" },
135
+ "studioPath": { type: "string" },
129
136
  "testNamePattern": {
130
137
  short: "t",
131
138
  type: "string"
@@ -166,6 +173,7 @@ function parseArgs(args) {
166
173
  files: positionals.length > 0 ? positionals : void 0,
167
174
  formatters: values.formatters,
168
175
  gameOutput: values.gameOutput,
176
+ headed: values.headed,
169
177
  help: values.help,
170
178
  outputFile: values.outputFile,
171
179
  packages: values.packages,
@@ -180,6 +188,7 @@ function parseArgs(args) {
180
188
  showLuau: values["no-show-luau"] === true ? false : values.showLuau,
181
189
  silent: values.silent,
182
190
  sourceMap: values.sourceMap,
191
+ studioPath: values.studioPath,
183
192
  testNamePattern: values.testNamePattern,
184
193
  testPathPattern: values.testPathPattern,
185
194
  timeout,
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as SnapshotFormatOptions, a as DisplayName, b as defineProject, c as GlobalTestConfig, d as ProjectEntry, f as ProjectTestConfig, g as SharedTestConfig, h as SHARED_TEST_KEYS, i as DEFAULT_CONFIG, l as InlineProjectConfig, m as ResolvedConfig, n as Config, o as FormatterEntry, p as ROOT_CLI_KEYS, r as ConfigInput, s as GLOBAL_TEST_KEYS, t as CliOptions, u as JEST_ARGV_EXCLUDED_KEYS, v as WorkspaceConfig, x as TypecheckConfig, y as defineConfig } from "./schema-CCF2E3k_.mjs";
1
+ import { _ as SnapshotFormatOptions, a as DisplayName, b as defineProject, c as GlobalTestConfig, d as ProjectEntry, f as ProjectTestConfig, g as SharedTestConfig, h as SHARED_TEST_KEYS, i as DEFAULT_CONFIG, l as InlineProjectConfig, m as ResolvedConfig, n as Config, o as FormatterEntry, p as ROOT_CLI_KEYS, r as ConfigInput, s as GLOBAL_TEST_KEYS, t as CliOptions, u as JEST_ARGV_EXCLUDED_KEYS, v as WorkspaceConfig, x as TypecheckConfig, y as defineConfig } from "./schema-i-pZhLCV.mjs";
2
2
  import { type } from "arktype";
3
3
  import { StorageClient } from "@bedrock-rbx/ocale/storage";
4
4
  import { WebSocket, WebSocketServer } from "ws";
@@ -352,7 +352,16 @@ interface Backend {
352
352
  readonly kind: BackendKind;
353
353
  runTests(options: BackendOptions): Promise<BackendResult>;
354
354
  }
355
- type BackendKind = "open-cloud" | "studio";
355
+ type BackendKind = "open-cloud" | "studio" | "studio-cli";
356
+ /**
357
+ * Whether this is a workspace (multi-package) run. Workspace jobs each carry
358
+ * their owning package name (`pkg`); single-/multi-project jobs never do, and
359
+ * the run layer builds them all-or-none — so any job with `pkg` means the whole
360
+ * run is a workspace run. The Studio backends key off this to drive the plugin's
361
+ * staged-materializer dispatch (`workspace.entries`) instead of the configs
362
+ * path. `buildWorkspaceEntries` then fails fast if a job is missing `pkg`, so a
363
+ * malformed (mixed) array surfaces as a clear error rather than a bad payload.
364
+ */
356
365
  //#endregion
357
366
  //#region packages/roblox-runner/dist/index.d.mts
358
367
  //#endregion
@@ -423,6 +432,154 @@ declare class OpenCloudBackend implements Backend {
423
432
  }
424
433
  declare function createOpenCloudBackend(credentials: OpenCloudCredentials): OpenCloudBackend;
425
434
  //#endregion
435
+ //#region src/staging/synthesizer.d.ts
436
+ interface StubMount {
437
+ absStubPath: string;
438
+ dataModelPath: string;
439
+ }
440
+ interface CoverageRoot {
441
+ /** Path relative to `packageDirectory` that points at the original luau root. */
442
+ luauRoot: string;
443
+ /** Absolute path to the instrumented shadow directory for the same root. */
444
+ shadowDir: string;
445
+ }
446
+ interface PackageDescriptor {
447
+ name: string;
448
+ /**
449
+ * Per-package `coverageCache` opt-out, forwarded to
450
+ * `prepareWorkspaceCoverage` so the cache gate honors each package's own
451
+ * declaration. Not read by synthesis itself.
452
+ */
453
+ coverageCache?: boolean;
454
+ /**
455
+ * Per-package coverage ignore patterns, forwarded to
456
+ * `prepareWorkspaceCoverage` so the matcher reflects the merged pkgConfig
457
+ * instead of the workspace-root default. Not read by synthesis itself.
458
+ */
459
+ coveragePathIgnorePatterns?: Array<string>;
460
+ /**
461
+ * When set, $path entries that fall inside any listed `luauRoot` are
462
+ * redirected to the corresponding `shadowDir` so the synthesized place
463
+ * picks up instrumented sources for this package only. Packages without
464
+ * `coverageRoots` use their original $path entries unchanged.
465
+ */
466
+ coverageRoots?: Array<CoverageRoot>;
467
+ /**
468
+ * Per-package `luauRoots`, forwarded to `prepareWorkspaceCoverage` so the
469
+ * short-circuit honors the user-listed source dirs. Not read by synthesis
470
+ * itself.
471
+ */
472
+ luauRoots?: Array<string>;
473
+ packageDirectory: string;
474
+ rojoProjectPath: string;
475
+ stubMounts?: Array<StubMount>;
476
+ }
477
+ //#endregion
478
+ //#region src/staging/place-builder.d.ts
479
+ interface BuildPlaceOptions {
480
+ /**
481
+ * Force `ServerScriptService.LoadStringEnabled = true` on the built place.
482
+ * Used by studio-cli's Clean Place, whose Run-mode runner gates on
483
+ * LoadString. Forwarded verbatim to {@link synthesize}.
484
+ */
485
+ loadStringEnabled?: boolean;
486
+ packages: Array<PackageDescriptor>;
487
+ placeFile: string;
488
+ projectFile: string;
489
+ wrap?: boolean;
490
+ }
491
+ //#endregion
492
+ //#region src/backends/studio-cli.d.ts
493
+ interface StudioCliLaunchRequest {
494
+ /** Full Studio CLI argument vector (already absolute paths). */
495
+ args: Array<string>;
496
+ /**
497
+ * Show the Studio window during the run (`--headed`) instead of the default
498
+ * hidden window. Maps to `windowsHide: !headed` in {@link spawnStudio}.
499
+ */
500
+ headed: boolean;
501
+ /**
502
+ * Absolute path of the place Studio opens. Used only to clear a stale
503
+ * `<place>.lock` a previously killed Studio could not remove itself.
504
+ */
505
+ placeFile: string;
506
+ /** Absolute path to the Studio executable. */
507
+ studioPath: string;
508
+ }
509
+ /**
510
+ * A launched Studio the host can kill once the result arrives (or on timeout).
511
+ * The injected seam: unit tests return a fake that drives a canned result frame
512
+ * over the mock WebSocket server instead of launching Studio.
513
+ */
514
+ interface StudioCliProcess {
515
+ /**
516
+ * Terminate Studio immediately (`TerminateProcess`), skipping graceful
517
+ * shutdown. Used on every hung/error/timeout path.
518
+ */
519
+ kill: () => void;
520
+ /**
521
+ * Graceful teardown: wait for Studio to release
522
+ * `<place>.lock` — which happens only after every edit-mode `BindToClose`
523
+ * handler ran and `ClosePlace` finished — then terminate it, skipping
524
+ * Studio's ~30s post-close telemetry drain. Hard-kills anyway after
525
+ * `graceCapMs` (a long-yielding handler). Returns immediately; the watch runs
526
+ * in the background, keeping node's event loop alive (the child handle + a
527
+ * poll timer) until it fires, so the CLI prints results now and the process
528
+ * exits once teardown completes. The caller closes the result server first,
529
+ * which is what lets the bootstrap return and `--quitAfterExecution` begin
530
+ * the graceful close.
531
+ */
532
+ killOnLockRelease: (graceCapMs: number) => void;
533
+ /** Subscribe to a spawn failure (e.g. a bad `studioPath`). */
534
+ onError: (listener: (error: Error) => void) => void;
535
+ }
536
+ /** Spawns Studio and returns the handle the host kills. */
537
+ type StudioCliLauncher = (request: StudioCliLaunchRequest) => StudioCliProcess;
538
+ interface StudioCliOptions {
539
+ /** Place Builder seam; defaults to the real {@link defaultBuildPlace}. */
540
+ buildPlace?: (options: BuildPlaceOptions) => BuildManifestArtifact;
541
+ /** Result-server factory seam; defaults to an ephemeral-port `ws` server. */
542
+ createServer?: () => WebSocketServer;
543
+ /** Studio-executable resolver seam; defaults to {@link discoverStudioPath}. */
544
+ discover?: (override: string | undefined) => string;
545
+ /**
546
+ * Backstop for the graceful teardown: hard-kill if `<place>.lock` isn't
547
+ * released within this many ms. Defaults to {@link GRACEFUL_SHUTDOWN_CAP_MS}.
548
+ */
549
+ gracefulShutdownTimeout?: number;
550
+ /**
551
+ * Show the Studio window during the run (`--headed`). CLI-only — never read
552
+ * from config. Defaults to false (hidden window).
553
+ */
554
+ headed?: boolean;
555
+ /** Process launcher seam; defaults to the real {@link spawnStudio}. */
556
+ launch?: StudioCliLauncher;
557
+ /** Explicit Studio executable path (override from config / CLI / env). */
558
+ studioPath?: string;
559
+ /** Run timeout in milliseconds. Defaults to 300000. */
560
+ timeout?: number;
561
+ }
562
+ declare class StudioCliBackend implements Backend {
563
+ private readonly buildPlace;
564
+ private readonly createServer;
565
+ private readonly discover;
566
+ private readonly gracefulShutdownTimeout;
567
+ private readonly headed;
568
+ private readonly launch;
569
+ private readonly studioPath?;
570
+ private readonly timeout;
571
+ readonly kind = "studio-cli";
572
+ constructor(options?: StudioCliOptions);
573
+ runTests(options: BackendOptions): Promise<BackendResult>;
574
+ /**
575
+ * Build the Clean Place for a normal (non-coverage) run and return its path.
576
+ * `loadStringEnabled` is forced on so the Run-mode runner's LoadString gate
577
+ * passes. Coverage runs skip this and open the instrumented place instead.
578
+ */
579
+ private buildCleanPlace;
580
+ }
581
+ declare function createStudioCliBackend(options?: StudioCliOptions): StudioCliBackend;
582
+ //#endregion
426
583
  //#region src/backends/studio.d.ts
427
584
  interface PreConnected {
428
585
  server: WebSocketServer;
@@ -942,6 +1099,16 @@ interface MappedCoverageResult {
942
1099
  files: Record<string, MappedFileCoverage>;
943
1100
  }
944
1101
  //#endregion
1102
+ //#region src/coverage-pipeline/agent-table-filter.d.ts
1103
+ /**
1104
+ * Decides whether a single coverage-universe source file is shown in the agent
1105
+ * **text table**. Receives a normalized-absolute source path (see
1106
+ * {@link narrowMappedForAgentTable}). This is a display-only narrowing layered
1107
+ * on top of `filterCoverageUniverse`: thresholds, the totals line, and the
1108
+ * lcov/html/json artifacts always keep the full universe.
1109
+ */
1110
+ type CoverageDisplayPredicate = (normalizedAbsolutePath: string) => boolean;
1111
+ //#endregion
945
1112
  //#region src/run/types.d.ts
946
1113
  type RunMode = "multi" | "single" | "workspace";
947
1114
  interface ProjectResult {
@@ -961,6 +1128,13 @@ interface SingleRunResult {
961
1128
  * place set it has.
962
1129
  */
963
1130
  coverageArtifacts?: CoverageArtifacts;
1131
+ /**
1132
+ * Narrows the agent coverage **text table** to the directly-filtered source
1133
+ * files on a filtered run (single file / `--testPathPattern`). Display-only:
1134
+ * thresholds, totals, and lcov/html/json keep the full universe. Undefined on
1135
+ * a full run.
1136
+ */
1137
+ coverageDisplayFilter?: CoverageDisplayPredicate;
964
1138
  mode: "single";
965
1139
  preCoverageMs: number;
966
1140
  runtimeResult?: ExecuteResult;
@@ -971,6 +1145,13 @@ interface MultiRunResult {
971
1145
  collectCoverageFrom?: Array<string>;
972
1146
  /** Producer record for the entry point to emit a Build Manifest from. */
973
1147
  coverageArtifacts?: CoverageArtifacts;
1148
+ /**
1149
+ * Narrows the agent coverage **text table** to the directly-filtered source
1150
+ * files on a filtered run (positional files / `--testPathPattern`, or the
1151
+ * selected `--project` scope). Display-only: thresholds, totals, and
1152
+ * lcov/html/json keep the full universe. Undefined on a full run.
1153
+ */
1154
+ coverageDisplayFilter?: CoverageDisplayPredicate;
974
1155
  merged: MultiProjectMerged;
975
1156
  mode: "multi";
976
1157
  preCoverageMs: number;
@@ -1427,4 +1608,4 @@ declare function visitExpression(expression: AstExpr, visitor: LuauVisitor): voi
1427
1608
  declare function visitStatement(statement: AstStat, visitor: LuauVisitor): void;
1428
1609
  declare function visitBlock(block: AstStatBlock, visitor: LuauVisitor): void; //#endregion
1429
1610
  //#endregion
1430
- export { type ArtifactBundle, type AstExpr, type AstExprBinary, type AstExprCall, type AstExprFunction, type AstStat, type AstStatBlock, BUILD_MANIFEST_VERSION, type Backend, type BackendOptions, type BuildManifest, type BuildManifestArtifact, type BuildManifestFileRecord, type BuildManifestProject, type CliOptions, type Config, type ConfigInput, type CoverageManifest, DEFAULT_CONFIG, type DisplayName, type ExecuteResult, type FormatOutputOptions, type FormatterEntry, GLOBAL_TEST_KEYS, type GameOutputEntry, type GitHubActionsFormatterOptions, type GlobalTestConfig, type InlineProjectConfig, type InstrumentedFileRecord, JEST_ARGV_EXCLUDED_KEYS, type JestArgv, type JestResult, type LuauSpan, type LuauVisitor, MANIFEST_VERSION, type MultiProjectMerged, type MultiRunResult, type NonInstrumentedFileRecord, OpenCloudBackend, type ProjectEntry, type ProjectInput, type ProjectResult, type ProjectTestConfig, ROOT_CLI_KEYS, type ReadBuildManifestOptions, type ReadBuildManifestResult, type ReadManifestResult as ReadCoverageManifestResult, type ResolvedConfig, type ResolvedProjectConfig, type RunMode, type RunOptions, type RunProjectsOptions, type RunProjectsResult, type RunResult, SHARED_TEST_KEYS, type SharedTestConfig, type SingleRunResult, StudioBackend, type TestCaseResult, type TestDefinition, type TestFileResult, type TestRecord, type TestStatus, type TscErrorInfo, type TypecheckOptions, type WorkspaceConfig, type WorkspaceRunResult, buildJestArgv, buildManifestSchema, manifestSchema as coverageManifestSchema, createOpenCloudBackend, createStudioBackend, defineConfig, defineProject, extractJsonFromOutput, formatAnnotations, formatExecuteOutput, formatFailure, formatGameOutputNotice, formatJobSummary, formatJson, formatResult, formatTestSummary, generateTestScript, hashFile, loadConfig, parseGameOutput, parseJestOutput, prepareArtifacts, readBuildManifest, readManifest as readCoverageManifest, resolveConfig, runJestRoblox, runProjects, runTypecheck, visitBlock, visitExpression, visitStatement, writeGameOutput, writeJsonFile };
1611
+ export { type ArtifactBundle, type AstExpr, type AstExprBinary, type AstExprCall, type AstExprFunction, type AstStat, type AstStatBlock, BUILD_MANIFEST_VERSION, type Backend, type BackendOptions, type BuildManifest, type BuildManifestArtifact, type BuildManifestFileRecord, type BuildManifestProject, type CliOptions, type Config, type ConfigInput, type CoverageManifest, DEFAULT_CONFIG, type DisplayName, type ExecuteResult, type FormatOutputOptions, type FormatterEntry, GLOBAL_TEST_KEYS, type GameOutputEntry, type GitHubActionsFormatterOptions, type GlobalTestConfig, type InlineProjectConfig, type InstrumentedFileRecord, JEST_ARGV_EXCLUDED_KEYS, type JestArgv, type JestResult, type LuauSpan, type LuauVisitor, MANIFEST_VERSION, type MultiProjectMerged, type MultiRunResult, type NonInstrumentedFileRecord, OpenCloudBackend, type ProjectEntry, type ProjectInput, type ProjectResult, type ProjectTestConfig, ROOT_CLI_KEYS, type ReadBuildManifestOptions, type ReadBuildManifestResult, type ReadManifestResult as ReadCoverageManifestResult, type ResolvedConfig, type ResolvedProjectConfig, type RunMode, type RunOptions, type RunProjectsOptions, type RunProjectsResult, type RunResult, SHARED_TEST_KEYS, type SharedTestConfig, type SingleRunResult, StudioBackend, StudioCliBackend, type TestCaseResult, type TestDefinition, type TestFileResult, type TestRecord, type TestStatus, type TscErrorInfo, type TypecheckOptions, type WorkspaceConfig, type WorkspaceRunResult, buildJestArgv, buildManifestSchema, manifestSchema as coverageManifestSchema, createOpenCloudBackend, createStudioBackend, createStudioCliBackend, defineConfig, defineProject, extractJsonFromOutput, formatAnnotations, formatExecuteOutput, formatFailure, formatGameOutputNotice, formatJobSummary, formatJson, formatResult, formatTestSummary, generateTestScript, hashFile, loadConfig, parseGameOutput, parseJestOutput, prepareArtifacts, readBuildManifest, readManifest as readCoverageManifest, resolveConfig, runJestRoblox, runProjects, runTypecheck, visitBlock, visitExpression, visitStatement, writeGameOutput, writeJsonFile };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as loadConfig, A as formatJobSummary, B as formatResult, C as BUILD_MANIFEST_VERSION, E as readBuildManifest, F as writeGameOutput, G as manifestSchema, I as createTimingCollector, J as applyAttribution, K as readManifest, L as formatJson, M as runProjects, N as formatGameOutputNotice, P as parseGameOutput, Q as mergeCliWithConfig, R as writeJsonFile, S as buildPlace, T as emitBuildManifest, U as hashFile, V as formatTestSummary, W as MANIFEST_VERSION, X as extractJsonFromOutput, Z as parseJestOutput, _ as COVERAGE_MANIFEST_PATH, a as loadRojoTree, at as SHARED_TEST_KEYS, b as visitExpression, c as StudioBackend, ct as defineProject, d as createOpenCloudBackend, et as resolveConfig, g as COVERAGE_BUILD_MANIFEST_PATH, h as generateTestScript, i as collectStubMounts, it as ROOT_CLI_KEYS, j as formatExecuteOutput, k as formatAnnotations, l as createStudioBackend, m as buildJestArgv, n as runJestRoblox, nt as GLOBAL_TEST_KEYS, o as runTypecheck, q as writeManifest, r as runSingleOrMulti, rt as JEST_ARGV_EXCLUDED_KEYS, s as resolveAllProjects, st as defineConfig, t as getRawProjects, tt as DEFAULT_CONFIG, u as OpenCloudBackend, v as findRojoProject, w as buildManifestSchema, x as visitStatement, y as visitBlock, z as formatFailure } from "./run-Bh8G33J1.mjs";
1
+ import { $ as parseJestOutput, B as writeJsonFile, C as visitStatement, D as emitBuildManifest, E as buildManifestSchema, F as formatGameOutputNotice, G as hashFile, H as formatResult, I as parseGameOutput, J as readManifest, K as MANIFEST_VERSION, L as writeGameOutput, M as formatJobSummary, N as formatExecuteOutput, O as readBuildManifest, P as runProjects, Q as extractJsonFromOutput, R as createTimingCollector, S as visitExpression, T as BUILD_MANIFEST_VERSION, U as formatTestSummary, V as formatFailure, X as applyAttribution, Y as writeManifest, _ as generateTestScript, a as loadRojoTree, at as JEST_ARGV_EXCLUDED_KEYS, b as findRojoProject, c as StudioBackend, d as createStudioCliBackend, et as mergeCliWithConfig, f as OpenCloudBackend, g as buildJestArgv, i as collectStubMounts, it as GLOBAL_TEST_KEYS, j as formatAnnotations, l as createStudioBackend, lt as defineConfig, n as runJestRoblox, nt as resolveConfig, o as runTypecheck, ot as ROOT_CLI_KEYS, p as createOpenCloudBackend, q as manifestSchema, r as runSingleOrMulti, rt as DEFAULT_CONFIG, s as resolveAllProjects, st as SHARED_TEST_KEYS, t as getRawProjects, tt as loadConfig, u as StudioCliBackend, ut as defineProject, v as COVERAGE_BUILD_MANIFEST_PATH, w as buildPlace, x as visitBlock, y as COVERAGE_MANIFEST_PATH, z as formatJson } from "./run-Cirdb_CB.mjs";
2
2
  import * as path$1 from "node:path";
3
3
  //#region src/artifacts/prepare-artifacts.ts
4
4
  const COVERAGE_DIR = path$1.dirname(COVERAGE_BUILD_MANIFEST_PATH);
@@ -81,4 +81,4 @@ async function buildCleanPlace(config) {
81
81
  });
82
82
  }
83
83
  //#endregion
84
- export { BUILD_MANIFEST_VERSION, DEFAULT_CONFIG, GLOBAL_TEST_KEYS, JEST_ARGV_EXCLUDED_KEYS, MANIFEST_VERSION, OpenCloudBackend, ROOT_CLI_KEYS, SHARED_TEST_KEYS, StudioBackend, buildJestArgv, buildManifestSchema, manifestSchema as coverageManifestSchema, createOpenCloudBackend, createStudioBackend, defineConfig, defineProject, extractJsonFromOutput, formatAnnotations, formatExecuteOutput, formatFailure, formatGameOutputNotice, formatJobSummary, formatJson, formatResult, formatTestSummary, generateTestScript, hashFile, loadConfig, parseGameOutput, parseJestOutput, prepareArtifacts, readBuildManifest, readManifest as readCoverageManifest, resolveConfig, runJestRoblox, runProjects, runTypecheck, visitBlock, visitExpression, visitStatement, writeGameOutput, writeJsonFile };
84
+ export { BUILD_MANIFEST_VERSION, DEFAULT_CONFIG, GLOBAL_TEST_KEYS, JEST_ARGV_EXCLUDED_KEYS, MANIFEST_VERSION, OpenCloudBackend, ROOT_CLI_KEYS, SHARED_TEST_KEYS, StudioBackend, StudioCliBackend, buildJestArgv, buildManifestSchema, manifestSchema as coverageManifestSchema, createOpenCloudBackend, createStudioBackend, createStudioCliBackend, defineConfig, defineProject, extractJsonFromOutput, formatAnnotations, formatExecuteOutput, formatFailure, formatGameOutputNotice, formatJobSummary, formatJson, formatResult, formatTestSummary, generateTestScript, hashFile, loadConfig, parseGameOutput, parseJestOutput, prepareArtifacts, readBuildManifest, readManifest as readCoverageManifest, resolveConfig, runJestRoblox, runProjects, runTypecheck, visitBlock, visitExpression, visitStatement, writeGameOutput, writeJsonFile };