@rulvar/testing 1.12.0 → 1.14.0
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as FAKE_MODEL, c as FakeAdapterOptions, d as FakeToolCallsValue, f as FakeWireErrorValue, i as createTestEngine, l as FakeCall, m as fakeWireError, n as TestEngine, o as FAKE_MODEL_REF, p as fakeToolCalls, r as TestRunHandle, s as FakeAdapter, t as CreateTestEngineOptions, u as FakeResponder } from "./test-engine-
|
|
1
|
+
import { a as FAKE_MODEL, c as FakeAdapterOptions, d as FakeToolCallsValue, f as FakeWireErrorValue, i as createTestEngine, l as FakeCall, m as fakeWireError, n as TestEngine, o as FAKE_MODEL_REF, p as fakeToolCalls, r as TestRunHandle, s as FakeAdapter, t as CreateTestEngineOptions, u as FakeResponder } from "./test-engine-DAXwdoOQ.js";
|
|
2
2
|
import { AgentProfile, ChatEvent, ChatRequest, InvocationRole, JournalEntry, JournalStore, ModelCaps, ModelSpec, ProviderAdapter, ResumePreview, RunOutcome, WireError, Workflow, createEngine } from "@rulvar/core";
|
|
3
3
|
|
|
4
4
|
//#region src/replay-strict.d.ts
|
|
@@ -27,6 +27,67 @@ declare function replayRun<A, R>(wf: Workflow<A, R>, args: A, options: ReplayRun
|
|
|
27
27
|
preview: ResumePreview;
|
|
28
28
|
}>;
|
|
29
29
|
//#endregion
|
|
30
|
+
//#region src/live.d.ts
|
|
31
|
+
/**
|
|
32
|
+
* True only when `RULVAR_LIVE_TESTS` is exactly `'1'` AND every named
|
|
33
|
+
* environment key is set to a non-empty value. Gate live tests as
|
|
34
|
+
* `it.skipIf(!liveTestEnabled('ANTHROPIC_API_KEY'))(...)` so an
|
|
35
|
+
* unrelated key in the shell never triggers a paid provider call from
|
|
36
|
+
* an ordinary test run.
|
|
37
|
+
*/
|
|
38
|
+
declare function liveTestEnabled(...requiredEnvKeys: string[]): boolean;
|
|
39
|
+
interface RunLiveSmokeOptions {
|
|
40
|
+
/** Total attempts including the first (default 3, minimum 1). */
|
|
41
|
+
attempts?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Backoff before retry n (1-based) is `baseDelayMs * n` (default
|
|
44
|
+
* 2000). Pass 0 to retry without sleeping (unit tests).
|
|
45
|
+
*/
|
|
46
|
+
baseDelayMs?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The classified result of a bounded live smoke. `attempts` is how many
|
|
50
|
+
* streams were actually opened; only `'exhausted'` reaches the
|
|
51
|
+
* configured bound.
|
|
52
|
+
*/
|
|
53
|
+
type LiveSmokeOutcome = {
|
|
54
|
+
status: "ok";
|
|
55
|
+
attempts: number;
|
|
56
|
+
events: ChatEvent[];
|
|
57
|
+
} | {
|
|
58
|
+
status: "failed";
|
|
59
|
+
attempts: number;
|
|
60
|
+
error: WireError;
|
|
61
|
+
events: ChatEvent[];
|
|
62
|
+
} | {
|
|
63
|
+
status: "exhausted";
|
|
64
|
+
attempts: number;
|
|
65
|
+
errors: WireError[];
|
|
66
|
+
} | {
|
|
67
|
+
status: "no-terminal";
|
|
68
|
+
attempts: number;
|
|
69
|
+
events: ChatEvent[];
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Drains `adapter.stream(req)` with a bounded retry policy and classifies
|
|
73
|
+
* the outcome instead of throwing:
|
|
74
|
+
*
|
|
75
|
+
* - `'ok'`: a `finish` event arrived (the events of the successful
|
|
76
|
+
* attempt are included for further assertions).
|
|
77
|
+
* - `'failed'`: a terminal error with `retryable: false`; never retried,
|
|
78
|
+
* diagnostics preserved.
|
|
79
|
+
* - `'exhausted'`: every attempt ended in a `retryable: true` error; the
|
|
80
|
+
* per-attempt errors are preserved in order.
|
|
81
|
+
* - `'no-terminal'`: the stream ended with neither `finish` nor `error`,
|
|
82
|
+
* which violates the provider SPI; never retried (spending again on a
|
|
83
|
+
* misbehaving adapter is wrong).
|
|
84
|
+
*
|
|
85
|
+
* Retries only ever follow typed retryable errors, so a live smoke never
|
|
86
|
+
* converts a real adapter failure into a pass and never spends more than
|
|
87
|
+
* `attempts` calls.
|
|
88
|
+
*/
|
|
89
|
+
declare function runLiveSmoke(adapter: Pick<ProviderAdapter, "stream">, req: ChatRequest, options?: RunLiveSmokeOptions): Promise<LiveSmokeOutcome>;
|
|
90
|
+
//#endregion
|
|
30
91
|
//#region src/cassettes/build-fixtures.d.ts
|
|
31
92
|
/** One cassette fixture file: id, provenance note, and the journal. */
|
|
32
93
|
/** @internal */
|
|
@@ -152,4 +213,4 @@ declare function replay(options: {
|
|
|
152
213
|
adapters?: ProviderAdapter[];
|
|
153
214
|
}): ProviderAdapter[];
|
|
154
215
|
//#endregion
|
|
155
|
-
export { type CassetteFixture, type CreateTestEngineOptions, FAKE_MODEL, FAKE_MODEL_REF, FakeAdapter, type FakeAdapterOptions, type FakeCall, type FakeResponder, type FakeToolCallsValue, type FakeWireErrorValue, M6_ORCH_GOAL, M6_ORCH_PROFILES, M6_ORCH_RUN_ID, RedactFn, type ReplayRunOptions, type TestEngine, type TestRunHandle, VcrCassette, VcrMissError, VcrRow, buildFrozenV1JournalRaw, buildM2CassetteFixtures, buildV2GoldenIdentity, createTestEngine, defaultRedact, fakeToolCalls, fakeWireError, handlesInRequest, normalizeM6Entries, readCassette, record, recordLiveCassettes, recordOrchestratorCrash, replay, replayRun, requestHash };
|
|
216
|
+
export { type CassetteFixture, type CreateTestEngineOptions, 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, 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
|
@@ -7,7 +7,10 @@ import { createHash } from "node:crypto";
|
|
|
7
7
|
* FakeAdapter (M1-T14): a REAL ProviderAdapter that resolves calls from
|
|
8
8
|
* declared patterns instead of the network, behind the same seam as live
|
|
9
9
|
* adapters, so unit tests run through the full engine: journal, scheduler,
|
|
10
|
-
* budget layers, and event stream. Calls cost zero USD.
|
|
10
|
+
* budget layers, and event stream. Calls cost zero USD. Honors the
|
|
11
|
+
* caller's AbortSignal exactly like a live adapter: an abort ends the
|
|
12
|
+
* stream promptly with no terminal event, so cancellation, deadline, and
|
|
13
|
+
* budget tests observe the same journal shapes as production adapters.
|
|
11
14
|
*/
|
|
12
15
|
/** Scripts a tool-calling turn from a responder. */
|
|
13
16
|
function fakeToolCalls(...calls) {
|
|
@@ -49,6 +52,38 @@ const FAKE_CAPS = {
|
|
|
49
52
|
outputUsdPerMTok: 0
|
|
50
53
|
}
|
|
51
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Races a responder promise against the caller's abort. On abort the
|
|
57
|
+
* pending responder is detached: its eventual value is discarded and an
|
|
58
|
+
* eventual rejection is swallowed (the caller cancelled; a late failure
|
|
59
|
+
* of the abandoned work must not become an unhandled rejection). A
|
|
60
|
+
* rejection that settles first propagates to the caller unchanged.
|
|
61
|
+
*/
|
|
62
|
+
async function raceAbort(pending, signal) {
|
|
63
|
+
if (signal === void 0) return {
|
|
64
|
+
aborted: false,
|
|
65
|
+
value: await pending
|
|
66
|
+
};
|
|
67
|
+
if (signal.aborted) {
|
|
68
|
+
pending.catch(() => void 0);
|
|
69
|
+
return { aborted: true };
|
|
70
|
+
}
|
|
71
|
+
let onAbort = () => void 0;
|
|
72
|
+
const abortPromise = new Promise((resolve) => {
|
|
73
|
+
onAbort = () => resolve({ aborted: true });
|
|
74
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
const raced = await Promise.race([pending.then((value) => ({
|
|
78
|
+
aborted: false,
|
|
79
|
+
value
|
|
80
|
+
})), abortPromise]);
|
|
81
|
+
if (raced.aborted) pending.catch(() => void 0);
|
|
82
|
+
return raced;
|
|
83
|
+
} finally {
|
|
84
|
+
signal.removeEventListener("abort", onAbort);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
52
87
|
function lastUserText(req) {
|
|
53
88
|
for (let i = req.messages.length - 1; i >= 0; i -= 1) {
|
|
54
89
|
const msg = req.messages[i];
|
|
@@ -61,7 +96,10 @@ var FakeAdapter = class {
|
|
|
61
96
|
id = "fake";
|
|
62
97
|
agents;
|
|
63
98
|
mintId = createCanonicalIdMinter();
|
|
64
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Every request this adapter served, in order. A request whose signal
|
|
101
|
+
* was already aborted on arrival was never served and is not recorded.
|
|
102
|
+
*/
|
|
65
103
|
calls = [];
|
|
66
104
|
constructor(options) {
|
|
67
105
|
this.agents = options.agents;
|
|
@@ -83,7 +121,9 @@ var FakeAdapter = class {
|
|
|
83
121
|
}
|
|
84
122
|
return fallback;
|
|
85
123
|
}
|
|
86
|
-
async *stream(req) {
|
|
124
|
+
async *stream(req, signal) {
|
|
125
|
+
const aborted = () => signal?.aborted === true;
|
|
126
|
+
if (aborted()) return;
|
|
87
127
|
const telemetry = req.providerOptions?.rulvar ?? {};
|
|
88
128
|
const call = {
|
|
89
129
|
prompt: lastUserText(req),
|
|
@@ -107,8 +147,13 @@ var FakeAdapter = class {
|
|
|
107
147
|
}
|
|
108
148
|
let value;
|
|
109
149
|
try {
|
|
110
|
-
|
|
150
|
+
if (typeof responder === "function") {
|
|
151
|
+
const raced = await raceAbort(Promise.resolve(responder(call)), signal);
|
|
152
|
+
if (raced.aborted) return;
|
|
153
|
+
value = raced.value;
|
|
154
|
+
} else value = responder;
|
|
111
155
|
} catch (thrown) {
|
|
156
|
+
if (aborted()) return;
|
|
112
157
|
yield {
|
|
113
158
|
type: "error",
|
|
114
159
|
error: {
|
|
@@ -120,14 +165,12 @@ var FakeAdapter = class {
|
|
|
120
165
|
};
|
|
121
166
|
return;
|
|
122
167
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
if (isFakeToolCalls(value)) {
|
|
168
|
+
const events = [];
|
|
169
|
+
if (isFakeWireError(value)) events.push({
|
|
170
|
+
type: "error",
|
|
171
|
+
error: value.error
|
|
172
|
+
});
|
|
173
|
+
else if (isFakeToolCalls(value)) {
|
|
131
174
|
const usage = {
|
|
132
175
|
inputTokens: Math.max(1, Math.ceil(call.prompt.length / 4)),
|
|
133
176
|
outputTokens: Math.max(1, value.calls.length * 8),
|
|
@@ -136,66 +179,70 @@ var FakeAdapter = class {
|
|
|
136
179
|
};
|
|
137
180
|
for (const toolCall of value.calls) {
|
|
138
181
|
const id = this.mintId();
|
|
139
|
-
|
|
182
|
+
events.push({
|
|
140
183
|
type: "tool-call-start",
|
|
141
184
|
id,
|
|
142
185
|
name: toolCall.name
|
|
143
|
-
};
|
|
144
|
-
|
|
186
|
+
});
|
|
187
|
+
events.push({
|
|
145
188
|
type: "tool-call-end",
|
|
146
189
|
id,
|
|
147
190
|
args: toolCall.args
|
|
148
|
-
};
|
|
191
|
+
});
|
|
149
192
|
}
|
|
150
|
-
|
|
193
|
+
events.push({
|
|
151
194
|
type: "finish",
|
|
152
195
|
finish: { reason: "tool-calls" },
|
|
153
196
|
usage
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
200
|
+
const usage = {
|
|
201
|
+
inputTokens: Math.max(1, Math.ceil(call.prompt.length / 4)),
|
|
202
|
+
outputTokens: Math.max(1, Math.ceil(text.length / 4)),
|
|
203
|
+
cacheReadTokens: 0,
|
|
204
|
+
cacheWriteTokens: 0
|
|
154
205
|
};
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
206
|
+
const forcedName = typeof req.toolChoice === "object" ? req.toolChoice.name : void 0;
|
|
207
|
+
if (forcedName !== void 0) {
|
|
208
|
+
let args = value;
|
|
209
|
+
if (typeof value === "string") try {
|
|
210
|
+
args = JSON.parse(value);
|
|
211
|
+
} catch {
|
|
212
|
+
args = { text: value };
|
|
213
|
+
}
|
|
214
|
+
const id = this.mintId();
|
|
215
|
+
events.push({
|
|
216
|
+
type: "tool-call-start",
|
|
217
|
+
id,
|
|
218
|
+
name: forcedName
|
|
219
|
+
});
|
|
220
|
+
events.push({
|
|
221
|
+
type: "tool-call-end",
|
|
222
|
+
id,
|
|
223
|
+
args
|
|
224
|
+
});
|
|
225
|
+
events.push({
|
|
226
|
+
type: "finish",
|
|
227
|
+
finish: { reason: "tool-calls" },
|
|
228
|
+
usage
|
|
229
|
+
});
|
|
230
|
+
} else {
|
|
231
|
+
events.push({
|
|
232
|
+
type: "text-delta",
|
|
233
|
+
text
|
|
234
|
+
});
|
|
235
|
+
events.push({
|
|
236
|
+
type: "finish",
|
|
237
|
+
finish: { reason: "stop" },
|
|
238
|
+
usage
|
|
239
|
+
});
|
|
171
240
|
}
|
|
172
|
-
const id = this.mintId();
|
|
173
|
-
yield {
|
|
174
|
-
type: "tool-call-start",
|
|
175
|
-
id,
|
|
176
|
-
name: forcedName
|
|
177
|
-
};
|
|
178
|
-
yield {
|
|
179
|
-
type: "tool-call-end",
|
|
180
|
-
id,
|
|
181
|
-
args
|
|
182
|
-
};
|
|
183
|
-
yield {
|
|
184
|
-
type: "finish",
|
|
185
|
-
finish: { reason: "tool-calls" },
|
|
186
|
-
usage
|
|
187
|
-
};
|
|
188
|
-
return;
|
|
189
241
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
yield {
|
|
195
|
-
type: "finish",
|
|
196
|
-
finish: { reason: "stop" },
|
|
197
|
-
usage
|
|
198
|
-
};
|
|
242
|
+
for (const event of events) {
|
|
243
|
+
if (aborted()) return;
|
|
244
|
+
yield event;
|
|
245
|
+
}
|
|
199
246
|
}
|
|
200
247
|
};
|
|
201
248
|
//#endregion
|
|
@@ -306,6 +353,78 @@ async function replayRun(wf, args, options) {
|
|
|
306
353
|
};
|
|
307
354
|
}
|
|
308
355
|
//#endregion
|
|
356
|
+
//#region src/live.ts
|
|
357
|
+
/**
|
|
358
|
+
* True only when `RULVAR_LIVE_TESTS` is exactly `'1'` AND every named
|
|
359
|
+
* environment key is set to a non-empty value. Gate live tests as
|
|
360
|
+
* `it.skipIf(!liveTestEnabled('ANTHROPIC_API_KEY'))(...)` so an
|
|
361
|
+
* unrelated key in the shell never triggers a paid provider call from
|
|
362
|
+
* an ordinary test run.
|
|
363
|
+
*/
|
|
364
|
+
function liveTestEnabled(...requiredEnvKeys) {
|
|
365
|
+
if (process.env.RULVAR_LIVE_TESTS !== "1") return false;
|
|
366
|
+
return requiredEnvKeys.every((key) => {
|
|
367
|
+
const value = process.env[key];
|
|
368
|
+
return value !== void 0 && value !== "";
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Drains `adapter.stream(req)` with a bounded retry policy and classifies
|
|
373
|
+
* the outcome instead of throwing:
|
|
374
|
+
*
|
|
375
|
+
* - `'ok'`: a `finish` event arrived (the events of the successful
|
|
376
|
+
* attempt are included for further assertions).
|
|
377
|
+
* - `'failed'`: a terminal error with `retryable: false`; never retried,
|
|
378
|
+
* diagnostics preserved.
|
|
379
|
+
* - `'exhausted'`: every attempt ended in a `retryable: true` error; the
|
|
380
|
+
* per-attempt errors are preserved in order.
|
|
381
|
+
* - `'no-terminal'`: the stream ended with neither `finish` nor `error`,
|
|
382
|
+
* which violates the provider SPI; never retried (spending again on a
|
|
383
|
+
* misbehaving adapter is wrong).
|
|
384
|
+
*
|
|
385
|
+
* Retries only ever follow typed retryable errors, so a live smoke never
|
|
386
|
+
* converts a real adapter failure into a pass and never spends more than
|
|
387
|
+
* `attempts` calls.
|
|
388
|
+
*/
|
|
389
|
+
async function runLiveSmoke(adapter, req, options) {
|
|
390
|
+
const attempts = Math.max(1, options?.attempts ?? 3);
|
|
391
|
+
const baseDelayMs = options?.baseDelayMs ?? 2e3;
|
|
392
|
+
const retryableErrors = [];
|
|
393
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
394
|
+
const events = [];
|
|
395
|
+
for await (const event of adapter.stream(req)) events.push(event);
|
|
396
|
+
if (events.some((event) => event.type === "finish")) return {
|
|
397
|
+
status: "ok",
|
|
398
|
+
attempts: attempt,
|
|
399
|
+
events
|
|
400
|
+
};
|
|
401
|
+
const errorEvent = events.find((event) => event.type === "error");
|
|
402
|
+
if (errorEvent === void 0) return {
|
|
403
|
+
status: "no-terminal",
|
|
404
|
+
attempts: attempt,
|
|
405
|
+
events
|
|
406
|
+
};
|
|
407
|
+
if (!errorEvent.error.retryable) return {
|
|
408
|
+
status: "failed",
|
|
409
|
+
attempts: attempt,
|
|
410
|
+
error: errorEvent.error,
|
|
411
|
+
events
|
|
412
|
+
};
|
|
413
|
+
retryableErrors.push(errorEvent.error);
|
|
414
|
+
if (attempt < attempts && baseDelayMs > 0) await delay(baseDelayMs * attempt);
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
status: "exhausted",
|
|
418
|
+
attempts,
|
|
419
|
+
errors: retryableErrors
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function delay(ms) {
|
|
423
|
+
return new Promise((resolve) => {
|
|
424
|
+
setTimeout(resolve, ms);
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
//#endregion
|
|
309
428
|
//#region src/cassettes/build-fixtures.ts
|
|
310
429
|
/**
|
|
311
430
|
* M2 cassette and frozen-fixture builders (M2-T12). Fixtures are
|
|
@@ -1970,4 +2089,4 @@ function replay(options) {
|
|
|
1970
2089
|
});
|
|
1971
2090
|
}
|
|
1972
2091
|
//#endregion
|
|
1973
|
-
export { FAKE_MODEL, FAKE_MODEL_REF, FakeAdapter, M6_ORCH_GOAL, M6_ORCH_PROFILES, M6_ORCH_RUN_ID, VcrMissError, buildFrozenV1JournalRaw, buildM2CassetteFixtures, buildV2GoldenIdentity, createTestEngine, defaultRedact, fakeToolCalls, fakeWireError, handlesInRequest, normalizeM6Entries, readCassette, record, recordLiveCassettes, recordOrchestratorCrash, replay, replayRun, requestHash };
|
|
2092
|
+
export { FAKE_MODEL, FAKE_MODEL_REF, FakeAdapter, M6_ORCH_GOAL, M6_ORCH_PROFILES, M6_ORCH_RUN_ID, VcrMissError, buildFrozenV1JournalRaw, buildM2CassetteFixtures, buildV2GoldenIdentity, createTestEngine, defaultRedact, fakeToolCalls, fakeWireError, handlesInRequest, liveTestEnabled, normalizeM6Entries, readCassette, record, recordLiveCassettes, recordOrchestratorCrash, replay, replayRun, requestHash, runLiveSmoke };
|
package/dist/matchers.d.ts
CHANGED
|
@@ -48,12 +48,15 @@ declare class FakeAdapter implements ProviderAdapter {
|
|
|
48
48
|
readonly id = "fake";
|
|
49
49
|
private readonly agents;
|
|
50
50
|
private readonly mintId;
|
|
51
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Every request this adapter served, in order. A request whose signal
|
|
53
|
+
* was already aborted on arrival was never served and is not recorded.
|
|
54
|
+
*/
|
|
52
55
|
readonly calls: FakeCall[];
|
|
53
56
|
constructor(options: FakeAdapterOptions);
|
|
54
57
|
caps(this: void): ModelCaps;
|
|
55
58
|
private match;
|
|
56
|
-
stream(req: ChatRequest): AsyncIterable<ChatEvent>;
|
|
59
|
+
stream(req: ChatRequest, signal?: AbortSignal): AsyncIterable<ChatEvent>;
|
|
57
60
|
}
|
|
58
61
|
//#endregion
|
|
59
62
|
//#region src/test-engine.d.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/testing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
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.
|
|
29
|
+
"@rulvar/core": "1.14.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.20.0",
|