@validate.qa/runner 1.0.96 → 1.0.98

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.
@@ -25,6 +25,58 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  mod
26
26
  ));
27
27
 
28
+ // ../runner-core/dist/services/isolated-child-host.js
29
+ var require_isolated_child_host = __commonJS({
30
+ "../runner-core/dist/services/isolated-child-host.js"(exports2) {
31
+ "use strict";
32
+ Object.defineProperty(exports2, "__esModule", { value: true });
33
+ exports2.serveIsolatedChild = serveIsolatedChild;
34
+ function runFault(fault) {
35
+ switch (fault) {
36
+ case "throw-async":
37
+ setImmediate(() => {
38
+ throw new Error("injected isolated child crash");
39
+ });
40
+ return new Promise(() => {
41
+ });
42
+ case "abort":
43
+ process.abort();
44
+ break;
45
+ case "hang":
46
+ setInterval(() => {
47
+ }, 1e3);
48
+ return new Promise(() => {
49
+ });
50
+ case "oom": {
51
+ const hold = [];
52
+ for (; ; )
53
+ hold.push(new Array(1e6).fill(hold.length));
54
+ }
55
+ case "report-env":
56
+ return Promise.resolve({ ok: true, env: { ...process.env } });
57
+ default:
58
+ break;
59
+ }
60
+ return null;
61
+ }
62
+ function serveIsolatedChild(markerEnv, handle) {
63
+ if (typeof process.send !== "function" || process.env[markerEnv] !== "1")
64
+ return;
65
+ const reply = (message) => {
66
+ process.send?.(message, () => process.exit(0));
67
+ };
68
+ process.on("disconnect", () => process.exit(1));
69
+ process.once("message", async (request) => {
70
+ try {
71
+ reply(await (runFault(request.fault) ?? handle(request.job)));
72
+ } catch (err) {
73
+ reply({ ok: false, message: err instanceof Error ? err.message : String(err) });
74
+ }
75
+ });
76
+ }
77
+ }
78
+ });
79
+
28
80
  // ../../node_modules/adm-zip/util/constants.js
