@systemfsoftware/vitest 0.0.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.mjs ADDED
@@ -0,0 +1,1523 @@
1
+
2
+ import { a as refuseBareEffect, c as refuseNoAssertion, i as refuseAsync, l as refusePositionalProp, n as narrowVacuous, r as presenceMessage, s as refuseHook, t as narrowNegated, u as refuseUnprovided } from "./refusals-BQtDszyU.mjs";
3
+ import * as V from "vitest";
4
+ import { inject } from "vitest";
5
+ import * as Function from "effect/Function";
6
+ import * as Context from "effect/Context";
7
+ import * as Fiber from "effect/Fiber";
8
+ import * as Equal from "effect/Equal";
9
+ import { Schema } from "effect";
10
+ import * as Effect from "effect/Effect";
11
+ import * as Cause from "effect/Cause";
12
+ import * as Duration from "effect/Duration";
13
+ import * as Exit from "effect/Exit";
14
+ import * as Layer from "effect/Layer";
15
+ import * as Schedule from "effect/Schedule";
16
+ import * as Scheduler from "effect/Scheduler";
17
+ import * as Schema$1 from "effect/Schema";
18
+ import * as Scope from "effect/Scope";
19
+ import * as TestClock from "effect/testing/TestClock";
20
+ import * as TestConsole from "effect/testing/TestConsole";
21
+ import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary";
22
+ import * as Data from "effect/Data";
23
+ import * as Clock from "effect/Clock";
24
+ export * from "vitest";
25
+ //#region src/internal/binding.ts
26
+ /**
27
+ * The run a test's fibers execute in. Effect code reads it through
28
+ * `Fiber.getCurrent`, so no node-only store is needed (KTD4).
29
+ *
30
+ * @internal
31
+ */
32
+ const Running = Context.Reference("effect-vitest/binding", { defaultValue: () => void 0 });
33
+ /** @internal */
34
+ const currentRun = () => Fiber.getCurrent()?.getRef(Running);
35
+ let syncRun = void 0;
36
+ /** @internal */
37
+ const withSyncRun = Function.dual(2, (run, body) => {
38
+ const previous = syncRun;
39
+ syncRun = run;
40
+ try {
41
+ return body();
42
+ } finally {
43
+ syncRun = previous;
44
+ }
45
+ });
46
+ /**
47
+ * The run the current execution belongs to: the fiber's binding first, then the
48
+ * synchronous slot for code that runs outside a fiber.
49
+ *
50
+ * @internal
51
+ */
52
+ const lookupRun = () => currentRun() ?? syncRun;
53
+ const NO_ERRORS = [];
54
+ /** @internal */
55
+ const errorCount = (ctx) => presentErrors(ctx.task.result?.errors).length;
56
+ const presentErrors = (errors) => errors === void 0 ? NO_ERRORS : errors;
57
+ //#endregion
58
+ //#region src/internal/equal.ts
59
+ let registered = false;
60
+ /** @internal */
61
+ const registerEqualTester = () => {
62
+ if (registered) return;
63
+ registered = true;
64
+ V.expect.addEqualityTesters([equalTester]);
65
+ };
66
+ const equalTester = (first, second) => equalTogether(first, second) ? true : void 0;
67
+ const equalTogether = (first, second) => bothEqualable(first, second) && Equal.equals(first, second);
68
+ const bothEqualable = (first, second) => Equal.isEqual(first) && Equal.isEqual(second);
69
+ //#endregion
70
+ //#region src/internal/errors.schema.ts
71
+ /** @internal */
72
+ var NonBooleanVerdict = class extends Schema.TaggedError()("NonBooleanVerdict", { detail: Schema.String }) {
73
+ get message() {
74
+ return this.detail;
75
+ }
76
+ };
77
+ /** @internal */
78
+ var LeakedState = class extends Schema.TaggedError()("LeakedState", { detail: Schema.String }) {
79
+ get message() {
80
+ return this.detail;
81
+ }
82
+ };
83
+ /** @internal */
84
+ var AfterFailedExpect = class extends Schema.TaggedError()("AfterFailedExpect", { detail: Schema.String }) {
85
+ get message() {
86
+ return this.detail;
87
+ }
88
+ };
89
+ /** @internal */
90
+ var Slop = class extends Schema.TaggedError()("Slop", { detail: Schema.String }) {
91
+ get message() {
92
+ return this.detail;
93
+ }
94
+ };
95
+ /** @internal */
96
+ var InvalidBudget = class extends Schema.TaggedError()("InvalidBudget", { detail: Schema.String }) {
97
+ get message() {
98
+ return this.detail;
99
+ }
100
+ };
101
+ //#endregion
102
+ //#region src/internal/owned.ts
103
+ /** @internal */
104
+ const OwnedAssertions = Context.Reference("effect-vitest/owned", { defaultValue: () => false });
105
+ /** @internal */
106
+ const isOwned = () => Fiber.getCurrent()?.getRef(OwnedAssertions) === true;
107
+ /** @internal */
108
+ const owned$1 = (self) => Effect.provideService(self, OwnedAssertions, true);
109
+ /**
110
+ * Counts an Effect-native check as an assertion of the run it happens in, so the run's own gate sees it.
111
+ *
112
+ * @internal
113
+ */
114
+ const recordAssertion$1 = () => {
115
+ const run = lookupRun();
116
+ if (run !== void 0) bump(run);
117
+ };
118
+ const bump = (run) => {
119
+ const { expect } = run.ctx;
120
+ expect.setState({ assertionCalls: expect.getState().assertionCalls + 1 });
121
+ };
122
+ const bindingOf = (running, owned) => ({ bind: (self) => self.pipe(Effect.provideService(Running, running), Effect.provideService(OwnedAssertions, owned)) });
123
+ /**
124
+ * The run binding of the test the caller is executing in.
125
+ *
126
+ * @internal
127
+ */
128
+ const captureRunBinding$1 = Effect.withFiber((fiber) => Effect.succeed(bindingOf(fiber.getRef(Running), fiber.getRef(OwnedAssertions))));
129
+ //#endregion
130
+ //#region src/internal/expect.ts
131
+ registerEqualTester();
132
+ const refuse = (detail) => {
133
+ throw new Slop({ detail });
134
+ };
135
+ const narrowedNames = (negated) => negated ? narrowNegated : narrowVacuous;
136
+ const narrowedName = (property, negated) => namedMatch(property, narrowedNames(negated));
137
+ const namedMatch = (property, names) => typeof property === "string" ? foundMatch(property, names) : void 0;
138
+ const foundMatch = (property, names) => names.find((name) => name === property);
139
+ const refuseNarrowed = (name) => () => refuse(presenceMessage(name));
140
+ const refusedMember = (property, negated) => {
141
+ const name = narrowedName(property, negated);
142
+ return name === void 0 ? void 0 : refuseNarrowed(name);
143
+ };
144
+ const memberAt = (target, property) => Reflect.get(target, property, target);
145
+ /**
146
+ * The narrowed names answer with the refusal, `not` re-enters the guard with the negation flipped, and every
147
+ * other member is Vitest's own, handed over unbound so Chai's property accessors keep working.
148
+ */
149
+ const guardOf = (assertion, negated) => new Proxy(assertion, guardHandler(negated));
150
+ const guardHandler = (negated) => ({ get: (target, property) => guardedMember(target, negated, property) });
151
+ const guardedMember = (target, negated, property) => property === "not" ? guardOf(target.not, !negated) : forwardedMember(target, negated, property);
152
+ const forwardedMember = (target, negated, property) => refusedMember(property, negated) ?? memberAt(target, property);
153
+ const plainAssertion = (actual, message) => counted(guardOf(V.expect(actual, message), false));
154
+ const countsInThisRun = () => lookupRun()?.shadow !== true;
155
+ /**
156
+ * A check outside a soft run counts on the run it happens in: an `owned` region's checks throw, and a
157
+ * check outside every run has no gate to satisfy.
158
+ */
159
+ const counted = (assertion) => {
160
+ if (countsInThisRun()) recordAssertion$1();
161
+ return assertion;
162
+ };
163
+ const softAssertion = (running, actual, message) => guardOf(running.ctx.expect.soft(actual, message), false);
164
+ const isSoftRun = (running) => running.shadow === false && isOwned() === false;
165
+ const liveAssertion = (running, actual, message) => isSoftRun(running) ? softAssertion(running, actual, message) : plainAssertion(actual, message);
166
+ /**
167
+ * A check inside a run is soft: it records the failure and the test stops at the next step. The second run
168
+ * and an `owned` region throw, so their own handler sees the failure.
169
+ */
170
+ const assertionFor = (actual, message) => {
171
+ const running = lookupRun();
172
+ return running === void 0 ? plainAssertion(actual, message) : liveAssertion(running, actual, message);
173
+ };
174
+ Function.dual(2, assertionFor);
175
+ const messageOf = (value) => typeof value === "string" ? value : void 0;
176
+ const applyCheck = (args) => assertionFor(args[0], messageOf(args[1]));
177
+ /** The statics are Vitest's; only `soft` is the fork's, and soft is what a check already is inside a run. */
178
+ const makeExpect = () => new Proxy(V.expect, {
179
+ get: (target, property) => property === "soft" ? assertionFor : memberAt(target, property),
180
+ apply: (_target, _thisArg, args) => applyCheck(args)
181
+ });
182
+ /**
183
+ * The fork's `expect`: soft within one Effect step, refusing the slop forms.
184
+ *
185
+ * @internal
186
+ */
187
+ const expect$1 = makeExpect();
188
+ const refuseHookNow = () => refuse(refuseHook);
189
+ const takesHook = (args) => typeof args[0] === "function";
190
+ const refuseHookPair = (fn, timeout) => {
191
+ return refuseHookNow();
192
+ };
193
+ /**
194
+ * @internal
195
+ */
196
+ const beforeEach$1 = Function.dual(takesHook, refuseHookPair);
197
+ /**
198
+ * @internal
199
+ */
200
+ const afterEach$1 = Function.dual(takesHook, refuseHookPair);
201
+ /**
202
+ * @internal
203
+ */
204
+ const beforeAll$1 = Function.dual(takesHook, V.beforeAll);
205
+ /**
206
+ * @internal
207
+ */
208
+ const afterAll$1 = Function.dual(takesHook, V.afterAll);
209
+ //#endregion
210
+ //#region src/internal/property/defaults.ts
211
+ /** @internal The `test.provide` key the shared config fills with this run's tier's check options. */
212
+ const checkDefaultsKey = "@systemfsoftware/vitest:property-check";
213
+ /** @internal The configured property-check defaults, or `undefined` when the run provided none. */
214
+ const providedCheckDefaults = () => inject(checkDefaultsKey);
215
+ //#endregion
216
+ //#region src/internal/property/coverage.ts
217
+ /**
218
+ * @internal The R14 coverage classes: labelled input predicates, each with a minimum share of runs.
219
+ *
220
+ * A class fails only when QuickCheck's sequential test (`stdConfidence`) is confident its share is below
221
+ * the minimum, and is satisfied once the test is confident the share is at least 0.9 of the minimum
222
+ * (`TOLERANCE` at a certainty of 10^9). The tolerance is what makes the test terminate: a class sitting
223
+ * exactly at its minimum would otherwise draw forever.
224
+ */
225
+ const CERTAINTY = 10 ** 9;
226
+ const TOLERANCE = .9;
227
+ const FIRST_BATCH = 64;
228
+ const BATCH_CAP = 4096;
229
+ const DRAW_CAP = 1e5;
230
+ const FAILURE_HEAD = "coverage";
231
+ /** The certainty's two-sided tail probability: `alpha / 2` for `alpha = 1 / CERTAINTY`. */
232
+ const TAIL_PROBABILITY = 1 / (2 * CERTAINTY);
233
+ const C1 = -.007784894002430293;
234
+ const C2 = -.3223964580411365;
235
+ const C3 = -2.400758277161838;
236
+ const C4 = -2.549732539343734;
237
+ const C5 = 4.374664141464968;
238
+ const C6 = 2.938163982698783;
239
+ const D1 = .007784695709041462;
240
+ const D2 = .3224671290700398;
241
+ const D3 = 2.445134137142996;
242
+ const D4 = 3.754408661907416;
243
+ const TAIL_NUMERATOR = [
244
+ C1,
245
+ C2,
246
+ C3,
247
+ C4,
248
+ C5,
249
+ C6
250
+ ];
251
+ const TAIL_DENOMINATOR = [
252
+ D1,
253
+ D2,
254
+ D3,
255
+ D4
256
+ ];
257
+ /** Horner's rule over a coefficient list, highest power first. */
258
+ const poly = (coefficients, x) => coefficients.reduceRight((total, coefficient) => total * x + coefficient, 0);
259
+ const tailSpread = (p) => Math.sqrt(-2 * Math.log(p));
260
+ const tailInverse = (spread) => poly(TAIL_NUMERATOR, spread) / poly(TAIL_DENOMINATOR, spread);
261
+ /** The inverse standard-normal CDF at the tail probability the certainty asks for. */
262
+ const TAIL_Z = tailInverse(tailSpread(TAIL_PROBABILITY));
263
+ const wilson = (hits, runs, z) => {
264
+ const share = hits / runs;
265
+ const zz = z * z;
266
+ return (share + zz / (2 * runs) + z * Math.sqrt(share * (1 - share) / runs + zz / (4 * runs * runs))) / (1 + zz / runs);
267
+ };
268
+ /**
269
+ * Thrown when a coverage class is confidently below its minimum share of runs.
270
+ *
271
+ * @internal
272
+ */
273
+ var CoverageBelowMinimum = class extends Data.Error {};
274
+ const wilsonLower = (entry, runs) => wilson(entry.hits, runs, -TAIL_Z);
275
+ const wilsonUpper = (entry, runs) => wilson(entry.hits, runs, TAIL_Z);
276
+ const covered = (entry, runs) => wilsonLower(entry, runs) >= TOLERANCE * entry.minimum;
277
+ const uncovered = (entry, runs) => wilsonUpper(entry, runs) < entry.minimum;
278
+ const decided = (entry, runs) => covered(entry, runs) || uncovered(entry, runs);
279
+ const settled = (entry, runs) => runs === 0 ? false : decided(entry, runs);
280
+ const entriesOf = (classes) => Object.keys(classes).map((label) => entryAt(classes, label));
281
+ const entryAt = (classes, label) => [label, classOf(classes, label)];
282
+ const classOf = (classes, label) => classes[label] ?? {
283
+ hits: 0,
284
+ minimum: 0
285
+ };
286
+ const judgeOf = (classes, runs) => ({
287
+ classes: new Map(entriesOf(classes)),
288
+ runs
289
+ });
290
+ const openLabels = (judge) => [...judge.classes].filter(([, entry]) => !settled(entry, judge.runs)).map(([label]) => label);
291
+ const failedEntries = (judge) => [...judge.classes].filter(([, entry]) => uncovered(entry, judge.runs));
292
+ const describeShare = (hits, runs) => `${String(hits)}/${String(runs)}`;
293
+ const describeFailure = (label, entry, runs) => `${FAILURE_HEAD} ${label}: ${describeShare(entry.hits, runs)} of runs, below the required ${String(entry.minimum)}`;
294
+ const reportFailures = (judge) => failedEntries(judge).map(([label, entry]) => describeFailure(label, entry, judge.runs)).join("\n");
295
+ /** @internal */
296
+ const countHit = Function.dual(2, (classes, label) => {
297
+ const prior = classes.get(label);
298
+ if (prior !== void 0) classes.set(label, {
299
+ hits: prior.hits + 1,
300
+ minimum: prior.minimum
301
+ });
302
+ });
303
+ const countLabels = (classes, labels) => {
304
+ for (const label of labels) countHit(classes, label);
305
+ return classes;
306
+ };
307
+ const countDraws = (classes, drawn) => drawn.reduce(countLabels, new Map(classes));
308
+ const withDraws = (judge, drawn) => ({
309
+ classes: countDraws(judge.classes, drawn),
310
+ runs: judge.runs + drawn.length
311
+ });
312
+ const drawBatch = (judge, draw, batch) => withDraws(judge, draw.more(sizeOf(judge, batch)));
313
+ const sizeOf = (judge, batch) => Math.min(batch, DRAW_CAP - judge.runs);
314
+ const grown = (batch) => Math.min(batch * 2, BATCH_CAP);
315
+ const keepDrawing = (judge) => openLabels(judge).length > 0 && judge.runs < DRAW_CAP;
316
+ const toppedUp = (judge, draw, batch) => keepDrawing(judge) ? toppedUp(drawBatch(judge, draw, batch), draw, grown(batch)) : judge;
317
+ const reportOf$1 = (judge) => failedEntries(judge).length === 0 ? void 0 : reportFailures(judge);
318
+ /**
319
+ * Runs R14's sequential draw: tops up the undecided classes until every class is decided or the draw cap
320
+ * is reached, then reports the confidently under-covered classes, naming each label and its observed share.
321
+ *
322
+ * @internal
323
+ */
324
+ const judgeCoverage = (options) => reportOf$1(toppedUp(judgeOf(options.classes, options.runs), options.draw, FIRST_BATCH));
325
+ //#endregion
326
+ //#region src/internal/property/error.schema.ts
327
+ /**
328
+ * Thrown at file end when no property in the file refuted a subject's constant impostor (R12).
329
+ *
330
+ * @internal
331
+ */
332
+ var VacuousProperty = class extends Schema$1.TaggedError()("VacuousProperty", { detail: Schema$1.String }) {
333
+ get message() {
334
+ return this.detail;
335
+ }
336
+ };
337
+ /**
338
+ * Thrown when a law kind is given a record `of`, which cannot be spread into its subject.
339
+ *
340
+ * @internal
341
+ */
342
+ var NonTupleOf = class extends Schema$1.TaggedError()("NonTupleOf", { detail: Schema$1.String }) {
343
+ get message() {
344
+ return this.detail;
345
+ }
346
+ };
347
+ //#endregion
348
+ //#region src/internal/property/impostor.ts
349
+ const SUBJECT_KEY = "the subject";
350
+ const NO_VALUE = "";
351
+ const isCallable = (value) => typeof value === "function";
352
+ const isCallableSubject = (target) => typeof target === "function";
353
+ const describeValue = (value) => isCallable(value) ? "a function" : String(value);
354
+ const describeEntry = (key, value) => `${key} always returned ${describeValue(value)}`;
355
+ const emptyState = () => ({ outputs: /* @__PURE__ */ new Map() });
356
+ const frozenAt = (state, key, compute) => state.outputs.has(key) ? state.outputs.get(key) : remember(state, key, compute());
357
+ const remember = (state, key, value) => {
358
+ state.outputs.set(key, value);
359
+ return value;
360
+ };
361
+ const callOutput = (state, fn, thisArg, args) => frozenAt(state, SUBJECT_KEY, () => Reflect.apply(fn, thisArg, args));
362
+ const memberWrapper = (state, key, fn) => (...values) => frozenAt(state, key, () => Reflect.apply(fn, void 0, values));
363
+ const memberWrapperOf = (state, fn, key) => fn === void 0 ? void 0 : memberWrapper(state, key, fn);
364
+ const appliedImpostor = (state, target, thisArg, args) => isCallableSubject(target) ? callOutput(state, target, thisArg, args) : void 0;
365
+ const memberImpostor = (state, target, key) => memberWrapperOf(state, target[key], key);
366
+ const readImpostor = (state, target, property) => isCallableSubject(target) ? Reflect.get(target, property, target) : memberRead(state, target, property);
367
+ const memberRead = (state, target, property) => typeof property === "string" ? memberImpostor(state, target, property) : void 0;
368
+ const impostorHandler = (state) => ({
369
+ apply: (target, thisArg, args) => appliedImpostor(state, target, thisArg, args),
370
+ get: (target, property) => readImpostor(state, target, property)
371
+ });
372
+ const describeFrozen = (state) => [...state.outputs].map(([key, value]) => describeEntry(key, value)).join("; ");
373
+ /**
374
+ * A constant impostor of `subject`: a function returns its first output forever, and so does each member
375
+ * of a record.
376
+ *
377
+ * @internal
378
+ */
379
+ const impostorOf = (subject) => {
380
+ const state = emptyState();
381
+ return {
382
+ impostor: new Proxy(subject, impostorHandler(state)),
383
+ frozen: () => describeFrozen(state)
384
+ };
385
+ };
386
+ /** @internal */
387
+ const subjectLabel = (subject) => isCallableSubject(subject) ? `a function of ${String(subject.length)} argument(s)` : `a record of ${Object.keys(subject).sort().join(", ")}`;
388
+ const judgeFor = (subject) => ({
389
+ label: subjectLabel(subject),
390
+ properties: [],
391
+ refuters: [],
392
+ frozen: NO_VALUE,
393
+ runs: 0
394
+ });
395
+ const trackProperty = (judge, property) => judge.properties.includes(property) ? judge : {
396
+ ...judge,
397
+ properties: [...judge.properties, property]
398
+ };
399
+ const trackRefuter = (judge, property) => judge.refuters.includes(property) ? judge : {
400
+ ...judge,
401
+ refuters: [...judge.refuters, property]
402
+ };
403
+ const trackVerdict = (judge, property, verdict) => {
404
+ const named = trackProperty(judge, property);
405
+ return verdict.refuted ? trackRefuter(named, property) : {
406
+ ...named,
407
+ frozen: verdict.frozen,
408
+ runs: verdict.runs
409
+ };
410
+ };
411
+ const recordJudge = (state, subject, property, verdict) => {
412
+ const prior = state.judges.get(subject) ?? judgeFor(subject);
413
+ state.judges.set(subject, trackVerdict(prior, property, verdict));
414
+ };
415
+ const isVacuous = (judge) => judge.refuters.length === 0;
416
+ const describeVacuous = (judge) => `${judge.properties.join(", ")}: no property in this file refuted the constant impostor of this subject (${judge.label}), so nothing here pins it down. It held for ${judge.runs} run(s) against a fake that ${judge.frozen === NO_VALUE ? "was never called" : judge.frozen}, whatever the input. Laws that only relate outputs to each other (additivity, idempotence, commutativity, round trips through the subject) hold for such constants. Pin the output to the input: compare against an independent model (\`subject(x)\` equals a straightforward reimplementation), or conjoin a base case (\`subject([one])\` equals its known value). Also check that the body calls the \`subject\` it was given, not the imported implementation.`;
417
+ const finaliseJudges = (state, onProblems) => {
418
+ const vacuous = [...state.judges.values()].filter(isVacuous).map(describeVacuous);
419
+ if (vacuous.length === 0) return;
420
+ throw onProblems(vacuous.join("\n\n"));
421
+ };
422
+ const ledgerOf = (state, onProblems) => ({
423
+ record: (subject, property, verdict) => recordJudge(state, subject, property, verdict),
424
+ finalise: () => finaliseJudges(state, onProblems)
425
+ });
426
+ /**
427
+ * Collects the impostor verdicts of every property in the file and decides, at file end, which subjects
428
+ * nothing refuted.
429
+ *
430
+ * @internal
431
+ */
432
+ const makeFileLedger = (onProblems) => ledgerOf({ judges: /* @__PURE__ */ new Map() }, onProblems);
433
+ //#endregion
434
+ //#region src/internal/property/kinds.ts
435
+ /**
436
+ * @internal The R13 law kinds: `model`, `metamorphic`, `roundTrip` and `invariant` are sugar over the
437
+ * gated property (R11/R12); `idempotent` and `deterministic` are declared exempt — they skip the impostor
438
+ * gate, are reported as exempt, and are the only exemptions.
439
+ *
440
+ * Each kind owns the comparison, so there is nowhere to write `return true`: the author hands over
441
+ * functions, and the kind relates their outputs with Effect's structural `Equal`.
442
+ */
443
+ /**
444
+ * The generated values as the argument list a law subject is called with.
445
+ *
446
+ * @internal
447
+ */
448
+ const spreadValues = (values) => Array.isArray(values) ? values : refuseRecordOf();
449
+ const refuseRecordOf = () => {
450
+ throw new NonTupleOf({ detail: "a law kind spreads the generated values into its subject, so `of` must be a tuple; a record `of` holds functions of the record, which only it.prop reaches" });
451
+ };
452
+ const call = (subject, values) => subject(...spreadValues(values));
453
+ const equalOutputs = (original, transformed) => Equal.equals(original, transformed);
454
+ /** @internal */
455
+ const modelHolds = (oracle) => (subject, values) => Equal.equals(call(subject, values), call(oracle, values));
456
+ /** @internal */
457
+ const metamorphicHolds = (relation) => (subject, values) => (relation.relate ?? equalOutputs)(call(subject, values), call(subject, relation.transform(...spreadValues(values))));
458
+ /** @internal */
459
+ const roundTripHolds = (decode) => (subject, values) => Equal.equals(decode(call(subject, values)), values);
460
+ /** @internal */
461
+ const invariantHolds = (holds) => (subject, values) => holds(call(subject, values), ...spreadValues(values));
462
+ const firstOf = (values) => values[0];
463
+ const sameTwice = (subject, value) => value === void 0 ? false : Equal.equals(subject(subject(value)), subject(value));
464
+ /** @internal */
465
+ const idempotentHolds = () => (subject, values) => sameTwice(subject, firstOf(spreadValues(values)));
466
+ /** @internal */
467
+ const deterministicHolds = () => (subject, values) => Equal.equals(call(subject, values), call(subject, values));
468
+ //#endregion
469
+ //#region src/internal/property/engine.ts
470
+ /**
471
+ * @internal The lawful property engine (R11-R15): a property names the function under test, takes a budget
472
+ * that merges its own fields over the configured default over `runs: 100`, accepts only a literal boolean
473
+ * verdict (or an `Effect` of one), judges coverage classes with QuickCheck's sequential test, and defers the
474
+ * constant-impostor verdict to the end of the file.
475
+ *
476
+ * `of` accepts what upstream accepts — a tuple or record of Schemas or Arbitraries (KTD10) — and `holds`
477
+ * receives the generated values exactly as drawn, typed by the gens that produced them.
478
+ */
479
+ const REWRITE = "it.prop(name, { of, subject, runs }, holds)";
480
+ const DEFAULT_RUNS = 100;
481
+ const isPositiveInteger = (runs) => Number.isInteger(runs) && runs > 0;
482
+ const describeRuns = (runs) => Number.isInteger(runs) ? `${String(runs)}` : "a non-integer";
483
+ const invalidPropertyBudget = (name, runs) => `${name}: pass a positive integer \`runs\`; received ${describeRuns(runs)}. Write ${REWRITE}.`;
484
+ const invalidProvidedBudget = (runs) => `the configured property budget (${checkDefaultsKey}) supplies \`runs\`: ${describeRuns(runs)}; it must be a positive integer.`;
485
+ const requirePositiveRuns = (message, runs) => {
486
+ if (isInvalidRuns(runs)) throw new InvalidBudget({ detail: message(runs) });
487
+ };
488
+ const isInvalidRuns = (runs) => runs !== void 0 && isPositiveInteger(runs) === false;
489
+ /** The property's own explicit fields, a property-level `runs` outranking the same field in `arbitrary`. */
490
+ const explicitOptions = (spec) => spec.runs === void 0 ? { ...spec.arbitrary } : {
491
+ ...spec.arbitrary,
492
+ runs: spec.runs
493
+ };
494
+ const mergedOptions = (provided, explicit) => ({
495
+ runs: DEFAULT_RUNS,
496
+ ...provided,
497
+ ...explicit
498
+ });
499
+ const resolvedRuns = (options) => options.runs ?? DEFAULT_RUNS;
500
+ /**
501
+ * The effective check options: the property's own fields over the configured default over `runs: 100`,
502
+ * merged field by field so a property that sets only `runs` still inherits the configured size and caps.
503
+ */
504
+ const resolveBudget = (name, spec, provided) => {
505
+ const explicit = explicitOptions(spec);
506
+ requirePositiveRuns((runs) => invalidPropertyBudget(name, runs), explicit.runs);
507
+ requirePositiveRuns(invalidProvidedBudget, provided?.runs);
508
+ const options = mergedOptions(provided, explicit);
509
+ return {
510
+ runs: resolvedRuns(options),
511
+ options
512
+ };
513
+ };
514
+ const toArbitrary = (input) => Schema$1.isSchema(input) ? Arbitrary.schema(input) : input;
515
+ const assignArbitrary = (built, of, key) => {
516
+ const input = of[key];
517
+ if (input !== void 0) built[key] = toArbitrary(input);
518
+ };
519
+ const recordArbitraries = (of) => {
520
+ const built = {};
521
+ for (const key of Object.keys(of)) assignArbitrary(built, of, key);
522
+ return built;
523
+ };
524
+ const isTupleOf = (of) => Array.isArray(of);
525
+ function arbitraryOf(of) {
526
+ return isTupleOf(of) ? Arbitrary.all(of.map(toArbitrary)) : Arbitrary.all(recordArbitraries(of));
527
+ }
528
+ const minimumOf = (entry) => entry[1];
529
+ const seedClasses = (entries) => {
530
+ const classes = /* @__PURE__ */ new Map();
531
+ for (const [label, entry] of entries) classes.set(label, {
532
+ hits: 0,
533
+ minimum: minimumOf(entry)
534
+ });
535
+ return classes;
536
+ };
537
+ const satisfiedLabels = (entries, values) => entries.filter(([, entry]) => entry[0](...spreadValues(values))).map(([label]) => label);
538
+ const observeRun = (classes, counter, entries, values) => {
539
+ counter.runs = counter.runs + 1;
540
+ for (const label of satisfiedLabels(entries, values)) countHit(classes, label);
541
+ };
542
+ const drawFor = (entries, arbitrary) => ({ more: (count) => Effect.runSync(Arbitrary.sampleEffect(arbitrary, { count }).pipe(Effect.orDie)).map((values) => satisfiedLabels(entries, values)) });
543
+ const noCoverage = () => ({
544
+ observe: () => void 0,
545
+ judge: () => void 0
546
+ });
547
+ const liveCoverage = (cover, arbitrary) => {
548
+ const entries = Object.entries(cover);
549
+ const classes = seedClasses(entries);
550
+ const counter = { runs: 0 };
551
+ return {
552
+ observe: (values) => observeRun(classes, counter, entries, values),
553
+ judge: () => judgeCoverage({
554
+ classes: Object.fromEntries(classes),
555
+ runs: counter.runs,
556
+ draw: drawFor(entries, arbitrary)
557
+ })
558
+ };
559
+ };
560
+ const makeCoverage = (cover, arbitrary) => cover === void 0 ? noCoverage() : liveCoverage(cover, arbitrary);
561
+ const describeVerdict = (verdict) => Effect.isEffect(verdict) ? "an Effect, which this lane never runs" : `a ${typeof verdict}`;
562
+ const nonBoolean = (verdict, violations) => {
563
+ violations.add(describeVerdict(verdict));
564
+ return true;
565
+ };
566
+ const literalVerdict = (verdict, violations) => typeof verdict === "boolean" ? verdict : nonBoolean(verdict, violations);
567
+ const effectVerdict = (lane, verdict, violations) => lane === "sync" ? Effect.succeed(nonBoolean(verdict, violations)) : Effect.map(verdict, (value) => literalVerdict(value, violations));
568
+ const verdictFor = (run, values, violations) => {
569
+ run.observe(values);
570
+ const verdict = run.holds(run.subject, values);
571
+ return Effect.isEffect(verdict) ? effectVerdict(run.lane, verdict, violations) : Effect.succeed(literalVerdict(verdict, violations));
572
+ };
573
+ const tolerateInterruption = (cause) => Cause.hasInterrupts(cause) ? Effect.interrupt : Effect.fail(cause);
574
+ const guardedVerdict = (run, violations) => (values) => Effect.catchCause(Effect.suspend(() => verdictFor(run, values, violations)), tolerateInterruption);
575
+ const violationMessage = (name, violations) => `${name}: the property returned no boolean; it returned ${violations.join(", ")}. A property must return a literal boolean verdict — asserting with expect(), or returning an Option, Result or object, is not a verdict; write ${REWRITE}.`;
576
+ const runCheck = (run) => {
577
+ const violations = /* @__PURE__ */ new Set();
578
+ return Arbitrary.checkEffect(run.arbitrary, guardedVerdict(run, violations), run.options).pipe(Effect.map((result) => ({
579
+ violations,
580
+ result
581
+ })));
582
+ };
583
+ const reportOf = (checked) => Arbitrary.formatCheckFailure(checked.result);
584
+ const violationOf = (name, checked) => checked.violations.size === 0 ? void 0 : violationMessage(name, [...checked.violations]);
585
+ const isRefuted = (checked) => checked.violations.size > 0 || reportOf(checked) !== void 0;
586
+ const dieViolation = (detail) => Effect.die(new NonBooleanVerdict({ detail }));
587
+ const dieReported = (name, report) => Effect.die(/* @__PURE__ */ new Error(`${name}: the property was falsified. ${report}`));
588
+ const dieUncovered = (failure) => Effect.die(new CoverageBelowMinimum({ message: failure }));
589
+ const impostorHolds = (holds, impostor) => (_subject, values) => holds(impostor.impostor, values);
590
+ const recordImpostor = (run, budget, impostor, checked) => {
591
+ const verdict = {
592
+ refuted: isRefuted(checked),
593
+ frozen: impostor.frozen(),
594
+ runs: budget.runs
595
+ };
596
+ gatedLedger.record(run.subject, run.name, verdict);
597
+ };
598
+ const impostorRun = (run, budget) => {
599
+ const impostor = impostorOf(run.subject);
600
+ const retry = {
601
+ ...run,
602
+ holds: impostorHolds(run.holds, impostor),
603
+ observe: noObserve
604
+ };
605
+ return runCheck(retry).pipe(Effect.map((checked) => recordImpostor(retry, budget, impostor, checked)));
606
+ };
607
+ const noObserve = () => void 0;
608
+ const gateRun = (run, budget, gate) => gate ? impostorRun(run, budget) : Effect.void;
609
+ const finishPassed = (run, budget, coverage, gate) => {
610
+ const failure = coverage.judge();
611
+ return failure === void 0 ? gateRun(run, budget, gate) : dieUncovered(`${run.name}: ${failure}`);
612
+ };
613
+ const settleReport = (run, budget, coverage, gate, report) => report === void 0 ? finishPassed(run, budget, coverage, gate) : dieReported(run.name, report);
614
+ const settle = (registration, run, budget, coverage, checked) => {
615
+ const violation = violationOf(registration.name, checked);
616
+ return violation === void 0 ? settleReport(run, budget, coverage, registration.gate, reportOf(checked)) : dieViolation(violation);
617
+ };
618
+ const checkOf = (registration, arbitrary, budget, observe) => ({
619
+ name: registration.name,
620
+ lane: registration.lane,
621
+ subject: registration.spec.subject,
622
+ holds: registration.holds,
623
+ arbitrary,
624
+ observe,
625
+ options: budget.options
626
+ });
627
+ const program = (registration) => Effect.gen(function* () {
628
+ const budget = resolveBudget(registration.name, registration.spec, providedCheckDefaults());
629
+ const arbitrary = arbitraryOf(registration.spec.of);
630
+ const coverage = makeCoverage(registration.spec.cover, arbitrary);
631
+ const run = checkOf(registration, arbitrary, budget, coverage.observe);
632
+ const checked = yield* runCheck(run);
633
+ yield* settle(registration, run, budget, coverage, checked);
634
+ });
635
+ const programOf = (registration) => () => registration.runtime.provide(program(registration));
636
+ const selfModelMessage = (name) => `${name}: the model is the subject itself; a model law must compare against an independent oracle.`;
637
+ const gatedLedger = makeFileLedger((message) => new VacuousProperty({ detail: message }));
638
+ V.afterAll(() => {
639
+ gatedLedger.finalise();
640
+ });
641
+ const registeredName = (name, kind, exempt) => exempt ? `${name} [exempt: ${kind}]` : name;
642
+ const isSelfModel = (subject, oracle) => Object.is(subject, oracle);
643
+ /**
644
+ * The property surface over one runtime: `prop`, `effectProp`, and the R13 law kinds routed through the gate.
645
+ *
646
+ * @internal
647
+ */
648
+ const makeProperty = (runtime) => {
649
+ const body = (name, spec, holds, lane, gate) => {
650
+ const registration = {
651
+ runtime,
652
+ name,
653
+ spec,
654
+ holds,
655
+ lane,
656
+ gate
657
+ };
658
+ runtime.register(name, programOf(registration));
659
+ };
660
+ const refuseSelfModel = (name) => {
661
+ runtime.register(name, () => Effect.die(new VacuousProperty({ detail: selfModelMessage(name) })));
662
+ };
663
+ const gated = (name, spec, holds, kind, exempt) => body(registeredName(name, kind, exempt), spec, holds, "sync", exempt === false);
664
+ const model = (name, spec, oracle) => isSelfModel(spec.subject, oracle) ? refuseSelfModel(name) : gated(name, spec, modelHolds(oracle), "model", false);
665
+ const idempotent = (name, spec) => gated(name, spec, idempotentHolds(), "idempotent", true);
666
+ const deterministic = (name, spec) => gated(name, spec, deterministicHolds(), "deterministic", true);
667
+ return {
668
+ prop: (name, spec, holds) => body(name, spec, holds, "sync", true),
669
+ effectProp: (name, spec, holds) => body(name, spec, holds, "effect", true),
670
+ law: {
671
+ model,
672
+ metamorphic: (name, spec, relation) => gated(name, spec, metamorphicHolds(relation), "metamorphic", false),
673
+ roundTrip: (name, spec, decode) => gated(name, spec, roundTripHolds(decode), "roundTrip", false),
674
+ invariant: (name, spec, holds) => gated(name, spec, invariantHolds(holds), "invariant", false),
675
+ idempotent,
676
+ deterministic
677
+ }
678
+ };
679
+ };
680
+ //#endregion
681
+ //#region src/internal/step-boundary.ts
682
+ /** @internal */
683
+ const makeStopState = (failed) => ({
684
+ stopped: /* @__PURE__ */ new WeakSet(),
685
+ failed
686
+ });
687
+ const alreadyStopped = (state, fiber) => state.stopped.has(fiber);
688
+ /** @internal */
689
+ const stopChecker = Function.dual(2, (state, fiber) => alreadyStopped(state, fiber) === false && state.failed());
690
+ const markStopped = (state, fiber) => {
691
+ state.stopped.add(fiber);
692
+ };
693
+ /** @internal */
694
+ const stopAfterFailedCheck = Function.dual(2, (scheduler, state) => ({
695
+ executionMode: scheduler.executionMode,
696
+ makeDispatcher: () => scheduler.makeDispatcher(),
697
+ shouldYield: (fiber) => checkFiber(state, scheduler, fiber)
698
+ }));
699
+ const checkFiber = (state, scheduler, fiber) => {
700
+ const steppable = fiber;
701
+ if (stopChecker(state, steppable) === false) return scheduler.shouldYield(fiber);
702
+ markStopped(state, steppable);
703
+ steppable.interruptUnsafe(steppable.id);
704
+ return true;
705
+ };
706
+ //#endregion
707
+ //#region src/internal/test-context.ts
708
+ /** @internal */
709
+ const vitestTestContextKey$1 = "@systemfsoftware/vitest/TestContext";
710
+ /**
711
+ * The running Vitest test, or `null` outside a run; provided on every test the fork runs.
712
+ *
713
+ * @internal
714
+ */
715
+ const VitestTestContext$1 = Context.Reference(vitestTestContextKey$1, { defaultValue: () => null });
716
+ //#endregion
717
+ //#region src/internal/virtual-time.ts
718
+ const roundNanos = (millis) => BigInt(Math.round(millis * 1e6));
719
+ const byWakeOrder = (first, second) => {
720
+ const time = first.at - second.at;
721
+ if (time !== 0) return time;
722
+ return tieBreak(first, second);
723
+ };
724
+ const tieBreak = (first, second) => {
725
+ const role = Number(first.observer) - Number(second.observer);
726
+ if (role !== 0) return role;
727
+ return first.sequence - second.sequence;
728
+ };
729
+ const sortSleepers = (sleepers) => {
730
+ sleepers.sort(byWakeOrder);
731
+ };
732
+ const dueNow = (sleepers, now, observer) => sleepers.filter((sleeper) => sleeper.at <= now && sleeper.observer === observer);
733
+ const removeAll = (time, due) => {
734
+ due.forEach((sleeper) => time.removeSleeper(sleeper));
735
+ };
736
+ const wakeAll = (due) => {
737
+ due.forEach((sleeper) => sleeper.wake());
738
+ };
739
+ const runnable = (frozen, queued) => frozen === false && queued > 0;
740
+ const settledForAdvance = (frozen, pending, queued) => runnable(frozen, queued) && pending === 0;
741
+ const shouldStartAdvance = (watching, time) => watching.checking === false && runnable(watching.frozen, time.sleepers().length);
742
+ const removeEntry = (entries, entry) => {
743
+ const index = entries.indexOf(entry);
744
+ if (index !== -1) entries.splice(index, 1);
745
+ };
746
+ const makeVirtualTime = () => {
747
+ const sleepers = [];
748
+ const state = {
749
+ now: 0,
750
+ sequence: 0,
751
+ pending: 0
752
+ };
753
+ return {
754
+ now: () => state.now,
755
+ sleepers: () => sleepers,
756
+ addSleeper: (sleeper) => {
757
+ sleepers.push(sleeper);
758
+ },
759
+ removeSleeper: (sleeper) => removeEntry(sleepers, sleeper),
760
+ advanceTo: (at) => {
761
+ state.now = Math.max(state.now, at);
762
+ },
763
+ claimSequence: () => claimNext(state),
764
+ pendingTasks: () => state.pending,
765
+ beginTask: () => {
766
+ state.pending = state.pending + 1;
767
+ },
768
+ completeTask: () => {
769
+ state.pending = state.pending - 1;
770
+ }
771
+ };
772
+ };
773
+ const claimNext = (state) => {
774
+ const claimed = state.sequence;
775
+ state.sequence = claimed + 1;
776
+ return claimed;
777
+ };
778
+ const runTracked = (time, task, advance) => {
779
+ try {
780
+ task();
781
+ } finally {
782
+ time.completeTask();
783
+ if (time.pendingTasks() === 0) advance();
784
+ }
785
+ };
786
+ const sleepUntil = (time, advance, observerOf, duration) => {
787
+ if (Duration.toMillis(duration) <= 0) return Effect.void;
788
+ return Effect.flatMap(Effect.fiberId, (fiberId) => Effect.callback((resume) => {
789
+ const sleeper = {
790
+ at: time.now() + Duration.toMillis(duration),
791
+ observer: fiberId === observerOf(),
792
+ sequence: time.claimSequence(),
793
+ wake: () => resume(Effect.void)
794
+ };
795
+ time.addSleeper(sleeper);
796
+ advance();
797
+ return Effect.sync(() => time.removeSleeper(sleeper));
798
+ }));
799
+ };
800
+ /** @internal */
801
+ const makeVirtualRuntime = () => {
802
+ const inner = new Scheduler.MixedScheduler();
803
+ const time = makeVirtualTime();
804
+ const watching = {
805
+ checking: false,
806
+ frozen: false,
807
+ observer: -1
808
+ };
809
+ const advanceWhenIdle = () => {
810
+ if (shouldStartAdvance(watching, time) === false) return;
811
+ watching.checking = true;
812
+ setImmediate(advanceFromIdle);
813
+ };
814
+ const advanceFromIdle = () => {
815
+ watching.checking = false;
816
+ if (settledForAdvance(watching.frozen, time.pendingTasks(), time.sleepers().length) === false) return;
817
+ wakeEarliest(time);
818
+ advanceWhenIdle();
819
+ };
820
+ const scheduler = {
821
+ executionMode: inner.executionMode,
822
+ shouldYield: (fiber) => inner.shouldYield(fiber),
823
+ makeDispatcher: () => {
824
+ const dispatcher = inner.makeDispatcher();
825
+ return {
826
+ scheduleTask: (task, priority) => {
827
+ time.beginTask();
828
+ dispatcher.scheduleTask(() => runTracked(time, task, advanceWhenIdle), priority);
829
+ },
830
+ flush: () => dispatcher.flush()
831
+ };
832
+ }
833
+ };
834
+ return {
835
+ clock: {
836
+ currentTimeMillisUnsafe: () => time.now(),
837
+ currentTimeMillis: Effect.sync(() => time.now()),
838
+ currentTimeNanosUnsafe: () => roundNanos(time.now()),
839
+ currentTimeNanos: Effect.sync(() => roundNanos(time.now())),
840
+ monotonicTimeNanosUnsafe: () => roundNanos(time.now()),
841
+ monotonicTimeNanos: Effect.sync(() => roundNanos(time.now())),
842
+ sleep: (duration) => sleepUntil(time, advanceWhenIdle, () => watching.observer, duration)
843
+ },
844
+ scheduler,
845
+ observe: Effect.flatMap(Effect.fiberId, (id) => Effect.sync(() => {
846
+ watching.observer = id;
847
+ })),
848
+ end: Effect.sync(() => {
849
+ watching.frozen = true;
850
+ })
851
+ };
852
+ };
853
+ const wakeEarliest = (time) => {
854
+ const ordered = [...time.sleepers()];
855
+ sortSleepers(ordered);
856
+ const first = ordered[0];
857
+ if (first === void 0) return;
858
+ time.advanceTo(first.at);
859
+ const due = dueNow(time.sleepers(), time.now(), first.observer);
860
+ removeAll(time, due);
861
+ wakeAll(due);
862
+ };
863
+ /**
864
+ * The clock a virtual-time test runs on: a `Clock` that also answers the
865
+ * `TestClock` interface, so `TestClock.adjust` moves virtual time by letting
866
+ * that much time pass instead of waiting for a real clock (KTD6, R7).
867
+ *
868
+ * @internal
869
+ */
870
+ const virtualClockLayer = (runtime) => Layer.succeed(Clock.Clock, virtualClockOf(runtime));
871
+ const virtualClockOf = (runtime) => {
872
+ const live = runtime.clock;
873
+ return {
874
+ currentTimeMillisUnsafe: () => live.currentTimeMillisUnsafe(),
875
+ currentTimeMillis: Effect.sync(() => live.currentTimeMillisUnsafe()),
876
+ currentTimeNanosUnsafe: () => live.currentTimeNanosUnsafe(),
877
+ currentTimeNanos: Effect.sync(() => live.currentTimeNanosUnsafe()),
878
+ monotonicTimeNanosUnsafe: () => live.monotonicTimeNanosUnsafe(),
879
+ monotonicTimeNanos: Effect.sync(() => live.monotonicTimeNanosUnsafe()),
880
+ sleep: (duration) => live.sleep(duration),
881
+ adjust: (duration) => Effect.sleep(duration),
882
+ setTime: (timestamp) => Effect.suspend(() => Effect.sleep(Duration.millis(Math.max(0, timestamp - live.currentTimeMillisUnsafe())))),
883
+ withLive: (effect) => Effect.provideService(effect, Clock.Clock, live)
884
+ };
885
+ };
886
+ //#endregion
887
+ //#region src/internal/runner.ts
888
+ const getCurrentSuite = V.TestRunner.getCurrentSuite;
889
+ /** @internal */
890
+ const addEqualityTesters$1 = () => {
891
+ registerEqualTester();
892
+ };
893
+ const numbered = (timeout) => ({ timeout });
894
+ const givenOptions = (timeout) => timeout ?? {};
895
+ /** @internal */
896
+ const testOptions = (timeout) => typeof timeout === "number" ? numbered(timeout) : givenOptions(timeout);
897
+ /** @internal */
898
+ const hookTimeout = (timeout) => timeout === void 0 ? void 0 : Duration.toMillis(Duration.fromInputUnsafe(timeout));
899
+ /**
900
+ * The positional form is refused at run time with the R15 rewrite; the public type already refuses it.
901
+ */
902
+ const refusePositional = (spec) => {
903
+ if (lacksInputs(spec)) throw new Slop({ detail: refusePositionalProp });
904
+ };
905
+ const lacksInputs = (spec) => isAbsent(spec) || hasNoOf(spec);
906
+ const isAbsent = (spec) => spec === null || spec === void 0;
907
+ const hasNoOf = (spec) => Object.hasOwn(spec, "of") === false;
908
+ const stopCheckOf = (ctx) => ctx === void 0 ? void 0 : failedAfter(ctx, errorCount(ctx));
909
+ const neverStop = () => void 0;
910
+ /** @internal */
911
+ const realTime = {
912
+ runtime: void 0,
913
+ owned: false,
914
+ stop: stopCheckOf
915
+ };
916
+ /** @internal */
917
+ const perRun = () => ({
918
+ runtime: makeVirtualRuntime(),
919
+ owned: true,
920
+ stop: stopCheckOf
921
+ });
922
+ const propertyEnv = {
923
+ runtime: void 0,
924
+ owned: false,
925
+ stop: neverStop
926
+ };
927
+ const logErrors = (cause) => Effect.forEach(Cause.prettyErrors(cause), (error) => Effect.logError(error), { discard: true });
928
+ const failedAfter = (ctx, before) => () => errorCount(ctx) > before;
929
+ const signalOf = (ctx) => ctx?.signal;
930
+ const signalOnly = (signal) => signal === void 0 ? {} : { signal };
931
+ const withScheduler = (signal, scheduler) => ({
932
+ ...signalOnly(signal),
933
+ scheduler
934
+ });
935
+ const runOptions = (ctx, scheduler) => scheduler === void 0 ? signalOnly(signalOf(ctx)) : withScheduler(signalOf(ctx), scheduler);
936
+ const baseScheduler = (env) => env.runtime?.scheduler;
937
+ const stopScheduler = (base, failedCheck) => stopAfterFailedCheck(base ?? new Scheduler.MixedScheduler(), makeStopState(failedCheck));
938
+ const pickScheduler = (env, failedCheck) => failedCheck === void 0 ? baseScheduler(env) : stopScheduler(baseScheduler(env), failedCheck);
939
+ const runPromise = (effect, ctx, env = realTime) => {
940
+ const failedCheck = env.stop(ctx);
941
+ return runExit(effect, ctx, env, failedCheck).then(reportExit(failedCheck));
942
+ };
943
+ const runExit = (effect, ctx, env, failedCheck) => {
944
+ const program = wrapOwned(effect, ownedRuntime(env));
945
+ return Effect.runPromiseExit(program, runOptions(ctx, pickScheduler(env, failedCheck)));
946
+ };
947
+ const ownedRuntime = (env) => env.owned === false ? void 0 : env.runtime;
948
+ const observeOf = (runtime) => runtime === void 0 ? Effect.void : runtime.observe;
949
+ const endOf = (runtime) => runtime === void 0 ? Effect.void : runtime.end;
950
+ const wrapOwned = (effect, runtime) => observeOf(runtime).pipe(Effect.andThen(effect.pipe(Effect.ensuring(endOf(runtime)))));
951
+ const stoppedSettled = () => Promise.resolve(void 0);
952
+ const stoppedExit = (cause) => Cause.hasInterruptsOnly(cause) ? stoppedSettled() : failExit(cause);
953
+ const stoppedChecked = (cause, failed) => failed ? stoppedExit(cause) : failExit(cause);
954
+ const failedOrThrow = (cause, failedCheck) => failedCheck === void 0 ? failExit(cause) : stoppedChecked(cause, failedCheck());
955
+ const reportExit = (failedCheck) => (exit) => Exit.isSuccess(exit) ? Promise.resolve(exit.value) : failedOrThrow(exit.cause, failedCheck);
956
+ /**
957
+ * A body that needs a service nothing provides dies with Effect's own `Service not found`. That is a
958
+ * test-authoring error — the layers were never wired — so it is reported as the unprovided rewrite (R9)
959
+ * instead of as a raw defect. Failures an `owned` region is responsible for are recovered by their owner
960
+ * and never reach here.
961
+ */
962
+ const isUnprovided = (error) => error instanceof Error && error.message.startsWith("Service not found");
963
+ const failExit = (cause) => logErrors(cause).pipe(Effect.runPromise).then(() => rethrowSquashed(cause));
964
+ const rethrowSquashed = (cause) => {
965
+ const error = Cause.squash(cause);
966
+ if (isUnprovided(error)) throw new Slop({ detail: refuseUnprovided });
967
+ throw error;
968
+ };
969
+ const runTest = (ctx, env = realTime) => (effect) => {
970
+ const promise = runPromise(effect, ctx, env);
971
+ if (ctx !== void 0) trackAbort(ctx, promise);
972
+ return promise;
973
+ };
974
+ const trackAbort = (ctx, promise) => {
975
+ const onAbort = () => {
976
+ ctx.onTestFinished(() => promise.then(noop, noop));
977
+ };
978
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
979
+ promise.then(() => detach(ctx, onAbort), () => detach(ctx, onAbort));
980
+ if (ctx.signal.aborted) onAbort();
981
+ };
982
+ const detach = (ctx, onAbort) => {
983
+ ctx.signal.removeEventListener("abort", onAbort);
984
+ };
985
+ const noop = () => {};
986
+ const clockLayerOf = (env) => env.runtime === void 0 ? TestClock.layer() : virtualClockLayer(env.runtime);
987
+ /** @internal */
988
+ const testEnv = (env) => Layer.mergeAll(TestConsole.layer, clockLayerOf(env));
989
+ /**
990
+ * The run a test's fibers execute in: the test's own context as `Running`, and the same context under the
991
+ * fork's `VitestTestContext` for every library built on the fork.
992
+ */
993
+ const bindRun = (effect, ctx, shadow) => effect.pipe(Effect.provideService(Running, {
994
+ ctx,
995
+ shadow
996
+ }), Effect.provideService(VitestTestContext$1, ctx));
997
+ const isObject = (output) => typeof output === "object" && output !== null;
998
+ const isThenable = (value) => "then" in value ? typeof value.then === "function" : false;
999
+ const isPromiseLike = (output) => isObject(output) && isThenable(output);
1000
+ const refusePromiseBody = () => {
1001
+ throw new Slop({ detail: refuseAsync });
1002
+ };
1003
+ const refuseAfter = (promise) => {
1004
+ Promise.resolve(promise).catch(noop);
1005
+ return refusePromiseBody();
1006
+ };
1007
+ const checkPromiseBody = (output) => {
1008
+ if (isPromiseLike(output)) refuseAfter(output);
1009
+ };
1010
+ const outputIsEffect = (output) => Effect.isEffect(output);
1011
+ const buildSingleRun = (body, mapOne, env, shadow) => (ctx) => {
1012
+ const output = withSyncRun({
1013
+ ctx,
1014
+ shadow
1015
+ }, () => body(ctx));
1016
+ checkPromiseBody(output);
1017
+ return outputIsEffect(output) ? runBuilt(ctx, env, mapOne, output, shadow) : Promise.resolve(void 0);
1018
+ };
1019
+ const runBuilt = (ctx, env, mapOne, output, shadow) => runTest(ctx, env)(mapOne(bindRun(Effect.suspend(() => output), ctx, shadow)));
1020
+ /**
1021
+ * Runs a property's program as the test that registered it, under the same binding the Effect lanes use, so a
1022
+ * check or an annotation inside `holds` belongs to the test that declared the property. The env is the bare
1023
+ * `propertyEnv`: no step boundary interrupts a property, so its `false` verdict still shrinks (R6, U4).
1024
+ */
1025
+ const runProperty = (ctx, program) => buildSingleRun(() => program(), (effect) => effect, propertyEnv, false)(ctx);
1026
+ const property = makeProperty({
1027
+ register: (name, program) => {
1028
+ V.it(name, (ctx) => runProperty(ctx, program));
1029
+ },
1030
+ provide: (effect) => effect
1031
+ });
1032
+ const isName = (first) => typeof first === "string";
1033
+ const byName = (args) => isName(args[0]);
1034
+ const byLayer = (args) => Layer.isLayer(args[0]);
1035
+ const byEffect = (args) => Effect.isEffect(args[0]);
1036
+ const propImpl = (name, spec, holds) => {
1037
+ refusePositional(spec);
1038
+ property.prop(name, spec, holds);
1039
+ };
1040
+ /** @internal */
1041
+ const prop$1 = Function.dual(byName, propImpl);
1042
+ const outcomeOf = (promise) => promise.then(() => void 0, (error) => error);
1043
+ /**
1044
+ * The second run's failure: a diagnosis the fork itself made is rethrown as that diagnosis, because the
1045
+ * second run exists to catch leaked state, not to relabel a refusal as a leak (F7). Anything else is the leak.
1046
+ */
1047
+ const throwSecondRun = (error) => {
1048
+ if (isRefusal(error)) throw error;
1049
+ return throwLeaked(error);
1050
+ };
1051
+ const leakedOrNothing = (error) => {
1052
+ if (error !== void 0) throwSecondRun(error);
1053
+ };
1054
+ const makeTesterWith = (mapEffect, it, envFor, rerunnable) => {
1055
+ const runOne = (ctx, args, self, shadow) => {
1056
+ const env = envFor();
1057
+ return buildSingleRun(() => self(...args), (effect) => mapEffect(effect, env), env, shadow)(ctx);
1058
+ };
1059
+ /**
1060
+ * The second run: the same layer build the first run used, built again, with the shadow flag set so its
1061
+ * checks throw instead of reporting softly (R5, KTD7).
1062
+ */
1063
+ const rerun = (ctx, args, self) => outcomeOf(runOne(ctx, args, self, true)).then(leakedOrNothing);
1064
+ const run = (ctx, args, self) => outcomeOf(runOne(ctx, args, self, false)).then((error) => afterFirst(ctx, args, self, error));
1065
+ const afterFirst = (ctx, args, self, error) => {
1066
+ if (error !== void 0) throwAfterCheck(ctx, error);
1067
+ gateNoAssertion(ctx);
1068
+ return rerunIfClean(ctx, args, self);
1069
+ };
1070
+ const rerunIfClean = (ctx, args, self) => shouldRerun(ctx) ? rerun(ctx, args, self) : Promise.resolve();
1071
+ const shouldRerun = (ctx) => rerunnable && isClean(ctx);
1072
+ const registerOn = (registrar, name, self, timeout) => {
1073
+ registrar(name, testOptions(timeout), (ctx) => run(ctx, [ctx], self));
1074
+ };
1075
+ const dualTest = (register) => Function.dual(byName, register);
1076
+ const f = dualTest((name, self, timeout) => registerOn(it, name, self, timeout));
1077
+ const skip = dualTest((name, self, timeout) => registerOn(it.skip, name, self, timeout));
1078
+ const skipIf = (condition) => dualTest((name, self, timeout) => registerOn(it.skipIf(condition), name, self, timeout));
1079
+ const runIf = (condition) => dualTest((name, self, timeout) => registerOn(it.runIf(condition), name, self, timeout));
1080
+ const only = dualTest((name, self, timeout) => registerOn(it.only, name, self, timeout));
1081
+ const each = (cases) => (name, self, timeout) => it.for(cases)(name, testOptions(timeout), (row, ctx) => run(ctx, [row], self));
1082
+ const fails = dualTest((name, self, timeout) => registerOn(V.it.fails, name, self, timeout));
1083
+ const effectProperty = makeProperty({
1084
+ register: (name, program) => {
1085
+ it(name, (ctx) => runProperty(ctx, program));
1086
+ },
1087
+ provide: (effect) => mapEffect(effect, envFor())
1088
+ });
1089
+ const testerProp = (name, spec, holds) => {
1090
+ refusePositional(spec);
1091
+ effectProperty.effectProp(name, spec, holds);
1092
+ };
1093
+ return Object.assign(f, {
1094
+ skip,
1095
+ skipIf,
1096
+ runIf,
1097
+ only,
1098
+ each,
1099
+ fails,
1100
+ prop: testerProp
1101
+ });
1102
+ };
1103
+ const makeTester = (mapEffect, it = V.it) => makeTesterWith(mapEffect, it, perRun, true);
1104
+ const makeSharedTester = (mapEffect, it, envFor) => makeTesterWith(mapEffect, it, envFor, false);
1105
+ const isSlop = (error) => Schema$1.is(Slop)(error);
1106
+ /** Every failure the fork itself names, as `Schema.is`: the families a second run must not relabel. */
1107
+ const refusalChecks = [
1108
+ isSlop,
1109
+ Schema$1.is(AfterFailedExpect),
1110
+ Schema$1.is(InvalidBudget),
1111
+ Schema$1.is(NonBooleanVerdict),
1112
+ Schema$1.is(LeakedState)
1113
+ ];
1114
+ const isRefusal = (error) => refusalChecks.some((matches) => matches(error));
1115
+ const throwAfterCheck = (ctx, error) => {
1116
+ if (isSlop(error)) throw error;
1117
+ return afterFailedCheck(ctx, error);
1118
+ };
1119
+ const afterFailedCheck = (ctx, error) => {
1120
+ if (errorCount(ctx) > 0) throwAfterFailed();
1121
+ throw error;
1122
+ };
1123
+ const throwAfterFailed = () => {
1124
+ throw new AfterFailedExpect({ detail: "thrown after a failed expect above, most likely caused by it" });
1125
+ };
1126
+ /** Whether a run left no failure behind: the second run is only worth doing for a clean first one. */
1127
+ const isClean = (ctx) => errorCount(ctx) === 0;
1128
+ const silentRun = (ctx) => errorCount(ctx) === 0 && assertionCalls(ctx) === 0;
1129
+ /**
1130
+ * The test's own assertion count. Every check the fork runs inside a test binds to that test's `expect`,
1131
+ * so a sibling test's checks are never visible here.
1132
+ */
1133
+ const assertionCalls = (ctx) => ctx.expect.getState().assertionCalls;
1134
+ const gateNoAssertion = (ctx) => {
1135
+ if (silentRun(ctx)) throwNoAssertion();
1136
+ };
1137
+ const throwNoAssertion = () => {
1138
+ throw new Slop({ detail: refuseNoAssertion });
1139
+ };
1140
+ const clipped = (message) => message.length > 400 ? message.slice(0, 400) : message;
1141
+ const throwLeaked = (outcome) => {
1142
+ throw new LeakedState({ detail: "✗ this test passed, then failed when run again on a fresh build of its services: something keeps state outside its layer, so tests here see each other's changes. The second run failed with: " + clipped(outcome.message) });
1143
+ };
1144
+ const CONCURRENT_BLOCK = {
1145
+ concurrent: true,
1146
+ shuffle: true
1147
+ };
1148
+ const ALONE_BLOCK = {
1149
+ concurrent: false,
1150
+ shuffle: false
1151
+ };
1152
+ const withDefault = (given, fallback) => given ?? fallback;
1153
+ const concurrencyIn = (options) => options?.concurrent;
1154
+ const timeoutIn = (options) => options?.timeout;
1155
+ const excludedIn = (options) => options?.excludeTestServices;
1156
+ const sharedIn = (options) => options?.shared;
1157
+ const memoMapIn = (options) => options?.memoMap;
1158
+ const excludeTestServicesOf = (options) => withDefault(excludedIn(options), false);
1159
+ const blockEnvOf = (inherited) => withDefault(inherited, {
1160
+ runtime: makeVirtualRuntime(),
1161
+ owned: false,
1162
+ stop: stopCheckOf
1163
+ });
1164
+ const memoMapOf = (options) => withDefault(memoMapIn(options), Effect.runSync(Layer.makeMemoMap));
1165
+ const blockHookTimeout = (options) => hookTimeout(timeoutIn(options));
1166
+ const isSharedBlock = (options) => sharedIn(options) === true;
1167
+ /**
1168
+ * A named block's suite options: its tests run concurrently and in shuffled order (R4). A declared-shared
1169
+ * block is the stateful exception — its tests read each other's writes through one build, so it stays in
1170
+ * declaration order and runs alone.
1171
+ */
1172
+ const describeOptions = (options, shared) => mergeBlockOptions(options, shared ? ALONE_BLOCK : CONCURRENT_BLOCK);
1173
+ const mergeBlockOptions = (options, fallback) => ({
1174
+ concurrent: withDefault(concurrencyIn(options), fallback.concurrent),
1175
+ shuffle: fallback.shuffle
1176
+ });
1177
+ /**
1178
+ * The block's layer with the test's own services merged in, unless the block excludes them. `Layer`'s output
1179
+ * is contravariant, so the merged layer is the *narrower* `Layer<R, E>` the callers provide to a run.
1180
+ */
1181
+ const mergeTestEnv = (layer_, env, excludeTestServices) => excludeTestServices ? layer_ : Layer.provideMerge(layer_, env);
1182
+ const sharedContext = (shared, memoMap, scope) => Layer.buildWithMemoMap(shared, memoMap, scope).pipe(Effect.orDie, Effect.cached, Effect.runSync);
1183
+ const makeScopeCloser = (scope) => {
1184
+ let closed = false;
1185
+ return () => {
1186
+ if (closed) return Promise.resolve();
1187
+ closed = true;
1188
+ return runPromise(Scope.close(scope, Exit.void)).then(noop);
1189
+ };
1190
+ };
1191
+ /**
1192
+ * Opens a block from its arguments: an anonymous body, or a named one. The sole implementation behind both
1193
+ * arities, so `layer(L)(fn)` and `layer(L)("name", fn)` cannot drift apart.
1194
+ */
1195
+ const blockOpener = (anonymous, named) => (first, second) => routeBlock(first, second, anonymous, named);
1196
+ const routeBlock = (first, second, anonymous, named) => {
1197
+ if (typeof first === "string") return openNamed(first, second, named);
1198
+ return anonymous(first);
1199
+ };
1200
+ const openNamed = (name, f, named) => {
1201
+ if (f !== void 0) named(name, f);
1202
+ };
1203
+ const layerFor = (layer_, options, inherited) => isSharedBlock(options) ? sharedBlock(layer_, options, inherited) : freshBlock(layer_, options);
1204
+ const freshBlock = (layer_, options) => {
1205
+ const excludeTestServices = excludeTestServicesOf(options);
1206
+ const withTestEnv = (env) => mergeTestEnv(layer_(env), testEnv(env), excludeTestServices);
1207
+ const nestedOpen = (nestedLayer, nestedOptions) => layerFor((env) => Layer.provideMerge(nestedLayer, withTestEnv(env)), {
1208
+ ...nestedOptions,
1209
+ excludeTestServices
1210
+ }, void 0);
1211
+ const nestedLayer = Function.dual(byLayer, nestedOpen);
1212
+ const makeIt = (it) => makeItProxy(it, {
1213
+ effect: makeTester((effect, env) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, Layer.orDie(Layer.fresh(withTestEnv(env))))), it),
1214
+ prop: prop$1,
1215
+ law: property.law,
1216
+ flakyTest: flakyTest$1,
1217
+ layer: nestedLayer
1218
+ }, true);
1219
+ return blockOpener((f) => f(makeIt(V.it)), (name, f) => {
1220
+ V.describe(name, describeOptions(options, false), () => f(makeIt(V.it)));
1221
+ });
1222
+ };
1223
+ const nestedBlock = (nested, memoMap) => ({
1224
+ ...nested,
1225
+ memoMap: Layer.forkMemoMapUnsafe(memoMap),
1226
+ shared: true
1227
+ });
1228
+ const sharedOverrides = (shared, contextEffect, memoMap, blockEnv) => {
1229
+ const nestedOpen = (nestedLayer, nestedOptions) => layerFor(() => Layer.provideMerge(nestedLayer, shared), nestedBlock(nestedOptions, memoMap), blockEnv);
1230
+ const nestedLayer = Function.dual(byLayer, nestedOpen);
1231
+ return {
1232
+ effect: makeSharedTester((effect) => Effect.flatMap(contextEffect, (context) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, context))), V.it, () => blockEnv),
1233
+ prop: prop$1,
1234
+ law: property.law,
1235
+ flakyTest: flakyTest$1,
1236
+ layer: nestedLayer
1237
+ };
1238
+ };
1239
+ const sharedBlock = (layer_, options, inherited) => {
1240
+ const blockEnv = blockEnvOf(inherited);
1241
+ const memoMap = memoMapOf(options);
1242
+ const scope = Scope.makeUnsafe();
1243
+ const shared = mergeTestEnv(layer_(blockEnv), testEnv(blockEnv), excludeTestServicesOf(options));
1244
+ const contextEffect = sharedContext(shared, memoMap, scope);
1245
+ const makeIt = (it) => makeItProxy(it, sharedOverrides(shared, contextEffect, memoMap, blockEnv), false);
1246
+ const closeScope = makeScopeCloser(scope);
1247
+ return blockOpener((f) => sharedAnonymous(f, makeIt, options, blockTasksOf(), blockEnv, contextEffect, closeScope), (name, f) => describeShared(name, f, makeIt, options, contextEffect, closeScope, blockEnv));
1248
+ };
1249
+ const describeShared = (name, f, makeIt, options, contextEffect, closeScope, blockEnv) => {
1250
+ V.describe(name, describeOptions(options, true), () => {
1251
+ V.beforeAll(() => runPromise(Effect.asVoid(contextEffect), void 0, blockEnv), blockHookTimeout(options));
1252
+ V.afterAll(() => closeScope(), blockHookTimeout(options));
1253
+ return f(makeIt(V.it));
1254
+ });
1255
+ };
1256
+ const blockTasksOf = () => suiteTasks(getCurrentSuite());
1257
+ const suiteTasks = (suite) => suite.tasks;
1258
+ const sharedAnonymous = (register, makeIt, options, blockTasks, blockEnv, contextEffect, closeScope) => {
1259
+ const previousTasks = new Set(blockTasks);
1260
+ register(makeIt(V.it));
1261
+ const added = collectTasks(blockTasksOf().filter((task) => previousTasks.has(task) === false));
1262
+ if (added.length === 0) {
1263
+ V.afterAll(() => closeScope(), blockHookTimeout(options));
1264
+ return;
1265
+ }
1266
+ openAddedTests(added, blockEnv, contextEffect, closeScope, options);
1267
+ };
1268
+ const openAddedTests = (added, blockEnv, contextEffect, closeScope, options) => {
1269
+ const blockTaskSet = new Set(added);
1270
+ V.beforeEach((ctx) => sharedBeforeEach(ctx, blockTaskSet, countDown(added.length, closeScope), contextEffect, blockEnv), blockHookTimeout(options));
1271
+ V.afterAll(() => closeScope(), blockHookTimeout(options));
1272
+ };
1273
+ const countDown = (remaining, closeScope) => {
1274
+ let left = remaining;
1275
+ return () => {
1276
+ left -= 1;
1277
+ return left === 0 ? closeScope() : void 0;
1278
+ };
1279
+ };
1280
+ const sharedBeforeEach = (ctx, blockTaskSet, finish, contextEffect, blockEnv) => {
1281
+ if (blockTaskSet.has(ctx.task) === false) return void 0;
1282
+ ctx.onTestFinished(() => finish());
1283
+ return runPromise(Effect.asVoid(contextEffect), ctx, blockEnv);
1284
+ };
1285
+ const collectTasks = (tasks) => {
1286
+ const acc = [];
1287
+ for (const task of tasks) collectOne(task, acc);
1288
+ return acc;
1289
+ };
1290
+ const collectOne = (task, acc) => {
1291
+ if (isRunnableTest(task)) acc.push(task);
1292
+ else collectNested(task, acc);
1293
+ };
1294
+ const collectNested = (task, acc) => {
1295
+ if (task.tasks !== void 0) collectTasks(task.tasks).forEach((nested) => acc.push(nested));
1296
+ };
1297
+ const notSkipped = (task) => task.mode !== "skip" && task.mode !== "todo";
1298
+ const isRunnableTest = (task) => task.type === "test" && notSkipped(task);
1299
+ const layerImpl = (layer_, options) => layerFor(() => layer_, options, void 0);
1300
+ /** @internal */
1301
+ const layer$1 = Function.dual(byLayer, layerImpl);
1302
+ const withinTimeout = (elapsed, timeout) => Duration.isLessThanOrEqualTo(Duration.fromInputUnsafe(elapsed), Duration.fromInputUnsafe(timeout));
1303
+ const retryWithin = (timeout) => Schedule.while(Schedule.recurs(10), (state) => Effect.succeed(withinTimeout(state.elapsed, timeout)));
1304
+ const flakyTestImpl = (self, timeout = Duration.seconds(30)) => self.pipe(Effect.scoped, Effect.sandbox, (sandboxed) => Effect.retry(sandboxed, retryWithin(timeout)), Effect.orDie);
1305
+ /** @internal */
1306
+ const flakyTest$1 = Function.dual(byEffect, flakyTestImpl);
1307
+ const isFunction = (arg) => typeof arg === "function";
1308
+ /**
1309
+ * The two returns the bare lane refuses: a promise, which runs outside the test runtime (R9), and an Effect,
1310
+ * which the bare lane never runs — it belongs on `it.effect`.
1311
+ */
1312
+ const refuseBareOutput = (output) => {
1313
+ checkPromiseBody(output);
1314
+ if (outputIsEffect(output)) throw new Slop({ detail: refuseBareEffect });
1315
+ };
1316
+ /** One pass of the bare body: it runs bound to the run's context, and its return is refused here. */
1317
+ const bareRunOnce = (ctx, self, shadow) => {
1318
+ const returned = withSyncRun({
1319
+ ctx,
1320
+ shadow
1321
+ }, () => self(ctx));
1322
+ if (isObject(returned)) refuseBareOutput(returned);
1323
+ };
1324
+ /** The bare body's outcome: `undefined` when it passed, or the failure it threw or was refused with. */
1325
+ const bareOutcome = (ctx, self, shadow) => outcomeOf(Promise.resolve().then(() => bareRunOnce(ctx, self, shadow)));
1326
+ const shouldRerunBare = (ctx, rerunnable) => rerunnable && isClean(ctx);
1327
+ const afterBareGate = (ctx, self, rerunnable) => shouldRerunBare(ctx, rerunnable) ? bareOutcome(ctx, self, true).then(leakedOrNothing) : Promise.resolve();
1328
+ const afterBareFirst = (ctx, self, rerunnable, failure) => {
1329
+ if (failure !== void 0) throwAfterCheck(ctx, failure);
1330
+ gateNoAssertion(ctx);
1331
+ return afterBareGate(ctx, self, rerunnable);
1332
+ };
1333
+ /**
1334
+ * The bare `it(name, body)` lane: Vitest's own registration with the fork's defaults instead of an Effect
1335
+ * lane. The body is synchronous, so it is gated for a leftover assertion and run a second time on its own,
1336
+ * which is what catches a body that only passes while nothing else touched its module (R5, R9).
1337
+ */
1338
+ const runDefault = (ctx, self, rerunnable) => bareOutcome(ctx, self, false).then((failure) => afterBareFirst(ctx, self, rerunnable, failure));
1339
+ const defaultLane = (self, rerunnable) => (ctx) => runDefault(ctx, self, rerunnable);
1340
+ /**
1341
+ * The fork's `it`: Vitest's test API with the fork's Effect lanes and lawful property lanes attached,
1342
+ * callable data-first (`it(name, body)`) or data-last (`it(body, options?)(name)`).
1343
+ *
1344
+ * @internal
1345
+ */
1346
+ const makeMethods$1 = (it) => makeItProxy(it, {
1347
+ effect: makeTester((effect, env) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, testEnv(env))), it),
1348
+ live: makeTesterWith((effect) => Effect.scoped(effect), it, () => realTime, true),
1349
+ flakyTest: flakyTest$1,
1350
+ layer: layer$1,
1351
+ prop: prop$1,
1352
+ law: property.law
1353
+ }, true);
1354
+ /** @internal */
1355
+ const { effect: effect$1, live: live$1 } = makeMethods$1(V.it);
1356
+ function makeItProxy(it, overrides, rerunnable) {
1357
+ return new Proxy(it, {
1358
+ apply: (target, thisArg, args) => routeCall(target, thisArg, args, rerunnable),
1359
+ get: (target, property, receiver) => memberOrOverride(target, overrides, property, receiver)
1360
+ });
1361
+ }
1362
+ function routeCall(target, thisArg, args, rerunnable) {
1363
+ return isName(args[0]) ? applied(target, thisArg, args, rerunnable) : (name) => applied(target, thisArg, [
1364
+ name,
1365
+ args[0],
1366
+ args[1]
1367
+ ], rerunnable);
1368
+ }
1369
+ /**
1370
+ * The route's registration. Vitest takes `[name, body]`, `[name, body, options]` or `[name, options, body]`,
1371
+ * so the body is the argument that is a function, and it is the one the fork's bare lane wraps. Vitest's own
1372
+ * helpers (`it.skip`, `it.each`, ...) come through the proxy's `get` trap and are handed over untouched.
1373
+ */
1374
+ function applied(target, thisArg, args, rerunnable) {
1375
+ Reflect.apply(target, thisArg, args.map((arg) => isFunction(arg) ? defaultLane(arg, rerunnable) : arg));
1376
+ }
1377
+ function memberOrOverride(target, overrides, property, receiver) {
1378
+ return Object.hasOwn(overrides, property) ? memberOf(overrides, property, receiver) : memberOf(target, property, receiver);
1379
+ }
1380
+ function memberOf(target, property, receiver) {
1381
+ return Reflect.get(target, property, receiver);
1382
+ }
1383
+ const describeImpl = (name, f) => V.describe(name, {
1384
+ concurrent: true,
1385
+ shuffle: true
1386
+ }, (it) => f(makeMethods$1(it)));
1387
+ /** @internal */
1388
+ const describeWrapped$1 = Function.dual(byName, describeImpl);
1389
+ //#endregion
1390
+ //#region src/mod.ts
1391
+ /**
1392
+ * @since 4.0.0
1393
+ */
1394
+ const addEqualityTesters = addEqualityTesters$1;
1395
+ /**
1396
+ * @since 4.0.0
1397
+ */
1398
+ const effect = effect$1;
1399
+ /**
1400
+ * @since 4.0.0
1401
+ */
1402
+ const live = live$1;
1403
+ /**
1404
+ * Share a `Layer` between multiple tests, optionally wrapping
1405
+ * the tests in a `describe` block if a name is provided.
1406
+ *
1407
+ * Named layers accept `concurrent` to override inherited suite concurrency.
1408
+ * Anonymous layers always inherit the enclosing suite's concurrency.
1409
+ * Use `ctx.expect` in concurrent tests for test-local snapshots and assertion counts.
1410
+ *
1411
+ * @since 4.0.0
1412
+ *
1413
+ * ```ts
1414
+ * import { assert, layer } from "@systemfsoftware/vitest"
1415
+ * import { Effect, Layer, Context } from "effect"
1416
+ *
1417
+ * class Foo extends Context.Service<Foo, "foo">()("Foo") {
1418
+ * static layer = Layer.succeed(Foo, "foo")
1419
+ * }
1420
+ *
1421
+ * class Bar extends Context.Service<Bar, "bar">()("Bar") {
1422
+ * static layer = Layer.effect(
1423
+ * Bar,
1424
+ * Effect.map(Foo, () => "bar" as const)
1425
+ * )
1426
+ * }
1427
+ *
1428
+ * layer(Foo.layer)("layer", (it) => {
1429
+ * it.effect("adds context", () =>
1430
+ * Effect.gen(function*() {
1431
+ * const foo = yield* Foo
1432
+ * assert.strictEqual(foo, "foo")
1433
+ * }))
1434
+ *
1435
+ * it.layer(Bar.layer)("nested", (it) => {
1436
+ * it.effect("adds context", () =>
1437
+ * Effect.gen(function*() {
1438
+ * const foo = yield* Foo
1439
+ * const bar = yield* Bar
1440
+ * assert.strictEqual(foo, "foo")
1441
+ * assert.strictEqual(bar, "bar")
1442
+ * }))
1443
+ * })
1444
+ * })
1445
+ * ```
1446
+ */
1447
+ const layer = layer$1;
1448
+ /**
1449
+ * @since 4.0.0
1450
+ */
1451
+ const flakyTest = flakyTest$1;
1452
+ /**
1453
+ * @since 4.0.0
1454
+ */
1455
+ const prop = prop$1;
1456
+ /**
1457
+ * @since 4.0.0
1458
+ */
1459
+ const owned = owned$1;
1460
+ /**
1461
+ * @since 4.0.0
1462
+ */
1463
+ const recordAssertion = recordAssertion$1;
1464
+ /**
1465
+ * The run binding of the test the effect is executing in. `bind` re-provides it
1466
+ * to an effect a library runs on a runtime of its own — its own scheduler, a
1467
+ * worker, a simulation kernel — so the checks inside it count as this test's
1468
+ * assertions, report softly, and see the same `owned` regions as the test.
1469
+ *
1470
+ * @since 4.0.0
1471
+ */
1472
+ const captureRunBinding = captureRunBinding$1;
1473
+ /**
1474
+ * The key the running test's context is published under. A library that carries its own view of the task
1475
+ * context builds that view on this key, so the context a case lane sees and a property lane sees agree.
1476
+ *
1477
+ * @since 4.0.0
1478
+ */
1479
+ const vitestTestContextKey = vitestTestContextKey$1;
1480
+ /**
1481
+ * The running Vitest `TestContext`, provided on every test the fork runs — the Effect lanes, both property
1482
+ * lanes, and each `it.each` row. It is `null` outside a run.
1483
+ *
1484
+ * @since 4.0.0
1485
+ */
1486
+ const VitestTestContext = VitestTestContext$1;
1487
+ /**
1488
+ * @since 4.0.0
1489
+ */
1490
+ const it = makeMethods$1(V.it);
1491
+ /**
1492
+ * @since 4.0.0
1493
+ */
1494
+ const makeMethods = makeMethods$1;
1495
+ /**
1496
+ * @since 4.0.0
1497
+ */
1498
+ const describeWrapped = describeWrapped$1;
1499
+ const describe = describeWrapped$1;
1500
+ /**
1501
+ * The fork's `expect`: soft within one Effect step, refusing the slop forms.
1502
+ *
1503
+ * @since 4.0.0
1504
+ */
1505
+ const expect = expect$1;
1506
+ /**
1507
+ * @since 4.0.0
1508
+ */
1509
+ const beforeEach = beforeEach$1;
1510
+ /**
1511
+ * @since 4.0.0
1512
+ */
1513
+ const afterEach = afterEach$1;
1514
+ /**
1515
+ * @since 4.0.0
1516
+ */
1517
+ const beforeAll = beforeAll$1;
1518
+ /**
1519
+ * @since 4.0.0
1520
+ */
1521
+ const afterAll = afterAll$1;
1522
+ //#endregion
1523
+ export { VitestTestContext, addEqualityTesters, afterAll, afterEach, beforeAll, beforeEach, captureRunBinding, describe, describeWrapped, effect, expect, flakyTest, it, layer, live, makeMethods, owned, prop, recordAssertion, vitestTestContextKey };