@rulvar/testing 1.15.0 → 1.16.1

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/index.d.ts CHANGED
@@ -43,6 +43,14 @@ declare const DEFAULT_LIVE_SMOKE_ATTEMPTS = 3;
43
43
  * is a bounded spend, so it refuses configurations that are not.
44
44
  */
45
45
  declare const MAX_LIVE_SMOKE_ATTEMPTS = 10;
46
+ /**
47
+ * Hard ceiling on every scheduled backoff: Node's maximum timer delay
48
+ * (2^31 - 1 ms). Anything above it would not sleep longer, it would be
49
+ * clamped to 1 ms with a TimeoutOverflowWarning, so both `baseDelayMs`
50
+ * and the largest scheduled delay, `baseDelayMs * (attempts - 1)`, are
51
+ * validated against this bound before any stream opens.
52
+ */
53
+ declare const MAX_LIVE_SMOKE_DELAY_MS = 2147483647;
46
54
  interface RunLiveSmokeOptions {
47
55
  /**
48
56
  * Total attempts including the first: an integer from 1 to
@@ -53,8 +61,11 @@ interface RunLiveSmokeOptions {
53
61
  /**
54
62
  * Backoff before retry n (1-based) is `baseDelayMs * n`: a
55
63
  * non-negative integer (default 2000). Pass 0 to retry without
56
- * sleeping (unit tests). Anything else rejects with ConfigError
57
- * before any stream opens.
64
+ * sleeping (unit tests). The value AND the largest scheduled delay,
65
+ * `baseDelayMs * (attempts - 1)`, must not exceed
66
+ * {@link MAX_LIVE_SMOKE_DELAY_MS} (Node's timer maximum, which would
67
+ * otherwise clamp the sleep to 1 ms). Anything else rejects with
68
+ * ConfigError before any stream opens.
58
69
  */
59
70
  baseDelayMs?: number;
60
71
  }
@@ -239,4 +250,4 @@ declare function replay(options: {
239
250
  adapters?: ProviderAdapter[];
240
251
  }): ProviderAdapter[];
241
252
  //#endregion
242
- export { type CassetteFixture, type CreateTestEngineOptions, DEFAULT_LIVE_SMOKE_ATTEMPTS, FAKE_MODEL, FAKE_MODEL_REF, FakeAdapter, type FakeAdapterOptions, type FakeCall, type FakeResponder, type FakeToolCallsValue, type FakeWireErrorValue, type LiveSmokeOutcome, M6_ORCH_GOAL, M6_ORCH_PROFILES, M6_ORCH_RUN_ID, MAX_LIVE_SMOKE_ATTEMPTS, RedactFn, type ReplayRunOptions, type RunLiveSmokeOptions, type TestEngine, type TestRunHandle, VcrCassette, VcrMissError, VcrRow, buildFrozenV1JournalRaw, buildM2CassetteFixtures, buildV2GoldenIdentity, createTestEngine, defaultRedact, fakeToolCalls, fakeWireError, handlesInRequest, liveTestEnabled, normalizeM6Entries, readCassette, record, recordLiveCassettes, recordOrchestratorCrash, replay, replayRun, requestHash, runLiveSmoke };
253
+ export { type CassetteFixture, type CreateTestEngineOptions, DEFAULT_LIVE_SMOKE_ATTEMPTS, FAKE_MODEL, FAKE_MODEL_REF, FakeAdapter, type FakeAdapterOptions, type FakeCall, type FakeResponder, type FakeToolCallsValue, type FakeWireErrorValue, type LiveSmokeOutcome, M6_ORCH_GOAL, M6_ORCH_PROFILES, M6_ORCH_RUN_ID, MAX_LIVE_SMOKE_ATTEMPTS, MAX_LIVE_SMOKE_DELAY_MS, RedactFn, type ReplayRunOptions, type RunLiveSmokeOptions, type TestEngine, type TestRunHandle, VcrCassette, VcrMissError, VcrRow, buildFrozenV1JournalRaw, buildM2CassetteFixtures, buildV2GoldenIdentity, createTestEngine, defaultRedact, fakeToolCalls, fakeWireError, handlesInRequest, liveTestEnabled, normalizeM6Entries, readCassette, record, recordLiveCassettes, recordOrchestratorCrash, replay, replayRun, requestHash, runLiveSmoke };
package/dist/index.js CHANGED
@@ -397,6 +397,14 @@ const DEFAULT_LIVE_SMOKE_ATTEMPTS = 3;
397
397
  */
398
398
  const MAX_LIVE_SMOKE_ATTEMPTS = 10;
399
399
  /**
400
+ * Hard ceiling on every scheduled backoff: Node's maximum timer delay
401
+ * (2^31 - 1 ms). Anything above it would not sleep longer, it would be
402
+ * clamped to 1 ms with a TimeoutOverflowWarning, so both `baseDelayMs`
403
+ * and the largest scheduled delay, `baseDelayMs * (attempts - 1)`, are
404
+ * validated against this bound before any stream opens.
405
+ */
406
+ const MAX_LIVE_SMOKE_DELAY_MS = 2147483647;
407
+ /**
400
408
  * Drains `adapter.stream(req)` with a bounded retry policy and classifies
401
409
  * the outcome instead of throwing:
402
410
  *
@@ -424,7 +432,7 @@ const MAX_LIVE_SMOKE_ATTEMPTS = 10;
424
432
  */
