Skip to content

Configuration

This document describes every configuration file, environment variable, and per-package override used by the xmachines-js monorepo.


Environment Variables

The monorepo itself has no application runtime that reads environment variables. The only variables are for the dev container tooling and the CI/CD release pipeline.

Dev Container Variables

The dev container currently defines no workspace-specific environment variables; .devcontainer/.env (loaded via env_file in .devcontainer/compose.yaml) is available for local overrides if a future tool needs one. Copy .devcontainer/.env.sample to .devcontainer/.env to get started.

CI / Release Pipeline Variable

VariableRequiredDefaultDescription
SEMREL_SKIP_STEPSOptional"" (empty — run all steps)A single RegExp string matched against every semantic-release plugin step ID. Use | for alternation to skip multiple steps. Set via the GitLab “Run pipeline” UI or API trigger.

Defined in: .gitlab-ci.yml, consumed by release.config.mjs.

Examples:

Terminal window
# Skip one package publish
SEMREL_SKIP_STEPS="@semantic-release/npm:packages/play-xstate"
# Skip all demo publish steps
SEMREL_SKIP_STEPS="examples/demo"
# Skip all exec steps (build, typedoc, etc.)
SEMREL_SKIP_STEPS="@semantic-release/exec"
# Publish-only retry (skip build + demos)
SEMREL_SKIP_STEPS="@semantic-release/exec|examples/demo"
# Skip git commit and GitLab release
SEMREL_SKIP_STEPS="@semantic-release/git|@semantic-release/gitlab"

Configuration Files

TypeScript — tsconfig.json (root)

Location: /tsconfig.json

The root TypeScript config is a build coordinator only — it compiles nothing itself. It lists every package and example via references so that tsc --build can determine the correct build order automatically.

{
"files": [],
"references": [
// Layer 0 — no internal deps
{ "path": "./packages/play-signals" },
{ "path": "./packages/play" },
{ "path": "./packages/docs" },
// Layer 1 — depends on Layer 0
{ "path": "./packages/play-actor" },
// Layer 2 — depends on Layer 0 + 1
{ "path": "./packages/play-router" },
{ "path": "./packages/play-xstate" }
// ...etc.
]
}

Every package must be listed here and must have composite: true in its own tsconfig.json.

Test build coordinator: /tsconfig.test.json — identical structure but references each package’s tsconfig.test.json for type-checking tests without emitting output.

TypeScript — Shared Base Config (@xmachines/shared/tsconfig)

Location: packages/shared/config/tsconfig.json Exported as: @xmachines/shared/tsconfig

All packages extend this base. Key compiler settings:

