diff --git a/.changeset/effect-rc116.md b/.changeset/effect-rc116.md new file mode 100644 index 00000000..7ab874af --- /dev/null +++ b/.changeset/effect-rc116.md @@ -0,0 +1,11 @@ +--- +"@typeonce/effect-machine": minor +"@typeonce/effect-machine-react": minor +"@typeonce/effect-machine-devtools": minor +--- + +Upgrade Effect and companion packages to `4.0.0-rc.116`. Install matching Effect packages when upgrading. + +`MachineTest.scenarios`, `finiteModels`, and `runtimeCommands` now produce native `Arbitrary` values from `effect/unstable/arbitrary/Arbitrary`. Custom input, event, and command generators must use that module instead of FastCheck. Use `Arbitrary.schema(schema)` for schema-derived generators, `Arbitrary.array` for sequences, and `Arbitrary.sampleEffect` or `Arbitrary.checkEffect` to sample and check them. In `@effect/vitest`, replace `fastCheck: { numRuns }` with `arbitrary: { runs }`. Generate new replay tokens; earlier FastCheck seeds and paths do not reproduce the same cases. + +Command sequences shrink by removing irrelevant commands while retaining the remaining values. Fix finite-model verification of exit and entry paths when an ancestor's initial choice resolves inside an active compound state. diff --git a/package.json b/package.json index b69cde10..993c080a 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,10 @@ }, "devDependencies": { "@changesets/cli": "2.31.0", - "@effect/vitest": "4.0.0-rc.112", + "@effect/vitest": "4.0.0-rc.116", "@types/node": "25.7.0", "dprint": "0.55.2", - "effect": "4.0.0-rc.112", + "effect": "4.0.0-rc.116", "pagefind": "1.5.2", "tinybench": "2.9.0", "tstyche": "7.2.1", diff --git a/packages/devtools/package.json b/packages/devtools/package.json index 5fb7dafb..b9b8889b 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -32,9 +32,9 @@ "dev": "tsx src/bin.ts" }, "dependencies": { - "@effect/platform-browser": "4.0.0-rc.112", - "@effect/platform-node": "4.0.0-rc.112", - "@effect/platform-node-shared": "4.0.0-rc.112", + "@effect/platform-browser": "4.0.0-rc.116", + "@effect/platform-node": "4.0.0-rc.116", + "@effect/platform-node-shared": "4.0.0-rc.116", "@typeonce/effect-machine": "workspace:^", "chokidar": "4.0.3", "elkjs": "0.12.0", @@ -42,11 +42,11 @@ "vite": "8.1.5" }, "peerDependencies": { - "effect": "4.0.0-rc.112" + "effect": "4.0.0-rc.116" }, "devDependencies": { "@types/node": "25.7.0", - "effect": "4.0.0-rc.112", + "effect": "4.0.0-rc.116", "tsx": "4.21.0" }, "files": [ diff --git a/packages/devtools/src/bin.ts b/packages/devtools/src/bin.ts index 5a954b86..689b56bb 100644 --- a/packages/devtools/src/bin.ts +++ b/packages/devtools/src/bin.ts @@ -12,37 +12,37 @@ import * as StaticSite from "./internal/staticSite.js" import * as MachineRegistry from "./MachineRegistry.js" import * as ProjectInspector from "./ProjectInspector.js" -const root = Flag.directory("root", { mustExist: true }).pipe( +const root = Flag.Directory("root", { mustExist: true }).pipe( Flag.withDescription("Project root to inspect"), Flag.withDefault(process.cwd()) ) -const include = Flag.string("include").pipe( +const include = Flag.String("include").pipe( Flag.withDescription("Machine source glob relative to the project root"), Flag.withDefault("**/src/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}") ) -const host = Flag.string("host").pipe( +const host = Flag.String("host").pipe( Flag.withDescription("Host for the local visualizer"), Flag.withDefault("127.0.0.1") ) -const port = Flag.integer("port").pipe( +const port = Flag.Int("port").pipe( Flag.withDescription("Port for the local visualizer"), Flag.withDefault(5173) ) -const open = Flag.boolean("open").pipe( +const open = Flag.Boolean("open").pipe( Flag.withDescription("Open the visualizer in the default browser"), Flag.withDefault(false) ) -const watchPolling = Flag.boolean("watch-polling").pipe( +const watchPolling = Flag.Boolean("watch-polling").pipe( Flag.withDescription("Use polling instead of native file-system events"), Flag.withDefault(false) ) -const outputDirectory = Flag.directory("out-dir").pipe( +const outputDirectory = Flag.Directory("out-dir").pipe( Flag.withAlias("o"), Flag.withDescription("Directory to write the static website"), Flag.withDefault(".effect-machine/site") diff --git a/packages/effect-machine-react/package.json b/packages/effect-machine-react/package.json index f7ca2544..80182e80 100644 --- a/packages/effect-machine-react/package.json +++ b/packages/effect-machine-react/package.json @@ -49,17 +49,17 @@ "@typeonce/effect-machine": "workspace:^" }, "peerDependencies": { - "@effect/atom-react": "4.0.0-rc.112", - "effect": "4.0.0-rc.112", + "@effect/atom-react": "4.0.0-rc.116", + "effect": "4.0.0-rc.116", "react": ">=19.0.0 <20.0.0", "scheduler": ">=0.25.0 <0.28.0" }, "devDependencies": { - "@effect/atom-react": "4.0.0-rc.112", + "@effect/atom-react": "4.0.0-rc.116", "@testing-library/react": "16.3.0", "@types/react": "19.2.16", "@types/react-dom": "19.2.3", - "effect": "4.0.0-rc.112", + "effect": "4.0.0-rc.116", "jsdom": "27.2.0", "react": "19.2.7", "react-dom": "19.2.7", diff --git a/packages/effect-machine/README.md b/packages/effect-machine/README.md index 71292ec3..92bfd5ff 100644 --- a/packages/effect-machine/README.md +++ b/packages/effect-machine/README.md @@ -596,6 +596,26 @@ pass complete decoded objects when defining scenarios manually. Pure planner tests do not execute invokes or time. Use a started machine and a probe when those semantics matter. +Generated scenarios, finite models, and runtime commands use +`effect/unstable/arbitrary/Arbitrary`. Custom generator options accept native +`Arbitrary` values. For example: + +```ts +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" + +const generated = MachineTest.scenarios(Counter, { maxEvents: 20 }) +const samples = yield* Arbitrary.sampleEffect(generated.arbitrary, { + count: 10, + seed: 42 +}) +``` + +Use `Arbitrary.schema(schema)` to derive a generator and `Arbitrary.array(item, +{ maxLength })` for custom sequences. `Arbitrary.checkEffect` checks properties +and returns a replay token for a failing case. With `@effect/vitest`, configure +property runs using `{ arbitrary: { runs: 100, seed: 42 } }`. FastCheck replay +paths and seeds do not preserve the same generated cases after migration. + ## Entrypoints ```ts diff --git a/packages/effect-machine/package.json b/packages/effect-machine/package.json index 3eaf3ee1..def9e2a4 100644 --- a/packages/effect-machine/package.json +++ b/packages/effect-machine/package.json @@ -70,12 +70,12 @@ "test:types": "tstyche" }, "peerDependencies": { - "effect": "4.0.0-rc.112" + "effect": "4.0.0-rc.116" }, "devDependencies": { - "@effect/vitest": "4.0.0-rc.112", + "@effect/vitest": "4.0.0-rc.116", "@types/node": "25.7.0", - "effect": "4.0.0-rc.112", + "effect": "4.0.0-rc.116", "tinybench": "2.9.0", "tstyche": "7.2.1", "typescript": "6.0.3", diff --git a/packages/effect-machine/src/internal/testing/machine/arbitrary.ts b/packages/effect-machine/src/internal/testing/machine/arbitrary.ts index 0028581e..a9a3a00a 100644 --- a/packages/effect-machine/src/internal/testing/machine/arbitrary.ts +++ b/packages/effect-machine/src/internal/testing/machine/arbitrary.ts @@ -1,6 +1,6 @@ import * as Schema from "effect/Schema" import * as SchemaAST from "effect/SchemaAST" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" /** * Warning emitted when schema arbitrary generation must enforce an opaque @@ -39,8 +39,7 @@ const reportChecks = ( path: ReadonlyArray ): void => { const visit = (check: SchemaAST.Check, covered: boolean): void => { - const arbitrary = check.annotations?.arbitrary - const nextCovered = covered || arbitrary?.constraint !== undefined || arbitrary?.candidate !== undefined + const nextCovered = covered || check.annotations?.arbitraryConstraint !== undefined if (check._tag !== "Filter") { for (const child of check.checks) visit(child, nextCovered) } else if (!nextCovered) { @@ -97,6 +96,14 @@ const reportFor = (ast: SchemaAST.AST): SchemaArbitraryReport => { /** @internal */ export const toArbitraryWithReport = (schema: S) => ({ - value: Schema.toArbitrary(schema)(FastCheck), + value: Arbitrary.schema(schema), report: reportFor(schema.ast) }) + +/** Selects one of the supplied generators without replacing its shrink tree. @internal */ +export const chooseArbitrary = ( + ...choices: readonly [Arbitrary.Arbitrary, ...Array>] +): Arbitrary.Arbitrary => + Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: choices.length - 1 }))).pipe( + Arbitrary.flatMap((index) => choices[index]!) + ) diff --git a/packages/effect-machine/src/internal/testing/machine/finiteModel.ts b/packages/effect-machine/src/internal/testing/machine/finiteModel.ts index f97c61c7..06e865f9 100644 --- a/packages/effect-machine/src/internal/testing/machine/finiteModel.ts +++ b/packages/effect-machine/src/internal/testing/machine/finiteModel.ts @@ -10,8 +10,9 @@ */ import * as Schema from "effect/Schema" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import * as Machine from "../../../Machine.js" +import { chooseArbitrary } from "./arbitrary.js" /** * An atomic state in a finite generated model. @@ -307,7 +308,7 @@ export interface FiniteModelDiagnostics { * @since 0.4.0 */ export interface FiniteModels { - readonly arbitrary: FastCheck.Arbitrary + readonly arbitrary: Arbitrary.Arbitrary readonly diagnostics: FiniteModelDiagnostics } @@ -397,26 +398,32 @@ const resolveOptions = (options: FiniteModelOptions): FiniteModelDiagnostics["li const rawStateArbitrary = ( depth: number, limits: FiniteModelDiagnostics["limits"] -): FastCheck.Arbitrary => { - const leaf = FastCheck.boolean().map((final): RawState => final ? { _tag: "Final" } : { _tag: "Atomic" }) +): Arbitrary.Arbitrary => { + const leaf = Arbitrary.schema(Schema.Boolean).pipe( + Arbitrary.map((final): RawState => final ? { _tag: "Final" } : { _tag: "Atomic" }) + ) if (depth >= limits.maxDepth) return leaf const nested = rawStateArbitrary(depth + 1, limits) - const compound = FastCheck.array(nested, { + const compound = Arbitrary.array(nested, { minLength: 1, maxLength: limits.maxChildren - }).chain((states) => - FastCheck.integer({ min: 0, max: states.length - 1 }).map((initialIndex): RawCompoundState => ({ - _tag: "Compound", - initialIndex, - states - })) + }).pipe( + Arbitrary.flatMap((states) => + Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: states.length - 1 }))).pipe( + Arbitrary.map((initialIndex): RawCompoundState => ({ + _tag: "Compound", + initialIndex, + states + })) + ) + ) ) - const parallel = FastCheck.array(nested, { + const parallel = Arbitrary.array(nested, { minLength: 2, maxLength: limits.maxParallelRegions - }).map((states): RawParallelState => ({ _tag: "Parallel", states })) - return FastCheck.oneof(leaf, compound, parallel) + }).pipe(Arbitrary.map((states): RawParallelState => ({ _tag: "Parallel", states }))) + return chooseArbitrary(leaf, compound, parallel) } const normalizeStates = (raw: ReadonlyArray): ReadonlyArray => { @@ -659,7 +666,7 @@ const makeTransitionArbitrary = ( events: ReadonlyArray, maxTransitions: number, historyScenarios: ReadonlyArray = [] -): FastCheck.Arbitrary> => { +): Arbitrary.Arbitrary> => { const states = flattenStates(roots) const sourceOrder = new Map(states.map((state, index) => [state.path, index])) const stateByPath = new Map(states.map((state) => [state.path, state])) @@ -773,46 +780,63 @@ const makeTransitionArbitrary = ( const materialize = ( selected: ReadonlyArray, allowTargetlessEvents = true - ): FastCheck.Arbitrary> => { - if (selected.length === 0) return FastCheck.constant([]) + ): Arbitrary.Arbitrary> => { + if (selected.length === 0) return Arbitrary.Constant([]) const decisions = selected.map((candidate) => - FastCheck.record({ + Arbitrary.all({ // Targetless event transitions remain useful witnesses. Generated // automatic transitions always exit their source so stabilization is // acyclic by construction; targetless automatic semantics are covered // by focused examples rather than mixed into the finite-model oracle. - targetIndex: FastCheck.integer({ - min: candidate.trigger.type === "event" && allowTargetlessEvents ? 0 : 1, - max: candidate.targets.length - }), - targetValueOffset: FastCheck.integer({ min: 0, max: 2 }), - reenter: FastCheck.boolean() + targetIndex: Arbitrary.schema( + Schema.Int.check( + Schema.isBetween({ + minimum: candidate.trigger.type === "event" && allowTargetlessEvents ? 0 : 1, + maximum: candidate.targets.length + }) + ) + ), + targetValueOffset: Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 2 }))), + reenter: Arbitrary.schema(Schema.Boolean) }) ) - return FastCheck.tuple(...decisions).map((values) => - selected.map((candidate, index): FiniteTransition => { - const decision = values[index]! - const target = decision.targetIndex === 0 ? undefined : candidate.targets[decision.targetIndex - 1] - const targetState = target === undefined ? undefined : states.find(({ path }) => path === target) - const targetValue = targetState?.node._tag === "Atomic" && - decision.targetValueOffset !== 0 - ? targetState.node.value + decision.targetValueOffset - : undefined - const targetFields = target === undefined - ? {} - : { target, ...(targetValue === undefined ? {} : { targetValue }) } - const trigger = candidate.trigger - return trigger.type === "event" - ? { source: candidate.source, trigger, reenter: decision.reenter, ...targetFields } - : { source: candidate.source, trigger, ...targetFields } - }) + return Arbitrary.all([...decisions]).pipe( + Arbitrary.map((values) => + selected.map((candidate, index): FiniteTransition => { + const decision = values[index]! + const target = decision.targetIndex === 0 ? undefined : candidate.targets[decision.targetIndex - 1] + const targetState = target === undefined ? undefined : states.find(({ path }) => path === target) + const targetValue = targetState?.node._tag === "Atomic" && + decision.targetValueOffset !== 0 + ? targetState.node.value + decision.targetValueOffset + : undefined + const targetFields = target === undefined + ? {} + : { target, ...(targetValue === undefined ? {} : { targetValue }) } + const trigger = candidate.trigger + return trigger.type === "event" + ? { source: candidate.source, trigger, reenter: decision.reenter, ...targetFields } + : { source: candidate.source, trigger, ...targetFields } + }) + ) ) } const optionalBudget = maxTransitions - mandatory.length - const general = FastCheck.subarray([...eventCandidates, ...automaticCandidates], { - minLength: 0, - maxLength: Math.min(optionalBudget, eventCandidates.length + automaticCandidates.length) - }).chain((selected) => materialize(selected).map((transitions) => orderTransitions([...mandatory, ...transitions]))) + const candidates = [...eventCandidates, ...automaticCandidates] + const selectedCandidates = candidates.length === 0 + ? Arbitrary.Constant>([]) + : Arbitrary.schema( + Schema.Array( + Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: candidates.length - 1 })) + ).check(Schema.isUnique(), Schema.isMaxLength(Math.min(optionalBudget, candidates.length))) + ).pipe( + Arbitrary.map((indices) => [...indices].sort((a, b) => a - b).map((index) => candidates[index]!)) + ) + const general = selectedCandidates.pipe( + Arbitrary.flatMap((selected) => + materialize(selected).pipe(Arbitrary.map((transitions) => orderTransitions([...mandatory, ...transitions]))) + ) + ) if (mandatory.length > 0 || optionalBudget < 2 || automaticCandidates.length === 0) return general @@ -836,10 +860,14 @@ const makeTransitionArbitrary = ( }) if (chainCandidates.length === 0) return general - const chain = FastCheck.constantFrom(...chainCandidates).chain(({ automatic, event }) => - materialize([event, automatic], false).map((transitions) => orderTransitions(transitions)) + const chain = Arbitrary.schema( + Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: chainCandidates.length - 1 })) + ).pipe(Arbitrary.map((index) => chainCandidates[index]!)).pipe( + Arbitrary.flatMap(({ automatic, event }) => + materialize([event, automatic], false).pipe(Arbitrary.map((transitions) => orderTransitions(transitions))) + ) ) - return FastCheck.oneof(general, chain) + return chooseArbitrary(general, chain) } /** @@ -858,20 +886,20 @@ const makeTransitionArbitrary = ( */ export const finiteModels = (options: FiniteModelOptions = {}): FiniteModels => { const limits = resolveOptions(options) - const rawRoots = FastCheck.array(rawStateArbitrary(1, limits), { + const rawRoots = Arbitrary.array(rawStateArbitrary(1, limits), { minLength: 1, maxLength: limits.maxRoots }) - const arbitrary = rawRoots.chain((raw) => { + const arbitrary = rawRoots.pipe(Arbitrary.flatMap((raw) => { const activeRoots = normalizeStates(raw) - return FastCheck.tuple( - FastCheck.integer({ min: 0, max: activeRoots.length - 1 }), - FastCheck.integer({ min: 1, max: limits.maxEvents }), - FastCheck.array(FastCheck.constantFrom("shallow" as const, "deep" as const), { + return Arbitrary.all([ + Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: activeRoots.length - 1 }))), + Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: limits.maxEvents }))), + Arbitrary.array(Arbitrary.schema(Schema.Literals(["shallow" as const, "deep" as const])), { minLength: 0, maxLength: limits.maxHistoryStates }) - ).chain(([initialIndex, eventCount, historyDecisions]) => { + ]).pipe(Arbitrary.flatMap(([initialIndex, eventCount, historyDecisions]) => { const events = Array.from({ length: eventCount }, (_, index) => `Event${index}`) const initial = activeRoots[initialIndex]!.key const generated = addGeneratedHistory( @@ -885,11 +913,11 @@ export const finiteModels = (options: FiniteModelOptions = {}): FiniteModels => const roots = generated.scenarios.length === 0 ? addGeneratedChoices(generated.roots, limits.maxChoiceStates) : generated.roots - return makeTransitionArbitrary(roots, initial, events, limits.maxTransitions, generated.scenarios).map( - (transitions) => freezeModel(roots, initial, events, transitions, generated.scenarios) + return makeTransitionArbitrary(roots, initial, events, limits.maxTransitions, generated.scenarios).pipe( + Arbitrary.map((transitions) => freezeModel(roots, initial, events, transitions, generated.scenarios)) ) - }) - }) + })) + })) return { arbitrary, diff --git a/packages/effect-machine/src/internal/testing/machine/referenceModel.ts b/packages/effect-machine/src/internal/testing/machine/referenceModel.ts index bba832a0..4775399d 100644 --- a/packages/effect-machine/src/internal/testing/machine/referenceModel.ts +++ b/packages/effect-machine/src/internal/testing/machine/referenceModel.ts @@ -970,12 +970,13 @@ const evaluateTransition = ( if (!changed) { return { ...selection, next, targetPath, changed, exitPaths: [], entryPaths: [] } } - const naturalBoundary = targetPath === undefined - ? getState(index, transition.source).parent - : leastCommonAncestor(index, selection.leaf, targetPath) const choiceEntry = transition.target === undefined ? undefined : entryChoicePath(index, transition.target) - const choiceResolvesToActiveAncestor = choiceEntry !== undefined && - isPathInSubtree(transition.source, resolveChoicePath(index, choiceEntry)) + const lifecycleTarget = choiceEntry === undefined ? targetPath : choiceChainResolvedTargetPath(index, choiceEntry) + const naturalBoundary = lifecycleTarget === undefined + ? getState(index, transition.source).parent + : leastCommonAncestor(index, selection.leaf, lifecycleTarget) + const choiceResolvesToActiveAncestor = choiceEntry !== undefined && lifecycleTarget !== undefined && + isPathInSubtree(transition.source, lifecycleTarget) const boundary = reenters(transition) ? !choiceResolvesToActiveAncestor ? broadenBoundary(index, naturalBoundary, getState(index, transition.source).parent) diff --git a/packages/effect-machine/src/internal/testing/machine/runtime.ts b/packages/effect-machine/src/internal/testing/machine/runtime.ts index 46b3cda4..0e892e96 100644 --- a/packages/effect-machine/src/internal/testing/machine/runtime.ts +++ b/packages/effect-machine/src/internal/testing/machine/runtime.ts @@ -11,12 +11,14 @@ import * as Duration from "effect/Duration" import * as Effect from "effect/Effect" import * as Inspectable from "effect/Inspectable" import * as Queue from "effect/Queue" +import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" -import { FastCheck, TestClock } from "effect/testing" +import { TestClock } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import * as Machine from "../../../Machine.js" import type { CausalRuntimeEvidence, Probe, ProbeStep, RuntimeInvariant } from "../../../testing/MachineTest.js" import * as Protocol from "../../machine/protocol.js" -import { type SchemaArbitraryReport, toArbitraryWithReport } from "./arbitrary.js" +import { chooseArbitrary, type SchemaArbitraryReport, toArbitraryWithReport } from "./arbitrary.js" import { assertRuntimeInvariants, type RuntimeInvariantError } from "./runtimeInvariant.js" type AnyMachine = Machine.Machine.Any @@ -1188,7 +1190,7 @@ export const runRuntimeCommands: typeof runEnqueuedCommands = runEnqueuedCommand * inspection, assertion, and the next command. Use `probe.await.until` only * for later asynchronous work such as timer, invoke, or child delivery. * Processing failures are attributed to the exact submitted command and retain - * the successfully checked prefix for FastCheck shrinking and replay. + * the successfully checked prefix for Arbitrary shrinking and replay. * * Use `runEnqueuedCommands` instead when the behavior under test intentionally * depends on burst enqueueing or outstanding mailbox work. @@ -1460,12 +1462,12 @@ export const verifyCausalCommands = ( export interface RuntimeCommandsOptions { readonly minCommands?: number readonly maxCommands?: number - readonly eventArbitrary?: FastCheck.Arbitrary> - readonly advanceArbitrary?: FastCheck.Arbitrary + readonly eventArbitrary?: Arbitrary.Arbitrary> + readonly advanceArbitrary?: Arbitrary.Arbitrary readonly includeAdvance?: boolean readonly includeStop?: boolean readonly includeCheckpoint?: boolean - readonly additionalCommands?: ReadonlyArray>>> + readonly additionalCommands?: ReadonlyArray>>> } /** @@ -1489,7 +1491,7 @@ export interface RuntimeCommandsDiagnostics { * @since 0.4.0 */ export interface RuntimeCommands { - readonly arbitrary: FastCheck.Arbitrary>>> + readonly arbitrary: Arbitrary.Arbitrary>>> readonly diagnostics: RuntimeCommandsDiagnostics } @@ -1503,9 +1505,8 @@ const validateCommandLength = (name: "minCommands" | "maxCommands", value: numbe * Derives a shrinkable command sequence from public event schemas and explicit * clock/stop/checkpoint command choices. * - * This deliberately returns ordinary Effect FastCheck arbitraries instead of - * adapting the runner through `asyncModelRun`: the latter requires Promise - * callbacks and would erase Effect error and service channels. + * Returns native Arbitrary values for use with `Arbitrary.checkEffect` or + * Effect property tests, preserving typed errors and required services. * * @category constructors * @since 0.4.0 @@ -1527,33 +1528,34 @@ export const runtimeCommands = ( ? Protocol.inputEventSchemas(machine).map((schema) => { const derived = toArbitraryWithReport(schema) reports.push(derived.report) - return derived.value as FastCheck.Arbitrary> + return derived.value as Arbitrary.Arbitrary> }) : [] const eventArbitrary = options.eventArbitrary ?? (eventArbitraries.length === 0 ? undefined - : FastCheck.oneof( + : chooseArbitrary( ...eventArbitraries as [ - FastCheck.Arbitrary>, - ...Array>> + Arbitrary.Arbitrary>, + ...Array>> ] )) - const commandArbitraries: Array>>> = [] - if (eventArbitrary !== undefined) commandArbitraries.push(eventArbitrary.map(sendCommand)) + const commandArbitraries: Array>>> = [] + if (eventArbitrary !== undefined) commandArbitraries.push(eventArbitrary.pipe(Arbitrary.map(sendCommand))) if (options.includeAdvance !== false) { - const advanceArbitrary = options.advanceArbitrary ?? FastCheck.nat({ max: 60_000 }) - commandArbitraries.push(advanceArbitrary.map(advanceCommand)) + const advanceArbitrary = options.advanceArbitrary ?? + Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 60_000 }))) + commandArbitraries.push(advanceArbitrary.pipe(Arbitrary.map(advanceCommand))) } - if (options.includeStop !== false) commandArbitraries.push(FastCheck.constant(stopCommand())) - if (options.includeCheckpoint !== false) commandArbitraries.push(FastCheck.constant(checkpointCommand())) + if (options.includeStop !== false) commandArbitraries.push(Arbitrary.Constant(stopCommand())) + if (options.includeCheckpoint !== false) commandArbitraries.push(Arbitrary.Constant(checkpointCommand())) commandArbitraries.push(...options.additionalCommands ?? []) if (commandArbitraries.length === 0) { if (minCommands > 0) { throw new Error("MachineTest.runtimeCommands cannot generate a non-empty command sequence without commands") } return { - arbitrary: FastCheck.constant([]), + arbitrary: Arbitrary.Constant([]), diagnostics: { events: options.eventArbitrary !== undefined ? "override" : eventArbitraries.length === 0 ? "none" : "schema", schemaReports: reports, @@ -1565,7 +1567,7 @@ export const runtimeCommands = ( } return { - arbitrary: FastCheck.array(FastCheck.oneof(...commandArbitraries), { + arbitrary: Arbitrary.array(chooseArbitrary(commandArbitraries[0]!, ...commandArbitraries.slice(1)), { minLength: minCommands, maxLength: maxCommands }), diff --git a/packages/effect-machine/src/internal/testing/machine/verification.ts b/packages/effect-machine/src/internal/testing/machine/verification.ts index 7e9b753d..48e88f80 100644 --- a/packages/effect-machine/src/internal/testing/machine/verification.ts +++ b/packages/effect-machine/src/internal/testing/machine/verification.ts @@ -9,7 +9,7 @@ import * as Effect from "effect/Effect" import * as Graph from "effect/Graph" import * as Schema from "effect/Schema" import * as SchemaAST from "effect/SchemaAST" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import * as Machine from "../../../Machine.js" import type { Coverage, @@ -32,7 +32,7 @@ import type { VerifyOptions } from "../../../testing/MachineTest.js" import * as Protocol from "../../machine/protocol.js" -import { toArbitraryWithReport } from "./arbitrary.js" +import { chooseArbitrary, toArbitraryWithReport } from "./arbitrary.js" import type { FiniteModel } from "./finiteModel.js" import * as ReferenceModel from "./referenceModel.js" import { rawConfigurationPaths } from "./trace.js" @@ -82,17 +82,17 @@ export const scenarios = ( index, report: derived.report }) - return derived.value as FastCheck.Arbitrary> + return derived.value as Arbitrary.Arbitrary> }) : [] const eventsArbitrary = options.eventsArbitrary ?? (eventArbitraries.length === 0 - ? FastCheck.constant>>([]) - : FastCheck.array( - FastCheck.oneof( + ? Arbitrary.Constant>>([]) + : Arbitrary.array( + chooseArbitrary( ...eventArbitraries as [ - FastCheck.Arbitrary>, - ...Array>> + Arbitrary.Arbitrary>, + ...Array>> ] ), { minLength: minEvents, maxLength: maxEvents } @@ -103,7 +103,7 @@ export const scenarios = ( throw new Error("MachineTest.scenarios cannot override input for a machine without an input schema") } return { - arbitrary: eventsArbitrary.map((events) => ({ events }) as Scenario), + arbitrary: eventsArbitrary.pipe(Arbitrary.map((events) => ({ events }) as Scenario)), diagnostics: { input: "none", events: options.eventsArbitrary !== undefined ? "override" : eventArbitraries.length === 0 ? "empty" : "schema", @@ -112,7 +112,7 @@ export const scenarios = ( } } - let inputArbitrary: FastCheck.Arbitrary> + let inputArbitrary: Arbitrary.Arbitrary> if (options.inputArbitrary !== undefined) { inputArbitrary = options.inputArbitrary } else { @@ -122,16 +122,16 @@ export const scenarios = ( index: undefined, report: derived.report }) - inputArbitrary = derived.value as FastCheck.Arbitrary> + inputArbitrary = derived.value as Arbitrary.Arbitrary> } return { - arbitrary: FastCheck.tuple(inputArbitrary, eventsArbitrary).map(([input, events]) => + arbitrary: Arbitrary.all([inputArbitrary, eventsArbitrary]).pipe(Arbitrary.map(([input, events]) => ({ input, events }) as Scenario - ), + )), diagnostics: { input: options.inputArbitrary !== undefined ? "override" : "schema", events: options.eventsArbitrary !== undefined ? "override" : eventArbitraries.length === 0 ? "empty" : "schema", diff --git a/packages/effect-machine/src/testing/MachineTest.ts b/packages/effect-machine/src/testing/MachineTest.ts index e3b39266..c45e0999 100644 --- a/packages/effect-machine/src/testing/MachineTest.ts +++ b/packages/effect-machine/src/testing/MachineTest.ts @@ -7,7 +7,7 @@ import type * as Effect from "effect/Effect" import type * as Graph from "effect/Graph" import type * as Schema from "effect/Schema" -import type { FastCheck } from "effect/testing" +import type * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import type { EnsureExecutable } from "../internal/machine/readiness.js" import type { SchemaArbitraryReport } from "../internal/testing/machine/arbitrary.js" import * as ExplorationImpl from "../internal/testing/machine/exploration.js" @@ -179,13 +179,13 @@ export type ScenarioOptions = & { readonly minEvents?: number readonly maxEvents?: number - readonly eventsArbitrary?: FastCheck.Arbitrary>> + readonly eventsArbitrary?: Arbitrary.Arbitrary>> } & (Machine.Machine.InputSchema extends typeof Schema.Void ? { readonly inputArbitrary?: never } : { - readonly inputArbitrary?: FastCheck.Arbitrary> + readonly inputArbitrary?: Arbitrary.Arbitrary> }) /** @@ -219,14 +219,14 @@ export interface ScenarioDiagnostics { * @since 0.4.0 */ export interface Scenarios { - readonly arbitrary: FastCheck.Arbitrary> + readonly arbitrary: Arbitrary.Arbitrary> readonly diagnostics: ScenarioDiagnostics } /** * Derives valid machine inputs and public events from their schemas. * - * Unsupported schema derivations fail immediately through `Schema.toArbitrary`. + * Unsupported schema derivations fail immediately through `Arbitrary.schema`. * Non-fatal derivation warnings are returned instead of being hidden. * * **Example** @@ -1216,7 +1216,7 @@ export { InvariantError } from "../internal/testing/machine/invariant.js" * * Every invariant and matching observation is evaluated so one failure * contains all relevant evidence. Combine this with `scenarios` and `run` in - * an Effect property test to retain FastCheck shrinking. + * an Effect property test to retain Arbitrary shrinking. * * @category verification * @since 0.4.0 diff --git a/packages/effect-machine/test/internal/machine/activities.test.ts b/packages/effect-machine/test/internal/machine/activities.test.ts index 5367043b..0d5916ca 100644 --- a/packages/effect-machine/test/internal/machine/activities.test.ts +++ b/packages/effect-machine/test/internal/machine/activities.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Duration, Effect, Schema, Stream } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../../src/index.js" import { activityDefinitions } from "../../../src/internal/machine/activities.js" import { makeMermaidRenderer } from "../../machine/visualization/mermaid.js" @@ -166,8 +166,8 @@ describe("machine activity metadata", () => { assert(Machine.activityDefinitions(activityMachine).every(({ source }) => paths.has(source))) }) it.effect.prop("keeps generated timer ids, durations, events, and owners aligned with helper declarations", { - durationMillis: FastCheck.integer({ min: 0, max: 604800000 }), - idSuffix: FastCheck.nat({ max: 1000000 }) + durationMillis: Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 604800000 }))), + idSuffix: Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 1000000 }))) }, ({ durationMillis, idSuffix }) => Effect.sync(() => { const id = `generated-timer-${idSuffix}` @@ -196,7 +196,7 @@ describe("machine activity metadata", () => { duration: Duration.format(Duration.fromInputUnsafe(durationMillis)) }) assert(Machine.stateNodes(generated).some(({ path }) => path === definition?.source)) - }), { fastCheck: { numRuns: 100, seed: 68241 } }) + }), { arbitrary: { runs: 100, seed: 68241 } }) it("collects static descriptors in topology and declaration order", () => { assert.deepStrictEqual(activityDefinitions(machine), [ { diff --git a/packages/effect-machine/test/internal/machine/strategyDifferential.test.ts b/packages/effect-machine/test/internal/machine/strategyDifferential.test.ts index 8c452348..0632af24 100644 --- a/packages/effect-machine/test/internal/machine/strategyDifferential.test.ts +++ b/packages/effect-machine/test/internal/machine/strategyDifferential.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Deferred, Effect, Fiber, Option, Schema, Stream } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../../src/index.js" import * as Configuration from "../../../src/internal/machine/configuration.js" import * as ExecutionPlan from "../../../src/internal/machine/executionPlan.js" @@ -1519,7 +1519,7 @@ describe("machine planner and runtime strategies", () => { maxHistoryStates: 0, maxChoiceStates: 0 }) - const samples = FastCheck.sample(generated.arbitrary, { numRuns: 120, seed: 81109 }) + const samples = yield* Arbitrary.sampleEffect(generated.arbitrary, { count: 120, seed: 81109 }) let compared = 0 for (let index = 0; index < samples.length && compared < 24; index++) { const model = samples[index]! diff --git a/packages/effect-machine/test/machine/ProtocolOwnership.test.ts b/packages/effect-machine/test/machine/ProtocolOwnership.test.ts index 5c69f766..1b22251a 100644 --- a/packages/effect-machine/test/machine/ProtocolOwnership.test.ts +++ b/packages/effect-machine/test/machine/ProtocolOwnership.test.ts @@ -71,10 +71,16 @@ describe("protocol ownership", () => { Effect.gen(function*() { const Value = boundary === "encode" ? Schema.Number.pipe( - Schema.encode({ decode: SchemaGetter.passthrough(), encode: SchemaGetter.onSome(() => Effect.interrupt) }) + Schema.encode({ + decode: SchemaGetter.passthrough(), + encode: SchemaGetter.transformEffect(() => Effect.interrupt) + }) ) : Schema.Number.pipe( - Schema.decode({ decode: SchemaGetter.onSome(() => Effect.interrupt), encode: SchemaGetter.passthrough() }) + Schema.decode({ + decode: SchemaGetter.transformEffect(() => Effect.interrupt), + encode: SchemaGetter.passthrough() + }) ) const State = Schema.TaggedStruct("State", { value: Value }) const InitialRoot1 = Machine.state({ states: { State } }) diff --git a/packages/effect-machine/test/machine/RuntimeDifferential.test.ts b/packages/effect-machine/test/machine/RuntimeDifferential.test.ts index 00d20943..6ae9a1e1 100644 --- a/packages/effect-machine/test/machine/RuntimeDifferential.test.ts +++ b/packages/effect-machine/test/machine/RuntimeDifferential.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Effect, Fiber, Schema, Stream } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../src/index.js" import { MachineTest } from "../../src/testing/index.js" import type { DifferentialStep } from "./support/runtimeDifferential.js" @@ -325,7 +325,7 @@ describe("pure planning and managed runtime differential", () => { }) as Effect.Effect) it.effect("matches deterministic generated start and resumed executions", () => Effect.gen(function*() { - const samples = FastCheck.sample(generated.arbitrary, { numRuns: 36, seed: 93701 }) + const samples = yield* Arbitrary.sampleEffect(generated.arbitrary, { count: 36, seed: 93701 }) let activeParallel = 0 let eventful = 0 let resumedContinuation = 0 diff --git a/packages/effect-machine/test/machine/SnapshotCodecAdversarial.test.ts b/packages/effect-machine/test/machine/SnapshotCodecAdversarial.test.ts index 9dc037a5..fb9b8df8 100644 --- a/packages/effect-machine/test/machine/SnapshotCodecAdversarial.test.ts +++ b/packages/effect-machine/test/machine/SnapshotCodecAdversarial.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Cause, Effect, Exit, Option, Schema } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../src/index.js" class Root extends Schema.TaggedClass("CodecRoot")("CodecRoot", { id: Schema.NonEmptyString @@ -311,7 +311,7 @@ const expectDecodeFailure = Effect.fnUntraced( ) describe("snapshot codec adversarial boundaries", () => { it.effect.prop("turns arbitrary JSON-shaped boundary input into values or typed failures, never defects", { - input: FastCheck.jsonValue() + input: Arbitrary.schema(Schema.Json) }, ({ input }) => Effect.gen(function*() { const encodeExit = yield* Effect.exit(Machine.encodeSnapshot(topologyMachine, input as any)) @@ -329,7 +329,7 @@ describe("snapshot codec adversarial boundaries", () => { error.value instanceof Machine.MachineSchemaDecodeError ) } - }), { fastCheck: { numRuns: 100, seed: 83117 } }) + }), { arbitrary: { runs: 100, seed: 83117 } }) it.effect("round-trips active parallel and completed final configurations through JSON", () => Effect.gen(function*() { for (const snapshot of [topologyActive(), topologyFinal()]) { diff --git a/packages/effect-machine/test/machine/Totality.test.ts b/packages/effect-machine/test/machine/Totality.test.ts index c0659152..a9c0ffb1 100644 --- a/packages/effect-machine/test/machine/Totality.test.ts +++ b/packages/effect-machine/test/machine/Totality.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Cause, Effect, Exit, Fiber, Schema, Stream } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { isDeepStrictEqual } from "node:util" import { Machine } from "../../src/index.js" import { MachineTest } from "../../src/testing/index.js" @@ -14,17 +14,19 @@ const generatedModels = MachineTest.finiteModels({ maxHistoryStates: 2, maxChoiceStates: 2 }) -const generatedCases = generatedModels.arbitrary.chain((model) => - FastCheck.tuple( - FastCheck.array(FastCheck.constantFrom(...model.events), { minLength: 0, maxLength: 12 }), - FastCheck.nat({ max: 12 }) - ).map(([events, boundary]) => ({ - model, - events, - boundary: Math.min(boundary, events.length) - })) +const generatedCases = generatedModels.arbitrary.pipe( + Arbitrary.flatMap((model) => + Arbitrary.all([ + Arbitrary.array(Arbitrary.schema(Schema.Literals([...model.events])), { minLength: 0, maxLength: 12 }), + Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 12 }))) + ]).pipe(Arbitrary.map(([events, boundary]) => ({ + model, + events, + boundary: Math.min(boundary, events.length) + }))) + ) ) -const generatedResumeCases = generatedCases.map((generated) => { +const generatedResumeCases = generatedCases.pipe(Arbitrary.map((generated) => { // Automatic-transition restoration has focused witnesses in the finite // reference-model suite. Keep this broad continuation property on // deterministic state-changing event targets (plus complete generated @@ -53,7 +55,7 @@ const generatedResumeCases = generatedCases.map((generated) => { events, boundary: Math.min(generated.boundary, events.length) } -}) +})) const assertNoUnexpectedDefect: (operation: string, exit: Exit.Exit) => asserts exit is Exit.Success = (operation, exit) => { if (Exit.isFailure(exit) && Cause.hasDies(exit.cause)) { @@ -176,7 +178,7 @@ describe("machine operation totality", () => { const decoded = yield* decodeWithoutDefect(machine, transported, `decodeSnapshot ${index}`) yield* assertLogicalSnapshotEquivalent(machine, snapshot, decoded, `snapshot ${index}`) } - }), { fastCheck: { numRuns: 150, seed: 61607 } }) + }), { arbitrary: { runs: 150, seed: 61607 } }) it.effect.prop("resumes a round-tripped stable boundary and preserves continuation semantics", { generated: generatedResumeCases }, ({ generated }) => @@ -216,7 +218,7 @@ describe("machine operation totality", () => { } yield* assertLogicalSnapshotEquivalent(machine, fullTrace.final, finalRuntime.state, "resumed continuation") yield* ref.stop - }), { fastCheck: { numRuns: 75, seed: 72719 } }) + }), { arbitrary: { runs: 75, seed: 72719 } }) it.effect("round-trips transformed state values and completion output", () => Effect.gen(function*() { const Value = Schema.TaggedStruct("Value", { amount: Schema.NumberFromString }) diff --git a/packages/effect-machine/test/machine/support/activityLifecycleModel.ts b/packages/effect-machine/test/machine/support/activityLifecycleModel.ts index 8b44e43f..e7f7e765 100644 --- a/packages/effect-machine/test/machine/support/activityLifecycleModel.ts +++ b/packages/effect-machine/test/machine/support/activityLifecycleModel.ts @@ -1,5 +1,6 @@ import { Cause, Data, Deferred, Effect, Exit, Queue, Ref } from "effect" -import { FastCheck } from "effect/testing" +import * as Schema from "effect/Schema" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../../src/index.js" export type ActivityOutcome = "succeeded" | "cancelled" | "failed" @@ -124,12 +125,14 @@ export const lifecycleCommandSamples = (options?: { readonly seed?: number readonly maxCommands?: number }): ReadonlyArray> => - FastCheck.sample( - FastCheck.array( - FastCheck.constantFrom("enter", "leave", "restart"), - { minLength: 1, maxLength: options?.maxCommands ?? 24 } - ), - { numRuns: options?.numRuns ?? 40, seed: options?.seed ?? 82_419 } + Effect.runSync( + Arbitrary.sampleEffect( + Arbitrary.array(Arbitrary.schema(Schema.Literals(["enter", "leave", "restart"])), { + minLength: 1, + maxLength: options?.maxCommands ?? 24 + }), + { count: options?.numRuns ?? 40, seed: options?.seed ?? 82_419 } + ) ) export const expectedLifecycle = (commands: ReadonlyArray): LifecycleExpectation => { diff --git a/packages/effect-machine/test/testing/FiniteModel.test.ts b/packages/effect-machine/test/testing/FiniteModel.test.ts index d8299994..0b47fe1d 100644 --- a/packages/effect-machine/test/testing/FiniteModel.test.ts +++ b/packages/effect-machine/test/testing/FiniteModel.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Effect } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../src/index.js" import { MachineTest } from "../../src/testing/index.js" @@ -173,7 +173,7 @@ describe("MachineTest finite models", () => { eventlessTransitions: true, acyclicAutomaticTransitions: true }) - const samples = FastCheck.sample(generated.arbitrary, { numRuns: 250, seed: 10_241 }) + const samples = Effect.runSync(Arbitrary.sampleEffect(generated.arbitrary, { count: 250, seed: 10_241 })) for (const model of samples) { assertValid(model, generated.diagnostics.limits) assert.ok( @@ -203,7 +203,7 @@ describe("MachineTest finite models", () => { it.effect("generates firing event-to-always and event-to-completion chains", () => Effect.gen(function*() { - const samples = FastCheck.sample(generated.arbitrary, { numRuns: 2_000, seed: 12_773 }) + const samples = yield* Arbitrary.sampleEffect(generated.arbitrary, { count: 2_000, seed: 12_773 }) const witnesses = new Map<"always" | "done", { readonly event: string readonly model: MachineTest.FiniteModel @@ -241,7 +241,7 @@ describe("MachineTest finite models", () => { it.effect("replays exact generated root and nested value-mutation/capture/restore scenarios", () => Effect.gen(function*() { - const samples = FastCheck.sample(generated.arbitrary, { numRuns: 1_000, seed: 15_361 }) + const samples = yield* Arbitrary.sampleEffect(generated.arbitrary, { count: 1_000, seed: 15_361 }) const candidates = samples.flatMap((model) => (model.historyScenarios ?? []).map((scenario) => ({ model, scenario })) ) @@ -268,7 +268,7 @@ describe("MachineTest finite models", () => { })) it("compiles state nodes and transition definitions through the public Machine API", () => { - for (const model of FastCheck.sample(generated.arbitrary, { numRuns: 100, seed: 20_482 })) { + for (const model of Effect.runSync(Arbitrary.sampleEffect(generated.arbitrary, { count: 100, seed: 20_482 }))) { const machine = MachineTest.compileModel(model) const expectedStates = flatten(model) const actualStates = Machine.stateNodes(machine) @@ -336,14 +336,14 @@ describe("MachineTest finite models", () => { } }) - const executable = generated.arbitrary.chain((model) => { + const executable = generated.arbitrary.pipe(Arbitrary.flatMap((model) => { const machine = MachineTest.compileModel(model) - return MachineTest.scenarios(machine, { minEvents: 0, maxEvents: 20 }).arbitrary.map((scenario) => ({ + return MachineTest.scenarios(machine, { minEvents: 0, maxEvents: 20 }).arbitrary.pipe(Arbitrary.map((scenario) => ({ model, machine, scenario - })) - }) + }))) + })) it.effect.prop( "runs and independently verifies schema-valid scenarios for generated machines", @@ -352,7 +352,7 @@ describe("MachineTest finite models", () => { MachineTest.run(generated.machine, generated.scenario).pipe( Effect.flatMap((trace) => MachineTest.verify(generated.machine, trace)) ), - { fastCheck: { numRuns: 250, seed: 30_723 } } + { arbitrary: { runs: 250, seed: 30_723 } } ) it.effect("keeps the natural cross-root lifecycle boundary for reentering transitions", () => @@ -411,27 +411,23 @@ describe("MachineTest finite models", () => { it("keeps every visited shrink valid and replays the minimal counterexample", () => { const visited: Array = [] - const property = FastCheck.property(generated.arbitrary, (model) => { + const property = (model: MachineTest.FiniteModel) => { assertValid(model, generated.diagnostics.limits) MachineTest.compileModel(model) visited.push(model) return false - }) - const result = FastCheck.check(property, { numRuns: 20, seed: 40_964 }) + } + const result = Effect.runSync(Arbitrary.checkEffect(generated.arbitrary, property, { runs: 20, seed: 40_964 })) - assert.strictEqual(result.failed, true) + assert.strictEqual(result._tag, "Falsified") + if (result._tag !== "Falsified") return assert.ok(visited.length > 1, "the failing input should have been shrunk") - assert.ok(result.counterexample !== null) - const smallest = result.counterexample![0] + const smallest = result.shrunkInput assertValid(smallest, generated.diagnostics.limits) - const replay = FastCheck.check(property, { - numRuns: 1, - seed: result.seed, - path: result.counterexamplePath - }) - assert.strictEqual(replay.failed, true) - assert.deepStrictEqual(replay.counterexample?.[0], smallest) + const replay = Effect.runSync(Arbitrary.checkEffect(generated.arbitrary, property, { replay: result.replay })) + assert.strictEqual(replay._tag, "Falsified") + if (replay._tag === "Falsified") assert.deepStrictEqual(replay.shrunkInput, smallest) }) it("rejects hand-authored dangling and duplicate transition registrations", () => { diff --git a/packages/effect-machine/test/testing/Invariant.test.ts b/packages/effect-machine/test/testing/Invariant.test.ts index 07b0562f..a19a1a2e 100644 --- a/packages/effect-machine/test/testing/Invariant.test.ts +++ b/packages/effect-machine/test/testing/Invariant.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Effect, Schema } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../src/index.js" import { MachineTest } from "../../src/testing/index.js" class Account extends Schema.TaggedClass("Account")("Account", { @@ -171,16 +171,20 @@ describe("MachineTest invariants", () => { ({ snapshot }) => snapshot.state.value.balance >= 0 ) const safeScenarios = MachineTest.scenarios(safeMachine, { - eventsArbitrary: FastCheck.array(FastCheck.integer({ min: 0, max: 5 }).map((amount) => new Deposit({ amount }))) + eventsArbitrary: Arbitrary.array( + Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 5 }))).pipe( + Arbitrary.map((amount) => new Deposit({ amount })) + ) + ) }) it.effect.prop( - "rechecks invariants after every FastCheck shrink", + "rechecks invariants after every Arbitrary shrink", { scenario: safeScenarios.arbitrary }, ({ scenario }) => MachineTest.run(safeMachine, scenario).pipe( Effect.flatMap((trace) => MachineTest.assertInvariants(safeMachine, trace, [safe])) ), - { fastCheck: { numRuns: 25 } } + { arbitrary: { runs: 25 } } ) it("validates invariant metadata eagerly", () => { const define = MachineTest.invariants(safeMachine) diff --git a/packages/effect-machine/test/testing/MachineTest.test.ts b/packages/effect-machine/test/testing/MachineTest.test.ts index 0d7589cc..900d926b 100644 --- a/packages/effect-machine/test/testing/MachineTest.test.ts +++ b/packages/effect-machine/test/testing/MachineTest.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Effect, Schema } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../src/index.js" import { MachineTest } from "../../src/testing/index.js" class TestInput extends Schema.Class("TestInput")({ @@ -67,7 +67,7 @@ describe("MachineTest", () => { it("derives complete scenarios from machine schemas and reports diagnostics", () => { const machine = makeTraceMachine(() => undefined) const generated = MachineTest.scenarios(machine, { minEvents: 2, maxEvents: 2 }) - const samples = FastCheck.sample(generated.arbitrary, 10) + const samples = Effect.runSync(Arbitrary.sampleEffect(generated.arbitrary, { count: 10 })) assert.strictEqual(generated.diagnostics.input, "schema") assert.strictEqual(generated.diagnostics.events, "schema") assert.strictEqual(generated.diagnostics.schemas.length, 3) @@ -91,10 +91,13 @@ describe("MachineTest", () => { const generated = MachineTest.scenarios(machine, { minEvents: 10, maxEvents: 10, - inputArbitrary: FastCheck.constant(input), - eventsArbitrary: FastCheck.constant(events) + inputArbitrary: Arbitrary.Constant(input), + eventsArbitrary: Arbitrary.Constant(events) }) - assert.deepStrictEqual(FastCheck.sample(generated.arbitrary, 1), [{ input, events }]) + assert.deepStrictEqual(Effect.runSync(Arbitrary.sampleEffect(generated.arbitrary, { count: 1 })), [{ + input, + events + }]) assert.strictEqual(generated.diagnostics.input, "override") assert.strictEqual(generated.diagnostics.events, "override") }) diff --git a/packages/effect-machine/test/testing/ReferenceModel.test.ts b/packages/effect-machine/test/testing/ReferenceModel.test.ts index c3740ccd..82923a5f 100644 --- a/packages/effect-machine/test/testing/ReferenceModel.test.ts +++ b/packages/effect-machine/test/testing/ReferenceModel.test.ts @@ -1,7 +1,7 @@ import { assert, describe, it } from "@effect/vitest" import * as Effect from "effect/Effect" import * as Schema from "effect/Schema" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../src/index.js" import { MachineTest } from "../../src/testing/index.js" const event = (_tag: string): { @@ -1886,33 +1886,35 @@ describe("MachineTest finite-model reference interpreter", () => { maxChildren: 3, maxEvents: 4, maxTransitions: 20 - }).arbitrary.chain((model) => { - const randomEvents = FastCheck.array(FastCheck.constantFrom(...model.events), { maxLength: 20 }) + }).arbitrary.pipe(Arbitrary.flatMap((model) => { + const randomEvents = Arbitrary.array(Arbitrary.schema(Schema.Literals([...model.events])), { maxLength: 20 }) const historyScenarios = model.historyScenarios ?? [] // Execute the generator-authored witness itself. This prevents an // unrelated value-bearing transition under the same owner from silently // standing in for the mutation that must be captured and restored. - return FastCheck.oneof( - ...(historyScenarios.length === 0 - ? [FastCheck.constant({ events: [] as ReadonlyArray, scenario: undefined, firstUse: false })] - : historyScenarios.map((scenario) => - FastCheck.constant({ events: scenario.events, scenario, firstUse: false }) - )), - ...(historyScenarios.length === 0 - ? [] - : historyScenarios.map((scenario) => - FastCheck.constant({ events: [scenario.resume.event], scenario, firstUse: true }) - )), - randomEvents.map((events) => ({ events, scenario: undefined, firstUse: false })) - ).map(({ events, firstUse, scenario }) => ({ + const scenarios = historyScenarios.flatMap((scenario) => [ + { events: scenario.events, scenario, firstUse: false }, + { events: [scenario.resume.event], scenario, firstUse: true } + ]) + return Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: scenarios.length }))).pipe( + Arbitrary.flatMap((index): Arbitrary.Arbitrary<{ + events: ReadonlyArray + scenario: MachineTest.FiniteHistoryScenario | undefined + firstUse: boolean + }> => + index === scenarios.length + ? randomEvents.pipe(Arbitrary.map((events) => ({ events, scenario: undefined, firstUse: false }))) + : Arbitrary.Constant(scenarios[index]!) + ) + ).pipe(Arbitrary.map(({ events, firstUse, scenario }) => ({ model: firstUse && scenario !== undefined ? { ...model, initial: scenario.resume.source.split(".")[0]!, historyScenarios: [] } : model, events, scenario, firstUse - })) - }) + }))) + })) it.effect.prop("stress-checks planner traces across shrinkable generated parallel/history models and scenarios", { generated }, ({ generated }) => { @@ -1940,5 +1942,79 @@ describe("MachineTest finite-model reference interpreter", () => { }), Effect.flatMap((trace) => MachineTest.verifyModel(generated.model, trace)) ) - }, { timeout: 30000, fastCheck: { numRuns: 1500, seed: 51205 } }) + }, { timeout: 30000, arbitrary: { runs: 1500, seed: 51205 } }) }) + +for (const reenter of [false, true]) { + it.effect(`retains the active compound when an ancestor initial choice resolves inside it (reenter: ${reenter})`, () => + Effect.gen(function*() { + const model: MachineTest.FiniteModel = { + roots: [{ + _tag: "Compound", + key: "workspace", + value: 0, + initial: "route", + states: [ + { + _tag: "Compound", + key: "editor", + value: 1, + initial: "first", + states: [ + { + _tag: "Parallel", + key: "first", + value: 2, + output: "first", + states: [ + { _tag: "Atomic", key: "a", value: 3 }, + { _tag: "Atomic", key: "b", value: 4 } + ] + }, + { + _tag: "Parallel", + key: "second", + value: 5, + output: "second", + states: [ + { _tag: "Atomic", key: "a", value: 6 }, + { _tag: "Atomic", key: "b", value: 7 } + ] + } + ] + }, + { _tag: "Choice", key: "route", targets: ["workspace.editor"], selected: "workspace.editor" } + ] + }], + initial: "workspace", + events: ["Next", "Reset"], + transitions: [ + { + source: "workspace", + trigger: { type: "event", event: "Next" }, + target: "workspace.editor.second", + reenter: false + }, + { + source: "workspace.editor.second.b", + trigger: { type: "event", event: "Reset" }, + target: "workspace", + reenter + } + ] + } + const machine = MachineTest.compileModel(model) + const trace = yield* MachineTest.run(machine, { events: [{ _tag: "Next" }, { _tag: "Reset" }] }) + yield* MachineTest.verifyModel(model, trace) + assert.deepStrictEqual(trace.steps[1]!.plan.microsteps[0]!.exitPaths, [ + "workspace.editor.second.b", + "workspace.editor.second.a", + "workspace.editor.second" + ]) + assert.deepStrictEqual(trace.steps[1]!.plan.microsteps[0]!.entryPaths, [ + "workspace.editor.first", + "workspace.editor.first.a", + "workspace.editor.first.b" + ]) + })) +} diff --git a/packages/effect-machine/test/testing/Runtime.test.ts b/packages/effect-machine/test/testing/Runtime.test.ts index 28baca26..68968d83 100644 --- a/packages/effect-machine/test/testing/Runtime.test.ts +++ b/packages/effect-machine/test/testing/Runtime.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest" import { Cause, Clock, Effect, Exit, Option, Schema, Stream } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { Machine } from "../../src/index.js" import { MachineTest } from "../../src/testing/index.js" class Counter extends Schema.TaggedClass("Counter")("Counter", { @@ -104,10 +104,10 @@ describe("MachineTest runtime commands", () => { const generated = MachineTest.runtimeCommands(machine, { minCommands: 4, maxCommands: 4, - eventArbitrary: FastCheck.constant(new Add({ amount: 1 })), - advanceArbitrary: FastCheck.constant(10) + eventArbitrary: Arbitrary.Constant(new Add({ amount: 1 })), + advanceArbitrary: Arbitrary.Constant(10) }) - const samples = FastCheck.sample(generated.arbitrary, 20) + const samples = Effect.runSync(Arbitrary.sampleEffect(generated.arbitrary, { count: 20 })) assert.strictEqual(generated.diagnostics.events, "override") assert.strictEqual(generated.diagnostics.includesAdvance, true) assert.strictEqual(generated.diagnostics.includesStop, true) @@ -122,18 +122,52 @@ describe("MachineTest runtime commands", () => { } const onlySends = MachineTest.runtimeCommands(machine, { maxCommands: 10, - eventArbitrary: FastCheck.constant(new Add({ amount: 1 })), + eventArbitrary: Arbitrary.Constant(new Add({ amount: 1 })), includeAdvance: false, includeStop: false, includeCheckpoint: false }) - const shrunk = FastCheck.check(FastCheck.property(onlySends.arbitrary, (commands) => commands.length === 0), { - numRuns: 20 + const shrunk = Effect.runSync( + Arbitrary.checkEffect(onlySends.arbitrary, (commands) => commands.length === 0, { runs: 20, seed: 42 }) + ) + assert.strictEqual(shrunk._tag, "Falsified") + if (shrunk._tag === "Falsified") { + assert.strictEqual(shrunk.shrunkInput.length, 1) + } + }) + it("shrinks custom command arrays by removing unrelated commands and replays the result", () => { + const generated = MachineTest.runtimeCommands(propertyMachine, { + maxCommands: 6, + eventArbitrary: Arbitrary.schema(Schema.Literals([1, 2, 3])).pipe( + Arbitrary.map((amount) => new Add({ amount })) + ), + includeAdvance: false, + includeStop: false, + includeCheckpoint: false }) - assert.strictEqual(shrunk.failed, true) - if (shrunk.failed) { - assert.strictEqual((shrunk.counterexample?.[0] as ReadonlyArray).length, 1) + const property = (commands: ReadonlyArray>) => { + let seenTwo = false + for (const command of commands) { + if (command._tag !== "Send") continue + if (command.event.amount === 3 && seenTwo) return false + if (command.event.amount === 2) seenTwo = true + } + return true } + const result = Effect.runSync(Arbitrary.checkEffect(generated.arbitrary, property, { + runs: 100, + seed: 0, + size: 6 + })) + assert.strictEqual(result._tag, "Falsified") + if (result._tag !== "Falsified") return + assert.deepStrictEqual( + result.shrunkInput.map((command) => command._tag === "Send" ? command.event.amount : undefined), + [2, 3] + ) + const replay = Effect.runSync(Arbitrary.checkEffect(generated.arbitrary, property, { replay: result.replay })) + assert.strictEqual(replay._tag, "Falsified") + if (replay._tag === "Falsified") assert.deepStrictEqual(replay.shrunkInput, result.shrunkInput) }) it.effect.prop("checks schema-generated commands against a pure model after every shrink", { commands: generatedRuntimeCommands.arbitrary @@ -199,7 +233,7 @@ describe("MachineTest runtime commands", () => { }) assert.strictEqual(transcript.records.length, commands.length) yield* ref.stop - }), { fastCheck: { numRuns: 100, seed: 18241 } }) + }), { arbitrary: { runs: 100, seed: 18241 } }) it.effect("buffers public changes so a checkpoint can verify multiple queued sends in order", () => Effect.gen(function*() { const machine = makeCounterMachine() @@ -645,10 +679,14 @@ describe("MachineTest causal runtime commands", () => { it.effect.prop("checks and shrinks generated command sequences at causal boundaries", { commands: MachineTest.runtimeCommands(causalMachine, { maxCommands: 20, - eventArbitrary: FastCheck.oneof( - FastCheck.integer({ min: -10, max: 10 }).map((amount) => new Add({ amount })), - FastCheck.constant(new Ignored({})), - FastCheck.constant(new Noop({})) + eventArbitrary: Arbitrary.schema(Schema.Literals(["Add", "Ignored", "Noop"])).pipe( + Arbitrary.flatMap((tag): Arbitrary.Arbitrary => + tag === "Add" + ? Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: -10, maximum: 10 }))).pipe( + Arbitrary.map((amount) => new Add({ amount })) + ) + : Arbitrary.Constant(tag === "Ignored" ? new Ignored({}) : new Noop({})) + ) ), includeAdvance: false, includeStop: false, @@ -676,7 +714,7 @@ describe("MachineTest causal runtime commands", () => { }) assert.strictEqual(transcript.finalModel, transcript.final.state.state.value.count) yield* ref.stop - }), { fastCheck: { numRuns: 100, seed: 31590 } }) + }), { arbitrary: { runs: 100, seed: 31590 } }) it.effect("represents stopped sends without turning an expected rejection into a failed property", () => Effect.gen(function*() { const ref = yield* Machine.start(causalMachine) @@ -944,10 +982,14 @@ describe("MachineTest causal runtime commands", () => { it.effect.prop("checks generated causal commands with reusable runtime laws and planner agreement", { commands: MachineTest.runtimeCommands(causalMachine, { maxCommands: 20, - eventArbitrary: FastCheck.oneof( - FastCheck.integer({ min: -10, max: 10 }).map((amount) => new Add({ amount })), - FastCheck.constant(new Ignored({})), - FastCheck.constant(new Noop({})) + eventArbitrary: Arbitrary.schema(Schema.Literals(["Add", "Ignored", "Noop"])).pipe( + Arbitrary.flatMap((tag): Arbitrary.Arbitrary => + tag === "Add" + ? Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: -10, maximum: 10 }))).pipe( + Arbitrary.map((amount) => new Add({ amount })) + ) + : Arbitrary.Constant(tag === "Ignored" ? new Ignored({}) : new Noop({})) + ) ), includeAdvance: false, includeStop: false, @@ -973,7 +1015,7 @@ describe("MachineTest causal runtime commands", () => { }) yield* MachineTest.assertPlannerRuntimeAgreement(causalMachine, transcript) yield* ref.stop - }), { fastCheck: { numRuns: 100, seed: 81440 } }) + }), { arbitrary: { runs: 100, seed: 81440 } }) it.effect("reports the exact field when causal evidence disagrees with fresh planning", () => Effect.gen(function*() { const ref = yield* Machine.start(causalMachine) diff --git a/packages/effect-machine/test/testing/Verification.test.ts b/packages/effect-machine/test/testing/Verification.test.ts index 1842f0ad..c5ed280d 100644 --- a/packages/effect-machine/test/testing/Verification.test.ts +++ b/packages/effect-machine/test/testing/Verification.test.ts @@ -1323,6 +1323,6 @@ describe("MachineTest.verify", () => { MachineTest.run(navigationMachine, scenario).pipe( Effect.flatMap((trace) => MachineTest.verify(navigationMachine, trace)) ), - { fastCheck: { numRuns: 50 } } + { arbitrary: { runs: 50 } } ) }) diff --git a/packages/effect-machine/typetest/testing/FiniteModel.tst.ts b/packages/effect-machine/typetest/testing/FiniteModel.tst.ts index 63c7dbb0..ba92fa76 100644 --- a/packages/effect-machine/typetest/testing/FiniteModel.tst.ts +++ b/packages/effect-machine/typetest/testing/FiniteModel.tst.ts @@ -1,4 +1,4 @@ -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { describe, expect, it } from "tstyche" import { Machine } from "../../src/index.js" import { MachineTest } from "../../src/testing/index.js" @@ -34,7 +34,7 @@ describe("MachineTest finite models", () => { }) it("exposes the model arbitrary and resolved diagnostics", () => { - expect(generated.arbitrary).type.toBe>() + expect(generated.arbitrary).type.toBe>() expect(generated.diagnostics.limits.maxRoots).type.toBe<1 | 2 | 3>() expect(generated.diagnostics.limits.maxParallelRegions).type.toBe<2 | 3>() expect(generated.diagnostics.limits.maxHistoryStates).type.toBe() @@ -79,7 +79,7 @@ describe("MachineTest finite models", () => { const machine = MachineTest.compileModel(model) expect(machine).type.toBe() expect(MachineTest.scenarios(machine).arbitrary).type.toBe< - FastCheck.Arbitrary> + Arbitrary.Arbitrary> >() }) diff --git a/packages/effect-machine/typetest/testing/MachineTest.tst.ts b/packages/effect-machine/typetest/testing/MachineTest.tst.ts index 71013479..4939a651 100644 --- a/packages/effect-machine/typetest/testing/MachineTest.tst.ts +++ b/packages/effect-machine/typetest/testing/MachineTest.tst.ts @@ -1,5 +1,5 @@ import { Cause, Context, Data, Effect, Schema } from "effect" -import { FastCheck } from "effect/testing" +import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary" import { describe, expect, it } from "tstyche" import { Machine } from "../../src/index.js" import { MachineTest } from "../../src/testing/index.js" @@ -37,7 +37,7 @@ describe("MachineTest", () => { }) it("preserves input and public event types in generated scenarios", () => { const generated = MachineTest.scenarios(machine) - expect(generated.arbitrary).type.toBe>>() + expect(generated.arbitrary).type.toBe>>() type Scenario = MachineTest.Scenario expect().type.toBe() expect().type.toBe>() @@ -45,11 +45,11 @@ describe("MachineTest", () => { }) it("types whole-value arbitrary overrides", () => { const options: MachineTest.ScenarioOptions = { - inputArbitrary: FastCheck.constant(new Input({ id: "test" })), - eventsArbitrary: FastCheck.constant([new PublicEvent({ value: 1 })]) + inputArbitrary: Arbitrary.Constant(new Input({ id: "test" })), + eventsArbitrary: Arbitrary.Constant([new PublicEvent({ value: 1 })]) } - expect(options.inputArbitrary).type.toBe | undefined>() - expect(options.eventsArbitrary).type.toBe> | undefined>() + expect(options.inputArbitrary).type.toBe | undefined>() + expect(options.eventsArbitrary).type.toBe> | undefined>() }) it("omits input for machines without an input schema", () => { const noInput = Machine.make({ @@ -122,7 +122,7 @@ describe("MachineTest", () => { }) it("keeps runtime commands on the public event protocol", () => { const generated = MachineTest.runtimeCommands(machine) - expect(generated.arbitrary).type.toBe>>>() + expect(generated.arbitrary).type.toBe>>>() expect(MachineTest.sendCommand(new PublicEvent({ value: 1 }))).type.toBe>() expect(MachineTest.sendCommand(new InternalEvent({}))).type.not.toBeAssignableTo< MachineTest.RuntimeCommand diff --git a/packages/effect-machine/typetest/unstable/cluster/ClusterMachine.tst.ts b/packages/effect-machine/typetest/unstable/cluster/ClusterMachine.tst.ts index 24884575..ef03edb5 100644 --- a/packages/effect-machine/typetest/unstable/cluster/ClusterMachine.tst.ts +++ b/packages/effect-machine/typetest/unstable/cluster/ClusterMachine.tst.ts @@ -1,4 +1,4 @@ -import { Context, Effect, type Layer, Option, Schema, SchemaGetter } from "effect" +import { Context, Effect, type Layer, Schema, SchemaGetter } from "effect" import { type MessageStorage, type Sharding } from "effect/unstable/cluster" import type { Rpc, RpcGroup } from "effect/unstable/rpc" import { describe, expect, it } from "tstyche" @@ -285,12 +285,12 @@ describe("ClusterMachine", () => { it("retains snapshot codec service requirements", () => { const ContextualNumber = Schema.Number.pipe( Schema.decode({ - decode: SchemaGetter.onSome((value) => Effect.as(SnapshotDecoding, Option.some(value))), + decode: SchemaGetter.transformEffect((value) => Effect.as(SnapshotDecoding, value)), encode: SchemaGetter.passthrough() }), Schema.encode({ decode: SchemaGetter.passthrough(), - encode: SchemaGetter.onSome((value) => Effect.as(SnapshotEncoding, Option.some(value))) + encode: SchemaGetter.transformEffect((value) => Effect.as(SnapshotEncoding, value)) }) ) class ContextualCount extends Schema.TaggedClass("ContextualCount")("ContextualCount", { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index addca482..a738e1fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,8 +5,8 @@ settings: excludeLinksFromLockfile: false overrides: - effect: 4.0.0-rc.112 - '@effect/vitest': 4.0.0-rc.112 + effect: 4.0.0-rc.116 + '@effect/vitest': 4.0.0-rc.116 importers: @@ -16,8 +16,8 @@ importers: specifier: 2.31.0 version: 2.31.0(@types/node@25.7.0) '@effect/vitest': - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112(effect@4.0.0-rc.112)(vitest@4.1.10(@types/node@25.7.0)(jsdom@27.2.0)(vite@8.1.5(@types/node@25.7.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0))) + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116(effect@4.0.0-rc.116)(vitest@4.1.10(@types/node@25.7.0)(jsdom@27.2.0)(vite@8.1.5(@types/node@25.7.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0))) '@types/node': specifier: 25.7.0 version: 25.7.0 @@ -25,8 +25,8 @@ importers: specifier: 0.55.2 version: 0.55.2 effect: - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112 + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116 pagefind: specifier: 1.5.2 version: 1.5.2 @@ -52,14 +52,14 @@ importers: packages/devtools: dependencies: '@effect/platform-browser': - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112(effect@4.0.0-rc.112) + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116(effect@4.0.0-rc.116) '@effect/platform-node': - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1) + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116(effect@4.0.0-rc.116)(redis@6.2.1) '@effect/platform-node-shared': - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112(effect@4.0.0-rc.112) + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116(effect@4.0.0-rc.116) '@typeonce/effect-machine': specifier: workspace:^ version: link:../effect-machine @@ -80,8 +80,8 @@ importers: specifier: 25.7.0 version: 25.7.0 effect: - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112 + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116 tsx: specifier: 4.21.0 version: 4.21.0 @@ -89,14 +89,14 @@ importers: packages/effect-machine: devDependencies: '@effect/vitest': - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112(effect@4.0.0-rc.112)(vitest@4.1.10(@types/node@25.7.0)(jsdom@27.2.0)(vite@8.1.5(@types/node@25.7.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0))) + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116(effect@4.0.0-rc.116)(vitest@4.1.10(@types/node@25.7.0)(jsdom@27.2.0)(vite@8.1.5(@types/node@25.7.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0))) '@types/node': specifier: 25.7.0 version: 25.7.0 effect: - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112 + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116 tinybench: specifier: 2.9.0 version: 2.9.0 @@ -117,8 +117,8 @@ importers: version: link:../effect-machine devDependencies: '@effect/atom-react': - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112(effect@4.0.0-rc.112)(react@19.2.7)(scheduler@0.27.0) + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116(effect@4.0.0-rc.116)(react@19.2.7)(scheduler@0.27.0) '@testing-library/react': specifier: 16.3.0 version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -129,8 +129,8 @@ importers: specifier: 19.2.3 version: 19.2.3(@types/react@19.2.16) effect: - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112 + specifier: 4.0.0-rc.116 + version: 4.0.0-rc.116 jsdom: specifier: 27.2.0 version: 27.2.0 @@ -370,36 +370,36 @@ packages: cpu: [x64] os: [win32] - '@effect/atom-react@4.0.0-rc.112': - resolution: {integrity: sha512-Ksf90KQa6D4UDnMj4M84arlVKNIuwxxF2GvgXBzuaYi2LsgririsUhKQ5SYaK1euf8oIYeamEuthoZSbUQmIhw==} + '@effect/atom-react@4.0.0-rc.116': + resolution: {integrity: sha512-Kk5tEw0U5PoAcUaELWwQ34wuCVyLFZsjyyRePSIh3hL6rjORLamPfigJ8xDqYZS3CJg8HMtbND1eVethv404Jw==} peerDependencies: - effect: 4.0.0-rc.112 + effect: 4.0.0-rc.116 react: '>=19.0.0 <20.0.0' scheduler: '>=0.25.0 <0.28.0' - '@effect/platform-browser@4.0.0-rc.112': - resolution: {integrity: sha512-GSlqNDnjILz2EqOFPhVdMEHxlPq6SGb8+KOpNnLfRvVJjXRTMawEO+v1PCuwt2CHAqESbJAa2Nk+qcQvTrv4MQ==} + '@effect/platform-browser@4.0.0-rc.116': + resolution: {integrity: sha512-ak23bBZcaMiItFwjdWqbp9KVFMR7b5R/w4iT+Dj/xwd0AiHx0GAe7uRkQ6cdhkSdW9aBnuN9paZTglG2Y8cTOA==} peerDependencies: - effect: 4.0.0-rc.112 + effect: 4.0.0-rc.116 - '@effect/platform-node-shared@4.0.0-rc.112': - resolution: {integrity: sha512-ttjz0xKamFN7vL8pNDYVwddJLjZvqKePc05djlz2VcdaKbLsnYbtMnL1rbOfHgEnIUSHGh7FkjaN4DM1Ov81sQ==} + '@effect/platform-node-shared@4.0.0-rc.116': + resolution: {integrity: sha512-4FB6UriGqArWFUaFqVchhfCRmmtOVzmD/k/bH6LErlKY8a9amt5dk6c3Arsi7AvUpChsdO+3jsG+P85HsxMkNA==} engines: {node: '>=18.0.0'} peerDependencies: - effect: 4.0.0-rc.112 + effect: 4.0.0-rc.116 - '@effect/platform-node@4.0.0-rc.112': - resolution: {integrity: sha512-/BMAcdNGQQskLmI0Zoa95KfTZkr9HV9N4NSxaSrusG6GeW6Ulp9KvZ+Rlaiw8lnOt43CXjFLdfll5/k5rxL4hQ==} + '@effect/platform-node@4.0.0-rc.116': + resolution: {integrity: sha512-uA66Nh998OcChBVX83tQDkBDvQQcVaMXodP9gNGWVeIwWWquy7bzIkAq/fMZGKs/BVFhFQ0XCp3VIqYjnXu5ng==} engines: {node: '>=18.0.0'} peerDependencies: - effect: 4.0.0-rc.112 + effect: 4.0.0-rc.116 redis: '>=5.0.0 <7.0.0' - '@effect/vitest@4.0.0-rc.112': - resolution: {integrity: sha512-mEKh/FI64mt8JK1/v9mpOrJYdnp+UFZdRUBMEdZMiKz7klg6NPqVgg/oeAGH6wOOQc2iAPcfc2H9BbAv1KyzMQ==} + '@effect/vitest@4.0.0-rc.116': + resolution: {integrity: sha512-q42ue4hAa5AcPBwqi8rvMvR1Jstr+JjsyyxEBUoBk+WWm8otnqFxQ+XbWslW1QSwdg5+2Fdgd7z59IValt9Oiw==} peerDependencies: - effect: 4.0.0-rc.112 - vitest: '>=4.1.0 <5.0.0' + effect: 4.0.0-rc.116 + vitest: '>=5.0.0 <6.0.0' '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} @@ -587,36 +587,6 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': - resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} - cpu: [arm64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': - resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==} - cpu: [x64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': - resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==} - cpu: [arm64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': - resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==} - cpu: [arm] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': - resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==} - cpu: [x64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': - resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==} - cpu: [x64] - os: [win32] - '@napi-rs/wasm-runtime@1.1.6': resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} peerDependencies: @@ -1161,8 +1131,8 @@ packages: resolution: {integrity: sha512-1d4D4SB9KiD2qFnBWbl3aoYjLvPVpZoth28yxTT00xJv2yXugdz0Xoyv7sGG0w0hzE6hQZMgd6B333GnySDpGQ==} hasBin: true - effect@4.0.0-rc.112: - resolution: {integrity: sha512-wXxwuh1Ywnv4cPRM3Wfa0vDwuOHnZ1TsTgHJkG9XgzND6inhBH9n1vBxhg3iIXOia/OrpmvVmd3lrD4vq6bF3A==} + effect@4.0.0-rc.116: + resolution: {integrity: sha512-nawqJHSjHV8XIBRZNZ+D7cLZpN3kkSjzy6aiT9ofKENsl7xAMFKEoDa0itN5JFl3GUQ1PIN1T1HNlva5+xyO/A==} elkjs@0.12.0: resolution: {integrity: sha512-YZcKynxVxYoKIOEpywEPwCFdg+BTbxQRNf3pbwdDCvc8O3kQD8bmIwSxKU1eOTVc4Xo+VG9Te+575mlfvOrhEQ==} @@ -1202,10 +1172,6 @@ packages: extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - fast-check@4.9.0: - resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==} - engines: {node: '>=12.17.0'} - fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -1450,11 +1416,6 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime@4.1.0: - resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} - engines: {node: '>=16'} - hasBin: true - minimatch@10.2.6: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} @@ -1466,22 +1427,11 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msgpackr-extract@3.0.4: - resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} - hasBin: true - - msgpackr@2.0.5: - resolution: {integrity: sha512-cef05H/dSYpLpqp3sj/qyZh5vhUYCalnaLO7j1yOmpsR0y/XwLVtK7r5gn+U/F7CTEfMowcGhlUQJDLcLf7jcA==} - nanoid@3.3.16: resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - node-gyp-build-optional-packages@5.2.2: - resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} - hasBin: true - obug@2.1.4: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} @@ -1583,9 +1533,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@8.4.2: - resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==} - quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -1774,8 +1721,8 @@ packages: undici-types@7.21.0: resolution: {integrity: sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==} - undici@8.10.0: - resolution: {integrity: sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==} + undici@8.10.2: + resolution: {integrity: sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==} engines: {node: '>=22.19.0'} universalify@0.1.2: @@ -2169,39 +2116,38 @@ snapshots: '@dprint/win32-x64@0.55.2': optional: true - '@effect/atom-react@4.0.0-rc.112(effect@4.0.0-rc.112)(react@19.2.7)(scheduler@0.27.0)': + '@effect/atom-react@4.0.0-rc.116(effect@4.0.0-rc.116)(react@19.2.7)(scheduler@0.27.0)': dependencies: - effect: 4.0.0-rc.112 + effect: 4.0.0-rc.116 react: 19.2.7 scheduler: 0.27.0 - '@effect/platform-browser@4.0.0-rc.112(effect@4.0.0-rc.112)': + '@effect/platform-browser@4.0.0-rc.116(effect@4.0.0-rc.116)': dependencies: - effect: 4.0.0-rc.112 + effect: 4.0.0-rc.116 - '@effect/platform-node-shared@4.0.0-rc.112(effect@4.0.0-rc.112)': + '@effect/platform-node-shared@4.0.0-rc.116(effect@4.0.0-rc.116)': dependencies: '@types/ws': 8.18.1 - effect: 4.0.0-rc.112 + effect: 4.0.0-rc.116 ws: 8.21.3 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1)': + '@effect/platform-node@4.0.0-rc.116(effect@4.0.0-rc.116)(redis@6.2.1)': dependencies: - '@effect/platform-node-shared': 4.0.0-rc.112(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 - mime: 4.1.0 + '@effect/platform-node-shared': 4.0.0-rc.116(effect@4.0.0-rc.116) + effect: 4.0.0-rc.116 redis: 6.2.1 - undici: 8.10.0 + undici: 8.10.2 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/vitest@4.0.0-rc.112(effect@4.0.0-rc.112)(vitest@4.1.10(@types/node@25.7.0)(jsdom@27.2.0)(vite@8.1.5(@types/node@25.7.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)))': + '@effect/vitest@4.0.0-rc.116(effect@4.0.0-rc.116)(vitest@4.1.10(@types/node@25.7.0)(jsdom@27.2.0)(vite@8.1.5(@types/node@25.7.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)))': dependencies: - effect: 4.0.0-rc.112 + effect: 4.0.0-rc.116 vitest: 4.1.10(@types/node@25.7.0)(jsdom@27.2.0)(vite@8.1.5(@types/node@25.7.0)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)) '@emnapi/core@1.11.1': @@ -2331,24 +2277,6 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': - optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 @@ -2758,10 +2686,7 @@ snapshots: '@dprint/win32-arm64': 0.55.2 '@dprint/win32-x64': 0.55.2 - effect@4.0.0-rc.112: - dependencies: - fast-check: 4.9.0 - msgpackr: 2.0.5 + effect@4.0.0-rc.116: {} elkjs@0.12.0: {} @@ -2815,10 +2740,6 @@ snapshots: extendable-error@0.1.7: {} - fast-check@4.9.0: - dependencies: - pure-rand: 8.4.2 - fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3057,8 +2978,6 @@ snapshots: braces: 3.0.3 picomatch: 2.3.2 - mime@4.1.0: {} - minimatch@10.2.6: dependencies: brace-expansion: 5.0.9 @@ -3067,29 +2986,8 @@ snapshots: ms@2.1.3: {} - msgpackr-extract@3.0.4: - dependencies: - node-gyp-build-optional-packages: 5.2.2 - optionalDependencies: - '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.4 - '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4 - optional: true - - msgpackr@2.0.5: - optionalDependencies: - msgpackr-extract: 3.0.4 - nanoid@3.3.16: {} - node-gyp-build-optional-packages@5.2.2: - dependencies: - detect-libc: 2.1.2 - optional: true - obug@2.1.4: {} outdent@0.5.0: {} @@ -3184,8 +3082,6 @@ snapshots: punycode@2.3.1: {} - pure-rand@8.4.2: {} - quansync@0.2.11: {} queue-microtask@1.2.3: {} @@ -3355,7 +3251,7 @@ snapshots: undici-types@7.21.0: {} - undici@8.10.0: {} + undici@8.10.2: {} universalify@0.1.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 565ec051..84668124 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,11 +2,15 @@ packages: - "packages/*" overrides: - effect: 4.0.0-rc.112 - "@effect/vitest": 4.0.0-rc.112 + effect: 4.0.0-rc.116 + "@effect/vitest": 4.0.0-rc.116 minimumReleaseAge: 1440 minimumReleaseAgeExclude: - effect - "@effect/vitest" + - "@effect/atom-react" + - "@effect/platform-browser" + - "@effect/platform-node" + - "@effect/platform-node-shared" - xstate-v6 diff --git a/scripts/devtools-pack-check.mjs b/scripts/devtools-pack-check.mjs index c72d3df6..e357dae1 100644 --- a/scripts/devtools-pack-check.mjs +++ b/scripts/devtools-pack-check.mjs @@ -109,7 +109,7 @@ try { }, null, 2)) await writeFile( join(consumer, "pnpm-workspace.yaml"), - `packages:\n - .\noverrides:\n "@typeonce/effect-machine": "file:${coreArchive}"\n` + `packages:\n - .\noverrides:\n "@typeonce/effect-machine": "file:${coreArchive}"\nminimumReleaseAgeExclude:\n - "effect"\n - "@effect/platform-browser"\n - "@effect/platform-node"\n - "@effect/platform-node-shared"\n` ) await writeFile(machineFile, readyMachine) diff --git a/scripts/react-pack-check.mjs b/scripts/react-pack-check.mjs index 5e1b2470..d7ffa84e 100644 --- a/scripts/react-pack-check.mjs +++ b/scripts/react-pack-check.mjs @@ -68,7 +68,7 @@ try { }, null, 2)) await writeFile( join(consumer, "pnpm-workspace.yaml"), - `packages:\n - .\noverrides:\n "@typeonce/effect-machine": "file:${coreArchive}"\n` + `packages:\n - .\noverrides:\n "@typeonce/effect-machine": "file:${coreArchive}"\nminimumReleaseAgeExclude:\n - "effect"\n - "@effect/atom-react"\n` ) run("pnpm", ["install", "--prefer-offline", "--ignore-scripts"], { cwd: consumer })