Skip to content

feat(ui): Add machine library for authoring flows#8919

Draft
alexcarpenter wants to merge 20 commits into
mainfrom
carp/mosaic-state-machine
Draft

feat(ui): Add machine library for authoring flows#8919
alexcarpenter wants to merge 20 commits into
mainfrom
carp/mosaic-state-machine

Conversation

@alexcarpenter

Copy link
Copy Markdown
Member

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

- Tighten Actor.send and Actor.can to accept TEvent only, removing the
  AnyEventObject escape hatch that silently swallowed typos
- Add context init option to CreateActorOptions so runtime dependencies
  can be injected at actor-creation time without module-level closures
- Add types.test-d.ts with @ts-expect-error guards and expectTypeOf
  assertions covering send/can rejection, snapshot types, and assign
- Extract delete-organization-machine and leave-organization-machine as
  module-level factories; migrate both sections to useMachine with the
  split-component pattern (loader + ready) so the machine is always
  instantiated with a non-null organization/membership
…mounts

In StrictMode (on by default in Next.js dev), React runs effects twice:
start → stop → start. The second start() hit the `if (started) return`
guard and bailed out, leaving status = 'stopped'. Every subsequent send()
then short-circuited, so dialogs driven by useMachine never opened.

Fix: stop() resets `started = false` to allow a clean restart, and start()
sets status = 'active' before entering state so a restart-after-stop works.
…-fire

After stop() + start() (StrictMode, Suspense remount), the actor was
re-entering whatever state it was stopped in. If that state had an invoke
(e.g. deleting), the async function would fire a second time.

Fix: start() now resets context and value to their initial values before
entering state, so a restart always begins from idle rather than continuing
a half-completed flow.

Adds a regression test for the double-invoke case.
…factories

useMachine creates the actor once, so any function passed inline to the
machine factory is captured from the first render only. If membership or
organization changed under the same mount, the old .destroy() would fire.

Fix: pin a ref to the prop on every render; the machine receives a stable
wrapper that reads from the ref at call time.

Adds a regression test that re-renders with a fresh destroy prop before
triggering the flow and asserts the fresh function is invoked.
…Effect

Replaces the ref-wrapper workaround in section components with the XState
pattern: actor.setContext() silently patches context each render so invoke
sources always call the latest prop-derived function without stale closures.
Section machine factories are now module-level singletons; the destroy/leave
function is injected via useMachine options.context instead of a closure.
This reverts commit a521f2e.
TransitionConfig, Transition, InvokeConfig, StateConfig, and MachineConfig
now accept an optional TStates extends string = string generic. When callers
supply it, initial and transition targets are constrained to that literal union;
the default of string preserves the existing behaviour for untyped machines.

StateConfig.on keys are now Partial<Record<TEvent['type'], ...>> so event type
typos (e.g. 'CANEEL' instead of 'CANCEL') are caught at the createMachine call
site rather than silently accepted. Type tests cover both constraints.
@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jun 19, 2026 3:06am
swingset Ready Ready Preview, Comment Jun 19, 2026 3:06am

Request Review

@changeset-bot

changeset-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e1c94a8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 57860450-a0f8-4c8d-899d-93ee18483235

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the ui label Jun 18, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jun 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8919

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8919

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8919

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8919

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@8919

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8919

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8919

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8919

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8919

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8919

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8919

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8919

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8919

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8919

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8919

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8919

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8919

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8919

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8919

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8919

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8919

commit: e1c94a8

Adds a `UseMachineOptions` type that extends `CreateActorOptions` with
an optional `onDone` callback, fired once when the machine reaches a
final state. Auth flows can now declaratively hand off (navigate,
redirect) without a `useEffect` watching `snapshot.status` externally.
Resolves a promise with the first snapshot satisfying a predicate.
Rejects when the actor becomes non-active without the predicate passing,
or when an optional timeout elapses (WaitForTimeoutError). Eliminates
manual subscribe wiring in async tests and flow orchestration.
Adds setTimeout-based delayed transitions via a new `after` key on state
nodes — the idiomatic way to model OTP expiry and resend cooldowns without
leaking timers into components. Timers are cancelled on state exit and on
stop(), so they never outlive the state or the actor.
No auth flow or component pulls it in; deferred()+tick() covers test
needs. Can be added when something concrete requires it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant