@systemfsoftware/vitest 0.0.0 → 0.1.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 CHANGED
@@ -1,59 +1,96 @@
1
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";
2
+ import { _ as refuseScopedLiveLane, d as refuseAsyncBody, f as refuseEffectLane, g as refuseScopedLane, i as markTask, l as noCheckRefusal, o as isRefusal$1, p as refuseHook, u as refusalOf$1, v as refuseSyncBody, x as unprovidedText, y as refuseUnyielded } from "./guard-BocOhXt2.mjs";
3
+ import { a as isCheck, i as checksFor, n as vitestTestContextKey$1, o as makeLedger, r as Asserted, t as VitestTestContext$1 } from "./test-context-hvSoKLtw.mjs";
3
4
  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";
5
+ import { afterAll, assertType, beforeAll, expectTypeOf, inject, inject as inject$1, onTestFailed, onTestFinished, recordArtifact, vi, vitest } from "vitest";
11
6
  import * as Cause from "effect/Cause";
12
7
  import * as Duration from "effect/Duration";
8
+ import * as Effect from "effect/Effect";
13
9
  import * as Exit from "effect/Exit";
10
+ import * as Function from "effect/Function";
14
11
  import * as Layer from "effect/Layer";
15
12
  import * as Schedule from "effect/Schedule";
16
- import * as Scheduler from "effect/Scheduler";
17
13
  import * as Schema$1 from "effect/Schema";
18
14
  import * as Scope from "effect/Scope";
19
15
  import * as TestClock from "effect/testing/TestClock";
20
16
  import * as TestConsole from "effect/testing/TestConsole";
17
+ import * as Equal from "effect/Equal";
18
+ import { Schema } from "effect";
21
19
  import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary";
22
20
  import * as Data from "effect/Data";
23
21
  import * as Clock from "effect/Clock";
24
- export * from "vitest";
25
- //#region src/internal/binding.ts
22
+ import * as Scheduler from "effect/Scheduler";
23
+ //#region src/internal/property/defaults.ts
24
+ /** @internal The `test.provide` key the shared config fills with this run's tier's check options. */
25
+ const checkDefaultsKey = "@systemfsoftware/vitest:property-check";
26
+ /** @internal The configured property-check defaults, or `undefined` when the run provided none. */
27
+ const providedCheckDefaults = () => inject$1(checkDefaultsKey);
28
+ //#endregion
29
+ //#region src/internal/driver.ts
26
30
  /**
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).
31
+ * The driver: one Effect that steps the body's generator in the fiber the test runs in.
29
32
  *
30
- * @internal
33
+ * A `yield*` is a step, so the runner sees every one of them (KTD1):
34
+ * - a branded check is judged against this test's ledger, so it counts, and a failed judgement fails the check
35
+ * itself and stops the test right there (R8);
36
+ * - anything else opens a new observed state and then runs, which is what makes a second check on one state a
37
+ * second check in one window (R5), and a check inside a loop the same window as its siblings.
38
+ *
39
+ * The steps run in the same fiber as the test, so layers, virtual time, concurrency, the second run and scope
40
+ * finalizers keep working through the runner unchanged. A step that fails, and the test being interrupted, both
41
+ * return the generator: its `finally` blocks run and the steps after it never start (R8). When the body returns,
42
+ * the gate refuses what the body wrote and never judged, then a body that judged nothing at all (R4).
31
43
  */
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
- }
44
+ /**
45
+ * A value the body yielded, as the driver runs it. What a body needs of its environment belongs to the layer the
46
+ * runner provides around this whole Effect, so the driver erases the channels here, exactly as the runner erases
47
+ * a body's own (`outputIsEffect` in `runner.ts`): the layer block is where a requirement is checked, and a body
48
+ * that needs a service nothing provides is refused by the runner at run time (R9).
49
+ */
50
+ const isRunnable = (value) => Effect.isEffect(value);
51
+ /** A body that yielded something the driver cannot run ran nothing, so nothing in the test could fail. */
52
+ const refuseUnsteppable = "✗ the body yielded a value that is not an Effect. Yield the Effect itself, or a check: yield* expect(actual).toEqual(expected).";
53
+ /**
54
+ * A yielded Effect: a branded check judges itself, and anything else opens a new observed state before it runs,
55
+ * which is what resets the ledger's one-check window (R5).
56
+ */
57
+ const runEffect = (value, ledger) => isCheck(value) ? value : Effect.andThen(Effect.sync(() => ledger.asserted.step()), value);
58
+ /** One step: anything the driver cannot run is refused, because then no step of the test ran at all. */
59
+ const runStep = (value, ledger) => isRunnable(value) ? runEffect(value, ledger) : Effect.die(refusalOf$1(refuseUnsteppable));
60
+ /** Steps the iterator to its end, feeding each yielded Effect's result back into the body. */
61
+ const stepThrough = (steps, ledger, input) => Effect.suspend(() => {
62
+ const state = steps.next(input);
63
+ if (state.done === true) return Effect.void;
64
+ return Effect.flatMap(runStep(state.value, ledger), (value) => stepThrough(steps, ledger, value));
65
+ });
66
+ /**
67
+ * After the body returns: a check written and never yielded is refused first, then a body that judged none.
68
+ * Both carry the same text the refusal carries on its compile channel, branded by `refusalOf` so the runner's
69
+ * second run re-throws them as the refusals they are.
70
+ */
71
+ const gate = (ledger) => Effect.suspend(() => ledger.unyielded() > 0 ? refuse(refuseUnyielded) : judgedNone(ledger));
72
+ const judgedNone = (ledger) => ledger.judged() === 0 ? refuse(noCheckRefusal) : Effect.void;
73
+ const refuse = (text) => Effect.die(refusalOf$1(text));
74
+ /**
75
+ * A failure or an interruption returns the generator, so its `finally` blocks run and the step after the failed
76
+ * check never does (R8).
77
+ */
78
+ const unwind = (steps) => Effect.sync(() => {
79
+ steps.return?.(void 0);
80
+ });
81
+ const driveBody = (body, args, ledger) => Effect.suspend(() => {
82
+ const steps = body(...args);
83
+ return Effect.onExit(Effect.flatMap(stepThrough(steps, ledger, void 0), () => gate(ledger)), (exit) => Exit.isSuccess(exit) ? Effect.void : unwind(steps));
45
84
  });