SettingValueNotes
targetESNextLatest ECMAScript output
moduleNodeNextESM with .js extension imports required
moduleResolutionNodeNextMatches module setting
customConditions["source"]Resolves @xmachines/* to TypeScript source in dev/test
stricttrueFull strict mode
noUnusedLocalstrueError on unused variables
noUnusedParameterstrueError on unused parameters
noFallthroughCasesInSwitchtrue
noImplicitReturnstrue
noImplicitOverridetrue
exactOptionalPropertyTypestrue
declarationtrueEmits .d.ts files
declarationMaptrueEmits .d.ts.map for IDE go-to-source
sourceMaptrue
verbatimModuleSyntaxtruePreserves import type
isolatedModulestrue
removeCommentsfalsePreserves JSDoc in output

Per-package tsconfig.json pattern:

{
"extends": "@xmachines/shared/tsconfig",
"compilerOptions": {
"composite": true,
"rootDir": "./src",
"outDir": "./dist"
},
"references": [{ "path": "../dependency-package" }]
}

Test tsconfig (@xmachines/shared/tsconfig-test):

Location: packages/shared/config/tsconfig.test.json

Test builds add noEmit: true, allowImportingTsExtensions: true, and vitest/globals types. Used by pnpm run test:build to type-check test files without emitting output.

Linting — oxlint.config.ts (root)

Location: /oxlint.config.ts Tool: oxlint ^1.62.0 Run: pnpm run lint / pnpm run lint:fix

The root config extends the shared base (@xmachines/shared/oxlint) and adds workspace-level ignore patterns:

/oxlint.config.ts
export default defineConfig({
extends: [sharedConfig],
ignorePatterns: [
".opencode",
"node_modules",
"dist",
"build",
"coverage",
"docs/api/_media",
"*.min.js",
"CHANGELOG.md",
],
});

Shared base rules (packages/shared/config/oxlint.config.ts):

PluginCategorySeverity
typescriptcorrectnesserror
unicornsuspiciouswarn
importperfwarn
import/no-cycleerror
typescript/no-explicit-anyerror
typescript/no-unused-varserror (ignores _ prefixed names)
unicorn/filename-caseoff

Per-package override pattern:

// packages/<name>/oxlint.config.ts
import sharedConfig from "@xmachines/shared/oxlint";
import { defineConfig } from "oxlint";
export default defineConfig({
extends: [sharedConfig],
// add package-specific rules here
});

Formatting — oxfmt.config.ts (root)

Location: /oxfmt.config.ts Tool: oxfmt ^0.47.0 Run: pnpm run format / pnpm run format:check

The root config extends the shared base and adds workspace-level ignore patterns:

/oxfmt.config.ts
export default defineConfig({
...sharedConfig,
ignorePatterns: [
...(sharedConfig.ignorePatterns ?? []),
".opencode",
".agents",
".githuman",
".devcontainer",
"CHANGELOG.md",
"Thumbs.db",
],
});

Shared base format settings (packages/shared/config/oxfmt.config.ts):

SettingValue
printWidth100
tabWidth4
useTabstrue
semitrue
singleQuotefalse
trailingComma"all"
insertFinalNewlinetrue

JSON / YAML override (applied via overrides):

SettingValue
useTabsfalse
tabWidth2

Editor — .editorconfig

Location: /.editorconfig

Setting[*][*.{json,yml,yaml}][*.md]
indent_styletabspace
indent_size42
end_of_linelf
charsetutf-8
trim_trailing_whitespacetruefalse
insert_final_newlinetrue

Testing — vitest.config.ts (root)

Location: /vitest.config.ts Tool: Vitest ^4.1.5 Run: pnpm test / pnpm run test:coverage

Root Vitest config is a workspace coordinator that lists all per-package configs under test.projects. It sets conservative monorepo-wide defaults:

SettingValueNotes
pool"forks"Process-isolated workers
maxWorkers4Root default; per-project configs may override
isolatetrue
fileParallelismfalseConservative default; safe packages opt in with true
teardownTimeout30000 ms
hookTimeout30000 ms
testTimeout10000 ms

Coverage thresholds (monorepo aggregate — vitest run --coverage):

TypeThreshold
Lines80%
Functions80%
Branches75%
Statements80%

Coverage provider: v8. Individual packages may declare higher per-package thresholds in their own vitest.config.ts.

Browser test config: /vitest.browser.config.ts Run with pnpm run test:browser. Uses Playwright Chromium via Vitest browser mode. testTimeout is 20000 ms; maxWorkers is 2 (default; demo integration projects override with pool: "threads" + maxWorkers: 2).

Per-Package Vitest Config — @xmachines/shared/vitest

Location: packages/shared/config/vitest.ts Exported as: @xmachines/shared/vitest

Packages use defineXmVitestConfig(import.meta.url, overrides) to create their config. It automatically injects:

  • resolve.alias via xmAliases() so @xmachines/* imports resolve to TypeScript source without a prior build.
  • vitest.node.setup.ts for non-browser projects (if not already present).
  • vitest.setup.ts for all projects (if not already present).

Typical per-package usage:

// packages/<name>/vitest.config.ts
import { defineXmVitestConfig } from "@xmachines/shared/vitest";
export default defineXmVitestConfig(import.meta.url, {
test: {
name: "play-example",
include: ["test/**/*.test.ts"],
},
});

Vite Source Aliases — @xmachines/shared/vite-aliases