425
433
  async function runLiveSmoke(adapter, req, options) {
426
434
  const attempts = validatedAttempts(options?.attempts);
427
- const baseDelayMs = validatedBaseDelayMs(options?.baseDelayMs);
435
+ const baseDelayMs = validatedBaseDelayMs(options?.baseDelayMs, attempts);
428
436
  const retryableErrors = [];
429
437
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
430
438
  const events = [];
@@ -470,12 +478,26 @@ async function runLiveSmoke(adapter, req, options) {
470
478
  }
471
479
  function validatedAttempts(value) {
472
480
  if (value === void 0) return 3;
473
- if (!Number.isSafeInteger(value) || value < 1 || value > 10) throw new ConfigError(`runLiveSmoke attempts must be an integer from 1 to 10, got ${String(value)}`);
481
+ if (!Number.isSafeInteger(value) || value < 1 || value > 10) throw new ConfigError(`runLiveSmoke attempts must be an integer from 1 to 10, got ${String(value)}`, { data: {
482
+ field: "attempts",
483
+ value: String(value),
484
+ max: 10
485
+ } });
474
486
  return value;
475
487
  }
476
- function validatedBaseDelayMs(value) {
488
+ function validatedBaseDelayMs(value, attempts) {
477
489
  if (value === void 0) return 2e3;
478
- if (!Number.isSafeInteger(value) || value < 0) throw new ConfigError(`runLiveSmoke baseDelayMs must be a non-negative integer, got ${String(value)}`);
490
+ if (!Number.isSafeInteger(value) || value < 0 || value > 2147483647) throw new ConfigError(`runLiveSmoke baseDelayMs must be an integer from 0 to ${MAX_LIVE_SMOKE_DELAY_MS} (Node's timer maximum), got ${String(value)}`, { data: {
491
+ field: "baseDelayMs",
492
+ value: String(value),
493
+ max: MAX_LIVE_SMOKE_DELAY_MS
494
+ } });
495
+ const largestDelayMs = value * (attempts - 1);
496
+ if (largestDelayMs > 2147483647) throw new ConfigError(`runLiveSmoke largest scheduled backoff baseDelayMs * (attempts - 1) = ${String(largestDelayMs)} exceeds ${MAX_LIVE_SMOKE_DELAY_MS} (Node's timer maximum); lower baseDelayMs or attempts`, { data: {
497
+ field: "baseDelayMs * (attempts - 1)",
498
+ value: String(largestDelayMs),
499
+ max: MAX_LIVE_SMOKE_DELAY_MS
500
+ } });
479
501
  return value;
480
502
  }
481
503
  function delay(ms) {
@@ -2148,4 +2170,4 @@ function replay(options) {
2148
2170
  });
2149
2171
  }
2150
2172
  //#endregion
2151
- export { DEFAULT_LIVE_SMOKE_ATTEMPTS, FAKE_MODEL, FAKE_MODEL_REF, FakeAdapter, M6_ORCH_GOAL, M6_ORCH_PROFILES, M6_ORCH_RUN_ID, MAX_LIVE_SMOKE_ATTEMPTS, VcrMissError, buildFrozenV1JournalRaw, buildM2CassetteFixtures, buildV2GoldenIdentity, createTestEngine, defaultRedact, fakeToolCalls, fakeWireError, handlesInRequest, liveTestEnabled, normalizeM6Entries, readCassette, record, recordLiveCassettes, recordOrchestratorCrash, replay, replayRun, requestHash, runLiveSmoke };
2173
+ export { DEFAULT_LIVE_SMOKE_ATTEMPTS, FAKE_MODEL, FAKE_MODEL_REF, FakeAdapter, M6_ORCH_GOAL, M6_ORCH_PROFILES, M6_ORCH_RUN_ID, MAX_LIVE_SMOKE_ATTEMPTS, MAX_LIVE_SMOKE_DELAY_MS, VcrMissError, buildFrozenV1JournalRaw, buildM2CassetteFixtures, buildV2GoldenIdentity, createTestEngine, defaultRedact, fakeToolCalls, fakeWireError, handlesInRequest, liveTestEnabled, normalizeM6Entries, readCassette, record, recordLiveCassettes, recordOrchestratorCrash, replay, replayRun, requestHash, runLiveSmoke };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/testing",
3
- "version": "1.15.0",
3
+ "version": "1.16.1",
4
4
  "description": "Rulvar test harness: createTestEngine, FakeAdapter, VCR cassettes, replay-strict runs, matchers.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -26,7 +26,7 @@
26
26
  "access": "public"
27
27
  },
28
28
  "dependencies": {
29
- "@rulvar/core": "1.15.0"
29
+ "@rulvar/core": "1.16.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^22.20.0",