85
+ /** The body's steps, with this test's ledger provided as the `Asserted` every check in it reaches for. */
86
+ const driven = (body, args, ledger) => Effect.provideService(driveBody(body, args, ledger), Asserted, ledger.asserted);
46
87
  /**
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.
88
+ * Runs one test's body: `body` called with `args`, stepped against `ledger`, with the ledger's `asserted`
89
+ * provided as `Asserted` so every check in the body — including one yielded inside a forked fiber — reaches it.
49
90
  *
50
91
  * @internal
51
92
  */
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;
93
+ const drive = Function.dual(3, driven);
57
94
  //#endregion
58
95
  //#region src/internal/equal.ts
59
96
  let registered = false;
@@ -81,12 +118,6 @@ var LeakedState = class extends Schema.TaggedError()("LeakedState", { detail: Sc
81
118
  }
82
119
  };
83
120
  /** @internal */
84
- var AfterFailedExpect = class extends Schema.TaggedError()("AfterFailedExpect", { detail: Schema.String }) {
85
- get message() {
86
- return this.detail;
87
- }
88
- };
89
- /** @internal */
90
121
  var Slop = class extends Schema.TaggedError()("Slop", { detail: Schema.String }) {
91
122
  get message() {
92
123
  return this.detail;
@@ -99,120 +130,6 @@ var InvalidBudget = class extends Schema.TaggedError()("InvalidBudget", { detail
99
130
  }
100
131
  };
101
132
  //#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
133
  //#region src/internal/property/coverage.ts
217
134
  /**
218
135
  * @internal The R14 coverage classes: labelled input predicates, each with a minimum share of runs.
@@ -678,42 +595,6 @@ const makeProperty = (runtime) => {
678
595
  };
679
596
  };
680
597
  //#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
598
  //#region src/internal/virtual-time.ts
718
599
  const roundNanos = (millis) => BigInt(Math.round(millis * 1e6));
719
600
  const byWakeOrder = (first, second) => {
@@ -885,6 +766,18 @@ const virtualClockOf = (runtime) => {
885
766
  };
886
767
  //#endregion
887
768
  //#region src/internal/runner.ts
769
+ /**
770
+ * The fork's runner: the test lanes, the layer blocks, the property lanes and the run pipeline.
771
+ *
772
+ * A test body is a generator the driver steps (KTD1), so `expect` is the body's own parameter and every check is
773
+ * yielded. Everything #512 promised holds unchanged: a fresh build of a test's layers, virtual time that advances
774
+ * when the fibers are idle, concurrent and shuffled blocks, a declared-shared block as the only sharing, a second
775
+ * run on a fresh build that reports leaked state, and Effect `Equal` in `toEqual`.
776
+ *
777
+ * What is a refusal here: a body that is not a generator, and the habit lanes `it.effect`, `it.scoped` and
778
+ * `it.scopedLive` (R2, R9). Registration marks every task this file adds (KTD8), so the guard in
779
+ * `@systemfsoftware/vitest/guard` can tell the fork's tests from a test registered by Vitest itself.
780
+ */
888
781
  const getCurrentSuite = V.TestRunner.getCurrentSuite;
889
782
  /** @internal */
890
783
  const addEqualityTesters$1 = () => {
@@ -896,80 +789,66 @@ const givenOptions = (timeout) => timeout ?? {};
896
789
  const testOptions = (timeout) => typeof timeout === "number" ? numbered(timeout) : givenOptions(timeout);
897
790
  /** @internal */
898
791
  const hookTimeout = (timeout) => timeout === void 0 ? void 0 : Duration.toMillis(Duration.fromInputUnsafe(timeout));
792
+ const isName = (first) => typeof first === "string";
793
+ const byName = (args) => isName(args[0]);
794
+ const byLayer = (args) => Layer.isLayer(args[0]);
795
+ const byEffect = (args) => Effect.isEffect(args[0]);
899
796
  /**
900
- * The positional form is refused at run time with the R15 rewrite; the public type already refuses it.
797
+ * The positional property form is refused at run time with the R15 rewrite; the public type refuses it first
798
+ * because a `PropertySpec` is an object and the positional shape is not.
901
799
  */
800
+ const refusePositionalProp = "✗ it.prop(name, [arbitraries], predicate) is removed. Name the function under test and pass a budget: it.prop(name, { of, subject, runs }, holds).";
801
+ const isAbsent = (spec) => spec === null || spec === void 0;
802
+ const hasNoOf = (spec) => Object.hasOwn(spec, "of") === false;
803
+ const lacksInputs = (spec) => isAbsent(spec) || hasNoOf(spec);
902
804
  const refusePositional = (spec) => {
903
805
  if (lacksInputs(spec)) throw new Slop({ detail: refusePositionalProp });
904
806
  };
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
807
  /** @internal */
911
808
  const realTime = {
912
809
  runtime: void 0,
913
- owned: false,
914
- stop: stopCheckOf
810
+ owned: false
915
811
  };
916
- /** @internal */
812
+ /**
813
+ * A run with its own virtual clock: the clock only ever advances when the run's fibers are idle.
814
+ *
815
+ * @internal
816
+ */
917
817
  const perRun = () => ({
918
818
  runtime: makeVirtualRuntime(),
919
- owned: true,
920
- stop: stopCheckOf
819
+ owned: true
921
820
  });
922
- const propertyEnv = {
923
- runtime: void 0,
924
- owned: false,
925
- stop: neverStop
926
- };
821
+ const propertyEnv = realTime;
927
822
  const logErrors = (cause) => Effect.forEach(Cause.prettyErrors(cause), (error) => Effect.logError(error), { discard: true });
928
- const failedAfter = (ctx, before) => () => errorCount(ctx) > before;
929
823
  const signalOf = (ctx) => ctx?.signal;
930
824
  const signalOnly = (signal) => signal === void 0 ? {} : { signal };
931
- const withScheduler = (signal, scheduler) => ({
932
- ...signalOnly(signal),
933
- scheduler
825
+ const schedulerOf = (env) => env.runtime === void 0 ? {} : { scheduler: env.runtime.scheduler };
826
+ const runOptions = (ctx, env) => ({
827
+ ...signalOnly(signalOf(ctx)),
828
+ ...schedulerOf(env)
934
829
  });
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
830
  const ownedRuntime = (env) => env.owned === false ? void 0 : env.runtime;
948
831
  const observeOf = (runtime) => runtime === void 0 ? Effect.void : runtime.observe;
949
832
  const endOf = (runtime) => runtime === void 0 ? Effect.void : runtime.end;
950
833
  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);
834
+ const runExit = (effect, ctx, env) => Effect.runPromiseExit(wrapOwned(effect, ownedRuntime(env)), runOptions(ctx, env));
956
835
  /**
957
836
  * 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.
837
+ * test-authoring error — the layers were never wired — so it is reported as the unprovided rewrite (R9) instead
838
+ * of as a raw defect.
961
839
  */
962
840
  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
841
  const rethrowSquashed = (cause) => {
965
842
  const error = Cause.squash(cause);
966
- if (isUnprovided(error)) throw new Slop({ detail: refuseUnprovided });
843
+ if (isUnprovided(error)) throw new Slop({ detail: unprovidedText });
967
844
  throw error;
968
845
  };
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;
846
+ const failExit = (cause) => logErrors(cause).pipe(Effect.runPromise).then(() => rethrowSquashed(cause));
847
+ const reportExit = (exit) => Exit.isSuccess(exit) ? Promise.resolve(exit.value) : failExit(exit.cause);
848
+ const runPromise = (effect, ctx, env = realTime) => runExit(effect, ctx, env).then(reportExit);
849
+ const noop = () => {};
850
+ const detach = (ctx, onAbort) => {
851
+ ctx.signal.removeEventListener("abort", onAbort);
973
852
  };
974
853
  const trackAbort = (ctx, promise) => {
975
854
  const onAbort = () => {
@@ -979,168 +858,104 @@ const trackAbort = (ctx, promise) => {
979
858
  promise.then(() => detach(ctx, onAbort), () => detach(ctx, onAbort));
980
859
  if (ctx.signal.aborted) onAbort();
981
860
  };
982
- const detach = (ctx, onAbort) => {
983
- ctx.signal.removeEventListener("abort", onAbort);
861
+ const runTest = (ctx, env = realTime) => (effect) => {
862
+ const promise = runPromise(effect, ctx, env);
863
+ if (ctx !== void 0) trackAbort(ctx, promise);
864
+ return promise;
984
865
  };
985
- const noop = () => {};
986
866
  const clockLayerOf = (env) => env.runtime === void 0 ? TestClock.layer() : virtualClockLayer(env.runtime);
987
867
  /** @internal */
988
868
  const testEnv = (env) => Layer.mergeAll(TestConsole.layer, clockLayerOf(env));
989
869
  /**
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.
870
+ * The run a test's fibers execute in: the test's own context under the fork's `VitestTestContext`, so a library
871
+ * built on the fork reads the same context the runner does.
992
872
  */
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)));
873
+ const bindRun = (effect, ctx) => effect.pipe(Effect.provideService(VitestTestContext$1, ctx));
874
+ const refuseBody = (text) => {
875
+ throw refusalOf$1(text);
876
+ };
877
+ /** The constructor names of the function kinds that hand back a promise instead of a generator. */
878
+ const AWAITED_KINDS = {
879
+ AsyncFunction: true,
880
+ AsyncGeneratorFunction: true
881
+ };
882
+ /** The constructor name of the one function kind the driver steps: a generator hands its steps over. */
883
+ const GENERATOR_KIND = "GeneratorFunction";
884
+ const kindOf = (body) => body.constructor.name;
885
+ const isGeneratorBody = (body) => kindOf(body) === GENERATOR_KIND;
886
+ const isSteps = (returned) => "next" in returned && typeof returned.next === "function";
887
+ /** The iterator a generator handed over; only a generator reaches here, so the guard is for the compiler alone. */
888
+ const asSteps = (returned) => isSteps(returned) ? returned : refuseBody(refuseSyncBody);
1020
889
  /**
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).
890
+ * The refusal a body reaches before it runs (R2). An `async` body hands back a promise and runs outside the test
891
+ * runtime. A plain body can only be told from one by calling it, and nothing in a refused body may run, so it is
892
+ * refused as a sync body, whose rewrite is to pass a generator; the compile channel prints each shape's own text.
1024
893
  */
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);
894
+ const refusalOf = (body) => AWAITED_KINDS[kindOf(body)] === true ? refuseAsyncBody : refuseSyncBody;
895
+ /** The body's generator, or the refusal for a body the driver cannot step. */
896
+ const stepsOfBody = (body, checks) => isGeneratorBody(body) ? asSteps(body(checks)) : refuseBody(refusalOf(body));
897
+ /** The same for a row body: Vitest's `it.for` order is the row first, then the checks (R9). */
898
+ const stepsOfRow = (row, body, checks) => isGeneratorBody(body) ? asSteps(body(row, checks)) : refuseBody(refusalOf(body));
899
+ const bodyOf = (ledger, body) => () => asSteps(body(checksFor(ledger)));
900
+ /** The body a lane registered, closed over its checks: the generator itself, or the refusal for what it is not. */
901
+ const laneBodyOf = (body) => (checks) => typeof body === "string" ? refuseBody(refuseSyncBody) : stepsOfBody(body, checks);
902
+ /** The row body a lane registered: Vitest's `it.for` order is the row first, then the checks (R9). */
903
+ const rowBodyOf = (row, body) => (checks) => typeof body === "string" ? refuseBody(refuseSyncBody) : stepsOfRow(row, body, checks);
904
+ /** One test's run: a fresh ledger, and the lane's own services around the program the driver built. */
905
+ const runLaned = (ctx, mapEffect, env, body) => {
906
+ const ledger = makeLedger(ctx);
907
+ return runTest(ctx, env)(mapEffect(bindRun(drive(bodyOf(ledger, body), [], ledger), ctx), env));
1039
908
  };
1040
- /** @internal */
1041
- const prop$1 = Function.dual(byName, propImpl);
1042
909
  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
910
  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 = [
911
+ /** Every failure the fork itself names: the branded refusals, plus the Schema-tagged families. */
912
+ const otherRefusals = [
1108
913
  isSlop,
1109
- Schema$1.is(AfterFailedExpect),
1110
914
  Schema$1.is(InvalidBudget),
1111
915
  Schema$1.is(NonBooleanVerdict),
1112
916
  Schema$1.is(LeakedState)
1113
917
  ];
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" });
918
+ const isRefusal = (error) => isRefusal$1(error) || otherRefusals.some((matches) => matches(error));
919
+ const clipped = (message) => message.length > 400 ? message.slice(0, 400) : message;
920
+ const throwLeaked = (outcome) => {
921
+ 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) });
1125
922
  };
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
923
  /**
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.
924
+ * The second run's failure: a diagnosis the fork itself made is rethrown as that diagnosis, because the second
925
+ * run exists to catch leaked state, not to relabel a refusal as a leak. Anything else is the leak.
1132
926
  */
1133
- const assertionCalls = (ctx) => ctx.expect.getState().assertionCalls;
1134
- const gateNoAssertion = (ctx) => {
1135
- if (silentRun(ctx)) throwNoAssertion();
927
+ const throwSecondRun = (error) => {
928
+ if (isRefusal(error)) throw error;
929
+ return throwLeaked(error);
1136
930
  };
1137
- const throwNoAssertion = () => {
1138
- throw new Slop({ detail: refuseNoAssertion });
931
+ const leakedOrNothing = (error) => {
932
+ if (error !== void 0) throwSecondRun(error);
1139
933
  };
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) });
934
+ const NO_ERRORS = [];
935
+ const presentErrors = (errors) => errors === void 0 ? NO_ERRORS : errors;
936
+ /** Whether a run left no failure behind: only a clean first run is worth running a second time. */
937
+ const isClean = (ctx) => presentErrors(ctx.task.result?.errors).length === 0;
938
+ /**
939
+ * Runs a property's program as the test that registered it, under the same context the generator lanes use. The
940
+ * env is the bare `propertyEnv`: a `false` verdict is the property's own shrink path, so nothing interrupts it.
941
+ */
942
+ const runProperty = (ctx, program) => runTest(ctx, propertyEnv)(bindRun(Effect.suspend(program), ctx));
943
+ const property = makeProperty({
944
+ register: (name, program) => marked(() => V.it(name, (ctx) => runProperty(ctx, program))),
945
+ provide: (effect) => effect
946
+ });
947
+ const propImpl = (name, spec, holds) => {
948
+ refusePositional(spec);
949
+ property.prop(name, spec, holds);
1143
950
  };
951
+ /** @internal */
952
+ const prop$1 = Function.dual(byName, propImpl);
953
+ /**
954
+ * The law kinds over the sync property family: the top-level `it.law`.
955
+ *
956
+ * @internal
957
+ */
958
+ const law$1 = property.law;
1144
959
  const CONCURRENT_BLOCK = {
1145
960
  concurrent: true,
1146
961
  shuffle: true
@@ -1158,25 +973,24 @@ const memoMapIn = (options) => options?.memoMap;
1158
973
  const excludeTestServicesOf = (options) => withDefault(excludedIn(options), false);
1159
974
  const blockEnvOf = (inherited) => withDefault(inherited, {
1160
975
  runtime: makeVirtualRuntime(),
1161
- owned: false,
1162
- stop: stopCheckOf
976
+ owned: false
1163
977
  });
1164
978
  const memoMapOf = (options) => withDefault(memoMapIn(options), Effect.runSync(Layer.makeMemoMap));
1165
979
  const blockHookTimeout = (options) => hookTimeout(timeoutIn(options));
1166
980
  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
981
  const mergeBlockOptions = (options, fallback) => ({
1174
982
  concurrent: withDefault(concurrencyIn(options), fallback.concurrent),
1175
983
  shuffle: fallback.shuffle
1176
984
  });
1177
985
  /**
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.
986
+ * A named block's suite options: its tests run concurrently and in shuffled order (R4). A declared-shared block
987
+ * is the stateful exception — its tests read each other's writes through one build, so it stays in declaration
988
+ * order and runs alone.
989
+ */
990
+ const describeOptions = (options, shared) => mergeBlockOptions(options, shared ? ALONE_BLOCK : CONCURRENT_BLOCK);
991
+ /**
992
+ * The block's layer with the test's own services merged in, unless the block excludes them. `Layer`'s output is
993
+ * contravariant, so the merged layer is the *narrower* `Layer<R, E>` the callers provide to a run.
1180
994
  */
1181
995
  const mergeTestEnv = (layer_, env, excludeTestServices) => excludeTestServices ? layer_ : Layer.provideMerge(layer_, env);
1182
996
  const sharedContext = (shared, memoMap, scope) => Layer.buildWithMemoMap(shared, memoMap, scope).pipe(Effect.orDie, Effect.cached, Effect.runSync);
@@ -1200,6 +1014,31 @@ const routeBlock = (first, second, anonymous, named) => {
1200
1014
  const openNamed = (name, f, named) => {
1201
1015
  if (f !== void 0) named(name, f);
1202
1016
  };
1017
+ /** Runs a registration and marks every test task it added, which is how the guard knows the fork registered it. */
1018
+ const marked = (register) => {
1019
+ const before = suiteTasks(getCurrentSuite()).length;
1020
+ register();
1021
+ markAdded(before);
1022
+ };
1023
+ const markAdded = (before) => {
1024
+ for (const task of collectTasks(suiteTasks(getCurrentSuite()).slice(before))) markTask(task);
1025
+ };
1026
+ const blockTasksOf = () => suiteTasks(getCurrentSuite());
1027
+ const suiteTasks = (suite) => suite.tasks;
1028
+ const collectTasks = (tasks) => {
1029
+ const acc = [];
1030
+ for (const task of tasks) collectOne(task, acc);
1031
+ return acc;
1032
+ };
1033
+ const collectNested = (task, acc) => {
1034
+ if (task.tasks !== void 0) collectTasks(task.tasks).forEach((nested) => acc.push(nested));
1035
+ };
1036
+ const collectOne = (task, acc) => {
1037
+ if (isRunnableTest(task)) acc.push(task);
1038
+ else collectNested(task, acc);
1039
+ };
1040
+ const notSkipped = (task) => task.mode !== "skip" && task.mode !== "todo";
1041
+ const isRunnableTest = (task) => task.type === "test" && notSkipped(task);
1203
1042
  const layerFor = (layer_, options, inherited) => isSharedBlock(options) ? sharedBlock(layer_, options, inherited) : freshBlock(layer_, options);
1204
1043
  const freshBlock = (layer_, options) => {
1205
1044
  const excludeTestServices = excludeTestServicesOf(options);
@@ -1209,13 +1048,7 @@ const freshBlock = (layer_, options) => {
1209
1048
  excludeTestServices
1210
1049
  }, void 0);
1211
1050
  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);
1051
+ const makeIt = (it) => methodsFor((effect, env) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, Layer.orDie(Layer.fresh(withTestEnv(env))))), it, perRun, true, nestedLayer);
1219
1052
  return blockOpener((f) => f(makeIt(V.it)), (name, f) => {
1220
1053
  V.describe(name, describeOptions(options, false), () => f(makeIt(V.it)));
1221
1054
  });
@@ -1225,24 +1058,15 @@ const nestedBlock = (nested, memoMap) => ({
1225
1058
  memoMap: Layer.forkMemoMapUnsafe(memoMap),
1226
1059
  shared: true
1227
1060
  });
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
1061
  const sharedBlock = (layer_, options, inherited) => {
1240
1062
  const blockEnv = blockEnvOf(inherited);
1241
1063
  const memoMap = memoMapOf(options);
1242
1064
  const scope = Scope.makeUnsafe();
1243
1065
  const shared = mergeTestEnv(layer_(blockEnv), testEnv(blockEnv), excludeTestServicesOf(options));
1244
1066
  const contextEffect = sharedContext(shared, memoMap, scope);
1245
- const makeIt = (it) => makeItProxy(it, sharedOverrides(shared, contextEffect, memoMap, blockEnv), false);
1067
+ const nestedOpen = (nestedLayer, nestedOptions) => layerFor(() => Layer.provideMerge(nestedLayer, shared), nestedBlock(nestedOptions, memoMap), blockEnv);
1068
+ const nestedLayer = Function.dual(byLayer, nestedOpen);
1069
+ const makeIt = (it) => methodsFor((effect) => Effect.flatMap(contextEffect, (context) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, context))), it, () => blockEnv, false, nestedLayer);
1246
1070
  const closeScope = makeScopeCloser(scope);
