diff --git a/.changeset/repository-oxlint.md b/.changeset/repository-oxlint.md new file mode 100644 index 00000000..853d812b --- /dev/null +++ b/.changeset/repository-oxlint.md @@ -0,0 +1,3 @@ +--- + +--- diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000..45551e82 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,93 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "jsPlugins": ["@typeonce/oxlint-plugin-effect-machine"], + "categories": { + "correctness": "error" + }, + // Type-aware rules run through oxlint-tsgolint, with each file's nearest tsconfig. + "options": { + "typeAware": true + }, + "rules": { + "typescript/no-unnecessary-type-assertion": "error", + "typescript/switch-exhaustiveness-check": ["error", { "considerDefaultExhaustiveForUnions": true }], + "typescript/no-floating-promises": "error", + "typescript/no-misused-promises": "error", + "eslint/no-unused-vars": [ + "error", + { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_", "ignoreRestSiblings": true } + ], + // Effect.gen bodies capture the instance as `self`. + "typescript/no-this-alias": ["error", { "allowedNames": ["self"] }], + // Unions such as `unknown | StoppedError` and `string | "dynamic"` document the intended values. + "typescript/no-redundant-type-constituents": "off", + // Runtime hot paths preallocate arrays, as Effect does. + "unicorn/no-new-array": "off" + }, + "overrides": [ + { + // Public and internal types keep phantom type parameters for variance and inference. `noUnusedLocals` checks the rest. + "files": ["packages/*/src/**"], + "rules": { + "eslint/no-unused-vars": "off" + } + }, + { + // Type tests, type-performance fixtures, and scripts belong to no tsconfig, so type information would use the wrong compiler options. + "files": ["packages/*/typetest/**", "packages/*/perf/**", "scripts/**"], + "rules": { + "typescript/await-thenable": "off", + "typescript/no-array-delete": "off", + "typescript/no-base-to-string": "off", + "typescript/no-duplicate-type-constituents": "off", + "typescript/no-floating-promises": "off", + "typescript/no-for-in-array": "off", + "typescript/no-implied-eval": "off", + "typescript/no-meaningless-void-operator": "off", + "typescript/no-misused-promises": "off", + "typescript/no-misused-spread": "off", + "typescript/no-unnecessary-type-assertion": "off", + "typescript/no-unsafe-unary-minus": "off", + "typescript/no-useless-default-assignment": "off", + "typescript/require-array-sort-compare": "off", + "typescript/restrict-template-expressions": "off", + "typescript/switch-exhaustiveness-check": "off", + "typescript/unbound-method": "off" + } + }, + { + // Type tests bind values only to check their types, and type-performance fixtures stay byte-stable for comparisons. + "files": ["packages/*/typetest/**", "packages/*/perf/types/**"], + "rules": { + "eslint/no-unused-vars": "off" + } + }, + { + // Dogfood the Effect Machine rules on the example machines and machine fixtures. + "files": [ + "packages/devtools/src/internal/browser/example-machine.ts", + "packages/devtools/src/internal/browser/*-example.ts", + "packages/effect-machine/test/examples/**", + "packages/effect-machine/test/fixtures/**" + ], + "rules": { + "effect-machine/no-async-planning-callback": "error", + "effect-machine/no-browser-api-in-planning": "error", + "effect-machine/no-conditional-none-branch": "error", + "effect-machine/no-conflicting-invocation-identity": "error", + "effect-machine/no-nondeterministic-planning": "error", + "effect-machine/no-redundant-resolve": "error", + "effect-machine/prefer-inline-handle": "error" + } + } + ], + "ignorePatterns": [ + "**/dist/**", + "**/node_modules/**", + ".data/**", + ".agents/**", + "references/**", + // Intentionally unparsable fixture for devtools error reporting. + "packages/devtools/test/fixtures/broken-machine.mjs" + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 41f7632b..6b4e4732 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,6 +101,28 @@ provide the in-process compiler API that tstyche uses. TypeScript 7 still compiles the public types through the package projects and the runtime tests, but type assertions are evaluated only with TypeScript 6. +## Formatting and linting + +`pnpm format` formats the repository with dprint, as Effect does. `pnpm lint` +builds the local `@typeonce/oxlint-plugin-effect-machine` package and runs +Oxlint with the configuration in `.oxlintrc.json`. Both run as part of +`pnpm check`. + +Type-aware rules run through `oxlint-tsgolint`, which reads each file's nearest +tsconfig. The root `tsconfig.json` references `tsconfig.tests.json` so tests get +the repository's compiler options. Type tests, type-performance fixtures, and +scripts belong to no tsconfig and skip type-aware rules. + +The repository's own Effect Machine rules check the devtools example machines +and the machine fixtures under `packages/effect-machine/test`. Fix a finding +rather than suppressing it. When code must keep the reported pattern, for +example a cast that TypeScript 6 requires but the TypeScript 7 checker used by +`oxlint-tsgolint` does not, suppress only that line and give the reason: + +```ts +// oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- TypeScript 6 infers `readonly string[]` without it. +``` + ## API reference data `pnpm docs:api` generates an Effect-compatible TypeDoc dataset under diff --git a/package.json b/package.json index 814f729d..c178acba 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "typecheck:ts7": "pnpm --dir tooling/typescript-7 run typecheck", "perf:types": "pnpm build && node scripts/type-performance.mjs", "perf:runtime": "pnpm build && node --expose-gc scripts/runtime-performance.mjs", + "lint": "pnpm --filter @typeonce/oxlint-plugin-effect-machine build && oxlint", "format": "dprint fmt", "format:check": "dprint check", "docs:api": "node scripts/api-reference/generate.mjs", @@ -29,7 +30,7 @@ "react:pack-check": "node scripts/react-pack-check.mjs", "devtools:pack-check": "node scripts/devtools-pack-check.mjs", "oxlint-plugin:pack-check": "node scripts/oxlint-plugin-pack-check.mjs", - "check": "pnpm format:check && pnpm check:architecture && pnpm check:ci && pnpm docs:api:check && pnpm docs:site:check && pnpm typecheck && pnpm typecheck:ts7 && pnpm build && pnpm test && pnpm test:types && pnpm test:consumer && pnpm pack:check && pnpm react:pack-check && pnpm devtools:pack-check && pnpm oxlint-plugin:pack-check", + "check": "pnpm format:check && pnpm lint && pnpm check:architecture && pnpm check:ci && pnpm docs:api:check && pnpm docs:site:check && pnpm typecheck && pnpm typecheck:ts7 && pnpm build && pnpm test && pnpm test:types && pnpm test:consumer && pnpm pack:check && pnpm react:pack-check && pnpm devtools:pack-check && pnpm oxlint-plugin:pack-check", "changeset": "changeset", "version-packages": "changeset version && dprint fmt", "release": "pnpm build && changeset publish" @@ -37,9 +38,12 @@ "devDependencies": { "@changesets/cli": "2.31.0", "@effect/vitest": "4.1.1", + "@typeonce/oxlint-plugin-effect-machine": "workspace:*", "@types/node": "26.6.6", "dprint": "0.64.3", "effect": "4.1.1", + "oxlint": "1.90.0", + "oxlint-tsgolint": "7.0.2005", "pagefind": "1.5.2", "tinybench": "2.9.0", "tstyche": "7.2.5", diff --git a/packages/devtools/src/internal/browser/chart-layout.ts b/packages/devtools/src/internal/browser/chart-layout.ts index b74f0806..8f334c29 100644 --- a/packages/devtools/src/internal/browser/chart-layout.ts +++ b/packages/devtools/src/internal/browser/chart-layout.ts @@ -261,6 +261,7 @@ const nodeMetric = (node: ChartNode, selfLoops: number): NodeMetric => { } } +// oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- TypeScript 6 does not type the CommonJS default export as constructable. const ELK = ELKBundle as unknown as new(args?: ELKConstructorArguments) => ElkApi const elk = new ELK() @@ -464,24 +465,22 @@ const makeGraph = ( return { id: "chart-root", children: children(null), - edges: [ - ...model.edges.map((edge): ElkExtendedEdge => { - const label = labelMetric(edge) - const edgeLayout = policy.edge(edge) - const runtimeTarget = edge.kind === "runtime" ? runtimeByEdgeId.get(edge.id) : undefined - return { - id: edge.id, - sources: [sourcePortId(edge)], - targets: [runtimeTarget === undefined ? targetPortId(edge) : runtimeTargetPortId(runtimeTarget)], - labels: [{ text: edge.accessibleLabel, width: label.width, height: label.height }], - layoutOptions: { - "elk.layered.priority.direction": edgeLayout.direction === "forward" ? "10" : "1", - "elk.layered.priority.shortness": "5", - "elk.layered.priority.straightness": "5" - } + edges: model.edges.map((edge): ElkExtendedEdge => { + const label = labelMetric(edge) + const edgeLayout = policy.edge(edge) + const runtimeTarget = edge.kind === "runtime" ? runtimeByEdgeId.get(edge.id) : undefined + return { + id: edge.id, + sources: [sourcePortId(edge)], + targets: [runtimeTarget === undefined ? targetPortId(edge) : runtimeTargetPortId(runtimeTarget)], + labels: [{ text: edge.accessibleLabel, width: label.width, height: label.height }], + layoutOptions: { + "elk.layered.priority.direction": edgeLayout.direction === "forward" ? "10" : "1", + "elk.layered.priority.shortness": "5", + "elk.layered.priority.straightness": "5" } - }) - ], + } + }), layoutOptions: { "elk.algorithm": "layered", "elk.direction": "DOWN", @@ -916,20 +915,20 @@ const normalizeEndpointDirection = ( const validEndpoints = (route: ReadonlyArray): boolean => isOutwardStep(route[0]!, route[1]!, side) && chartRouteLength(route.slice(0, 2)) >= 9 && (!preserveOpposite || - isOutwardStep(route.at(-1)!, route.at(-2)!, oppositeSide!) && chartRouteLength(route.slice(-2)) >= 9) + isOutwardStep(route.at(-1)!, route.at(-2)!, oppositeSide) && chartRouteLength(route.slice(-2)) >= 9) const tail = points.slice(1) const attached = compactPoints([start, ...tail]) if (attached.length >= 2 && validEndpoints(attached)) return attached if (tail.length === 1 && preserveOpposite) { - tail.unshift(outwardPoint(end, oppositeSide!, 18)) + tail.unshift(outwardPoint(end, oppositeSide, 18)) } const stub = outwardPoint(start, side, 18) const endpointObstacles = [ ...obstacles, routeNodeRect(node), - ...(preserveOpposite ? [routeNodeRect(opposite!)] : []) + ...(preserveOpposite ? [routeNodeRect(opposite)] : []) ] return endpointConnections(stub, tail[0]!, routeNodeRect(node), side) .map((connection) => compactPoints([start, ...connection, ...tail.slice(1)])) diff --git a/packages/devtools/src/internal/browser/example-machine.ts b/packages/devtools/src/internal/browser/example-machine.ts index e794146d..2c42b793 100644 --- a/packages/devtools/src/internal/browser/example-machine.ts +++ b/packages/devtools/src/internal/browser/example-machine.ts @@ -158,7 +158,7 @@ export const machine = Machine.make({ initial: { target: "application.workflow.running.editing", decoded: true, - data: ({}) => new Editing({}) + data: () => new Editing({}) }, states: { editing: { diff --git a/packages/devtools/src/internal/browser/invoke-outcomes-example.ts b/packages/devtools/src/internal/browser/invoke-outcomes-example.ts index 5e31cac1..7a8a13c0 100644 --- a/packages/devtools/src/internal/browser/invoke-outcomes-example.ts +++ b/packages/devtools/src/internal/browser/invoke-outcomes-example.ts @@ -101,7 +101,7 @@ class StreamValue extends Schema.TaggedClass("InvokeGalleryStreamVa const GalleryEvents = Machine.eventsFromSchemas(RunEffect, RunStream, RunTimer, RunProcess, RunChild, Reset) const GalleryInternalEvents = Machine.internalEventsFromSchemas(StreamValue) const processLogic = Machine.logic({ - initial: "starting" as "starting" | "ready", + initial: "starting", run: () => Effect.fail("process stopped") }) const GalleryStates = Machine.state({ @@ -156,7 +156,7 @@ export const invokeOutcomesMachine = Machine.make({ initial: { target: "Gallery.Choose", decoded: true, - data: ({}) => new Choose({}) + data: () => new Choose({}) }, on: { Reset: { target: "Gallery.Choose", decoded: true, data: () => (new Choose({})) } @@ -259,6 +259,7 @@ export const invokeOutcomesMachine = Machine.make({ resolve: ({ select, snapshot }) => snapshot.state === "ready" ? select.ready({ data: new Completed({ source: "process", result: "ready" }), decoded: true }) + // oxlint-disable-next-line effect-machine/no-conditional-none-branch -- `Machine.can` does not query snapshot outcomes; the titled branch documents the wait. : select.waiting() } } diff --git a/packages/devtools/src/internal/browser/parallel-completion-example.ts b/packages/devtools/src/internal/browser/parallel-completion-example.ts index 772b9db1..40dd5a68 100644 --- a/packages/devtools/src/internal/browser/parallel-completion-example.ts +++ b/packages/devtools/src/internal/browser/parallel-completion-example.ts @@ -136,7 +136,7 @@ export const parallelCompletionMachine = Machine.make({ } }, Order: { - initial: { payment: ({}) => ({ attempts: 0 }), fulfillment: ({}) => ({ warehouse: "north" }) }, + initial: { payment: () => ({ attempts: 0 }), fulfillment: () => ({ warehouse: "north" }) }, on: { CancelOrder: { target: "Cancelled", diff --git a/packages/devtools/src/internal/browser/parallel-owner-routing-example.ts b/packages/devtools/src/internal/browser/parallel-owner-routing-example.ts index eb452fff..db519f1c 100644 --- a/packages/devtools/src/internal/browser/parallel-owner-routing-example.ts +++ b/packages/devtools/src/internal/browser/parallel-owner-routing-example.ts @@ -104,7 +104,7 @@ export const parallelOwnerRoutingMachine = Machine.make({ }, states: { Print: { - initial: { Options: ({}) => ({}), Operation: ({}) => ({}) }, + initial: { Options: () => ({}), Operation: () => ({}) }, invoke: { src: "source1" }, on: { PrintRequested: { @@ -118,7 +118,7 @@ export const parallelOwnerRoutingMachine = Machine.make({ Options: { initial: { target: "Print.Options.Ready", - data: ({}) => ({}) + data: () => ({}) }, states: { Ready: {} @@ -127,7 +127,7 @@ export const parallelOwnerRoutingMachine = Machine.make({ Operation: { initial: { target: "Print.Operation.Active", - data: ({}) => ({}) + data: () => ({}) }, states: { Active: {}, diff --git a/packages/devtools/src/internal/browser/planner-example.ts b/packages/devtools/src/internal/browser/planner-example.ts index babc8c18..44b48c67 100644 --- a/packages/devtools/src/internal/browser/planner-example.ts +++ b/packages/devtools/src/internal/browser/planner-example.ts @@ -156,7 +156,7 @@ export const plannerMachine = Machine.make({ initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ owner: input.owner }) + data: ({ root: { input } }) => new Idle({ owner: input.owner }) }, root: ({ input }) => ({ input }), states: { diff --git a/packages/devtools/src/internal/browser/transition-semantics-example.ts b/packages/devtools/src/internal/browser/transition-semantics-example.ts index 26fc4f84..7d29272f 100644 --- a/packages/devtools/src/internal/browser/transition-semantics-example.ts +++ b/packages/devtools/src/internal/browser/transition-semantics-example.ts @@ -241,7 +241,7 @@ export const transitionSemanticsMachine = Machine.make({ initial: { target: "Workspace.Review.Checking", decoded: true, - data: ({}) => new Checking({ checks: ["types", "tests"] }) + data: () => new Checking({ checks: ["types", "tests"] }) }, onDone: { target: "Workspace.Finished", diff --git a/packages/devtools/src/internal/machineDocument.ts b/packages/devtools/src/internal/machineDocument.ts index f7c20636..fa921529 100644 --- a/packages/devtools/src/internal/machineDocument.ts +++ b/packages/devtools/src/internal/machineDocument.ts @@ -7,9 +7,7 @@ const enabled = Machine.enabled as ( snapshot: unknown ) => ReadonlyArray -const inputEventSchemas = Machine.inputEventSchemas as ( - machine: Machine.Machine.Any -) => ReadonlyArray +const inputEventSchemas = Machine.inputEventSchemas const isRecord = (value: unknown): value is Record => typeof value === "object" && value !== null && !Array.isArray(value) diff --git a/packages/devtools/src/internal/projectInspector.ts b/packages/devtools/src/internal/projectInspector.ts index a140fed9..3e292782 100644 --- a/packages/devtools/src/internal/projectInspector.ts +++ b/packages/devtools/src/internal/projectInspector.ts @@ -207,7 +207,7 @@ const evaluate = ( return runWorker(request).pipe( Effect.flatMap((message) => { if (message._tag !== "InspectedMachines") { - return Effect.fail(new Error(`Unexpected worker response: ${message._tag}`)) + return Effect.fail(new Error(`Unexpected worker response: ${String(message._tag)}`)) } return Effect.forEach( message.results, diff --git a/packages/devtools/test/internal/browser/ChartEndpoints.test.ts b/packages/devtools/test/internal/browser/ChartEndpoints.test.ts index 47debfa6..8f99fe69 100644 --- a/packages/devtools/test/internal/browser/ChartEndpoints.test.ts +++ b/packages/devtools/test/internal/browser/ChartEndpoints.test.ts @@ -97,8 +97,8 @@ describe("chart route endpoints", () => { edge.kind === "transition" && edge.edge.source === "Failed" && edge.edge.target === "Working" ) assert.isDefined(retry) - assert.isAtLeast(chartRouteLength(retry!.points.slice(0, 2)), 9) - assert.isAtLeast(chartRouteLength(retry!.points.slice(-2)), 9) + assert.isAtLeast(chartRouteLength(retry.points.slice(0, 2)), 9) + assert.isAtLeast(chartRouteLength(retry.points.slice(-2)), 9) }) // Rotate the same geometry to exercise every side, then reverse the edge to diff --git a/packages/effect-machine-react/src/internal/react.ts b/packages/effect-machine-react/src/internal/react.ts index 70ccdd97..dca1a506 100644 --- a/packages/effect-machine-react/src/internal/react.ts +++ b/packages/effect-machine-react/src/internal/react.ts @@ -99,5 +99,5 @@ export const createMachineContext = + return Object.freeze({ Provider, useMachine }) } diff --git a/packages/effect-machine-react/test/MachineAtom.test.tsx b/packages/effect-machine-react/test/MachineAtom.test.tsx index 26eecffb..8b9db8b1 100644 --- a/packages/effect-machine-react/test/MachineAtom.test.tsx +++ b/packages/effect-machine-react/test/MachineAtom.test.tsx @@ -73,7 +73,7 @@ describe("useMachineAtom", () => { await waitFor(() => assert.strictEqual(screen.getByTestId("value").textContent, "0")) const initialOwnerRenders = ownerRenders const initialReaderRenders = readerRenders - await act(() => { + await act(async () => { registry.set(current!.send, new Increment({})) }) await waitFor(() => assert.strictEqual(screen.getByTestId("value").textContent, "1")) @@ -117,14 +117,14 @@ describe("useMachineAtom", () => { await waitFor(() => assert.strictEqual(screen.getByTestId("reached").textContent, "false")) assert.strictEqual(reachedThreshold(current!), reachedThreshold(current!)) const initialReaderRenders = readerRenders - await act(() => { + await act(async () => { registry.set(current!.send, new Increment({})) }) const active = registry.get(AtomMachine.select(current!, "Active")) assert.ok(AsyncResult.isSuccess(active)) assert.strictEqual(Option.getOrThrow(active.value).value, 1) assert.strictEqual(readerRenders, initialReaderRenders) - await act(() => { + await act(async () => { registry.set(current!.send, new Increment({})) }) await waitFor(() => assert.strictEqual(screen.getByTestId("reached").textContent, "true")) @@ -327,6 +327,7 @@ describe("useMachineAtom", () => { ) + // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- TypeScript 6 types the queried element as `HTMLElement`. const field = await waitFor(() => screen.getByTestId("field") as HTMLInputElement) // React restores a controlled input to its rendered value when the // handler leaves state unchanged, so each value must survive its event. diff --git a/packages/effect-machine-react/typetest/MachineAtom.tst.ts b/packages/effect-machine-react/typetest/MachineAtom.tst.ts index 3c72a89d..536111f4 100644 --- a/packages/effect-machine-react/typetest/MachineAtom.tst.ts +++ b/packages/effect-machine-react/typetest/MachineAtom.tst.ts @@ -41,7 +41,7 @@ const withInput = Machine.make({ }).handle({ initial: { target: "Editing", - data: ({}) => ({ draft: "" }) + data: () => ({ draft: "" }) }, root: ({ input }) => ({ count: input }), states: { Editing: {}, Closed: {} } diff --git a/packages/effect-machine/src/internal/machine/atom.ts b/packages/effect-machine/src/internal/machine/atom.ts index d7ccc543..c5d2a56e 100644 --- a/packages/effect-machine/src/internal/machine/atom.ts +++ b/packages/effect-machine/src/internal/machine/atom.ts @@ -218,7 +218,7 @@ type RefEmitted = Ref extends Machine.MachineRef( self: MachineAtom ): Stream.Stream => { - const prepared = preparedByMachineAtom.get(self as object) + const prepared = preparedByMachineAtom.get(self) if (prepared === undefined) { return Atom.toStreamResult(self.ref).pipe(Stream.flatMap((ref) => ref.emissions)) } @@ -246,7 +246,7 @@ export const emissions = ( export const inspection = ( self: MachineAtom ): Stream.Stream => { - const prepared = preparedByMachineAtom.get(self as object) + const prepared = preparedByMachineAtom.get(self) if (prepared === undefined) return Stream.empty return Stream.unwrap( Effect.gen(function*() { @@ -599,7 +599,7 @@ const makeChildSelector = ( family = (id) => atoms(id) as ChildMachineAtom byMachine.set(machine, family) } - return family(descriptor.id) as ChildMachineAtom + return family(descriptor.id) } } diff --git a/packages/effect-machine/src/internal/machine/cluster.ts b/packages/effect-machine/src/internal/machine/cluster.ts index 0a49e128..57b3f3fe 100644 --- a/packages/effect-machine/src/internal/machine/cluster.ts +++ b/packages/effect-machine/src/internal/machine/cluster.ts @@ -252,7 +252,7 @@ export const make = < ) } current = initial.state - emitted.push(...initial.emittedEvents as any) + emitted.push(...initial.emittedEvents) } if (!internalMachine.isFinal(rootMachine, current)) { @@ -264,7 +264,7 @@ export const make = < ) } current = planned.next - emitted.push(...planned.emittedEvents as any) + emitted.push(...planned.emittedEvents) } const encoded = yield* internalMachine.encodeSnapshot(rootMachine, current).pipe( diff --git a/packages/effect-machine/src/internal/machine/configuration.ts b/packages/effect-machine/src/internal/machine/configuration.ts index d77d7068..5f18f196 100644 --- a/packages/effect-machine/src/internal/machine/configuration.ts +++ b/packages/effect-machine/src/internal/machine/configuration.ts @@ -642,7 +642,7 @@ export const configurationFromSnapshotEffect = Effect.fnUntraced(function*( values, outputs, history: yield* historyFromSnapshotEffect(machine, snapshot) - } as ActiveConfiguration + } }) export const normalizeConfigurationEffect = ( @@ -838,7 +838,7 @@ export const configurationFromTargetPathEffect = Effect.fnUntraced(function*( values, outputs, history: current.history - } as ActiveConfiguration + } }) export const configurationFromTargetSnapshotEffect = Effect.fnUntraced(function*( @@ -911,7 +911,7 @@ export const configurationFromTargetSnapshotEffect = Effect.fnUntraced(function* values, outputs, history: new Map([...current.history, ...subtree.history]) - } as ActiveConfiguration + } }) export const normalizeTargetConfigurationEffect = ( @@ -929,7 +929,7 @@ export const normalizeTargetConfigurationEffect = > | undefined + target.values ) } return configurationFromTargetPathEffect( @@ -937,7 +937,7 @@ export const normalizeTargetConfigurationEffect = > | undefined + target.values ) } if (isSnapshot(target)) { @@ -1111,7 +1111,7 @@ export const normalizeTargetConfigurationSync = ( machine, current, snapshot, - target.values as Readonly> | undefined + target.values ) } return configurationFromTargetPathSync( @@ -1119,7 +1119,7 @@ export const normalizeTargetConfigurationSync = ( current, target.path, target.value, - target.values as Readonly> | undefined + target.values ) } if (isSnapshot(target)) { @@ -1364,10 +1364,7 @@ export const completeConfigurationEffect: < ) { yield* completeActiveFinalNodeEffect(machine, completed, path, event, outputs, completions) } - return { configuration: completed, completions } as { - readonly configuration: ActiveConfiguration - readonly completions: ReadonlyArray - } + return { configuration: completed, completions } }) const resolveFinalOutputSync = >( diff --git a/packages/effect-machine/src/internal/machine/construction.ts b/packages/effect-machine/src/internal/machine/construction.ts index d71349f8..a9d80a13 100644 --- a/packages/effect-machine/src/internal/machine/construction.ts +++ b/packages/effect-machine/src/internal/machine/construction.ts @@ -129,7 +129,7 @@ export const snapshot = (nodes: Machine.StateNodes, raw: unknown, path = ""): un const node = nodes.byPath.get(path)! target(nodes, path, config) const value = nodeValue(node, config) - if (node.type === "choice") return Topology.makeChoiceTarget(path, node.parent!) + if (node.type === "choice") return Topology.makeChoiceTarget(path, node.parent) if (node.type === "atomic" || node.type === "final") return { path, value } if (config.states === undefined) { throw new Error("Machine history fallback requires a complete explicit tree") diff --git a/packages/effect-machine/src/internal/machine/declaration.ts b/packages/effect-machine/src/internal/machine/declaration.ts index adbe4450..d92da0d2 100644 --- a/packages/effect-machine/src/internal/machine/declaration.ts +++ b/packages/effect-machine/src/internal/machine/declaration.ts @@ -161,7 +161,7 @@ export const invocation = ( !(source.kind === "effects" && Effect.isEffect(source.value)) && !(source.kind === "streams" && Stream.isStream(source.value)) if (parameterized !== (typeof input === "function")) { - throw new Error(`Machine invocation "${src}" requires input exactly when its source takes an argument`) + throw new Error(`Machine invocation "${String(src)}" requires input exactly when its source takes an argument`) } const resolve = parameterized ? (context: unknown) => diff --git a/packages/effect-machine/src/internal/machine/executionPlan.ts b/packages/effect-machine/src/internal/machine/executionPlan.ts index be1e17e1..e2eb8e3b 100644 --- a/packages/effect-machine/src/internal/machine/executionPlan.ts +++ b/packages/effect-machine/src/internal/machine/executionPlan.ts @@ -151,7 +151,7 @@ const compileIndexedExecutionDescriptor = ( finalPaths.push(node.path) } - const config = toImpl(machine).handlers[node.path] as CapturedStateConfig | undefined + const config = toImpl(machine).handlers[node.path] if (!supportsIndexedStateConfig(config)) { return undefined } diff --git a/packages/effect-machine/src/internal/machine/implementation.ts b/packages/effect-machine/src/internal/machine/implementation.ts index ca51d791..e28632ed 100644 --- a/packages/effect-machine/src/internal/machine/implementation.ts +++ b/packages/effect-machine/src/internal/machine/implementation.ts @@ -39,4 +39,4 @@ export interface MachineInternal extends Machine.Any { } /** The single conversion from an erased public machine to its captured implementation. */ -export const toImpl = (machine: Machine.Any): MachineInternal => machine as MachineInternal +export const toImpl = (machine: Machine.Any): MachineInternal => machine diff --git a/packages/effect-machine/src/internal/machine/invocation.ts b/packages/effect-machine/src/internal/machine/invocation.ts index 19ca664a..8447d70f 100644 --- a/packages/effect-machine/src/internal/machine/invocation.ts +++ b/packages/effect-machine/src/internal/machine/invocation.ts @@ -74,10 +74,8 @@ const makeChildOwner = (scope: Runtime.ProcessScope): ChildOwner => ({ child: Child & ChildMachine.Executable & ChildMachine.ParentCompatibility, ...options: ChildMachine.SpawnArgs ) => scope.spawn(child, ...options), - sendTo: ((descriptor: ChildMachine.Any, event: unknown) => scope.sendTo(descriptor, event)) as ChildOwner< - any - >["sendTo"], - stop: ((descriptor: ChildMachine.Any) => scope.stopChild(descriptor)) as ChildOwner["stop"] + sendTo: (descriptor: ChildMachine.Any, event: unknown) => scope.sendTo(descriptor, event), + stop: (descriptor: ChildMachine.Any) => scope.stopChild(descriptor) }) const resolveOne = ( diff --git a/packages/effect-machine/src/internal/machine/machine.ts b/packages/effect-machine/src/internal/machine/machine.ts index 6f5f833c..3b981a16 100644 --- a/packages/effect-machine/src/internal/machine/machine.ts +++ b/packages/effect-machine/src/internal/machine/machine.ts @@ -690,7 +690,7 @@ const flattenHandlers = ( throw new Error(`Machine choice state "${path}" requires a transition`) } } - handlers[path] = stateConfig as CapturedStateConfig + handlers[path] = stateConfig if (childConfig !== undefined) { const node = Topology.getStateNodeDefinition(path, states[key]!) if (node.states === undefined) { @@ -821,9 +821,7 @@ const makeStateHelpers = (): Machine.StateAccessors<{ readonly "": Machine.State >["getSnapshot"], matches: ((snapshot: Machine.AtomicSnapshot, path: string) => - Option.isSome(Topology.getSnapshotByPath(snapshot, path))) as Machine.StateAccessors< - { readonly "": Machine.StateNodeConfig } - >["matches"] + Option.isSome(Topology.getSnapshotByPath(snapshot, path))) } } @@ -870,7 +868,7 @@ const flattenEventProtocolInputs = ( inputs.flatMap((input) => Protocol.isEventProtocol(input, kind) ? Protocol.eventProtocolSchemas(input) - : [input as Machine.TaggedSchema] + : [input] ) export const events = >>( @@ -982,7 +980,7 @@ export const initialDefinition = ( export const inputEventSchemas = ( machine: M -): Machine.InputEvents => Protocol.inputEventSchemas(machine) as Machine.InputEvents +): Machine.InputEvents => Protocol.inputEventSchemas(machine) export const transitionDefinitions = ( machine: M diff --git a/packages/effect-machine/src/internal/machine/planner.ts b/packages/effect-machine/src/internal/machine/planner.ts index c51627bd..2ad99bbc 100644 --- a/packages/effect-machine/src/internal/machine/planner.ts +++ b/packages/effect-machine/src/internal/machine/planner.ts @@ -39,7 +39,7 @@ import * as Construction from "./construction.js" import { failPlanning, InfiniteTransitionError, MachineSchemaDecodeError, toStartupFailure } from "./errors.js" import { StoppedError } from "./errors.js" import type { PlanningError, StartupError } from "./errors.js" -import { type CapturedStateConfig, toImpl } from "./implementation.js" +import { toImpl } from "./implementation.js" import { isDataInitializer } from "./initialDeclaration.js" import { getStateInitializeValues, makeStateInitializeBuilder } from "./initialization.js" import * as InvocationDefinition from "./invocationDefinition.js" @@ -452,7 +452,7 @@ export function resolveInitialConfiguration( for (const path of target.children?.keys() ?? []) { const node = getNode(machine, path) if (node.type !== "choice") continue - const choice = resolveChoiceTarget(machine, partial, makeChoiceTarget(path, node.parent!), event) + const choice = resolveChoiceTarget(machine, partial, makeChoiceTarget(path, node.parent), event) for (const routed of [choice.target, ...choice.additionalTargets]) { const resolved = isInitialTarget(routed) ? resolveInitialTarget(machine, partial, routed, event) @@ -1058,7 +1058,7 @@ const selectInvocationTransition = < event: InvocationEvent.InvocationEvent ): ReadonlyArray> => { if (!configuration.active.has(event.path)) return [] - const config = toImpl(machine).handlers[event.path] as CapturedStateConfig | undefined + const config = toImpl(machine).handlers[event.path] const invoke = InvocationDefinition.definitions(config?.invoke).find((definition) => { const id = "child" in definition ? definition.child?.id : definition.id return String(id) === event.id @@ -1243,14 +1243,14 @@ const choiceFromTarget = ( const withChoiceValues = (target: unknown, values: Readonly>): unknown => { if (isChoiceTarget(target)) { - return makeChoiceTarget(target.path, target.parent, { ...values, ...(target.values ?? {}) }) + return makeChoiceTarget(target.path, target.parent, { ...values, ...target.values }) } if (isInitialTarget(target)) return { ...target, values: { ...values, ...target.values } } if (!isTarget(target) || Object.keys(values).length === 0) return target const snapshot = target[TargetSnapshotTypeId] return makeTarget(target.path, target.value, { ...(snapshot === undefined ? {} : { snapshot }), - values: { ...values, ...(target.values ?? {}) } + values: { ...values, ...target.values } }) } diff --git a/packages/effect-machine/src/internal/machine/protocol.ts b/packages/effect-machine/src/internal/machine/protocol.ts index eb142654..ae4044d4 100644 --- a/packages/effect-machine/src/internal/machine/protocol.ts +++ b/packages/effect-machine/src/internal/machine/protocol.ts @@ -75,7 +75,7 @@ const getBoundaryResultDecoder = (schema: Schema.Top): BoundaryResultDecoder => if (cached !== undefined) { return cached } - const decoder = Schema.decodeUnknownResult(Schema.toType(schema)) as BoundaryResultDecoder + const decoder = Schema.decodeUnknownResult(Schema.toType(schema)) boundaryResultDecoderCache.set(key, decoder) return decoder } @@ -123,7 +123,7 @@ const getEventProtocolDefinition = ( protocol: Machine.EventProtocol.Any, expectedKind?: Machine.EventProtocolKind ): EventProtocolDefinition => { - const definition = eventProtocolDefinitions.get(protocol as object) + const definition = eventProtocolDefinitions.get(protocol) if (definition === undefined) { throw new Error( "Machine expected an event protocol created with Machine.events, Machine.internalEvents, or Machine.emittedEvents" @@ -285,7 +285,7 @@ export const makeEventProtocol = < ): Machine.EventProtocol => { const ownedSchemas = Object.freeze(Array.from(schemas)) as unknown as Schemas const protocol = eventConstructors(kind, ownedSchemas) as Machine.EventProtocol - eventProtocolDefinitions.set(protocol as object, { kind, schemas: ownedSchemas }) + eventProtocolDefinitions.set(protocol, { kind, schemas: ownedSchemas }) return Object.freeze(protocol) as Machine.EventProtocol } @@ -333,7 +333,7 @@ const makeBoundarySync = ( options: DecodeBoundaryOptions ): A => { try { - return schema.make(input as never) as A + return schema.make(input) as A } catch (cause) { const issue = cause instanceof Error ? cause.cause : undefined throw new MachineSchemaDecodeError({ @@ -396,7 +396,7 @@ const decodeEventConstruction = ( if ( definition === undefined || (boundary === "emission" ? definition.kind !== "emitted" : definition.kind === "emitted") || - !constructors.has(definition.schema as object) + !constructors.has(definition.schema) ) { return Effect.fail(eventConstructionProtocolError(machine, construction, boundary)) } @@ -430,7 +430,7 @@ const decodeEventConstructionSync = ( if ( definition === undefined || (boundary === "emission" ? definition.kind !== "emitted" : definition.kind === "emitted") || - !constructors.has(definition.schema as object) + !constructors.has(definition.schema) ) { throw eventConstructionProtocolError(machine, construction, boundary) } diff --git a/packages/effect-machine/src/internal/machine/runtime.ts b/packages/effect-machine/src/internal/machine/runtime.ts index afa043f8..3ec01776 100644 --- a/packages/effect-machine/src/internal/machine/runtime.ts +++ b/packages/effect-machine/src/internal/machine/runtime.ts @@ -234,11 +234,7 @@ const prepareProcessWithStrategy = Effect.fnUntraced(function*< const start = yield* Effect.cached( ownedByScope(initialize).pipe( Effect.onExit((exit) => Deferred.done(started, exit)) - ) as Effect.Effect< - MachineRef, - InitialError, - Requirements | Scope.Scope - > + ) ) return { id: options?.id ?? sessionId, diff --git a/packages/effect-machine/src/internal/machine/runtimeCompiled.ts b/packages/effect-machine/src/internal/machine/runtimeCompiled.ts index c40cee85..29cf3169 100644 --- a/packages/effect-machine/src/internal/machine/runtimeCompiled.ts +++ b/packages/effect-machine/src/internal/machine/runtimeCompiled.ts @@ -269,7 +269,7 @@ class CompiledProcess implements MachineRef { event, this.inspectionSubject, this.causation() - )) as ProcessScope["sendTo"], + )), stopChild: this.childRuntime.stop, failCause: (cause: Cause.Cause) => this.failCause(cause), inspectInitial: this.inspector === undefined @@ -351,7 +351,7 @@ class CompiledProcess implements MachineRef { event, self.inspectionSubject, self.causation() - )) as ProcessScope["sendTo"], + )), stopChild: self.childRuntime.stop, failCause: (cause: Cause.Cause) => self.failCause(cause), inspectInitial: self.inspector === undefined diff --git a/packages/effect-machine/src/internal/machine/runtimeGeneric.ts b/packages/effect-machine/src/internal/machine/runtimeGeneric.ts index bf486856..cdaf9cd1 100644 --- a/packages/effect-machine/src/internal/machine/runtimeGeneric.ts +++ b/packages/effect-machine/src/internal/machine/runtimeGeneric.ts @@ -223,13 +223,11 @@ export const startGenericInternal: StartProcess = Effect.fnUntraced( ) const sendToTarget: ProcessScope["sendTo"] = inspector === undefined ? ((target: unknown, event: unknown) => - isMachineTarget(target) ? target.send(event) : sendTo(target as ChildSelector, event)) as ProcessScope< - Event - >["sendTo"] + isMachineTarget(target) ? target.send(event) : sendTo(target as ChildSelector, event)) : ((target: unknown, event: unknown) => isMachineTarget(target) ? sendMachineTarget(target, event, subject, currentCausation()) - : sendTo(target as ChildSelector, event, subject, currentCausation())) as ProcessScope["sendTo"] + : sendTo(target as ChildSelector, event, subject, currentCausation())) const scope: ProcessScope = { self, diff --git a/packages/effect-machine/src/internal/machine/runtimeProtocol.ts b/packages/effect-machine/src/internal/machine/runtimeProtocol.ts index 1bfc3630..32d9a8ff 100644 --- a/packages/effect-machine/src/internal/machine/runtimeProtocol.ts +++ b/packages/effect-machine/src/internal/machine/runtimeProtocol.ts @@ -106,7 +106,7 @@ export const succeedAcknowledgedMessage = ( if (message.deferred !== undefined) { Deferred.doneUnsafe(message.deferred, Effect.succeed(delivery as AcknowledgedDelivery)) } - message.inspection?.complete(delivery as AcknowledgedDelivery) + message.inspection?.complete(delivery) } } @@ -1013,7 +1013,7 @@ export const makeEmissionRuntime = (): EmissionRuntime => { export const childlessRuntime: ChildRuntime = { close: () => Effect.void, - spawn: (() => Effect.die(new Error("Childless machine logic cannot spawn a process"))) as ProcessSpawn, + spawn: (() => Effect.die(new Error("Childless machine logic cannot spawn a process"))), get: () => Effect.succeed(Option.none()), changes: () => noChildChanges, sendTo: () => Effect.void, @@ -1222,7 +1222,7 @@ export const makeChildRuntimeSync = ( ): Effect.Effect, any, any> { const descriptor = typeof logicOrChild === "object" && logicOrChild !== null && ChildMachineLogicTypeId in logicOrChild - ? logicOrChild as ChildMachine.Any + ? logicOrChild : undefined const logic = descriptor === undefined ? logicOrChild as ProcessLogic diff --git a/packages/effect-machine/src/internal/machine/topology.ts b/packages/effect-machine/src/internal/machine/topology.ts index 31f33bde..6bb82f35 100644 --- a/packages/effect-machine/src/internal/machine/topology.ts +++ b/packages/effect-machine/src/internal/machine/topology.ts @@ -8,7 +8,7 @@ import * as Option from "effect/Option" import { hasProperty } from "effect/Predicate" import * as Schema from "effect/Schema" import type { Machine } from "../../Machine.js" -import { type CapturedStateConfig, toImpl } from "./implementation.js" +import { toImpl } from "./implementation.js" export const TargetTypeId = "~effect/Machine/Target" @@ -111,6 +111,7 @@ export const makeTargetSelection = ( updatePath?: string ): TargetSelection => Object.freeze({ + // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- TypeScript 6 widens the computed key to `symbol` without it. [TargetSelectionTypeId]: TargetSelectionTypeId as typeof TargetSelectionTypeId, kind, scope, @@ -312,7 +313,7 @@ export const getStateNodeDefinition = ( } } const schema = hasProperty(definition, "schema") && Schema.isSchema(definition.schema) - ? definition.schema as Machine.TaggedSchema + ? definition.schema : undefined const annotations = schema === undefined ? definition.annotations : Schema.resolveAnnotations(schema) if (definition.type === "parallel" && !hasProperty(definition, "states")) { @@ -494,7 +495,7 @@ export const compileStateNodes = (states: Machine.StateSchemas): Machine.StateNo return { byPath, roots: compile(states, undefined) - } as Machine.StateNodes + } } const transitionBranches = (handler: unknown): ReadonlyArray => @@ -512,7 +513,7 @@ export const transitionDefinitions = ( ): ReadonlyArray => { const definitions: Array = [] for (const node of machine.stateNodes.byPath.values()) { - const config = toImpl(machine).handlers[node.path] as CapturedStateConfig | undefined + const config = toImpl(machine).handlers[node.path] if (config === undefined) { continue } diff --git a/packages/effect-machine/src/internal/testing/machine/arbitrary.ts b/packages/effect-machine/src/internal/testing/machine/arbitrary.ts index fabdf908..2e7b3b6c 100644 --- a/packages/effect-machine/src/internal/testing/machine/arbitrary.ts +++ b/packages/effect-machine/src/internal/testing/machine/arbitrary.ts @@ -87,6 +87,8 @@ const reportFor = (ast: SchemaAST.AST): SchemaArbitraryReport => { case "Suspend": visit(ast.thunk(), path) break + default: + break } stack.delete(ast) } diff --git a/packages/effect-machine/src/internal/testing/machine/exploration.ts b/packages/effect-machine/src/internal/testing/machine/exploration.ts index b598d2ff..eea7a2b2 100644 --- a/packages/effect-machine/src/internal/testing/machine/exploration.ts +++ b/packages/effect-machine/src/internal/testing/machine/exploration.ts @@ -86,11 +86,10 @@ const stateContext = (machine: M, trace: Trace): Explor const scenarioWithEvent = ( trace: Trace, event: Machine.Machine.InputEvent -): Scenario => - ({ - ...trace.scenario, - events: [...trace.scenario.events, event] - }) as Scenario +): Scenario => ({ + ...trace.scenario, + events: [...trace.scenario.events, event] +}) interface ExplorationEdgeDraft { readonly source: number diff --git a/packages/effect-machine/src/internal/testing/machine/finiteModel.ts b/packages/effect-machine/src/internal/testing/machine/finiteModel.ts index e7f0d2a5..2230bb6b 100644 --- a/packages/effect-machine/src/internal/testing/machine/finiteModel.ts +++ b/packages/effect-machine/src/internal/testing/machine/finiteModel.ts @@ -597,6 +597,7 @@ const addGeneratedHistory = ( event: leaveEvent, target: historyPath }), + // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- TypeScript 6 infers `readonly string[]` without it. events: Object.freeze([mutationEvent, leaveEvent, leaveEvent]) as readonly [string, string, string] }) }) @@ -1266,7 +1267,7 @@ const construction = ( [ requestedChild ?? (sourcePath === path ? state.node.states.find((child) => child._tag !== "History" && child._tag !== "Choice")!.key - : sourcePath!.slice(path.length + 1).split(".")[0]!) + : sourcePath.slice(path.length + 1).split(".")[0]!) ] : state.node.states.filter((child) => child._tag !== "History" && child._tag !== "Choice").map((child) => child.key @@ -1467,6 +1468,7 @@ export const compileModel = (model: FiniteModel): Machine.Machine.Any => { ])) const branches: Record>> = Object.create(null) const handlers = makeHandlers(model.roots, undefined, byPath, transitions, branches) + // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- TypeScript 6 rejects the erased branch record without it. const machine = Machine.make({ root: defined, branches, @@ -1481,5 +1483,5 @@ export const compileModel = (model: FiniteModel): Machine.Machine.Any => { }, states: handlers } as any - ) as Machine.Machine.Any + ) } diff --git a/packages/effect-machine/src/internal/testing/machine/runtime.ts b/packages/effect-machine/src/internal/testing/machine/runtime.ts index ba095935..516b0c15 100644 --- a/packages/effect-machine/src/internal/testing/machine/runtime.ts +++ b/packages/effect-machine/src/internal/testing/machine/runtime.ts @@ -1528,7 +1528,7 @@ export const runtimeCommands = ( ? Protocol.inputEventSchemas(machine).map((schema) => { const derived = toArbitraryWithReport(schema) reports.push(derived.report) - return derived.value as Arbitrary.Arbitrary> + return derived.value }) : [] const eventArbitrary = options.eventArbitrary ?? (eventArbitraries.length === 0 diff --git a/packages/effect-machine/src/internal/testing/machine/runtimeInvariant.ts b/packages/effect-machine/src/internal/testing/machine/runtimeInvariant.ts index 3f5db015..a42b718c 100644 --- a/packages/effect-machine/src/internal/testing/machine/runtimeInvariant.ts +++ b/packages/effect-machine/src/internal/testing/machine/runtimeInvariant.ts @@ -114,7 +114,7 @@ const normalizeTranscript = ( transcript: CausalRuntimeEvidence ): RuntimeInvariantTranscript => ({ commands: transcript.commands, - initial: transcript.initial as RuntimeInvariantTranscript["initial"], + initial: transcript.initial, records: transcript.records.map(({ actual, command, index }) => ({ index, command, @@ -122,7 +122,7 @@ const normalizeTranscript = ( snapshot: actual.snapshot as RuntimeInvariantRecord["snapshot"], awaited: actual.awaited as RuntimeInvariantRecord["awaited"] })), - final: transcript.final as RuntimeInvariantTranscript["final"] + final: transcript.final }) interface Observation { @@ -479,7 +479,7 @@ export const assertPlannerRuntimeAgreement = 0) { return yield* new PlannerRuntimeAgreementError({ - evidence: evidence as CausalRuntimeEvidence, + evidence: evidence, violations }) } diff --git a/packages/effect-machine/src/internal/testing/machine/verification.ts b/packages/effect-machine/src/internal/testing/machine/verification.ts index 35675f67..174a5f2e 100644 --- a/packages/effect-machine/src/internal/testing/machine/verification.ts +++ b/packages/effect-machine/src/internal/testing/machine/verification.ts @@ -55,7 +55,7 @@ const validateLength = (name: "minEvents" | "maxEvents", value: number): void => export const scenarios = ( machine: M, - options: ScenarioOptions = {} as ScenarioOptions + options: ScenarioOptions = {} ): Scenarios => { const eventSchemas = Protocol.inputEventSchemas(machine) const minEvents = options.minEvents ?? 0 @@ -82,7 +82,7 @@ export const scenarios = ( index, report: derived.report }) - return derived.value as Arbitrary.Arbitrary> + return derived.value }) : [] @@ -542,7 +542,7 @@ export const coverage = ( missing: undefined, hits: undefined, misses: undefined, - observed: Array.from(eventCounts, ([tag, count]) => ({ tag, count })) as ReadonlyArray>, + observed: Array.from(eventCounts, ([tag, count]) => ({ tag, count })), diagnostics: declaredEvents.diagnostics }, scenarios: { @@ -585,7 +585,7 @@ export const coverage = ( targets: historyTargets, resolvedTargets: resolvedHistoryTargets } - } as Coverage + } } type SnapshotObservationRole = "startup" | "event" | "microstep" @@ -731,7 +731,7 @@ export const observedGraph: ( id, snapshot: group.snapshot, ...(group.encoded === undefined ? {} : { encoded: group.encoded }), - configuration: rawConfigurationPaths(machine, group.snapshot) as ReadonlyArray>, + configuration: rawConfigurationPaths(machine, group.snapshot), observations: { total: group.startup + group.event + group.microstep, startup: group.startup, @@ -756,7 +756,7 @@ export const observedGraph: ( entryPaths: microstep.entryPaths as ReadonlyArray>, changed: microstep.changed })) - } as ObservedGraphEdge) + }) } }) return { @@ -767,7 +767,7 @@ export const observedGraph: ( new Set(startupSourceOccurrences.map((index) => nodesById.get(occurrenceIds[index]!)!)) ) } -}) as any +}) export class VerificationError extends Data.TaggedError("MachineTestVerificationError")<{ readonly violations: ReadonlyArray @@ -1774,7 +1774,7 @@ export const verify = ( validateSnapshotMetadata(starting, initialLocation, "initial starting state") validateTraceConfiguration( starting, - trace.initial.startingConfiguration as ReadonlyArray, + trace.initial.startingConfiguration, initialLocation, "initial starting" ) @@ -1782,7 +1782,7 @@ export const verify = ( if (new Set(trace.initial.initialEntryPaths).size !== trace.initial.initialEntryPaths.length) { add("microsteps.unique", initialLocation, "initial entry paths contain duplicates") } - if (!samePaths(trace.initial.initialEntryPaths as ReadonlyArray, starting.paths)) { + if (!samePaths(trace.initial.initialEntryPaths, starting.paths)) { add( "microsteps.order", initialLocation, @@ -1808,7 +1808,7 @@ export const verify = ( validatePlanCompletion(trace.initial.plan, initialState, initialCompletions, initialLocation, "initial plan") validateTraceConfiguration( initialState, - trace.initial.configuration as ReadonlyArray, + trace.initial.configuration, initialLocation, "initial" ) @@ -1817,7 +1817,7 @@ export const verify = ( } if ( selected.has("microsteps") && - !samePaths(trace.initial.initialEntryPaths as ReadonlyArray, trace.initial.plan.initialEntryPaths) + !samePaths(trace.initial.initialEntryPaths, trace.initial.plan.initialEntryPaths) ) { add("microsteps.continuity", initialLocation, "initial trace entry paths differ from its plan") } @@ -1830,7 +1830,7 @@ export const verify = ( validateSnapshotMetadata(before, location, `event ${eventIndex} before state`, true) validateTraceConfiguration( before, - step.beforeConfiguration as ReadonlyArray, + step.beforeConfiguration, location, `event ${eventIndex} before` ) @@ -1875,7 +1875,7 @@ export const verify = ( validateSnapshotMetadata(after, location, `event ${eventIndex} after state`, true) validateTraceConfiguration( after, - step.afterConfiguration as ReadonlyArray, + step.afterConfiguration, location, `event ${eventIndex} after` ) @@ -1889,7 +1889,7 @@ export const verify = ( const finalLocation: VerificationLocation = { eventIndex: finalEventIndex } const final = inspectSnapshot(trace.final, finalLocation, "trace final state") validateSnapshotMetadata(final, finalLocation, "trace final state", true) - validateTraceConfiguration(final, trace.finalConfiguration as ReadonlyArray, finalLocation, "final") + validateTraceConfiguration(final, trace.finalConfiguration, finalLocation, "final") if (selected.has("microsteps") && !sameValue(previous.root, final.root)) { add("microsteps.continuity", finalLocation, "trace final state differs from its final planned state") } diff --git a/packages/effect-machine/test/internal/machine/processLifecycle.test.ts b/packages/effect-machine/test/internal/machine/processLifecycle.test.ts index 4120e59c..c35d314d 100644 --- a/packages/effect-machine/test/internal/machine/processLifecycle.test.ts +++ b/packages/effect-machine/test/internal/machine/processLifecycle.test.ts @@ -16,7 +16,7 @@ describe("machine process lifecycle", () => { initial: () => Effect.succeed({ state: 1, done: false, output: undefined }), drain: { _tag: "Process", - run: (context) => + run: () => Ref.update(drainCount, (count) => count + 1).pipe( Effect.andThen(Deferred.succeed(drained, undefined)), Effect.as(Option.none()) @@ -1257,6 +1257,9 @@ describe("machine process lifecycle", () => { assert.strictEqual(yield* Ref.get(cleanupCount), 1) assert.notStrictEqual(snapshot.status, "active") switch (snapshot.status) { + case "active": + assert.fail("the process must have finished") + break case "done": assert.strictEqual(index % 2, 0) assert.deepStrictEqual(joined, Exit.succeed(index)) diff --git a/packages/effect-machine/test/internal/machine/strategyDifferential.test.ts b/packages/effect-machine/test/internal/machine/strategyDifferential.test.ts index d7785217..df7ba8eb 100644 --- a/packages/effect-machine/test/internal/machine/strategyDifferential.test.ts +++ b/packages/effect-machine/test/internal/machine/strategyDifferential.test.ts @@ -513,7 +513,7 @@ describe("machine planner and runtime strategies", () => { Opened: { initial: { target: "Opened.Idle", - data: ({}) => ({ value: 1 }) + data: () => ({ value: 1 }) }, states: { Idle: {} @@ -660,7 +660,7 @@ describe("machine planner and runtime strategies", () => { initial: { target: "Complete", decoded: true, - data: ({ root: { input: input } }) => new Complete({ value: input.value }) + data: ({ root: { input } }) => new Complete({ value: input.value }) }, root: ({ input }) => ({ input }), states: { @@ -1188,7 +1188,7 @@ describe("machine planner and runtime strategies", () => { }).handle({ initial: { target: "Flow", - data: ({ root: { input: input } }) => input + data: ({ root: { input } }) => input }, root: ({ input }) => ({ input }), states: { diff --git a/packages/effect-machine/test/machine/Choice.test.ts b/packages/effect-machine/test/machine/Choice.test.ts index 182f4ba7..c60b9172 100644 --- a/packages/effect-machine/test/machine/Choice.test.ts +++ b/packages/effect-machine/test/machine/Choice.test.ts @@ -195,14 +195,14 @@ describe("Machine choice pseudo-states", () => { }).handle({ initial: { target: "Root", - data: ({ root: { input: input } }) => ({ enabled: input.enabled }) + data: ({ root: { input } }) => ({ enabled: input.enabled }) }, root: ({ input }) => ({ input }), states: { Root: { initial: { target: "Root.Flow", - data: ({ root: { input: input } }) => ({ score: input.score }) + data: ({ root: { input } }) => ({ score: input.score }) }, states: { Flow: { diff --git a/packages/effect-machine/test/machine/DynamicChildren.test.ts b/packages/effect-machine/test/machine/DynamicChildren.test.ts index 202498ee..3f43ce3e 100644 --- a/packages/effect-machine/test/machine/DynamicChildren.test.ts +++ b/packages/effect-machine/test/machine/DynamicChildren.test.ts @@ -48,7 +48,7 @@ describe("dynamic child machines", () => { initial: { target: "PlantActive", decoded: true, - data: ({ root: { input: input } }) => new PlantActive({ id: input.id, produced: input.production }) + data: ({ root: { input } }) => new PlantActive({ id: input.id, produced: input.production }) }, root: ({ input }) => ({ input }), states: { @@ -57,7 +57,7 @@ describe("dynamic child machines", () => { Produce: { target: "PlantActive", decoded: true, - data: ({ event, state }) => (new PlantActive({ ...state, produced: state.produced + event.amount })) + data: ({ event, state }) => (new PlantActive({ id: state.id, produced: state.produced + event.amount })) }, Report: { none: true, @@ -120,7 +120,7 @@ describe("dynamic child machines", () => { initial: { target: "Commissioning", decoded: true, - data: ({ root: { input: input } }) => new Commissioning({ plants: input }) + data: ({ root: { input } }) => new Commissioning({ plants: input }) }, root: ({ input }) => ({ input }), states: { @@ -332,7 +332,7 @@ describe("dynamic child machines", () => { initial: { target: "WorkerIdle", decoded: true, - data: ({ root: { input: input } }) => new WorkerIdle({ id: input.id }) + data: ({ root: { input } }) => new WorkerIdle({ id: input.id }) }, root: ({ input }) => ({ input }), states: { diff --git a/packages/effect-machine/test/machine/InitialEntry.test.ts b/packages/effect-machine/test/machine/InitialEntry.test.ts index 848f1eb4..c1fa3232 100644 --- a/packages/effect-machine/test/machine/InitialEntry.test.ts +++ b/packages/effect-machine/test/machine/InitialEntry.test.ts @@ -54,7 +54,7 @@ const makeMachine = () => { opened: { initial: { target: "opened.idle", - data: ({}) => ({ count: 1 }) + data: () => ({ count: 1 }) }, states: { idle: {}, @@ -97,12 +97,12 @@ const makeParallelMachine = () => { } }, dashboard: { - initial: { filters: ({}) => ({ id: "all" }), results: ({}) => ({ count: 2 }) }, + initial: { filters: () => ({ id: "all" }), results: () => ({ count: 2 }) }, states: { filters: { initial: { target: "dashboard.filters.ready", - data: ({}) => ({ enabled: true }) + data: () => ({ enabled: true }) }, states: { ready: {} @@ -229,7 +229,7 @@ const makeNestedMachine = () => { opened: { initial: { target: "main.opened.idle", - data: ({}) => ({ count: 3 }) + data: () => ({ count: 3 }) }, states: { idle: {}, diff --git a/packages/effect-machine/test/machine/Machine.test.ts b/packages/effect-machine/test/machine/Machine.test.ts index 97f16c34..85f0f0dc 100644 --- a/packages/effect-machine/test/machine/Machine.test.ts +++ b/packages/effect-machine/test/machine/Machine.test.ts @@ -570,7 +570,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -654,7 +654,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -1719,7 +1719,7 @@ describe("Machine", () => { initial: { target: "NonEmptyIdle", decoded: true, - data: ({ root: { input: input } }) => new NonEmptyIdle({ userId: input.userId }) + data: ({ root: { input } }) => new NonEmptyIdle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -2284,11 +2284,9 @@ describe("Machine", () => { } }) const error = yield* Machine.encodeSnapshot(machine, { - ...{ - path: "" as const, - value: undefined, - state: { path: "NonEmptyIdle" as const, value: new NonEmptyIdle({ userId: "user-1" }) } - }, + path: "" as const, + value: undefined, + state: { path: "NonEmptyIdle" as const, value: new NonEmptyIdle({ userId: "user-1" }) }, completed: [{ path: "missing" as const, output: undefined }] }).pipe(Effect.flip) assert.instanceOf(error, Machine.MachineSchemaEncodeError) @@ -2387,7 +2385,7 @@ describe("Machine", () => { initial: { target: "idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5000,7 +4998,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5043,7 +5041,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5098,7 +5096,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5139,7 +5137,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5185,7 +5183,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5305,7 +5303,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5357,7 +5355,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5441,7 +5439,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5488,7 +5486,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5522,7 +5520,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5555,7 +5553,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5623,7 +5621,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5675,7 +5673,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5720,7 +5718,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5787,7 +5785,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -5970,7 +5968,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => { + data: ({ root: { input } }) => { starts += 1 return new Idle({ userId: input.userId }) } @@ -6085,7 +6083,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -6198,7 +6196,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -6532,7 +6530,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -6600,7 +6598,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -6664,7 +6662,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -6746,7 +6744,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -6813,7 +6811,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { @@ -7133,7 +7131,7 @@ describe("Machine", () => { initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ userId: input.userId }) + data: ({ root: { input } }) => new Idle({ userId: input.userId }) }, root: ({ input }) => ({ input }), states: { diff --git a/packages/effect-machine/test/machine/Paths.test.ts b/packages/effect-machine/test/machine/Paths.test.ts index a10672e3..56ce1d53 100644 --- a/packages/effect-machine/test/machine/Paths.test.ts +++ b/packages/effect-machine/test/machine/Paths.test.ts @@ -5,6 +5,7 @@ import { Machine } from "../../src/index.js" describe("declared state paths", () => { it("reserves the top-level root name for the root node", () => { const root = Machine.state({ states: { root: {}, Idle: {} } }) + // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- TypeScript 6 rejects the reserved name, which this test checks at runtime. assert.throws(() => Machine.make({ root, events: Machine.events({}) } as never), /reserved for the root/) }) diff --git a/packages/effect-machine/test/machine/PublicPrototype.test.ts b/packages/effect-machine/test/machine/PublicPrototype.test.ts index 5863a685..330e102f 100644 --- a/packages/effect-machine/test/machine/PublicPrototype.test.ts +++ b/packages/effect-machine/test/machine/PublicPrototype.test.ts @@ -25,6 +25,7 @@ it("uses the public pipeable and inspectable prototypes", () => { } assert.deepStrictEqual(inspectable.toJSON(), { _id: "Machine" }) assert.strictEqual(JSON.stringify(machine), "{\"_id\":\"Machine\"}") + // oxlint-disable-next-line typescript/no-base-to-string -- the test checks the custom `toString` of the machine prototype. assert.strictEqual(String(machine), "{\"_id\":\"Machine\"}") assert.deepStrictEqual(inspectable[Symbol.for("nodejs.util.inspect.custom")]!(), { _id: "Machine" }) }) diff --git a/packages/effect-machine/test/machine/SnapshotCodecAdversarial.test.ts b/packages/effect-machine/test/machine/SnapshotCodecAdversarial.test.ts index 45f15b08..7cd67e95 100644 --- a/packages/effect-machine/test/machine/SnapshotCodecAdversarial.test.ts +++ b/packages/effect-machine/test/machine/SnapshotCodecAdversarial.test.ts @@ -208,7 +208,9 @@ const historyMachine = Machine.make({ }) const historySnapshot = () => ({ - ...{ path: "" as const, value: undefined, state: { path: "Outside" as const, value: new Outside({}) } }, + path: "" as const, + value: undefined, + state: { path: "Outside" as const, value: new Outside({}) }, history: { "Workspace.recent": { mode: "shallow" as const, diff --git a/packages/effect-machine/test/machine/SynchronousDelivery.test.ts b/packages/effect-machine/test/machine/SynchronousDelivery.test.ts index 79ecf833..10982c6f 100644 --- a/packages/effect-machine/test/machine/SynchronousDelivery.test.ts +++ b/packages/effect-machine/test/machine/SynchronousDelivery.test.ts @@ -140,7 +140,7 @@ describe("synchronous delivery to an idle machine", () => { Effect.forkChild({ startImmediately: true }) ) yield* ref.emissions.pipe( - Stream.runForEach((event) => Effect.sync(() => emitted.push((event as Published).value))), + Stream.runForEach((event) => Effect.sync(() => emitted.push(event.value))), Effect.forkChild({ startImmediately: true }) ) yield* ref.send(new Changed({ value: "a" })) diff --git a/packages/effect-machine/test/machine/Visualization.test.ts b/packages/effect-machine/test/machine/Visualization.test.ts index cafaec99..6e51e564 100644 --- a/packages/effect-machine/test/machine/Visualization.test.ts +++ b/packages/effect-machine/test/machine/Visualization.test.ts @@ -145,7 +145,7 @@ const makeMachine = (unsafeStart = false) => initial: { target: "application.workflow.running.editing", decoded: true, - data: ({}) => new Editing({}) + data: () => new Editing({}) }, states: { editing: {}, @@ -205,7 +205,7 @@ const lifecycleDefinition = Machine.make({ root: LifecycleStates, events: Machine.eventsFromSchemas() }) -const makeLifecycleMachine = (unsafe: "always" | "done" | undefined = undefined) => +const makeLifecycleMachine = (unsafe?: "always" | "done") => lifecycleDefinition.handle({ initial: { target: "idle", diff --git a/packages/effect-machine/test/testing/Exploration.test.ts b/packages/effect-machine/test/testing/Exploration.test.ts index 6e7514e6..fcabda46 100644 --- a/packages/effect-machine/test/testing/Exploration.test.ts +++ b/packages/effect-machine/test/testing/Exploration.test.ts @@ -370,7 +370,7 @@ describe("MachineTest bounded exploration", () => { initial: { target: "counter", decoded: true, - data: ({ root: { input: input } }) => new Counter({ count: input.count }) + data: ({ root: { input } }) => new Counter({ count: input.count }) }, root: ({ input }) => ({ input }), states: { diff --git a/packages/effect-machine/test/testing/FiniteModel.test.ts b/packages/effect-machine/test/testing/FiniteModel.test.ts index aa4f888e..11ff54e7 100644 --- a/packages/effect-machine/test/testing/FiniteModel.test.ts +++ b/packages/effect-machine/test/testing/FiniteModel.test.ts @@ -108,7 +108,7 @@ const assertValid = ( else { assert.ok(transition.target !== undefined) assert.strictEqual("reenter" in transition, false) - assert.ok(stateOrder.get(transition.target!)! > stateOrder.get(transition.source)!) + assert.ok(stateOrder.get(transition.target)! > stateOrder.get(transition.source)!) if (transition.trigger.type === "always") assert.strictEqual(source.node._tag, "Atomic") else assert.ok(source.node._tag === "Compound" || source.node._tag === "Parallel") } diff --git a/packages/effect-machine/test/testing/MachineTest.test.ts b/packages/effect-machine/test/testing/MachineTest.test.ts index ec49a321..7aad4b3a 100644 --- a/packages/effect-machine/test/testing/MachineTest.test.ts +++ b/packages/effect-machine/test/testing/MachineTest.test.ts @@ -33,7 +33,7 @@ const makeTraceMachine = (onAction: () => void) => { initial: { target: "Ready", decoded: true, - data: ({ root: { input: input } }) => new Ready({ count: input.userId.length - input.userId.length }) + data: ({ root: { input } }) => new Ready({ count: input.userId.length - input.userId.length }) }, root: ({ input }) => ({ input }), states: { diff --git a/packages/effect-machine/test/testing/Runtime.test.ts b/packages/effect-machine/test/testing/Runtime.test.ts index 04c39f3d..0aad89ab 100644 --- a/packages/effect-machine/test/testing/Runtime.test.ts +++ b/packages/effect-machine/test/testing/Runtime.test.ts @@ -181,7 +181,7 @@ describe("MachineTest runtime commands", () => { return Effect.succeed({ model: next, expected: { - result: model.stopped ? "SendRejected" : "SendAccepted" as string, + result: model.stopped ? "SendRejected" : "SendAccepted", count: next.count, status: model.stopped ? "stopped" as const : "active" as const }, @@ -193,7 +193,7 @@ describe("MachineTest runtime commands", () => { case "Stop": return Effect.succeed({ model: { ...model, stopped: true }, - expected: { result: "Stopped" as string, count: model.count, status: "stopped" as const }, + expected: { result: "Stopped", count: model.count, status: "stopped" as const }, synchronize: model.stopped ? MachineTest.RuntimeSynchronization.current : MachineTest.RuntimeSynchronization.next @@ -202,7 +202,7 @@ describe("MachineTest runtime commands", () => { return Effect.succeed({ model, expected: { - result: "ClockAdvanced" as string, + result: "ClockAdvanced", count: model.count, status: model.stopped ? "stopped" as const : "active" as const }, @@ -212,7 +212,7 @@ describe("MachineTest runtime commands", () => { return Effect.succeed({ model, expected: { - result: "Checkpoint" as string, + result: "Checkpoint", count: model.count, status: model.stopped ? "stopped" as const : "active" as const }, @@ -248,14 +248,14 @@ describe("MachineTest runtime commands", () => { const next = { count: model.count + command.event.amount, pending: model.pending + 1 } return Effect.succeed({ model: next, - expected: { count: next.count, result: "SendAccepted" as string }, + expected: { count: next.count, result: "SendAccepted" }, synchronize: MachineTest.RuntimeSynchronization.none }) } const next = { ...model, pending: 0 } return Effect.succeed({ model: next, - expected: { count: next.count, result: "Checkpoint" as string }, + expected: { count: next.count, result: "Checkpoint" }, synchronize: MachineTest.RuntimeSynchronization.until((snapshot) => snapshotCount(snapshot) === next.count ) @@ -312,7 +312,7 @@ describe("MachineTest runtime commands", () => { MachineTest.checkpointCommand("after timeout") ] const transcript = yield* MachineTest.runEnqueuedCommands(ref, commands, { - initialModel: { elapsed: 0, path: "Waiting" as "Waiting" | "TimedOut" }, + initialModel: { elapsed: 0, path: "Waiting" }, transition: (model, command) => { if (command._tag === "Advance") { const elapsed = model.elapsed + Number(command.duration) @@ -768,7 +768,7 @@ describe("MachineTest causal runtime commands", () => { const ref = yield* Machine.start(timerMachine) const probe = yield* MachineTest.probe(timerMachine, ref) const transcript = yield* MachineTest.runCausalCommands(probe, [MachineTest.advanceCommand(1000)], { - initialModel: "Waiting" as "Waiting" | "TimedOut", + initialModel: "Waiting", transition: () => Effect.succeed({ model: "TimedOut" as const, diff --git a/packages/effect-machine/test/testing/Verification.test.ts b/packages/effect-machine/test/testing/Verification.test.ts index 2bdc08c8..35161a9e 100644 --- a/packages/effect-machine/test/testing/Verification.test.ts +++ b/packages/effect-machine/test/testing/Verification.test.ts @@ -430,7 +430,7 @@ const historyMachine = Machine.make({ initial: { target: "workspace.editor.editing", decoded: true, - data: ({}) => new Editing({ revision: 0 }) + data: () => new Editing({ revision: 0 }) }, states: { editing: {} @@ -661,7 +661,7 @@ describe("MachineTest.verify", () => { assert.strictEqual(updateMicrostep.changed, false) assert.deepStrictEqual(updateMicrostep.exitPaths, []) assert.deepStrictEqual(updateMicrostep.entryPaths, []) - assert.strictEqual((updated.final.state.value as Counter).count, 1) + assert.strictEqual(updated.final.state.value.count, 1) yield* MachineTest.verify(counterMachine, updated) const noop = yield* MachineTest.run(counterMachine, { events: [new Noop({})] }) const step = noop.steps[0]! diff --git a/packages/effect-machine/test/unstable/cluster/ClusterMachine.test.ts b/packages/effect-machine/test/unstable/cluster/ClusterMachine.test.ts index 955fb68d..b460c6ef 100644 --- a/packages/effect-machine/test/unstable/cluster/ClusterMachine.test.ts +++ b/packages/effect-machine/test/unstable/cluster/ClusterMachine.test.ts @@ -167,6 +167,7 @@ const makeTestStorage = () => { entries.set(key, checkpoint) commits += 1 return ClusterMachine.CommitResult.Committed() + // oxlint-disable-next-line typescript/unbound-method -- `refail` is a standalone combinator that does not use `this`. }).pipe(ClusterError.PersistenceError.refail) ) }) diff --git a/packages/effect-machine/test/unstable/reactivity/AtomMachine.test.ts b/packages/effect-machine/test/unstable/reactivity/AtomMachine.test.ts index 723c1c9a..4ce35730 100644 --- a/packages/effect-machine/test/unstable/reactivity/AtomMachine.test.ts +++ b/packages/effect-machine/test/unstable/reactivity/AtomMachine.test.ts @@ -83,7 +83,7 @@ const makeInputCounterMachine = () => { initial: { target: "Count", decoded: true, - data: ({ root: { input: input } }) => new Count({ value: input }) + data: ({ root: { input } }) => new Count({ value: input }) }, root: ({ input }) => ({ input }), states: { diff --git a/packages/effect-machine/typetest/machine/DynamicChildren.tst.ts b/packages/effect-machine/typetest/machine/DynamicChildren.tst.ts index 257a5d58..ffcbcfe3 100644 --- a/packages/effect-machine/typetest/machine/DynamicChildren.tst.ts +++ b/packages/effect-machine/typetest/machine/DynamicChildren.tst.ts @@ -31,7 +31,7 @@ describe("dynamic child machines", () => { initial: { target: "ChildIdle", decoded: true, - data: ({ root: { input: input } }) => new ChildIdle({ id: input.id }) + data: ({ root: { input } }) => new ChildIdle({ id: input.id }) }, root: ({ input }) => ({ input }), states: { ChildIdle: { on: { ChildEvent: { none: true } } } } diff --git a/packages/effect-machine/typetest/machine/InitialEntry.tst.ts b/packages/effect-machine/typetest/machine/InitialEntry.tst.ts index 5429c670..bfd40223 100644 --- a/packages/effect-machine/typetest/machine/InitialEntry.tst.ts +++ b/packages/effect-machine/typetest/machine/InitialEntry.tst.ts @@ -68,7 +68,7 @@ describe("declared initial entry types", () => { opened: { initial: { target: "opened.idle", - data: ({}) => ({ count: 0 }) + data: () => ({ count: 0 }) }, states: { idle: {}, diff --git a/packages/effect-machine/typetest/machine/Resume.tst.ts b/packages/effect-machine/typetest/machine/Resume.tst.ts index b7a5f1dd..f8e697db 100644 --- a/packages/effect-machine/typetest/machine/Resume.tst.ts +++ b/packages/effect-machine/typetest/machine/Resume.tst.ts @@ -20,7 +20,7 @@ const machine = Machine.make({ initial: { target: "Idle", decoded: true, - data: ({ root: { input: input } }) => new Idle({ value: input.seed }) + data: ({ root: { input } }) => new Idle({ value: input.seed }) }, root: ({ input }) => ({ input }), states: { diff --git a/packages/effect-machine/typetest/testing/Exploration.tst.ts b/packages/effect-machine/typetest/testing/Exploration.tst.ts index 579155c5..0d0838c2 100644 --- a/packages/effect-machine/typetest/testing/Exploration.tst.ts +++ b/packages/effect-machine/typetest/testing/Exploration.tst.ts @@ -26,7 +26,7 @@ describe("MachineTest exploration", () => { initial: { target: "counter", decoded: true, - data: ({ root: { input: input } }) => new Counter({ count: input.seed }) + data: ({ root: { input } }) => new Counter({ count: input.seed }) }, root: ({ input }) => ({ input }), states: { diff --git a/packages/effect-machine/typetest/unstable/cluster/ClusterMachine.tst.ts b/packages/effect-machine/typetest/unstable/cluster/ClusterMachine.tst.ts index 72e90fb7..ca36e513 100644 --- a/packages/effect-machine/typetest/unstable/cluster/ClusterMachine.tst.ts +++ b/packages/effect-machine/typetest/unstable/cluster/ClusterMachine.tst.ts @@ -140,7 +140,7 @@ describe("ClusterMachine", () => { initial: { target: "Count", decoded: true, - data: ({ root: { input: input } }) => new Count({ value: input.value }) + data: ({ root: { input } }) => new Count({ value: input.value }) }, root: ({ input }) => ({ input }), states: { Count: {} } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11206b52..b44b881f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,6 +22,9 @@ importers: '@effect/vitest': specifier: 4.1.1 version: 4.1.1(effect@4.1.1)(vitest@5.0.5(@types/node@26.6.6)(jsdom@30.1.3)(vite@8.3.5(@types/node@26.6.6)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0))) + '@typeonce/oxlint-plugin-effect-machine': + specifier: workspace:* + version: link:packages/oxlint-plugin '@types/node': specifier: 26.6.6 version: 26.6.6 @@ -31,6 +34,12 @@ importers: effect: specifier: 4.1.1 version: 4.1.1 + oxlint: + specifier: 1.90.0 + version: 1.90.0(oxlint-tsgolint@7.0.2005) + oxlint-tsgolint: + specifier: 7.0.2005 + version: 7.0.2005 pagefind: specifier: 1.5.2 version: 1.5.2 @@ -162,7 +171,7 @@ importers: devDependencies: oxlint: specifier: 1.90.0 - version: 1.90.0 + version: 1.90.0(oxlint-tsgolint@7.0.2005) typescript: specifier: 6.0.3 version: 6.0.3 @@ -638,6 +647,36 @@ packages: '@oxc-project/types@0.156.0': resolution: {integrity: sha512-1Duyg48TdJNdyhEB8Xn1L36bT9Pv6GUtlKjHxOys8xkaF/rQq3XJE+bAurSK/AgIz5mGFsb77cn9Pb20+WCScw==} + '@oxlint-tsgolint/darwin-arm64@7.0.2005': + resolution: {integrity: sha512-VqDZxcZVjlonNLuuvraVNgDQt+1ndwa4yJo8OqsK4HYh/61ba9IlMStCVfi/aaIqRoDhNbAKC8gdcTrXqnb+Ug==} + cpu: [arm64] + os: [darwin] + + '@oxlint-tsgolint/darwin-x64@7.0.2005': + resolution: {integrity: sha512-4kUfLRNYoVpv0Tm3jPYrvxyfOc/LmdvFC3pKZ4tL6K7lA7K/VS81SAtmxRzbYXYSyb3SBWhwsuX+df4neqzxxA==} + cpu: [x64] + os: [darwin] + + '@oxlint-tsgolint/linux-arm64@7.0.2005': + resolution: {integrity: sha512-rhUNcPsBlKvhXtrbhbBUidtRBRdJ8fbEHN9H5Q5N59gby18ruGuDA7KUxl7oRnUzp797uykYQhkBDm+0KpfK/Q==} + cpu: [arm64] + os: [linux] + + '@oxlint-tsgolint/linux-x64@7.0.2005': + resolution: {integrity: sha512-tKs9yinDQZOHygM0e8EJnjTU3mzJtdfkOZP6Udu82iLMePUSHOhTJ9zzU/mSd1hQxURI4LTRRiFhfzC+PzwdNA==} + cpu: [x64] + os: [linux] + + '@oxlint-tsgolint/win32-arm64@7.0.2005': + resolution: {integrity: sha512-VYPnu1gjfbSv+ROhuyNfMs382D4zQU2ZRc8Uc3nuEZo1k2kf+1KfTrg4hrlWh73h9GI+dUWyo+DYCQzUBlPwcQ==} + cpu: [arm64] + os: [win32] + + '@oxlint-tsgolint/win32-x64@7.0.2005': + resolution: {integrity: sha512-ekU89cTGy9fpQzuvxIsFHrnoP8XoFjGIRgPEW6eqgY2NYjGy9pwpjF79o04WXRMJFKRrcaVqkZXUUBdLsqpQDw==} + cpu: [x64] + os: [win32] + '@oxlint/binding-android-arm-eabi@1.90.0': resolution: {integrity: sha512-sgfalb1HuxYjiAa4wESZ626z1FGusqNcXYHyB5oIOEprS2sbPg0UYEfo83uF1GNG70DIfZxHkXuE6KFrlSdn8Q==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1534,6 +1573,10 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + oxlint-tsgolint@7.0.2005: + resolution: {integrity: sha512-6ccPGu4u4J9bwYPHXBFPee3RZrX3BboRdU7k14zRDDB1lvN3O7J/vPRFdaEfa1+CWx4xnQQOp4ctoaF66oO0uw==} + hasBin: true + oxlint@1.90.0: resolution: {integrity: sha512-RkTN9EmgIUpLuhNoLhO3OliMGJlvb3Dk7XVjBZnfoMCHrVpuz5ESd97klfiyYeV7XfA2lNLnDCPMkUI+Qw5How==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2375,6 +2418,24 @@ snapshots: '@oxc-project/types@0.156.0': {} + '@oxlint-tsgolint/darwin-arm64@7.0.2005': + optional: true + + '@oxlint-tsgolint/darwin-x64@7.0.2005': + optional: true + + '@oxlint-tsgolint/linux-arm64@7.0.2005': + optional: true + + '@oxlint-tsgolint/linux-x64@7.0.2005': + optional: true + + '@oxlint-tsgolint/win32-arm64@7.0.2005': + optional: true + + '@oxlint-tsgolint/win32-x64@7.0.2005': + optional: true + '@oxlint/binding-android-arm-eabi@1.90.0': optional: true @@ -3059,7 +3120,16 @@ snapshots: outdent@0.5.0: {} - oxlint@1.90.0: + oxlint-tsgolint@7.0.2005: + optionalDependencies: + '@oxlint-tsgolint/darwin-arm64': 7.0.2005 + '@oxlint-tsgolint/darwin-x64': 7.0.2005 + '@oxlint-tsgolint/linux-arm64': 7.0.2005 + '@oxlint-tsgolint/linux-x64': 7.0.2005 + '@oxlint-tsgolint/win32-arm64': 7.0.2005 + '@oxlint-tsgolint/win32-x64': 7.0.2005 + + oxlint@1.90.0(oxlint-tsgolint@7.0.2005): optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.90.0 '@oxlint/binding-android-arm64': 1.90.0 @@ -3080,6 +3150,7 @@ snapshots: '@oxlint/binding-win32-arm64-msvc': 1.90.0 '@oxlint/binding-win32-ia32-msvc': 1.90.0 '@oxlint/binding-win32-x64-msvc': 1.90.0 + oxlint-tsgolint: 7.0.2005 p-filter@2.1.0: dependencies: diff --git a/scripts/api-reference-site/generate.mjs b/scripts/api-reference-site/generate.mjs index 4eb71733..04768d8c 100644 --- a/scripts/api-reference-site/generate.mjs +++ b/scripts/api-reference-site/generate.mjs @@ -1027,13 +1027,13 @@ export const highlightCode = (source, language) => { return tokens.map((token) => { let kind if (/^\s+$/u.test(token)) kind = undefined - else if (/^\/[/\*]/.test(token)) kind = "comment" + else if (/^\/[/*]/.test(token)) kind = "comment" else if (/^["'`]/.test(token)) kind = "string" else if (/^\d/.test(token)) kind = "number" else if (typeScriptKeywords.has(token)) kind = "keyword" else if (typeScriptTypes.has(token) || /^\p{Lu}/u.test(token)) kind = "type" else if (["class", "const", "function", "interface", "type"].includes(previous)) kind = "name" - else if (/^[{}()[\]<>.,:;?=|&+\-*\/!]$/.test(token)) kind = "punctuation" + else if (/^[{}()[\]<>.,:;?=|&+\-*/!]$/.test(token)) kind = "punctuation" if (!/^\s+$/u.test(token)) previous = token const escaped = escapeHtml(token) return kind === undefined ? escaped : `${escaped}` diff --git a/scripts/fixtures/consumer/deep-bound.ts b/scripts/fixtures/consumer/deep-bound.ts index fef8ff8f..c43b4d02 100644 --- a/scripts/fixtures/consumer/deep-bound.ts +++ b/scripts/fixtures/consumer/deep-bound.ts @@ -55,7 +55,7 @@ const childMachine = Machine.make({ initial: { target: "Done", decoded: true, - data: ({ root: { input: input } }) => ChildState.cases.Done.make({ value: input.value }) + data: ({ root: { input } }) => ChildState.cases.Done.make({ value: input.value }) }, root: ({ input }) => ({ input }), states: { diff --git a/tsconfig.json b/tsconfig.json index 91962d34..6e555244 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,11 @@ "$schema": "https://json.schemastore.org/tsconfig", "include": [], "references": [ - { "path": "tsconfig.packages.json" } + { + "path": "tsconfig.packages.json" + }, + { + "path": "tsconfig.tests.json" + } ] }