@isentinel/jest-roblox 0.3.9 → 0.3.10

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/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { B as parseGameOutput, J as formatBanner, N as outputMultiResult, P as outputSingleResult, bt as version, ct as loadConfig, ht as VALID_BACKENDS, n as runJestRoblox, st as mergeCliWithConfig, tt as LuauScriptError, v as formatMissingScopes, vt as isValidBackend, y as walkErrorChain, yt as ConfigError } from "./run-BM5sqclu.mjs";
1
+ import { B as parseGameOutput, J as formatBanner, N as outputMultiResult, P as outputSingleResult, bt as version, ct as loadConfig, ht as VALID_BACKENDS, n as runJestRoblox, st as mergeCliWithConfig, tt as LuauScriptError, v as formatMissingScopes, vt as isValidBackend, y as walkErrorChain, yt as ConfigError } from "./run-BKP7mem2.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";
package/dist/index.d.mts CHANGED
@@ -461,9 +461,25 @@ interface OpenCloudOptions {
461
461
  }
462
462
  declare class OpenCloudBackend implements Backend {
463
463
  private readonly runner;
464
- readonly kind = "open-cloud";
464
+ /** One-shot per run so parallel raced tasks don't repeat the warning. */
465
+ private raceWarned;
466
+ readonly kind: "open-cloud";
465
467
  constructor(credentials: OpenCloudCredentials, options?: OpenCloudOptions);
466
468
  runTests(options: BackendOptions): Promise<BackendResult>;
469
+ /**
470
+ * Optimistic version pinning. Pinned tasks
471
+ * (`/versions/{v}/luau-execution-session-tasks`) miss the warm-server pool
472
+ * whenever no server holds the freshly-uploaded version yet, costing a cold
473
+ * place boot per task (~10-45s, scaling with place size). Unpinned tasks
474
+ * boot the latest saved version from the warm pool, so the first attempt
475
+ * runs unpinned with a guard prepended: if the booted server is not on this
476
+ * run's version (a concurrent upload won the boot race), the task returns
477
+ * {@link PLACE_VERSION_RACE_SENTINEL} instead of running. On the sentinel,
478
+ * the task is retried once, pinned — correct by construction, no re-upload
479
+ * (the version exists even when it is no longer head), and no unpinned
480
+ * retry loop for a concurrent uploader to keep winning against.
481
+ */
482
+ private executeGuarded;
467
483
  private runBucket;
468
484
  private runStaticBuckets;
469
485
  private runWorkStealing;
@@ -606,7 +622,7 @@ declare class StudioCliBackend implements Backend {
606
622
  private readonly launch;
607
623
  private readonly studioPath?;
608
624
  private readonly timeout;
609
- readonly kind = "studio-cli";
625
+ readonly kind: "studio-cli";
610
626
  constructor(options?: StudioCliOptions);
611
627
  runTests(options: BackendOptions): Promise<BackendResult>;
612
628
  /**
@@ -635,7 +651,7 @@ declare class StudioBackend implements Backend {
635
651
  private readonly timeout;
636
652
  private preConnected?;
637
653
  private wss?;
638
- readonly kind = "studio";
654
+ readonly kind: "studio";
639
655
  constructor(options: StudioOptions);
640
656
  close(): void;
641
657
  runTests(options: BackendOptions): Promise<BackendResult>;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as writeManifest, A as buildManifestSchema, B as parseGameOutput, C as COVERAGE_MANIFEST_PATH, D as visitStatement, E as visitExpression, F as formatAnnotations, G as formatFailure, H as createTimingCollector, I as formatJobSummary, K as formatResult, L as formatExecuteOutput, M as readBuildManifest, O as buildPlace, Q as readManifest, R as runProjects, S as COVERAGE_BUILD_MANIFEST_PATH, T as visitBlock, U as formatJson, V as writeGameOutput, W as writeJsonFile, X as MANIFEST_VERSION, Y as hashFile, Z as manifestSchema, _ as createOpenCloudBackend, _t as defineProject, a as collectStubMounts, at as parseCoverageEnvelope, b as buildJestArgv, c as runTypecheck, ct as loadConfig, d as resolveAllProjects, dt as GLOBAL_TEST_KEYS, et as applyAttribution, f as StudioBackend, ft as JEST_ARGV_EXCLUDED_KEYS, g as OpenCloudBackend, gt as defineConfig, h as createStudioCliBackend, i as buildImplicitProject, it as normalizeRawCoverage, j as emitBuildManifest, k as BUILD_MANIFEST_VERSION, l as cleanLeftoverStubs, lt as resolveConfig, m as StudioCliBackend, mt as SHARED_TEST_KEYS, n as runJestRoblox, nt as extractJsonFromOutput, o as loadRojoTree, ot as mergeRawCoverage, p as createStudioBackend, pt as ROOT_CLI_KEYS, q as formatTestSummary, r as runSingleOrMulti, rt as parseJestOutput, s as prepareBakedCoverage, st as mergeCliWithConfig, t as getRawProjects, u as generateProjectStubs, ut as DEFAULT_CONFIG, w as findRojoProject, x as generateTestScript, z as formatGameOutputNotice } from "./run-BM5sqclu.mjs";
1
+ import { $ as writeManifest, A as buildManifestSchema, B as parseGameOutput, C as COVERAGE_MANIFEST_PATH, D as visitStatement, E as visitExpression, F as formatAnnotations, G as formatFailure, H as createTimingCollector, I as formatJobSummary, K as formatResult, L as formatExecuteOutput, M as readBuildManifest, O as buildPlace, Q as readManifest, R as runProjects, S as COVERAGE_BUILD_MANIFEST_PATH, T as visitBlock, U as formatJson, V as writeGameOutput, W as writeJsonFile, X as MANIFEST_VERSION, Y as hashFile, Z as manifestSchema, _ as createOpenCloudBackend, _t as defineProject, a as collectStubMounts, at as parseCoverageEnvelope, b as buildJestArgv, c as runTypecheck, ct as loadConfig, d as resolveAllProjects, dt as GLOBAL_TEST_KEYS, et as applyAttribution, f as StudioBackend, ft as JEST_ARGV_EXCLUDED_KEYS, g as OpenCloudBackend, gt as defineConfig, h as createStudioCliBackend, i as buildImplicitProject, it as normalizeRawCoverage, j as emitBuildManifest, k as BUILD_MANIFEST_VERSION, l as cleanLeftoverStubs, lt as resolveConfig, m as StudioCliBackend, mt as SHARED_TEST_KEYS, n as runJestRoblox, nt as extractJsonFromOutput, o as loadRojoTree, ot as mergeRawCoverage, p as createStudioBackend, pt as ROOT_CLI_KEYS, q as formatTestSummary, r as runSingleOrMulti, rt as parseJestOutput, s as prepareBakedCoverage, st as mergeCliWithConfig, t as getRawProjects, u as generateProjectStubs, ut as DEFAULT_CONFIG, w as findRojoProject, x as generateTestScript, z as formatGameOutputNotice } from "./run-BKP7mem2.mjs";
2
2
  import * as path$1 from "node:path";
3
3
  //#region src/artifacts/build-coverage-place.ts
4
4
  const CACHE_DIR$1 = path$1.join(".jest-roblox", "cache");
@@ -36,7 +36,7 @@ import { once } from "node:events";
36
36
  import { parseJSONC, parseYAML } from "confbox";
37
37
  import { Visitor, parseSync } from "oxc-parser";
38
38
  //#region package.json
39
- var version = "0.3.9";
39
+ var version = "0.3.10";
40
40
  //#endregion
41
41
  //#region src/config/errors.ts
42
42
  var ConfigError = class extends Error {
@@ -7554,6 +7554,15 @@ function readStringProperty(err, key) {
7554
7554
  }
7555
7555
  //#endregion
7556
7556
  //#region src/backends/open-cloud.ts
7557
+ /**
7558
+ * The value the version guard returns when the booted server is not running
7559
+ * the version this run uploaded — i.e. a concurrent upload won the boot race.
7560
+ * The backend retries that task once, pinned to the uploaded version.
7561
+ *
7562
+ * Embedded verbatim in a Luau double-quoted string literal, so it must not
7563
+ * contain backslashes, double quotes, or newlines.
7564
+ */
7565
+ const PLACE_VERSION_RACE_SENTINEL = "__JEST_ROBLOX_PLACE_VERSION_RACE__";
7557
7566
  const PARALLEL_AUTO_CAP = 3;
7558
7567
  const BASE_URL_ENV = "JEST_ROBLOX_OPEN_CLOUD_BASE_URL";
7559
7568
  const MAX_RETRIES_ENV = "JEST_ROBLOX_OCALE_MAX_RETRIES";
@@ -7561,11 +7570,14 @@ const DEFAULT_STREAM_POLL_MS = 250;
7561
7570
  const TrailingSlashesPattern = /\/+$/;
7562
7571
  var OpenCloudBackend = class {
7563
7572
  runner;
7573
+ /** One-shot per run so parallel raced tasks don't repeat the warning. */
7574
+ raceWarned = false;
7564
7575
  kind = "open-cloud";
7565
7576
  constructor(credentials, options) {
7566
7577
  this.runner = options?.runner ?? new OcaleRunner(credentials, resolveRunnerOptions());
7567
7578
  }
7568
7579
  async runTests(options) {
7580
+ this.raceWarned = false;
7569
7581
  const { jobs, parallel, scriptOverride, streaming, workStealing } = options;
7570
7582
  if (jobs.length === 0) throw new Error("OpenCloudBackend requires at least one job");
7571
7583
  if (workStealing === true && scriptOverride === void 0) throw new Error("OpenCloudBackend work-stealing mode requires scriptOverride");
@@ -7588,6 +7600,37 @@ var OpenCloudBackend = class {
7588
7600
  }
7589
7601
  };
7590
7602
  }
7603
+ /**
7604
+ * Optimistic version pinning. Pinned tasks
7605
+ * (`/versions/{v}/luau-execution-session-tasks`) miss the warm-server pool
7606
+ * whenever no server holds the freshly-uploaded version yet, costing a cold
7607
+ * place boot per task (~10-45s, scaling with place size). Unpinned tasks
7608
+ * boot the latest saved version from the warm pool, so the first attempt
7609
+ * runs unpinned with a guard prepended: if the booted server is not on this
7610
+ * run's version (a concurrent upload won the boot race), the task returns
7611
+ * {@link PLACE_VERSION_RACE_SENTINEL} instead of running. On the sentinel,
7612
+ * the task is retried once, pinned — correct by construction, no re-upload
7613
+ * (the version exists even when it is no longer head), and no unpinned
7614
+ * retry loop for a concurrent uploader to keep winning against.
7615
+ */
7616
+ async executeGuarded(options) {
7617
+ const { placeVersion, script, timeout } = options;
7618
+ const guarded = injectVersionGuard(script, placeVersion);
7619
+ const first = await this.runner.executeScript({
7620
+ script: guarded,
7621
+ timeout
7622
+ });
7623
+ if (first.outputs[0] !== "__JEST_ROBLOX_PLACE_VERSION_RACE__") return first;
7624
+ if (!this.raceWarned) {
7625
+ this.raceWarned = true;
7626
+ process.stderr.write("Warning: place version raced by a concurrent upload — raced tasks retried pinned (slower, cold place boot).\n");
7627
+ }
7628
+ return this.runner.executeScript({
7629
+ placeVersion,
7630
+ script,
7631
+ timeout
7632
+ });
7633
+ }
7591
7634
  async runBucket(bucket, placeVersion, scriptOverride) {
7592
7635
  const { indices, jobs } = bucket;
7593
7636
  const primary = jobs[0];
@@ -7598,7 +7641,7 @@ var OpenCloudBackend = class {
7598
7641
  };
7599
7642
  });
7600
7643
  const script = scriptOverride ?? generateTestScript(inputs);
7601
- const scriptResult = await this.runner.executeScript({
7644
+ const scriptResult = await this.executeGuarded({
7602
7645
  placeVersion,
7603
7646
  script,
7604
7647
  timeout: primary.config.timeout
@@ -7643,7 +7686,7 @@ var OpenCloudBackend = class {
7643
7686
  },
7644
7687
  places: [{ runTask: async () => {
7645
7688
  launched += 1;
7646
- return this.runner.executeScript({
7689
+ return this.executeGuarded({
7647
7690
  placeVersion,
7648
7691
  script: scriptOverride,
7649
7692
  timeout: primaryConfig.timeout
@@ -7743,6 +7786,22 @@ async function sleep(ms) {
7743
7786
  setTimeout(resolve, ms);
7744
7787
  });
7745
7788
  }
7789
+ /**
7790
+ * Insert the version guard after any leading `--!` directive lines — Luau
7791
+ * honors directives only in the leading comment block, so a plain line-1
7792
+ * prepend would silently disable a caller's `--!strict`/`--!native`/etc.
7793
+ */
7794
+ function injectVersionGuard(script, placeVersion) {
7795
+ const guard = `if game.PlaceVersion ~= ${String(placeVersion)} then return "${PLACE_VERSION_RACE_SENTINEL}" end`;
7796
+ const lines = script.split("\n");
7797
+ let insertAt = 0;
7798
+ for (const line of lines) {
7799
+ if (!line.startsWith("--!")) break;
7800
+ insertAt += 1;
7801
+ }
7802
+ lines.splice(insertAt, 0, guard);
7803
+ return lines.join("\n");
7804
+ }
7746
7805
  function resolveRunnerOptions() {
7747
7806
  const baseUrl = resolveOpenCloudBaseUrl();
7748
7807
  const maxRetries = resolveOcaleMaxRetries();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isentinel/jest-roblox",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "Jest-compatible CLI for running roblox-ts tests via Roblox Open Cloud",
5
5
  "keywords": [
6
6
  "jest",
@@ -48,7 +48,7 @@
48
48
  "!dist/**/*.tsbuildinfo"
49
49
  ],
50
50
  "dependencies": {
51
- "@bedrock-rbx/ocale": "0.1.0-beta.19",
51
+ "@bedrock-rbx/ocale": "0.1.0",
52
52
  "@jridgewell/trace-mapping": "0.3.31",
53
53
  "arktype": "2.2.0",
54
54
  "c12": "4.0.0-beta.5",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@isentinel/eslint-config": "6.0.0-beta.3",
70
- "@isentinel/roblox-ts": "4.0.10",
70
+ "@isentinel/roblox-ts": "4.0.11",
71
71
  "@isentinel/tsconfig": "1.2.0",
72
72
  "@isentinel/weld": "0.2.0",
73
73
  "@oxc-project/types": "0.123.0",