1247
1071
  return blockOpener((f) => sharedAnonymous(f, makeIt, options, blockTasksOf(), blockEnv, contextEffect, closeScope), (name, f) => describeShared(name, f, makeIt, options, contextEffect, closeScope, blockEnv));
1248
1072
  };
@@ -1253,8 +1077,6 @@ const describeShared = (name, f, makeIt, options, contextEffect, closeScope, blo
1253
1077
  return f(makeIt(V.it));
1254
1078
  });
1255
1079
  };
1256
- const blockTasksOf = () => suiteTasks(getCurrentSuite());
1257
- const suiteTasks = (suite) => suite.tasks;
1258
1080
  const sharedAnonymous = (register, makeIt, options, blockTasks, blockEnv, contextEffect, closeScope) => {
1259
1081
  const previousTasks = new Set(blockTasks);
1260
1082
  register(makeIt(V.it));
@@ -1282,20 +1104,6 @@ const sharedBeforeEach = (ctx, blockTaskSet, finish, contextEffect, blockEnv) =>
1282
1104
  ctx.onTestFinished(() => finish());
1283
1105
  return runPromise(Effect.asVoid(contextEffect), ctx, blockEnv);
1284
1106
  };
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
1107
  const layerImpl = (layer_, options) => layerFor(() => layer_, options, void 0);
