Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/effect-rc116.md
Original file line number Diff line number Diff line change
@@ -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.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 5 additions & 5 deletions packages/devtools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,21 @@
"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",
"typescript": "6.0.3",
"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": [
Expand Down
14 changes: 7 additions & 7 deletions packages/devtools/src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
8 changes: 4 additions & 4 deletions packages/effect-machine-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
20 changes: 20 additions & 0 deletions packages/effect-machine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions packages/effect-machine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -39,8 +39,7 @@ const reportChecks = (
path: ReadonlyArray<PropertyKey>
): void => {
const visit = (check: SchemaAST.Check<unknown>, 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) {
Expand Down Expand Up @@ -97,6 +96,14 @@ const reportFor = (ast: SchemaAST.AST): SchemaArbitraryReport => {

/** @internal */
export const toArbitraryWithReport = <S extends Schema.Constraint>(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 = <A>(
...choices: readonly [Arbitrary.Arbitrary<A>, ...Array<Arbitrary.Arbitrary<A>>]
): Arbitrary.Arbitrary<A> =>
Arbitrary.schema(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: choices.length - 1 }))).pipe(
Arbitrary.flatMap((index) => choices[index]!)
)
Loading
Loading