29
81
  var require_constants = __commonJS({
30
82
  "../../node_modules/adm-zip/util/constants.js"(exports2, module2) {
@@ -8915,11 +8967,22 @@ var require_tar_isolated = __commonJS({
8915
8967
  "../runner-core/dist/services/tar-isolated.js"(exports2) {
8916
8968
  "use strict";
8917
8969
  Object.defineProperty(exports2, "__esModule", { value: true });
8918
- exports2.TAR_ISOLATED_MAX_CHILDREN_PER_TENANT = exports2.TAR_ISOLATED_MAX_CHILDREN = exports2.TarIsolationError = exports2.TarIsolationUnavailableError = exports2.TAR_ISOLATION_UNAVAILABLE_MESSAGE = exports2.TAR_CHILD_MARKER_ENV = exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB = exports2.TAR_ISOLATED_TIMEOUT_MS = void 0;
8970
+ exports2.IsolationSlots = exports2.SlotPool = exports2.ISOLATED_MIN_CHILD_HEAP_MB = exports2.ISOLATED_MEMORY_MARGIN_MB = exports2.TAR_ISOLATED_MAX_CHILDREN_PER_TENANT = exports2.TAR_ISOLATED_MAX_CHILDREN = exports2.ISOLATED_MAX_CHILDREN = exports2.TarIsolationError = exports2.TarIsolationUnavailableError = exports2.TAR_ISOLATION_UNAVAILABLE_MESSAGE = exports2.TAR_CHILD_MARKER_ENV = exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB = exports2.TAR_ISOLATED_TIMEOUT_MS = void 0;
8971
+ exports2.isolatedChildEnv = isolatedChildEnv;
8919
8972
  exports2.tarChildEnv = tarChildEnv;
8973
+ exports2.resolveIsolatedChildEntry = resolveIsolatedChildEntry;
8920
8974
  exports2.resolveTarChildEntry = resolveTarChildEntry;
8975
+ exports2.deriveIsolatedChildBudget = deriveIsolatedChildBudget;
8976
+ exports2.deriveMaxIsolatedChildren = deriveMaxIsolatedChildren;
8977
+ exports2.effectiveChildHeapMb = effectiveChildHeapMb;
8978
+ exports2.envHeapCap = envHeapCap;
8979
+ exports2.readMemoryLimitBytes = readMemoryLimitBytes;
8980
+ exports2.isolatedChildBudget = isolatedChildBudget;
8981
+ exports2.maxIsolatedChildren = maxIsolatedChildren;
8921
8982
  exports2.tarIsolationLoad = tarIsolationLoad;
8983
+ exports2.isolationLoad = isolationLoad;
8922
8984
  exports2.runTarJobIsolated = runTarJobIsolated;
8985
+ exports2.runIsolatedJob = runIsolatedJob;
8923
8986
  exports2.extractTarGzIsolated = extractTarGzIsolated;
8924
8987
  exports2.extractZipIsolated = extractZipIsolated;
8925
8988
  exports2.slimRepoArchiveIsolated = slimRepoArchiveIsolated;
@@ -8927,8 +8990,10 @@ var require_tar_isolated = __commonJS({
8927
8990
  var node_child_process_1 = require("child_process");
8928
8991
  var node_fs_1 = require("fs");
8929
8992
  var promises_1 = require("fs/promises");
8993
+ var node_os_1 = require("os");
8930
8994
  var node_path_1 = require("path");
8931
8995
  var node_url_1 = require("url");
8996
+ var node_v8_1 = require("v8");
8932
8997
  var repo_acquire_js_1 = require_repo_acquire();
8933
8998
  exports2.TAR_ISOLATED_TIMEOUT_MS = 5 * 60 * 1e3;
8934
8999
  exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB = 512;
@@ -8957,23 +9022,26 @@ var require_tar_isolated = __commonJS({
8957
9022
  const n = Number(process.env[name]);
8958
9023
  return Number.isSafeInteger(n) && n > 0 ? n : fallback;
8959
9024
  }
8960
- function tarChildEnv(source = process.env) {
9025
+ function isolatedChildEnv(markerEnv, source = process.env) {
8961
9026
  const env = {};
8962
9027
  for (const key of CHILD_ENV_ALLOWLIST) {
8963
9028
  const value = source[key];
8964
9029
  if (typeof value === "string")
8965
9030
  env[key] = value;
8966
9031
  }
8967
- env[exports2.TAR_CHILD_MARKER_ENV] = "1";
9032
+ env[markerEnv] = "1";
8968
9033
  return env;
8969
9034
  }
8970
- function resolveTarChildEntry() {
8971
- if (typeof __dirname !== "string")
9035
+ function tarChildEnv(source = process.env) {
9036
+ return isolatedChildEnv(exports2.TAR_CHILD_MARKER_ENV, source);
9037
+ }
9038
+ function resolveIsolatedChildEntry(dir, basename) {
9039
+ if (typeof dir !== "string")
8972
9040
  return null;
8973
- const js = (0, node_path_1.join)(__dirname, `${CHILD_BASENAME}.js`);
9041
+ const js = (0, node_path_1.join)(dir, `${basename}.js`);
8974
9042
  if ((0, node_fs_1.existsSync)(js))
8975
9043
  return { file: js, execArgv: [] };
8976
- const ts = (0, node_path_1.join)(__dirname, `${CHILD_BASENAME}.ts`);
9044
+ const ts = (0, node_path_1.join)(dir, `${basename}.ts`);
8977
9045
  if (!(0, node_fs_1.existsSync)(ts))
8978
9046
  return null;
8979
9047
  try {
@@ -8982,10 +9050,76 @@ var require_tar_isolated = __commonJS({
8982
9050
  return null;
8983
9051
  }
8984
9052
  }
8985
- exports2.TAR_ISOLATED_MAX_CHILDREN = 2;
9053
+ function resolveTarChildEntry() {
9054
+ return resolveIsolatedChildEntry(typeof __dirname === "string" ? __dirname : void 0, CHILD_BASENAME);
9055
+ }
9056
+ exports2.ISOLATED_MAX_CHILDREN = 2;
9057
+ exports2.TAR_ISOLATED_MAX_CHILDREN = exports2.ISOLATED_MAX_CHILDREN;
8986
9058
  exports2.TAR_ISOLATED_MAX_CHILDREN_PER_TENANT = 1;
8987
- function maxChildren() {
8988
- return positiveIntEnv("REPO_ARCHIVE_EXTRACT_MAX_CHILDREN", exports2.TAR_ISOLATED_MAX_CHILDREN);
9059
+ exports2.ISOLATED_MEMORY_MARGIN_MB = 256;
9060
+ exports2.ISOLATED_MIN_CHILD_HEAP_MB = 256;
9061
+ var MB = 1024 * 1024;
9062
+ function deriveIsolatedChildBudget(input) {
9063
+ const freeMb = (input.memoryLimitBytes - input.heapLimitBytes) / MB - (input.marginMb ?? exports2.ISOLATED_MEMORY_MARGIN_MB);
9064
+ const fit = Math.floor(freeMb / Math.max(1, input.childHeapMb));
9065
+ if (Number.isFinite(fit) && fit >= 1)
9066
+ return { children: Math.min(exports2.ISOLATED_MAX_CHILDREN, fit), heapLimitMb: null };
9067
+ return { children: 1, heapLimitMb: Math.max(exports2.ISOLATED_MIN_CHILD_HEAP_MB, Number.isFinite(freeMb) ? Math.floor(freeMb) : 0) };
9068
+ }
9069
+ function deriveMaxIsolatedChildren(input) {
9070
+ return deriveIsolatedChildBudget(input).children;
9071
+ }
9072
+ function effectiveChildHeapMb(cap, budget) {
9073
+ if (cap.fromEnv || budget.heapLimitMb === null)
9074
+ return cap.mb;
9075
+ return Math.min(cap.mb, budget.heapLimitMb);
9076
+ }
9077
+ function envHeapCap(name, fallback) {
9078
+ const n = Number(process.env[name]);
9079
+ return Number.isSafeInteger(n) && n > 0 ? { mb: n, fromEnv: true } : { mb: fallback, fromEnv: false };
9080
+ }
9081
+ function readMemoryLimitBytes(read = (path) => (0, node_fs_1.readFileSync)(path, "utf8"), total = node_os_1.totalmem) {
9082
+ try {
9083
+ const raw = read("/sys/fs/cgroup/memory.max").trim();
9084
+ const limit = Number(raw);
9085
+ if (raw !== "max" && Number.isSafeInteger(limit) && limit > 0)
9086
+ return Math.min(limit, total());
9087
+ } catch {
9088
+ }
9089
+ return total();
9090
+ }
9091
+ var memoryLimitCache;
9092
+ var childHeapCaps = /* @__PURE__ */ new Set();
9093
+ function isolatedChildBudget() {
9094
+ const override = Number(process.env.ISOLATED_CHILDREN_MAX ?? process.env.REPO_ARCHIVE_EXTRACT_MAX_CHILDREN);
9095
+ if (Number.isSafeInteger(override) && override > 0)
9096
+ return { children: override, heapLimitMb: null };
9097
+ memoryLimitCache ??= readMemoryLimitBytes();
9098
+ let childHeapMb = 0;
9099
+ for (const cap of childHeapCaps)
9100
+ childHeapMb = Math.max(childHeapMb, cap().mb);
9101
+ return deriveIsolatedChildBudget({
9102
+ memoryLimitBytes: memoryLimitCache,
9103
+ heapLimitBytes: (0, node_v8_1.getHeapStatistics)().heap_size_limit,
9104
+ childHeapMb: childHeapMb || exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB
9105
+ });
9106
+ }
9107
+ function maxIsolatedChildren() {
9108
+ return isolatedChildBudget().children;
9109
+ }
9110
+ var heapReductionLogged = false;
9111
+ function childHeapMbFor(slots, log) {
9112
+ const cap = slots.heapCap();
9113
+ const budget = isolatedChildBudget();
9114
+ const mb = effectiveChildHeapMb(cap, budget);
9115
+ if (mb < cap.mb && !heapReductionLogged) {
9116
+ heapReductionLogged = true;
9117
+ const floored = budget.heapLimitMb === exports2.ISOLATED_MIN_CHILD_HEAP_MB ? ` (the ${exports2.ISOLATED_MIN_CHILD_HEAP_MB} MB floor: the ${exports2.ISOLATED_MEMORY_MARGIN_MB} MB margin is not fully kept)` : "";
9118
+ const line = `[isolated-child] isolated child heap reduced to ${mb} MB to fit memory${floored}; set ISOLATED_CHILDREN_MAX or the kind's heap env knob to override`;
9119
+ console.warn(line);
9120
+ log?.(line);
9121
+ }
9122
+ return mb;
8989
9123
  }
8990
9124
  var SlotPool = class {
8991
9125
  capacity;
@@ -9027,31 +9161,59 @@ var require_tar_isolated = __commonJS({
9027
9161
  this.inUse -= 1;
9028
9162
  }
9029
9163
  };
9030
- var globalSlots = new SlotPool(maxChildren);
9031
- var tenantSlots = /* @__PURE__ */ new Map();
9032
- function tenantPool(key) {
9033
- let pool = tenantSlots.get(key);
9034
- if (!pool) {
9035
- pool = new SlotPool(() => exports2.TAR_ISOLATED_MAX_CHILDREN_PER_TENANT);
9036
- tenantSlots.set(key, pool);
9037
- }
9038
- return pool;
9039
- }
9040
- function releaseTenant(key, pool) {
9041
- pool.release();
9042
- if (pool.inUse === 0 && pool.queued === 0 && tenantSlots.get(key) === pool)
9043
- tenantSlots.delete(key);
9044
- }
9164
+ exports2.SlotPool = SlotPool;
9165
+ var processSlots = new SlotPool(maxIsolatedChildren);
9166
+ var IsolationSlots = class {
9167
+ perTenant;
9168
+ heapCap;
9169
+ global = processSlots;
9170
+ /** Per-tenant pools, created on demand and dropped when idle. */
9171
+ tenants = /* @__PURE__ */ new Map();
9172
+ /** This kind's children still running. */
9173
+ live = /* @__PURE__ */ new Set();
9174
+ constructor(perTenant, heapCap) {
9175
+ this.perTenant = perTenant;
9176
+ this.heapCap = heapCap;
9177
+ childHeapCaps.add(heapCap);
9178
+ }
9179
+ tenantPool(key) {
9180
+ let pool = this.tenants.get(key);
9181
+ if (!pool) {
9182
+ pool = new SlotPool(() => this.perTenant);
9183
+ this.tenants.set(key, pool);
9184
+ }
9185
+ return pool;
9186
+ }
9187
+ /** Drop an idle tenant pool (after a release, or a wait that gave up). */
9188
+ forgetIfIdle(key, pool) {
9189
+ if (pool.inUse === 0 && pool.queued === 0 && this.tenants.get(key) === pool)
9190
+ this.tenants.delete(key);
9191
+ }
9192
+ releaseTenant(key, pool) {
9193
+ pool.release();
9194
+ this.forgetIfIdle(key, pool);
9195
+ }
9196
+ /** This kind's live children, process-wide slots held and waiting, and this kind's jobs waiting behind their own tenant. */
9197
+ load() {
9198
+ let tenantQueued = 0;
9199
+ for (const pool of this.tenants.values())
9200
+ tenantQueued += pool.queued;
9201
+ return { children: this.live.size, slots: this.global.inUse, queued: this.global.queued, tenantQueued };
9202
+ }
9203
+ };
9204
+ exports2.IsolationSlots = IsolationSlots;
9205
+ var tarSlots = new IsolationSlots(exports2.TAR_ISOLATED_MAX_CHILDREN_PER_TENANT, () => envHeapCap("REPO_ARCHIVE_EXTRACT_MAX_OLD_SPACE_MB", exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB));
9045
9206
  function tarIsolationLoad() {
9046
- let tenantQueued = 0;
9047
- for (const pool of tenantSlots.values())
9048
- tenantQueued += pool.queued;
9049
- return { children: liveChildren.size, slots: globalSlots.inUse, queued: globalSlots.queued, tenantQueued };
9207
+ return tarSlots.load();
9208
+ }
9209
+ function isolationLoad() {
9210
+ return { children: liveChildren.size, slots: processSlots.inUse, queued: processSlots.queued };
9050
9211
  }
9051
9212
  var liveChildren = /* @__PURE__ */ new Set();
9052
9213
  var exitHookInstalled = false;
9053
- function trackChild(child) {
9214
+ function trackChild(child, slots) {
9054
9215
  liveChildren.add(child);
9216
+ slots.live.add(child);
9055
9217
  if (exitHookInstalled)
9056
9218
  return;
9057
9219
  exitHookInstalled = true;
@@ -9060,6 +9222,23 @@ var require_tar_isolated = __commonJS({
9060
9222
  c.kill("SIGKILL");
9061
9223
  });
9062
9224
  }
9225
+ function untrackChild(child, slots) {
9226
+ liveChildren.delete(child);
9227
+ slots.live.delete(child);
9228
+ }
9229
+ var TAR_CHILD_SPEC = {
9230
+ name: CHILD_BASENAME,
9231
+ dir: typeof __dirname === "string" ? __dirname : void 0,
9232
+ resolveEntry: resolveTarChildEntry,
9233
+ markerEnv: exports2.TAR_CHILD_MARKER_ENV,
9234
+ slots: tarSlots,
9235
+ defaultTimeoutMs: () => positiveIntEnv("REPO_ARCHIVE_EXTRACT_TIMEOUT_MS", exports2.TAR_ISOLATED_TIMEOUT_MS),
9236
+ failureMessage: (subject, detail) => `CONFIG_ISSUE: The ${subject} couldn't be unpacked (${detail}).`,
9237
+ workerName: "archive extractor",
9238
+ childName: "extractor",
9239
+ jobName: "extraction",
9240
+ logPrefix: "[tar-isolated]"
9241
+ };
9063
9242
  function jobCwd(job) {
9064
9243
  switch (job.op) {
9065
9244
  case "extract":
@@ -9073,58 +9252,64 @@ var require_tar_isolated = __commonJS({
9073
9252
  }
9074
9253
  }
9075
9254
  function runTarJobIsolated(job, subject, options = {}) {
9076
- return runJob(job, subject, options, true);
9255
+ return runJob(TAR_CHILD_SPEC, job, subject, { ...options, cwd: jobCwd(job) }, true);
9256
+ }
9257
+ function runIsolatedJob(spec, job, subject, options = {}) {
9258
+ return runJob(spec, job, subject, options, true);
9077
9259
  }
9078
- async function runJob(job, subject, options, limited) {
9079
- const entry = options.testChildFile === void 0 ? resolveTarChildEntry() : options.testChildFile && (0, node_fs_1.existsSync)(options.testChildFile) ? { file: options.testChildFile, execArgv: [] } : null;
9260
+ async function runJob(spec, job, subject, options, limited) {
9261
+ const entry = options.testChildFile === void 0 ? spec.resolveEntry() : options.testChildFile && (0, node_fs_1.existsSync)(options.testChildFile) ? { file: options.testChildFile, execArgv: [] } : null;
9080
9262
  if (!entry)
9081
- throw new TarIsolationUnavailableError(`${CHILD_BASENAME}.js was not found beside ${typeof __dirname === "string" ? __dirname : "this module"}`);
9082
- const abortError = () => Object.assign(new Error(`Extraction of the ${subject} was stopped`), { name: "AbortError" });
9263
+ throw new TarIsolationUnavailableError(`${spec.name}.js was not found beside ${spec.dir ?? "this module"}`);
9264
+ const jobTitle = spec.jobName.charAt(0).toUpperCase() + spec.jobName.slice(1);
9265
+ const abortError = () => Object.assign(new Error(`${jobTitle} of the ${subject} was stopped`), { name: "AbortError" });
9083
9266
  if (options.signal?.aborted)
9084
9267
  throw abortError();
9085
- const timeoutMs = options.timeoutMs ?? positiveIntEnv("REPO_ARCHIVE_EXTRACT_TIMEOUT_MS", exports2.TAR_ISOLATED_TIMEOUT_MS);
9086
- const maxOldSpaceMb = options.maxOldSpaceMb ?? positiveIntEnv("REPO_ARCHIVE_EXTRACT_MAX_OLD_SPACE_MB", exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB);
9087
- const failure = (reason, detail) => new TarIsolationError(reason, `CONFIG_ISSUE: The ${subject} couldn't be unpacked (${detail}).${options.hint ? ` ${options.hint}` : ""}`);
9268
+ const timeoutMs = options.timeoutMs ?? spec.defaultTimeoutMs();
9269
+ const maxOldSpaceMb = options.maxOldSpaceMb ?? childHeapMbFor(spec.slots, options.log);
9270
+ const failure = (reason, detail) => new TarIsolationError(reason, `${spec.failureMessage(subject, detail)}${options.hint ? ` ${options.hint}` : ""}`);
9088
9271
  if (!limited)
9089
- return runChild(job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError);
9090
- const deadline = Date.now() + timeoutMs;
9272
+ return runChild(spec, job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError);
9273
+ const queueMs = options.queueTimeoutMs ?? timeoutMs;
9274
+ const deadline = Date.now() + queueMs;
9091
9275
  const queueTimeout = () => {
9092
- const err = failure("timeout", `no archive extractor was free within ${Math.round(timeoutMs / 1e3)} s`);
9093
- options.log?.(`[tar-isolated] ${err.message}`);
9276
+ const detail = `no ${spec.workerName} was free within ${Math.round(queueMs / 1e3)} s`;
9277
+ const err = spec.busyMessage ? new TarIsolationError("timeout", spec.busyMessage(subject, detail)) : failure("timeout", detail);
9278
+ options.log?.(`${spec.logPrefix} ${err.message}`);
9094
9279
  return err;
9095
9280
  };
9096
- const tenant = options.tenantKey ? { key: options.tenantKey, pool: tenantPool(options.tenantKey) } : null;
9281
+ const { slots } = spec;
9282
+ const tenant = options.tenantKey ? { key: options.tenantKey, pool: slots.tenantPool(options.tenantKey) } : null;
9097
9283
  if (tenant) {
9098
- const got = await tenant.pool.acquire(timeoutMs, options.signal);
9284
+ const got = await tenant.pool.acquire(queueMs, options.signal);
9099
9285
  if (got !== "ok") {
9100
- if (tenant.pool.inUse === 0 && tenant.pool.queued === 0)
9101
- tenantSlots.delete(tenant.key);
9286
+ slots.forgetIfIdle(tenant.key, tenant.pool);
9102
9287
  throw got === "aborted" ? abortError() : queueTimeout();
9103
9288
  }
9104
9289
  }
9105
9290
  let holdsGlobal = false;
9106
9291
  try {
9107
- const got = await globalSlots.acquire(deadline - Date.now(), options.signal);
9292
+ const got = await slots.global.acquire(deadline - Date.now(), options.signal);
9108
9293
  if (got !== "ok")
9109
9294
  throw got === "aborted" ? abortError() : queueTimeout();
9110
9295
  holdsGlobal = true;
9111
9296
  if (options.signal?.aborted)
9112
9297
  throw abortError();
9113
- return await runChild(job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError);
9298
+ return await runChild(spec, job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError);
9114
9299
  } finally {
9115
9300
  if (holdsGlobal)
9116
- globalSlots.release();
9301
+ slots.global.release();
9117
9302
  if (tenant)
9118
- releaseTenant(tenant.key, tenant.pool);
9303
+ slots.releaseTenant(tenant.key, tenant.pool);
9119
9304
  }
9120
9305
  }
9121
- function runChild(job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError) {
9306
+ function runChild(spec, job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError) {
9122
9307
  return new Promise((resolvePromise, rejectPromise) => {
9123
9308
  let child;
9124
9309
  try {
9125
9310
  child = (0, node_child_process_1.spawn)(process.execPath, [...entry.execArgv, `--max-old-space-size=${maxOldSpaceMb}`, entry.file], {
9126
- cwd: jobCwd(job),
9127
- env: tarChildEnv(),
9311
+ cwd: options.cwd,
9312
+ env: isolatedChildEnv(spec.markerEnv),
9128
9313
  stdio: ["ignore", "ignore", "pipe", "ipc"],
9129
9314
  serialization: "json",
9130
9315
  windowsHide: true
@@ -9133,7 +9318,7 @@ var require_tar_isolated = __commonJS({
9133
9318
  rejectPromise(new TarIsolationUnavailableError(err instanceof Error ? err.message : String(err)));
9134
9319
  return;
9135
9320
  }
9136
- trackChild(child);
9321
+ trackChild(child, spec.slots);
9137
9322
  let started = false;
9138
9323
  let timedOut = false;
9139
9324
  let aborted = false;
@@ -9157,7 +9342,7 @@ var require_tar_isolated = __commonJS({
9157
9342
  settled = true;
9158
9343
  clearTimeout(timer);
9159
9344
  options.signal?.removeEventListener("abort", onAbort);
9160
- liveChildren.delete(child);
9345
+ untrackChild(child, spec.slots);
9161
9346
  fn();
9162
9347
  };
9163
9348
  child.stderr?.setEncoding("utf8");
@@ -9200,18 +9385,18 @@ var require_tar_isolated = __commonJS({
9200
9385
  }
9201
9386
  const tail = stderr.trim();
9202
9387
  if (tail)
9203
- options.log?.(`[tar-isolated] extractor stderr: ${tail.replace(/\s+/g, " ").slice(-1e3)}`);
9388
+ options.log?.(`${spec.logPrefix} ${spec.childName} stderr: ${tail.replace(/\s+/g, " ").slice(-1e3)}`);
9204
9389
  let err;
9205
9390
  if (timedOut) {
9206
- err = failure("timeout", `extraction took longer than ${Math.round(timeoutMs / 1e3)} s and was stopped`);
9391
+ err = failure("timeout", `${spec.jobName} took longer than ${Math.round(timeoutMs / 1e3)} s and was stopped`);
9207
9392
  } else if (outOfMemory) {
9208
- err = failure("oom", `extraction ran out of memory (${maxOldSpaceMb} MB heap cap)`);
9393
+ err = failure("oom", `${spec.jobName} ran out of memory (${maxOldSpaceMb} MB heap cap)`);
9209
9394
  } else if (signal) {
9210
- err = failure("signal", `the extractor was killed by ${signal}`);
9395
+ err = failure("signal", `the ${spec.childName} was killed by ${signal}`);
9211
9396
  } else {
9212
- err = failure("crash", `the extractor crashed (exit code ${code ?? "unknown"})`);
9397
+ err = failure("crash", `the ${spec.childName} crashed (exit code ${code ?? "unknown"})`);
9213
9398
  }
9214
- options.log?.(`[tar-isolated] ${err.message}`);
9399
+ options.log?.(`${spec.logPrefix} ${err.message}`);
9215
9400
  rejectPromise(err);
9216
9401
  });
9217
9402
  });
@@ -9269,7 +9454,7 @@ var require_tar_isolated = __commonJS({
9269
9454
  }
9270
9455
  async function probeTarIsolation(options = {}) {
9271
9456
  try {
9272
- await runJob({ op: "ping" }, "archive", { timeoutMs: options.timeoutMs ?? 3e4 }, false);
9457
+ await runJob(TAR_CHILD_SPEC, { op: "ping" }, "archive", { timeoutMs: options.timeoutMs ?? 3e4 }, false);
9273
9458
  return { ok: true, detail: `archives are extracted in a separate process (${resolveTarChildEntry()?.file ?? "unknown"})` };
9274
9459
  } catch (err) {
9275
9460
  return { ok: false, detail: err instanceof Error ? err.message : String(err) };
@@ -9575,7 +9760,6 @@ var require_repo_acquire = __commonJS({
9575
9760
  var MAX_TAR_BYTES_AFTER_END = 1024 * 1024;
9576
9761
  var TAR_OVERHEAD_PER_ENTRY = 4096;
9577
9762
  var TAR_OVERHEAD_BASE = 16 * 1024 * 1024;
9578
- var TAR_COUNTED_TYPES = /* @__PURE__ */ new Set(["File", "OldFile", "ContiguousFile", "Directory", "GNUDumpDir"]);
9579
9763
  function refuseNestedGzip() {
9580
9764
  let head = Buffer.alloc(0);
9581
9765
  return new node_stream_1.Transform({
@@ -9639,7 +9823,7 @@ var require_repo_acquire = __commonJS({
9639
9823
  bad = new Error(`Tar entry too large: ${shownPath} (declared ${size} bytes > ${limits.maxFileBytes} cap)`);
9640
9824
  } else if ((declaredTotal += size) > limits.maxTotalBytes) {
9641
9825
  bad = new Error(`Tar total uncompressed size exceeds ${limits.maxTotalBytes} cap (tar-bomb guard)`);
9642
- } else if (TAR_COUNTED_TYPES.has(String(entry.type)) && !isUnsafeEntryName(path) && ++entries > limits.maxEntries) {
9826
+ } else if (++entries > limits.maxEntries) {
9643
9827
  bad = new Error(`Tar has too many entries: > ${limits.maxEntries} cap`);
9644
9828
  }
9645
9829
  if (bad)
@@ -10052,36 +10236,11 @@ var require_tar_extract_child = __commonJS({
10052
10236
  "use strict";
10053
10237
  Object.defineProperty(exports2, "__esModule", { value: true });
10054
10238
  var promises_1 = require("fs/promises");
10239
+ var isolated_child_host_js_1 = require_isolated_child_host();
10055
10240
  var repo_acquire_js_1 = require_repo_acquire();
10056
10241
  var repo_snapshot_slim_js_1 = require_repo_snapshot_slim();
10057
10242
  var tar_isolated_js_1 = require_tar_isolated();
10058
- async function handle(request) {
10059
- switch (request.fault) {
10060
- case "throw-async":
10061
- setImmediate(() => {
10062
- throw new Error("injected tar child crash");
10063
- });
10064
- return new Promise(() => {
10065
- });
10066
- case "abort":
10067
- process.abort();
10068
- break;
10069
- case "hang":
10070
- setInterval(() => {
10071
- }, 1e3);
10072
- return new Promise(() => {
10073
- });
10074
- case "oom": {
10075
- const hold = [];
10076
- for (; ; )
10077
- hold.push(new Array(1e6).fill(hold.length));
10078
- }
10079
- case "report-env":
10080
- return { ok: true, env: { ...process.env } };
10081
- default:
10082
- break;
10083
- }
10084
- const { job } = request;
10243
+ async function handle(job) {
10085
10244
  switch (job.op) {
10086
10245
  case "ping":
10087
10246
  return { ok: true };
@@ -10099,19 +10258,7 @@ var require_tar_extract_child = __commonJS({
10099
10258
  return { ok: false, message: "Unknown isolated tar job" };
10100
10259
  }
10101
10260
  }
10102
- function reply(message) {
10103
- process.send?.(message, () => process.exit(0));
10104
- }
10105
- if (typeof process.send === "function" && process.env[tar_isolated_js_1.TAR_CHILD_MARKER_ENV] === "1") {
10106
- process.on("disconnect", () => process.exit(1));
10107
- process.once("message", async (request) => {
10108
- try {
10109
- reply(await handle(request));
10110
- } catch (err) {
10111
- reply({ ok: false, message: err instanceof Error ? err.message : String(err) });
10112
- }
10113
- });
10114
- }
10261
+ (0, isolated_child_host_js_1.serveIsolatedChild)(tar_isolated_js_1.TAR_CHILD_MARKER_ENV, handle);
10115
10262
  }
10116
10263
  });
10117
10264
 
@@ -5,22 +5,19 @@ import {
5
5
  require_repo_acquire,
6
6
  require_repo_snapshot_slim,
7
7
  require_tar_isolated
8
- } from "./chunk-2BYRCDXC.mjs";
8
+ } from "./chunk-J4ST6MFT.mjs";
9
9
 
10
- // ../runner-core/dist/services/tar-extract-child.js
11
- var require_tar_extract_child = __commonJS({
12
- "../runner-core/dist/services/tar-extract-child.js"(exports) {
10
+ // ../runner-core/dist/services/isolated-child-host.js
11
+ var require_isolated_child_host = __commonJS({
12
+ "../runner-core/dist/services/isolated-child-host.js"(exports) {
13
13
  "use strict";
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- var promises_1 = __require("fs/promises");
16
- var repo_acquire_js_1 = require_repo_acquire();
17
- var repo_snapshot_slim_js_1 = require_repo_snapshot_slim();
18
- var tar_isolated_js_1 = require_tar_isolated();
19
- async function handle(request) {
20
- switch (request.fault) {
15
+ exports.serveIsolatedChild = serveIsolatedChild;
16
+ function runFault(fault) {
17
+ switch (fault) {
21
18
  case "throw-async":
22
19
  setImmediate(() => {
23
- throw new Error("injected tar child crash");
20
+ throw new Error("injected isolated child crash");
24
21
  });
25
22
  return new Promise(() => {
26
23
  });
@@ -38,11 +35,41 @@ var require_tar_extract_child = __commonJS({
38
35
  hold.push(new Array(1e6).fill(hold.length));
39
36
  }
40
37
  case "report-env":
41
- return { ok: true, env: { ...process.env } };
38
+ return Promise.resolve({ ok: true, env: { ...process.env } });
42
39
  default:
43
40
  break;
44
41
  }
45
- const { job } = request;
42
+ return null;
43
+ }
44
+ function serveIsolatedChild(markerEnv, handle) {
45
+ if (typeof process.send !== "function" || process.env[markerEnv] !== "1")
46
+ return;
47
+ const reply = (message) => {
48
+ process.send?.(message, () => process.exit(0));
49
+ };
50
+ process.on("disconnect", () => process.exit(1));
51
+ process.once("message", async (request) => {
52
+ try {
53
+ reply(await (runFault(request.fault) ?? handle(request.job)));
54
+ } catch (err) {
55
+ reply({ ok: false, message: err instanceof Error ? err.message : String(err) });
56
+ }
57
+ });
58
+ }
59
+ }
60
+ });
61
+
62
+ // ../runner-core/dist/services/tar-extract-child.js
63
+ var require_tar_extract_child = __commonJS({
64
+ "../runner-core/dist/services/tar-extract-child.js"(exports) {
65
+ "use strict";
66
+ Object.defineProperty(exports, "__esModule", { value: true });
67
+ var promises_1 = __require("fs/promises");
68
+ var isolated_child_host_js_1 = require_isolated_child_host();
69
+ var repo_acquire_js_1 = require_repo_acquire();
70
+ var repo_snapshot_slim_js_1 = require_repo_snapshot_slim();
71
+ var tar_isolated_js_1 = require_tar_isolated();
72
+ async function handle(job) {
46
73
  switch (job.op) {
47
74
  case "ping":
48
75
  return { ok: true };
@@ -60,19 +87,7 @@ var require_tar_extract_child = __commonJS({
60
87
  return { ok: false, message: "Unknown isolated tar job" };
61
88
  }
62
89
  }
63
- function reply(message) {
64
- process.send?.(message, () => process.exit(0));
65
- }
66
- if (typeof process.send === "function" && process.env[tar_isolated_js_1.TAR_CHILD_MARKER_ENV] === "1") {
67
- process.on("disconnect", () => process.exit(1));
68
- process.once("message", async (request) => {
69
- try {
70
- reply(await handle(request));
71
- } catch (err) {
72
- reply({ ok: false, message: err instanceof Error ? err.message : String(err) });
73
- }
74
- });
75
- }
90
+ (0, isolated_child_host_js_1.serveIsolatedChild)(tar_isolated_js_1.TAR_CHILD_MARKER_ENV, handle);
76
91
  }
77
92
  });
78
93
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@validate.qa/runner",
3
- "version": "1.0.96",
3
+ "version": "1.0.98",
4
4
  "description": "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
5
5
  "bin": {
6
6
  "validate-runner": "dist/cli.js"
@@ -8,7 +8,7 @@
8
8
  "scripts": {
9
9
  "build": "tsup",
10
10
  "dev": "tsx src/cli.ts",
11
- "test": "node --import tsx --test src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts",
11
+ "test": "node --import tsx --test src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts src/heartbeat-active-runs.test.ts src/result-post-structure-413.test.ts src/result-shed-routes-413.test.ts src/result-media-stale-epoch.test.ts",
12
12
  "test:mobile": "node --import tsx --test --test-concurrency=2 src/runner.mobile-ai-env.test.ts src/mobile-device-queue.test.ts src/device-lease.test.ts src/runner-watchdog.test.ts src/result-payload-budget.test.ts src/result-media-upload.test.ts src/services/appium-*.test.ts src/services/*mobile*.test.ts src/services/mobile/*.test.ts",
13
13
  "smoke:installed": "node scripts/installed-run-smoke.mjs",
14
14
  "prepublishOnly": "npm run build"
@@ -26,13 +26,14 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "@modelcontextprotocol/sdk": "^1.6.1",
29
- "@playwright/mcp": "^0.0.68",
30
- "@playwright/test": "1.59.0-alpha-1771104257000",
29
+ "@playwright/test": "1.64.1",
31
30
  "axe-core": "4.14.0",
32
31
  "commander": "^13.1.0",
33
32
  "dotenv": "^16.4.5",
34
33
  "mockttp": "^4.4.2",
35
- "openai": "^4.73.0"
34
+ "openai": "^4.73.0",
35
+ "playwright": "1.64.1",
36
+ "playwright-core": "1.64.1"
36
37
  },
37
38
  "devDependencies": {
38
39
  "@types/node": "^24.10.1",