Validate
Checks events against JSON Schema contracts and records a verdict. In pass mode it annotates the event and continues so a downstream step can route on the result. In strict mode it drops invalid events by stopping the chain. The transformer runs on both web and server.
Installation
npm install @walkeros/transformer-validate- Integrated
- Bundled
import { startFlow } from '@walkeros/collector';
import { transformerValidate } from '@walkeros/transformer-validate';
await startFlow({
transformers: {
validate: {
code: transformerValidate,
config: { settings: { contract: [contractWeb], mode: 'strict' } },
},
},
});"transformers": {
"validate": {
"package": "@walkeros/transformer-validate",
"config": { "settings": { "contract": ["$contract.web"], "mode": "strict" } },
"next": "ga4"
}
}How it works
The transformer reads the { ingest, event } context and validates the canonical event. Each contract entry is a constraint: a $contract.<name> reference resolves to the entity-action schemas for the event, an inline JSON Schema applies to the whole event. All entries are AND-ed and every error is aggregated. Set format: true to additionally check the canonical partial event shape (field types and structure); required-field rules come from contract.
mode decides what happens to an invalid event:
pass(default): write the verdict to the event and continue. A downstream destination or transformer routes onevent.source.valid.strict: record the errors, then stop the chain so the event never reaches downstream steps.
The verdict and the error list are written to two different places. The boolean verdict goes onto the event (output.isValid, default source.valid) as analytics-grade data that travels with the event. The issue list goes onto the ingest (output.errors, default validation) as observer-visible diagnostics, never event data, so it survives even a strict-mode drop. Set either path to an empty string to skip that write.
Configuration
This transformer uses the standard transformer config wrapper (consent, data, env, id, ...). For the shared fields see transformer configuration. Package-specific fields live under config.settings and are listed below.
Settings
| Property | Type | Description | More |
|---|---|---|---|
contract | Array<object> | Validation constraints. Each entry is a resolved $contract.* rule (with entity-action `events` schemas and/or a full-event `schema`) or an inline whole-event JSON Schema. All entries are AND-ed; every error is aggregated. | |
format | boolean | When true, also validate the canonical WalkerOS.Event structural shape (correct field types, no unknown fields). All fields are optional, so this checks structure and types, not presence. | |
mode | 'strict' | 'pass' | `strict` drops invalid events (chain-stop) after recording errors; `pass` annotates and continues. Default `pass`. | |
output | output | Where the verdict (on the event) and the issue list (on the ingest) are written. | |
isValid | string | Event dot-path for the boolean verdict. Default `source.valid`. Empty string = skip. | |
errors | string | Ingest dot-path for the issue list. Default `validation`. Empty string = skip. |
Mapping
This package does not define custom rule-level settings. For the standard rule fields (consent, condition, data, batch, name, policy) see mapping.
Examples
Filter gtm.* via a contract pattern (real event passes)
The same ^gtm\. rejection contract leaves a real "page view" untouched: it passes and is annotated source.valid:true.
{
"name": "page view",
"entity": "page",
"action": "view"
}return {
"event": {
"name": "page view",
"entity": "page",
"action": "view",
"source": {
"valid": true
}
}
}Strict validate against a contract (valid)
A "page view" with the required data.title passes the inline contract. The verdict source.valid:true is written to the event; the chain continues.
{
"name": "page view",
"entity": "page",
"action": "view",
"data": {
"title": "Home"
}
}return {
"event": {
"name": "page view",
"entity": "page",
"action": "view",
"data": {
"title": "Home"
},
"source": {
"valid": true
}
}
}Reference a contract
The canonical place for event shapes is the top-level contract block. Reference it with $contract.<name>; the bundler resolves the reference to a concrete schema before deploy, so the runtime transformer only ever sees resolved schemas.
{
"version": 4,
"contract": {
"web": { "events": { "order": { "complete": { "properties": { "data": { "required": ["total", "currency"] } } } } } }
},
"flows": {
"default": {
"transformers": {
"validate": {
"package": "@walkeros/transformer-validate",
"config": { "settings": {
"contract": ["$contract.web"],
"format": true,
"mode": "strict",
"output": { "isValid": "source.valid", "errors": "validation" }
} },
"next": "ga4"
}
}
}
}
}Filter unwanted events
There is no separate ignore or filter setting. To drop unwanted events (for example GTM lifecycle pings like gtm.js / gtm.dom), author an inline schema that rejects them and run mode: "strict". A schema where name must NOT match ^gtm\. fails those events while real events pass.
"validate": {
"package": "@walkeros/transformer-validate",
"config": { "settings": {
"contract": [{ "type": "object", "properties": { "name": { "not": { "pattern": "^gtm\\." } } } }],
"mode": "strict"
} }
}Limitations
- The error list may contain an extra parent entry pointing at a
propertieswrapper per failure, a quirk of the underlying JSON Schema engine. TheisValidverdict is unaffected: it isfalseexactly when there is at least one failure. - Issues are emitted at
level: "error"; there is no warn level.
Next steps
- Contract - named, inheritable event schemas referenced via
$contract - Create your own - build custom transformers