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
These variables are consumed by the Docker Compose workspace defined in .devcontainer/docker-compose.yaml. Copy .devcontainer/.env.sample to .devcontainer/.env to override defaults locally.
| Variable | Required | Default | Description |
|---|---|---|---|
OPENCODE_EXPERIMENTAL_OXFMT | Optional | false | Enables experimental oxfmt integration inside the opencode AI assistant running in the dev container. |
OPENCODE_PORT_MAPPING | Optional | 4096 | Docker port mapping for the opencode web interface (host_port:container_port or just host_port). |
Sample file: .devcontainer/.env.sample
# OPENCODE_EXPERIMENTAL_OXFMT=true# OPENCODE_PORT_MAPPING=4096:4096CI / Release Pipeline Variable
| Variable | Required | Default | Description |
|---|---|---|---|
SEMREL_SKIP_STEPS | Optional | "" (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:
# Skip one package publishSEMREL_SKIP_STEPS="@semantic-release/npm:packages/play-xstate"
# Skip all demo publish stepsSEMREL_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 releaseSEMREL_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:
| Setting | Value | Notes |
|---|---|---|
target | ESNext | Latest ECMAScript output |
module | NodeNext | ESM with .js extension imports required |
moduleResolution | NodeNext | Matches module setting |
customConditions | ["source"] | Resolves @xmachines/* to TypeScript source in dev/test |
strict | true | Full strict mode |
noUnusedLocals | true | Error on unused variables |
noUnusedParameters | true | Error on unused parameters |
noFallthroughCasesInSwitch | true | |
noImplicitReturns | true | |
noImplicitOverride | true | |
exactOptionalPropertyTypes | true | |
declaration | true | Emits .d.ts files |
declarationMap | true | Emits .d.ts.map for IDE go-to-source |
sourceMap | true | |
verbatimModuleSyntax | true | Preserves import type |
isolatedModules | true | |
removeComments | false | Preserves 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 npm 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: npm run lint / npm run lint:fix
The root config extends the shared base (@xmachines/shared/oxlint) and adds workspace-level ignore patterns:
export default defineConfig({ extends: [sharedConfig], ignorePatterns: [ ".planning", ".opencode", "node_modules", "dist", "build", "coverage", "docs/api/_media", "*.min.js", "CHANGELOG.md", ],});Shared base rules (packages/shared/config/oxlint.config.ts):
| Plugin | Category | Severity |
|---|---|---|
typescript | correctness | error |
unicorn | suspicious | warn |
import | perf | warn |
import/no-cycle | — | error |
typescript/no-explicit-any | — | error |
typescript/no-unused-vars | — | error (ignores _ prefixed names) |
unicorn/filename-case | — | off |
Per-package override pattern:
// packages/<name>/oxlint.config.tsimport 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: npm run format / npm run format:check
The root config extends the shared base and adds workspace-level ignore patterns:
export default defineConfig({ ...sharedConfig, ignorePatterns: [ ...(sharedConfig.ignorePatterns ?? []), ".planning", ".opencode", ".agents", ".githuman", ".devcontainer", "CHANGELOG.md", "Thumbs.db", ],});Shared base format settings (packages/shared/config/oxfmt.config.ts):
| Setting | Value |
|---|---|
printWidth | 100 |
tabWidth | 4 |
useTabs | true |
semi | true |
singleQuote | false |
trailingComma | "all" |
insertFinalNewline | true |
JSON / YAML override (applied via overrides):
| Setting | Value |
|---|---|
useTabs | false |
tabWidth | 2 |
Editor — .editorconfig
Location: /.editorconfig
| Setting | [*] | [*.{json,yml,yaml}] | [*.md] |
|---|---|---|---|
indent_style | tab | space | — |
indent_size | 4 | 2 | — |
end_of_line | lf | — | — |
charset | utf-8 | — | — |
trim_trailing_whitespace | true | — | false |
insert_final_newline | true | — | — |
Testing — vitest.config.ts (root)
Location: /vitest.config.ts
Tool: Vitest ^4.1.5
Run: npm test / npm 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:
| Setting | Value | Notes |
|---|---|---|
pool | "forks" | Process-isolated workers |
maxWorkers | 4 | Root default; per-project configs may override |
isolate | true | |
fileParallelism | false | Conservative default; safe packages opt in with true |
teardownTimeout | 30000 ms | |
hookTimeout | 30000 ms | |
testTimeout | 10000 ms |
Coverage thresholds (monorepo aggregate — vitest run --coverage):
| Type | Threshold |
|---|---|
| Lines | 80% |
| Functions | 80% |
| Branches | 75% |
| Statements | 80% |
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 npm 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.aliasviaxmAliases()so@xmachines/*imports resolve to TypeScript source without a prior build.vitest.node.setup.tsfor non-browser projects (if not already present).vitest.setup.tsfor all projects (if not already present).
Typical per-package usage:
// packages/<name>/vitest.config.tsimport { 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 aRecord<string, string>mapping every@xmachines/*package to its TypeScript source entry. Enables tests and dev servers to run without a priornpm run build.xmResolve(import.meta.url)— returns a full Viteresolveconfig withxmAliasespluspreserveSymlinks: trueandconditions: ["source"].xmCacheDir(import.meta.url, name)— returns a shared Vite cache path undernode_modules/.vite/<name>to prevent redundant dep optimizer runs across projects.xmOptimizeDeps(extra?)— returns a standardoptimizeDeps.includelist; pass framework-specific extras to pre-bundle them at startup.
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:
| Branch | Channel | Pre-release |
|---|---|---|
main | (default) | No |
pre/rc | pre/rc | rc |
beta | beta | true |
Tag format: v${version}
Plugin order:
@semantic-release/commit-analyzer— determines version bump from conventional commits@semantic-release/release-notes-generator— builds release notes@semantic-release/changelog— writesCHANGELOG.md@semantic-release/execsteps (in order):install—npm ciapply-patches—npm run apply-patchesset-workspace-versions— updates allpackage.jsonfiles to the new versionbuild—npm run buildtypedoc—npm run typedoc -w @xmachines/docsformat-docs—npm run format -w @xmachines/docs
@semantic-release/npm— one entry per published package (20 packages) + pack-only for demo apps@semantic-release/git— commitsCHANGELOG.md, updatedpackage.jsonfiles, and generated API docs@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
| Config | Required to Work | Optional / Has Default |
|---|---|---|
OPENCODE_EXPERIMENTAL_OXFMT | No | Default: false |
OPENCODE_PORT_MAPPING | No | Default: 4096 |
SEMREL_SKIP_STEPS | No | Default: "" (run all steps) |
Root tsconfig.json references | Yes — must list all packages | — |
Per-package composite: true | Yes — required for project references | — |
@xmachines/shared/tsconfig extends | Yes — all packages must extend it | — |
@xmachines/shared/oxlint extends | Yes — all packages must extend it | — |
@xmachines/shared/oxfmt extends | Yes — all packages must extend it | — |
Adding a New Package
When adding a new package, update these configuration files:
/tsconfig.json— add a{ "path": "./packages/<name>" }entry in the correct dependency layer./tsconfig.test.json— add a{ "path": "./packages/<name>/tsconfig.test.json" }entry./vitest.config.ts— add"packages/<name>/vitest.config.ts"totest.projects./vitest.browser.config.ts(if the package has browser tests) — add the browser config path totest.projects./release.config.mjs— add an entry toNPM_PACKAGESarray with the package’spkgRoot.
See AGENTS.md for the full new-package checklist.