Location: packages/shared/config/vite-aliases.ts Exported as: @xmachines/shared/vite-aliases

Provides two helpers used in all vitest.config.ts and vite.config.ts files:

  • xmAliases(import.meta.url) — returns a Record<string, string> mapping every @xmachines/* package to its TypeScript source entry. Enables tests and dev servers to run without a prior pnpm run build.
  • xmResolve(import.meta.url) — returns a full Vite resolve config with xmAliases plus preserveSymlinks: true and conditions: ["source"].
  • xmCacheDir(import.meta.url, name) — returns a shared Vite cache path under node_modules/.vite/<name> to prevent redundant dep optimizer runs across projects.
  • xmOptimizeDeps(include?) — returns an optimizeDeps.include list holding exactly the specifiers passed in. Nothing is pre-bundled by default, and every entry has to be a package the calling project declares: pnpm gives each package its own node_modules, so Vite cannot resolve an undeclared specifier — it warns on stderr and carries on, which reads as a failure in a passing job.

Release — release.config.mjs

Location: /release.config.mjs Tool: semantic-release via @semantic-release/* plugins

Defines the release branches and plugin chain used by the GitLab CI semantic-release job.

Release branches:

BranchChannelPre-release
main(default)No
rcrcrc
betabetatrue
alphaalphaalpha
preprepre

Only main is a protected branch today. That works because GITLAB_TOKEN — which the release needs, since CI_JOB_TOKEN can push neither the release commit nor the tag — is an unprotected group variable and so reaches every one of these refs. If it is ever made protected, each of these branches has to be protected too, or the release fails in verify-conditions with EGITNOPERMISSION rather than at the registry.

Tag format: v${version}

Plugin order:

  1. @semantic-release/commit-analyzer — determines version bump from conventional commits
  2. @semantic-release/release-notes-generator — builds release notes
  3. @semantic-release/changelog — writes CHANGELOG.md
  4. @semantic-release/exec steps (in order):
    • corepackcorepack enable
    • installpnpm install --frozen-lockfile
    • set-workspace-versions — updates all package.json files to the new version
    • buildpnpm run build
    • typedocpnpm --filter @xmachines/docs run typedoc
    • format-docspnpm --filter @xmachines/docs run format
  5. @semantic-release/npm — one entry per published package (20 packages) + pack-only for demo apps
  6. @semantic-release/git — commits CHANGELOG.md, updated package.json files, and generated API docs
  7. @semantic-release/gitlab — creates a GitLab release with tarball assets

Any step can be skipped via the SEMREL_SKIP_STEPS CI variable (see Environment Variables).


Per-Environment Overrides

This monorepo has no application-level config with per-environment overrides (no .env.development / .env.production files). All configuration is tool-level (TypeScript, linter, formatter, test runner) and is environment-agnostic.

The only environment-specific distinction is between local development (dev container with .devcontainer/.env) and CI (GitLab CI with pipeline variables), described in the sections above.


Required vs Optional Settings Summary

ConfigRequired to WorkOptional / Has Default
SEMREL_SKIP_STEPSNoDefault: "" (run all steps)
Root tsconfig.json referencesYes — must list all packages
Per-package composite: trueYes — required for project references
@xmachines/shared/tsconfig extendsYes — all packages must extend it
@xmachines/shared/oxlint extendsYes — all packages must extend it
@xmachines/shared/oxfmt extendsYes — all packages must extend it

Adding a New Package

When adding a new package, update these configuration files:

  1. /tsconfig.json — add a { "path": "./packages/<name>" } entry in the correct dependency layer.
  2. /tsconfig.test.json — add a { "path": "./packages/<name>/tsconfig.test.json" } entry.
  3. /vitest.config.ts — add "packages/<name>/vitest.config.ts" to test.projects.
  4. /vitest.browser.config.ts (if the package has browser tests) — add the browser config path to test.projects.
  5. /release.config.mjs — add an entry to NPM_PACKAGES array with the package’s pkgRoot.

See AGENTS.md for the full new-package checklist.