Rule Validation
Every adapter validates ChaosConfig from Node before it touches the page. Malformed configs throw ChaosConfigError with structured ValidationIssue[] so failures land in the test runner, not the browser console.
Where validation runs
Section titled “Where validation runs”Adapters call validateChaosConfig immediately on entry to injectChaos (and injectSWChaos). Page-side chaosUtils.start also re-validates after deserialization, so the engine never trusts an unvalidated config. Validated configs carry a non-enumerable brand on a global Symbol.for('chaos-maker.validated') so subsequent calls short-circuit when no validation options change.
Strict by default
Section titled “Strict by default”unknownFields is 'reject' by default. Pass 'warn' or 'ignore' only when you ship configs that intentionally carry extra metadata.
| Mode | Throws? | Returned config | Console output |
|---|---|---|---|
'reject' (default) | yes - ChaosConfigError listing every unknown path | n/a | n/a |
'warn' | no | parsed + unknowns stripped | one aggregated console.warn listing all unknown paths in deterministic sort order |
'ignore' | no | parsed + unknowns stripped | none |
Both 'warn' and 'ignore' strip unknown fields from the returned config so they cannot leak into the engine, presets, or interceptors.
import { validateChaosConfig } from '@chaos-maker/core';
const validated = validateChaosConfig(config, { unknownFields: 'warn' });Reading ChaosConfigError
Section titled “Reading ChaosConfigError”ChaosConfigError.issues is ValidationIssue[]. Issues are deterministically sorted by path then code and the rendered message caps at 50 entries with a ... and N more summary line.
import { injectChaos, ChaosConfigError } from '@chaos-maker/playwright';
try { await injectChaos(page, config);} catch (e) { if (e instanceof ChaosConfigError) { for (const issue of e.issues) { console.error(issue.path, issue.code, issue.ruleType, issue.message, issue.expected, issue.received); } } throw e;}error.messages returns the v0.4.x string array (path: message) for callers that grep stack traces.
Matcher issue codes
Section titled “Matcher issue codes”The advanced matchers surface contributes four issue codes alongside the existing set:
matcher_not_found- rule references amatchername not in thematchersregistry.matcher_collision- twomatcherskeys collide aftertrim().matcher_inline_conflict- rule mixesmatcherwith inline matcher fields.matcher_cycle- registry entry carries its ownmatcherfield. Reserved for future composition; observable today via untyped configs.
A new RuleType value matcher carries these on the issue. See Advanced matchers for the full surface.
Custom validators
Section titled “Custom validators”Run additional checks per RuleType. Validators receive each rule as Readonly<unknown>; narrow with a type guard. Mutating the rule arg is undefined behavior - Chaos Maker’s engine deep-clones the canonical config at expansion time, so your mutations may not propagate.
import { validateChaosConfig } from '@chaos-maker/core';
validateChaosConfig(config, { customValidators: { 'network.failure': (rule, ctx) => { const r = rule as { probability: number }; if (r.probability > 0.5) { return [{ path: ctx.path, code: 'custom', ruleType: ctx.ruleType, message: 'probability over 0.5 not allowed in CI', }]; } return []; }, },});schemaVersion
Section titled “schemaVersion”ChaosConfig.schemaVersion?: 1 is reserved for forward-compat. Omit unless a future major release explicitly bumps it. Unknown values are rejected with code: 'unknown_schema_version' before any other Zod parsing runs, so the failure message is unambiguous.
Deprecation rails
Section titled “Deprecation rails”Pass onDeprecation: (issue) => … to receive a ValidationIssue whenever a deprecated field is set. The registry is empty for this release - the rails exist so the first deprecation is a one-line addition.
JSON Schema artifact
Section titled “JSON Schema artifact”@chaos-maker/core ships dist/chaos-config.schema.json plus a sidecar dist/chaos-config.schema.notes.md. Wire the JSON file as a "$schema" reference for IDE autocomplete:
{ "$schema": "./node_modules/@chaos-maker/core/dist/chaos-config.schema.json", "network": { "failures": [ { "urlPattern": "/api", "statusCode": 503, "probability": 1 } ] }}The artifact is a tooling approximation. Several Zod refinements do not translate:
| Zod refinement | JSON Schema fate |
|---|---|
Group-name dedup (superRefine) | dropped |
Mutually exclusive counting (onNth / everyNth / afterN) | weakly approximated |
WebSocket close-code range (1000 or 3000-4999) | translated as ranges; verify with the runtime validator |
WebSocket reason UTF-8 byte length <= 123 | dropped (JSON Schema string length counts code points) |
GraphQL operation RegExp /g / /y flag rejection | dropped |
graphqlOperation accepting a RegExp instance | rendered as string |
Preset-name .trim() deduplication | dropped |
Use the JSON Schema for IDE / external linters; use validateChaosConfig for actual gating in CI and at runtime. Treat divergence as expected - fix the runtime, not the artifact.
Brand short-circuit
Section titled “Brand short-circuit”Validated configs carry a Symbol.for('chaos-maker.validated') brand carrying the validator’s version number. A subsequent validateChaosConfig call with the same input and no options returns the input unchanged (referential equality). Stamping is the final step - a config that fails any layer is never branded.
The brand is non-enumerable, non-writable, and non-configurable, so:
JSON.parse(JSON.stringify(cfg))strips it (re-validation on the page-side / SW boundary).serializeForTransport(cfg)strips it (re-validation in the browser).- User code cannot forge a brand.
Migration from v0.4.x
Section titled “Migration from v0.4.x”- v0.4.x configs validate unchanged - no new fields are required.
ChaosConfigError.messageis enhanced withcodeandexpected/received. Old test grep helpers can switch toerror.messagesto keep the v0.4.x string shape.injectChaos(page, config, { validation: { unknownFields: 'warn' } })is the migration path if your configs carry intentional extra metadata.