1300
1108
  /** @internal */
1301
1109
  const layer$1 = Function.dual(byLayer, layerImpl);
@@ -1304,82 +1112,101 @@ const retryWithin = (timeout) => Schedule.while(Schedule.recurs(10), (state) =>
1304
1112
  const flakyTestImpl = (self, timeout = Duration.seconds(30)) => self.pipe(Effect.scoped, Effect.sandbox, (sandboxed) => Effect.retry(sandboxed, retryWithin(timeout)), Effect.orDie);
1305
1113
  /** @internal */
1306
1114
  const flakyTest$1 = Function.dual(byEffect, flakyTestImpl);
1307
- const isFunction = (arg) => typeof arg === "function";
1308
1115
  /**
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`.
1116
+ * The lanes a removed habit name reaches: the call is refused with its rewrite, on the `this` type and at run
1117
+ * time, and the property the lane used to carry still runs (R9, KTD3).
1311
1118
  */
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);
1119
+ const refuseHabitLane = (text) => {
1120
+ throw refusalOf$1(text);
1332
1121
  };
1122
+ const effectLaneRefusal = (prop, text) => Object.assign(() => refuseHabitLane(text), { prop });
1333
1123
  /**
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).
1124
+ * The lanes over one `mapEffect`: `it`, its modifiers and `it.each` all register the same generator body, and
1125
+ * the Effect-lane property is bound to the same services the lane provides (R7, R9).
1337
1126
  */
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);
1127
+ const makeTesterWith = (mapEffect, it, envFor, rerunnable) => {
1128
+ const runOnce = (ctx, body) => runLaned(ctx, mapEffect, envFor(), body);
1129
+ const rerun = (ctx, body) => outcomeOf(runOnce(ctx, body)).then(leakedOrNothing);
1130
+ const shouldRerun = (ctx) => rerunnable && isClean(ctx);
1131
+ const rerunIfClean = (ctx, body) => shouldRerun(ctx) ? rerun(ctx, body) : Promise.resolve();
1132
+ const run = (ctx, body) => outcomeOf(runOnce(ctx, body)).then((error) => {
1133
+ if (error !== void 0) throw error;
1134
+ return rerunIfClean(ctx, body);
1135
+ });
1136
+ const registering = (registrar) => (name, body, timeout) => {
1137
+ const lane = laneBodyOf(body);
1138
+ marked(() => registrar(name, testOptions(timeout), (ctx) => run(ctx, lane)));
1139
+ };
1140
+ const dualLane = (registrar) => Function.dual(byName, registering(registrar));
1141
+ const base = dualLane(it);
1142
+ const skip = dualLane(it.skip);
1143
+ const skipIf = (condition) => dualLane(it.skipIf(condition));
1144
+ const runIf = (condition) => dualLane(it.runIf(condition));
1145
+ const only = dualLane(it.only);
1146
+ const fails = dualLane(it.fails);
1147
+ const each = (cases) => (name, body, timeout) => {
1148
+ marked(() => it.for(cases)(name, testOptions(timeout), (row, ctx) => run(ctx, rowBodyOf(row, body))));
1149
+ };
1150
+ const effectProperty = makeProperty({
1151
+ register: (name, program) => marked(() => it(name, (ctx) => runProperty(ctx, program))),
1152
+ provide: (effect) => mapEffect(effect, envFor())
1153
+ });
1154
+ const prop = (name, spec, holds) => {
1155
+ refusePositional(spec);
1156
+ effectProperty.effectProp(name, spec, holds);
1157
+ };
1158
+ return Object.assign(base, {
1159
+ skip,
1160
+ skipIf,
1161
+ runIf,
1162
+ only,
1163
+ fails,
1164
+ each,
1165
+ prop
1166
+ });
1167
+ };
1168
+ /** The block's methods: the generator lanes, the refusing habit lanes, and the block's own nested `layer`. */
1169
+ const methodsFor = (mapEffect, it, envFor, rerunnable, layer) => {
1170
+ const tester = makeTesterWith(mapEffect, it, envFor, rerunnable);
1171
+ return Object.assign(tester, {
1172
+ effect: effectLaneRefusal(tester.prop, refuseEffectLane),
1173
+ scoped: effectLaneRefusal(tester.prop, refuseScopedLane),
1174
+ scopedLive: effectLaneRefusal(tester.prop, refuseScopedLiveLane),
1175
+ prop: prop$1,
1176
+ law: property.law,
1177
+ flakyTest: flakyTest$1,
1178
+ layer
1179
+ });
1180
+ };
1181
+ const refuseHookNow = () => {
1182
+ throw refusalOf$1(refuseHook);
1183
+ };
1184
+ const takesHook = (args) => typeof args[0] === "function";
1185
+ const refuseHookPair = (fn, timeout) => {
1186
+ return refuseHookNow();
1187
+ };
1340
1188
  /**
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
1189
  * @internal
1345
1190
  */
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
- }
1191
+ const beforeEach$2 = Function.dual(takesHook, refuseHookPair);
1369
1192
  /**
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.
1193
+ * @internal
1373
1194
  */
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
- }
1195
+ const afterEach$1 = Function.dual(takesHook, refuseHookPair);
1196
+ /**
1197
+ * The fork's `it`: the generator lanes on virtual time, the real-clock lane, the lawful property lanes and the
1198
+ * layer blocks, callable data-first (`it(name, body)`) or data-last (`it(body, timeout?)(name)`).
1199
+ *
1200
+ * @internal
1201
+ */
1202
+ const makeMethods$1 = (it) => {
1203
+ const base = methodsFor((effect, env) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, testEnv(env))), it, perRun, true, layer$1);
1204
+ const live = makeTesterWith((effect) => Effect.scoped(effect), it, () => realTime, true);
1205
+ return Object.assign(base, {
1206
+ live,
1207
+ layer: layer$1
1208
+ });
1209
+ };
1383
1210
  const describeImpl = (name, f) => V.describe(name, {
1384
1211
  concurrent: true,
1385
1212
  shuffle: true
@@ -1389,60 +1216,50 @@ const describeWrapped$1 = Function.dual(byName, describeImpl);
1389
1216
  //#endregion
1390
1217
  //#region src/mod.ts
1391
1218
  /**
1219
+ * The fork's test API, with the generator lanes of R2 and R9.
1220
+ *
1392
1221
  * @since 4.0.0
1393
1222
  */
1394
- const addEqualityTesters = addEqualityTesters$1;
1223
+ const it = makeMethods$1(V.it);
1395
1224
  /**
1225
+ * The same lanes under the other name upstream publishes.
1226
+ *
1396
1227
  * @since 4.0.0
1397
1228
  */
1398
- const effect = effect$1;
1229
+ const test = it;
1399
1230
  /**
1231
+ * The fork's lawful collector: it forces the fork's concurrency and shuffle defaults and hands its body the
1232
+ * fork's methods.
1233
+ *
1400
1234
  * @since 4.0.0
1401
1235
  */
1402
- const live = live$1;
1236
+ const describeWrapped = describeWrapped$1;
1403
1237
  /**
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
1238
  * @since 4.0.0
1239
+ */
1240
+ const describe = describeWrapped$1;
1241
+ /**
1242
+ * Share a `Layer` between multiple tests, optionally wrapping the tests in a `describe` block if a name is
1243
+ * provided. Every test in the block gets its own fresh build of the layer, unless the block declares
1244
+ * `shared: true`.
1412
1245
  *
1413
1246
  * ```ts
1414
- * import { assert, layer } from "@systemfsoftware/vitest"
1415
- * import { Effect, Layer, Context } from "effect"
1247
+ * import { it, layer } from "@systemfsoftware/vitest"
1248
+ * import { Context, Effect, Layer } from "effect"
1416
1249
  *
1417
1250
  * class Foo extends Context.Service<Foo, "foo">()("Foo") {
1418
1251
  * static layer = Layer.succeed(Foo, "foo")
1419
1252
  * }
1420
1253
  *
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
1254
  * 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
- * }))
1255
+ * it("reads the service", function* ({ expect }) {
1256
+ * const foo = yield* Foo
1257
+ * yield* expect(foo).toEqual("foo")
1443
1258
  * })
1444
1259
  * })
1445
1260
  * ```
1261
+ *
1262
+ * @since 4.0.0
1446
1263
  */
1447
1264
  const layer = layer$1;
1448
1265
  /**
@@ -1456,68 +1273,42 @@ const prop = prop$1;
1456
1273
  /**
1457
1274
  * @since 4.0.0
1458
1275
  */
1459
- const owned = owned$1;
1460
- /**
1461
- * @since 4.0.0
1462
- */
1463
- const recordAssertion = recordAssertion$1;
1276
+ const law = law$1;
1464
1277
  /**
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.
1278
+ * The fork's test API over another Vitest `it`, so a library can register tests in its own suite.
1469
1279
  *
1470
1280
  * @since 4.0.0
1471
1281
  */
1472
- const captureRunBinding = captureRunBinding$1;
1282
+ const makeMethods = makeMethods$1;
1473
1283
  /**
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.
1284
+ * Refused: hooks share state between tests, and services come fresh per test from `layer`.
1476
1285
  *
1477
1286
  * @since 4.0.0
1478
1287
  */
1479
- const vitestTestContextKey = vitestTestContextKey$1;
1288
+ const beforeEach = beforeEach$2;
1480
1289
  /**
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.
1290
+ * Refused: hooks share state between tests, and services come fresh per test from `layer`.
1483
1291
  *
1484
1292
  * @since 4.0.0
1485
1293
  */
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;
1294
+ const afterEach = afterEach$1;
1495
1295
  /**
1496
1296
  * @since 4.0.0
1497
1297
  */
1498
- const describeWrapped = describeWrapped$1;
1499
- const describe = describeWrapped$1;
1298
+ const addEqualityTesters = addEqualityTesters$1;
1500
1299
  /**
1501
- * The fork's `expect`: soft within one Effect step, refusing the slop forms.
1300
+ * The key the running test's context is published under. A library that carries its own view of the task
1301
+ * context builds that view on this key, so the context a case lane sees and a property lane sees agree.
1502
1302
  *
1503
1303
  * @since 4.0.0
1504
1304
  */
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;
1305
+ const vitestTestContextKey = vitestTestContextKey$1;
1518
1306
  /**
1307
+ * The running Vitest `TestContext`, provided on every test the fork runs — every generator lane, both property
1308
+ * lanes, and each `it.each` row. It is `null` outside a run.
1309
+ *
1519
1310
  * @since 4.0.0
1520
1311
  */
1521
- const afterAll = afterAll$1;
1312
+ const VitestTestContext = VitestTestContext$1;
1522
1313
  //#endregion
1523
- export { VitestTestContext, addEqualityTesters, afterAll, afterEach, beforeAll, beforeEach, captureRunBinding, describe, describeWrapped, effect, expect, flakyTest, it, layer, live, makeMethods, owned, prop, recordAssertion, vitestTestContextKey };
1314
+ export { VitestTestContext, addEqualityTesters, afterAll, afterEach, assertType, beforeAll, beforeEach, describe, describeWrapped, expectTypeOf, flakyTest, inject, it, law, layer, makeMethods, onTestFailed, onTestFinished, prop, recordArtifact, test, vi, vitest, vitestTestContextKey };