Skip to content

Package surface cleanup: remove deprecated exports, split @xstate/scxml, fix files/exports and @xstate/svelte ESM - #5739

Open
davidkpiano wants to merge 10 commits into
nextfrom
beta/step5a-surface
Open

davidkpiano wants to merge 10 commits into
nextfrom
beta/step5a-surface

Conversation

@davidkpiano

@davidkpiano davidkpiano commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

Beta blocker B5. Four commits, one per item.

1. Deprecated exports removed

getInitialSnapshot, getNextSnapshot, NoInfer, AnyInterpreter, ResolvedStateMachineTypes. src/getNextSnapshot.ts → src/inertActorScope.ts (only createInertActorScope remained). Docs and the migration guide (§16) updated. Changeset xstate minor ("Removed").

2. files ⊆ exports

actions, invoke, guards, dev (and a stale actor/) were stub folders with no source and no importers; deleted and removed from files. New scripts/check-package-files.mjs (pnpm check:files) asserts every directory in files has an exports entry; CI step snippet in the PR notes below. createFSM is now exported only from xstate/fsm (was also root). Rename for review: xstate/graph's getStateNodes (returns all descendants) collided with the root getStateNodes(stateNode, stateValue); the graph one is now getDescendantStateNodes. Changeset xstate minor.

3. @xstate/scxml (decided: split)

New packages/xstate-scxml (@xstate/scxml, peer xstate, dependency saxes), laid out like @xstate/react. All SCXML source, tests (356), fixtures, and the SCION framework devDependency moved. Core loses ./scxml from exports/files/preconstruct entrypoints, plus saxes, @scion-scxml/test-framework, pkg-up. Core is now dependency-free, so the bin/xstate.js claim is true. Core exposes _parseDelayToMilliseconds (@experimental) and _microstepHooks moves from @internal to @experimental so the package typechecks against built types. xstate/scxml now fails with ERR_PACKAGE_PATH_NOT_EXPORTED. Changesets xstate minor, @xstate/scxml minor (first publish will be 1.0.0-alpha.1).

4. @xstate/svelte exports map (#5243)

Copying React's map would not fix it (same import → CJS-wrapper shape, and preconstruct cannot emit real ESM next to CJS). Svelte 5 is ESM-only, so the package is now ESM-only ("type": "module"), matching @xstate/store-svelte. require() works via require(esm) on Node 20.19+/22.12+. Verified by packing and importing/requiring from a temp dir; svelte-check 0 errors. Changeset @xstate/svelte patch.

CI step to add (after #5735 lands)

    - name: Package files
      run: pnpm check:files
      shell: bash

Verification

pnpm build, pnpm test (3293), @xstate/scxml tests (356), typecheck (+ adapter-consumers, fsm-only, examples), check-built-types, lint, format, knip, check:files all pass. Bundle thresholds fail on next today (pre-existing); this branch is within 2 B of next; #5735 resets them.


Devin Review

@changeset-bot

changeset-bot Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 10a596a

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

This PR includes changesets to release 4 packages
Name Type
xstate Minor
@xstate/store Patch
@xstate/svelte Patch
@xstate/scxml Minor

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 Sep 23, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 6f612979-1dbd-4bfb-a0ad-53aa5044a11c

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.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

Comment thread scripts/bench-core-runtime.mjs Outdated
Comment on lines +20 to +22
const { createFSM } = require(
xstatePath.replace(/xstate(\.[^/\\]*)$/, 'xstate-fsm$1')
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Custom benchmark bundles lose FSM support

When --xstate has a nonstandard basename, the regex reloads that root bundle for createFSM. Root no longer exports createFSM, so every FSM benchmark fails.

Learn more

The benchmark accepts --xstate=<path> as its runtime input. Previously that file supplied every benchmark API, including createFSM. After the root export removal, the script derives a second path by rewriting only filenames beginning with xstate. Any otherwise valid custom bundle named index.js, core.js, or similar remains unchanged, so the script requires the root bundle twice and receives no createFSM.

Example: With --xstate=/tmp/candidate/index.js, the replacement still yields /tmp/candidate/index.js. The root APIs load, but makeFSMTarget() calls undefined and aborts the run.

Recommended fix: Add an explicit --xstate-fsm option or resolve the xstate/fsm subpath from a package directory. Keep filename rewriting only as a documented fallback, and fail early with a clear message when the FSM entry cannot be resolved.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

const name = normalize(entry).replace(/\/$/, '');
const fullPath = join(packagesDir, dir, name);
if (name === 'dist' || binDirs.has(name)) continue;
if (!existsSync(fullPath) || !statSync(fullPath).isDirectory()) continue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Missing package paths bypass validation

When a listed package path is absent, check:files skips it instead of rejecting the stale manifest. Existing missing @xstate/store entries therefore pass the new gate.

Learn more

The files array controls what npm includes in a published tarball. A stale path is silently omitted by npm. The checker currently ignores the same absent path, so it cannot detect this package-surface drift. The repository already contains absent undo, persist, reset, and validate paths in the store manifest.

Example: @xstate/store lists undo, but no packages/xstate-store/undo directory exists. check:files exits successfully instead of identifying the stale entry.

Recommended fix: Report every nonexistent literal files path before checking whether an existing path is a directory. If glob patterns become supported later, expand them explicitly and reject patterns with no matches.

Suggested change
if (!existsSync(fullPath) || !statSync(fullPath).isDirectory()) continue;
if (!existsSync(fullPath)) {
errors.push(`${pkg.name}: "files" includes missing path "${name}"`);
continue;
}
if (!statSync(fullPath).isDirectory()) continue;

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant