{
  "$meta": {
    "package": "@walkeros/transformer-validate",
    "version": "4.5.0",
    "type": "transformer",
    "platform": [
      "server",
      "web"
    ],
    "docs": "https://www.walkeros.io/docs/transformers/validate",
    "source": "https://github.com/elbwalker/walkerOS/tree/main/packages/transformers/validate/src"
  },
  "schemas": {
    "settings": {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type": "object",
      "properties": {
        "contract": {
          "description": "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.",
          "type": "array",
          "items": {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {}
          }
        },
        "format": {
          "description": "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.",
          "type": "boolean"
        },
        "mode": {
          "description": "`strict` drops invalid events (chain-stop) after recording errors; `pass` annotates and continues. Default `pass`.",
          "type": "string",
          "enum": [
            "strict",
            "pass"
          ]
        },
        "output": {
          "description": "Where the verdict (on the event) and the issue list (on the ingest) are written.",
          "type": "object",
          "properties": {
            "isValid": {
              "description": "Event dot-path for the boolean verdict. Default `source.valid`. Empty string = skip.",
              "type": "string"
            },
            "errors": {
              "description": "Ingest dot-path for the issue list. Default `validation`. Empty string = skip.",
              "type": "string"
            }
          },
          "additionalProperties": false,
          "title": "output"
        }
      },
      "additionalProperties": false,
      "description": "Validate transformer: checks events against JSON Schema contracts and annotates a verdict on the event plus issues on the ingest."
    }
  },
  "examples": {
    "step": {
      "gtmFilterDropped": {
        "public": false,
        "title": "Filter gtm.* via a contract pattern (dropped)",
        "description": "A contract whose name must NOT match ^gtm\\. rejects \"gtm.js\". In strict mode the event is dropped. This is how you filter without an \"ignore\" field.",
        "in": {
          "name": "gtm.js"
        },
        "out": [
          [
            "return",
            false
          ]
        ]
      },
      "gtmFilterPasses": {
        "title": "Filter gtm.* via a contract pattern (real event passes)",
        "description": "The same ^gtm\\. rejection contract leaves a real \"page view\" untouched: it passes and is annotated source.valid:true.",
        "in": {
          "name": "page view",
          "entity": "page",
          "action": "view"
        },
        "out": [
          [
            "return",
            {
              "event": {
                "name": "page view",
                "entity": "page",
                "action": "view",
                "source": {
                  "valid": true
                }
              }
            }
          ]
        ]
      },
      "passAnnotateInvalid": {
        "public": false,
        "title": "Pass mode annotates an invalid event",
        "description": "In mode:\"pass\" an invalid event is not dropped: source.valid:false is written to the event so downstream destinations can route on it, and the error list is written to the ingest path \"validation\".",
        "in": {
          "name": "page view",
          "entity": "page",
          "action": "view",
          "data": {}
        },
        "out": [
          [
            "return",
            {
              "event": {
                "name": "page view",
                "entity": "page",
                "action": "view",
                "data": {},
                "source": {
                  "valid": false
                }
              }
            }
          ]
        ]
      },
      "strictInvalidPageView": {
        "public": false,
        "title": "Strict validate against a contract (invalid, dropped)",
        "description": "A \"page view\" missing the required data.title fails the contract. In strict mode the chain stops (event dropped). Validation errors are still written to the ingest for observers.",
        "in": {
          "name": "page view",
          "entity": "page",
          "action": "view",
          "data": {}
        },
        "out": [
          [
            "return",
            false
          ]
        ]
      },
      "strictValidPageView": {
        "title": "Strict validate against a contract (valid)",
        "description": "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.",
        "in": {
          "name": "page view",
          "entity": "page",
          "action": "view",
          "data": {
            "title": "Home"
          }
        },
        "out": [
          [
            "return",
            {
              "event": {
                "name": "page view",
                "entity": "page",
                "action": "view",
                "data": {
                  "title": "Home"
                },
                "source": {
                  "valid": true
                }
              }
            }
          ]
        ]
      }
    }
  },
  "hints": {
    "contract-resolution": {
      "text": "A $contract.* reference is resolved to a concrete JSON Schema at bundle time. The runtime transformer never sees a string: by the time push runs, settings.contract holds resolved ContractRule objects (entity-action event schemas and/or a full-event schema) or inline schemas. If you author a flow with $contract.web, the bundler inlines the matching schema before deploy.",
      "code": [
        {
          "lang": "json",
          "code": "{\n  \"transformers\": {\n    \"validate\": {\n      \"package\": \"@walkeros/transformer-validate\",\n      \"config\": {\n        \"settings\": {\n          \"contract\": [\n            \"$contract.web\"\n          ],\n          \"mode\": \"strict\"\n        }\n      }\n    }\n  }\n}"
        }
      ]
    },
    "mode-strict-vs-pass": {
      "text": "mode:\"strict\" drops invalid events by stopping the transformer chain (push returns false), so they never reach downstream destinations. mode:\"pass\" (the default) never drops: it annotates the event with the verdict and continues, and you route downstream on event.source.valid. Use strict to enforce a contract as a hard gate; use pass to observe quality without losing data.",
      "code": [
        {
          "lang": "json",
          "code": "{\n  \"transformers\": {\n    \"validate\": {\n      \"package\": \"@walkeros/transformer-validate\",\n      \"config\": {\n        \"settings\": {\n          \"contract\": [\n            \"$contract.web\"\n          ],\n          \"mode\": \"pass\"\n        }\n      }\n    }\n  }\n}"
        }
      ]
    },
    "output-split": {
      "text": "The verdict and the error list are written to two different places by design. The boolean verdict goes to the EVENT (default path source.valid): it is analytics-grade data that travels with the event to destinations, and source.valid stays type-clean under WalkerOS.Source. The error list goes to the INGEST (default path validation): it is observer-visible pipeline diagnostics, never analytics data, written in place so it survives even a strict-mode drop. Override either path via output.isValid / output.errors; set either to an empty string to skip that write.",
      "code": [
        {
          "lang": "json",
          "code": "{\n  \"transformers\": {\n    \"validate\": {\n      \"package\": \"@walkeros/transformer-validate\",\n      \"config\": {\n        \"settings\": {\n          \"contract\": [\n            \"$contract.web\"\n          ],\n          \"output\": {\n            \"isValid\": \"source.valid\",\n            \"errors\": \"validation\"\n          }\n        }\n      }\n    }\n  }\n}"
        }
      ]
    },
    "format-vs-contract": {
      "text": "format:true is a built-in structural check, not an authored schema. It validates the canonical WalkerOS.Event shape (correct field types, no unknown fields). All fields are optional, so it checks structure and types, not presence: a wrong-typed field or malformed structure fails, a missing field does not. It is independent of contract: turn it on to catch malformed events even when you have no contract, or alongside a contract to AND both checks. A contract is the place for your domain rules; format is the place for \"is this even a well-formed event\".",
      "code": [
        {
          "lang": "json",
          "code": "{\n  \"transformers\": {\n    \"validate\": {\n      \"package\": \"@walkeros/transformer-validate\",\n      \"config\": {\n        \"settings\": {\n          \"format\": true,\n          \"mode\": \"pass\"\n        }\n      }\n    }\n  }\n}"
        }
      ]
    },
    "gtm-filtering": {
      "text": "There is no ignore field. To filter unwanted events (for example GTM lifecycle pings like gtm.js / gtm.dom), author a contract that REJECTS them and run mode:\"strict\". A schema where name must NOT match ^gtm\\. fails those events, and strict mode drops them while real events pass. This keeps filtering declarative and contract-driven rather than a separate ad-hoc list.",
      "code": [
        {
          "lang": "json",
          "code": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"name\": {\n      \"not\": {\n        \"pattern\": \"^gtm\\\\.\"\n      }\n    }\n  }\n}"
        }
      ]
    },
    "known-limitations": {
      "text": "The errors list may contain an extra parent entry pointing at a properties wrapper per failure, a quirk of the underlying JSON Schema engine output. The isValid verdict is unaffected: a single real failure can surface as more than one error entry, but isValid is still false exactly when there is at least one failure. v1 emits level:\"error\" only (no warn level yet)."
    }
  }
}