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

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.

VariableRequiredDefaultDescription
OPENCODE_EXPERIMENTAL_OXFMTOptionalfalseEnables experimental oxfmt integration inside the opencode AI assistant running in the dev container.
OPENCODE_PORT_MAPPINGOptional4096Docker port mapping for the opencode web interface (host_port:container_port or just host_port).

Sample file: .devcontainer/.env.sample

.devcontainer/.env.sample
# OPENCODE_EXPERIMENTAL_OXFMT=true
# OPENCODE_PORT_MAPPING=4096:4096

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

/oxlint.config.ts
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):

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: npm run format / npm 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 ?? []),
".planning",
".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: 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:

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 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.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 npm 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(extra?) — returns a standard optimizeDeps.include list; 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:

BranchChannelPre-release
main(default)No
pre/rcpre/rcrc
betabetatrue

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):
    • installnpm ci
    • apply-patchesnpm run apply-patches
    • set-workspace-versions — updates all package.json files to the new version
    • buildnpm run build
    • typedocnpm run typedoc -w @xmachines/docs
    • format-docsnpm run format -w @xmachines/docs
  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
OPENCODE_EXPERIMENTAL_OXFMTNoDefault: false
OPENCODE_PORT_MAPPINGNoDefault: 4096
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.