Skip to main content
Ask your AI

Step examples

Add examples to any step in your flow config to document what it expects and produces. On a GA4 destination (@walkeros/web-destination-gtag with measurement ID G-DEMO123456):

"examples": {
"purchase": {
"in": { "name": "order complete", "data": { "id": "ORD-123", "total": 149.97 } },
"mapping": { "name": "purchase", "data": { "map": { "transaction_id": "data.id", "value": "data.total" } } },
"out": [["gtag", "event", "purchase", { "transaction_id": "ORD-123", "value": 149.97, "send_to": "G-DEMO123456" }]]
}
}

Examples are test fixtures, documentation, and simulation data in one place. They live in your flow config, get stripped at build time, and have zero runtime impact.

Examples are mainly internal: packages use them for tests, docs and demos. In your own flow, use them sparingly, where one shows something the package examples cannot, for example a client flow's source with an example of its default behaviour. Mark such a fixture "public": false to keep it out of docs, UI and MCP default output while tests and --simulate still use it.

What are step examples​

Every source, transformer, and destination in a walkerOS flow is a step. Each step receives input and produces output. Step examples are named { in, out } pairs that capture concrete data for each step: what goes in, what comes out.

They serve multiple purposes simultaneously:

  • Documentation: developers see real data shapes, not just type signatures
  • Testing: your tests use examples as fixtures, and walkeros validate checks that connected steps have compatible examples
  • Simulation: debug your pipeline before deployment
  • LLM context: AI tools use examples to suggest correct mappings

The { in, out } format​

Each example has a human-readable name and two fields:

{
  "examples": {
    "page view": {
      "in": {
        "name": "page view",
        "data": { "title": "Home", "path": "/" }
      },
      "out": [["gtag", "event", "page_view", { "send_to": "G-DEMO123456" }]]
    },
    "purchase": {
      "in": {
        "name": "order complete",
        "data": { "id": "ORD-123", "total": 149.97, "currency": "EUR" }
      },
      "mapping": {
        "name": "purchase",
        "data": {
          "map": {
            "transaction_id": "data.id",
            "value": "data.total",
            "currency": "data.currency"
          }
        }
      },
      "out": [["gtag", "event", "purchase", {
        "transaction_id": "ORD-123",
        "value": 149.97,
        "currency": "EUR",
        "send_to": "G-DEMO123456"
      }]]
    }
  }
}
FieldTypeDescription
inanyThe data the step receives
mappingobjectThe mapping rule applied to this event (destinations only)
outarrayThe effects the step produces, in order. Each entry is a tuple [callable, ...args]: a vendor call such as ["gtag", ...] for a destination, ["elb", event] for a source, ["return", value] for a transformer. [] means no observable effect
out: [["return", false]]arrayThe transformer rejects or filters this event
triggerobjectSources only: how the example fires the source (type, such as load, click or POST, and options). This is simulation and test input, not the event's trigger field, which is a string
commandstringRoute in through a walker command instead of pushing it as an event (config, consent, user, run)
title, descriptionstringLabels shown in docs and MCP output
publicbooleanfalse hides a test-only example from docs, UI and default MCP output (default true)

For destinations, the optional mapping field captures the mapping rule that transforms the walkerOS event into the vendor-specific output. This ties the example to a specific mapping configuration, so tests and simulations can register it dynamically as { [entity]: { [action]: example.mapping } }.

Command examples​

Some destinations need to react to walker commands (consent updates, user identification, run state) rather than events. Set the command field on a step example so the test runner invokes elb('walker <command>', in) instead of pushing in as a regular event:

{
  "command": "consent",
  "in": { "marketing": true, "functional": true },
  "out": [
    ["gtag", "consent", "default", { "ad_storage": "denied", "ad_user_data": "denied", "ad_personalization": "denied", "analytics_storage": "denied" }],
    ["gtag", "consent", "update", { "ad_storage": "granted", "ad_user_data": "granted", "ad_personalization": "granted", "analytics_storage": "granted" }]
  ]
}

Supported commands: config, consent, user, run. When command is set, mapping is ignored, because commands don't flow through event mapping. See the walkeros-using-step-examples skill for the full test-runner pattern.

You can add as many named examples as you need. Tests refer to each example by its name, and the MCP flow_examples tool lists them by name for each step.

Three type zones​

A walkerOS flow has three distinct type zones. The shape of in and out depends on where the step sits:

ARBITRARY walkerOS.Event ARBITRARY
┌─────────┐ ┌──────────────────────┐ ┌──────────────┐
│ Source │ → │ Collector → Transform │ → │ Destination │
│ ingest │ │ → ... → Transform │ │ output │
└─────────┘ └──────────────────────┘ └──────────────┘
package- known structure, package-
specific varying properties specific
  • Source in: raw HTTP request body, DOM event, dataLayer push (package-specific)
  • Source out: the elb call that pushes the walkerOS.Event, as [["elb", event]]
  • Transformer in and destination in: walkerOS.Event (known structure, varying properties)
  • Transformer out: [["return", { "event": event }]], or [["return", false]] when the transformer drops the event
  • Destination out: the vendor calls, such as gtag() or fbq() arguments or an HTTP request (vendor-specific)

Adding examples to a flow​

Here is a complete flow config with examples on a source, transformer, and destination:

{
  "version": 4,
  "flows": {
    "default": {
      "config": { "platform": "web" },
      "sources": {
        "browser": {
          "package": "@walkeros/web-source-browser",
          "examples": {
            "page view": {
              "trigger": { "type": "load", "options": { "url": "https://example.com/", "title": "Home" } },
              "in": "",
              "out": [["elb", {
                "name": "page view",
                "data": { "domain": "example.com", "title": "Home", "referrer": "", "id": "/" },
                "context": {},
                "globals": {},
                "trigger": "load",
                "source": { "type": "browser", "platform": "web", "url": "https://example.com/", "referrer": "" }
              }]]
            }
          }
        }
      },
      "transformers": {
        "validate": {
          "package": "@walkeros/transformer-validate",
          "config": {
            "settings": { "contract": [{ "type": "object", "properties": { "data": { "required": ["title"] } } }] }
          },
          "examples": {
            "page view": {
              "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 }
              } }]]
            }
          }
        }
      },
      "destinations": {
        "ga4": {
          "package": "@walkeros/web-destination-gtag",
          "config": {
            "settings": { "ga4": { "measurementId": "G-DEMO123456" } }
          },
          "before": "validate",
          "examples": {
            "page view": {
              "in": { "name": "page view", "data": { "title": "Home" } },
              "mapping": {
                "name": "page_view",
                "data": { "map": { "page_title": "data.title" } }
              },
              "out": [["gtag", "event", "page_view", { "page_title": "Home", "send_to": "G-DEMO123456" }]]
            }
          }
        }
      }
    }
  }
}

The ga4 destination runs validate in its before chain, so cross-step validation checks the transformer's out against the destination's in.

Examples are stripped by the bundler at build time. They never appear in your production bundle.

Using examples with the CLI​

Simulate a step with an example's input​

Push an event through one step with its external calls mocked. --event takes the input itself, so pass an example's in value:

# Push the "page view" example's in value through the ga4 destination
walkeros push flow.json --simulate destination.ga4 --event '{"name":"page view","data":{"title":"Home"}}'

The CLI runs the event through the flow with the step mocked and prints what the step did: for a destination, the matched mapping rule and every recorded vendor call. See simulation output. It does not compare anything against the step's out and exits with code 1 only when the push fails with an error, so a mapping that produces the wrong output still exits 0. To assert on out, run the examples in your own tests (see Using examples in tests).

Validate examples​

walkeros validate checks examples statically. It does not run any step:

walkeros validate flow.json

For steps connected through next (source to transformer, transformer to transformer) or a destination's before chain, it checks that at least one out of the upstream step is structurally compatible with an in of the downstream step. Both values must have the same type. Two objects are compatible when they share at least half of the keys of the object with fewer keys; any two arrays are compatible regardless of their content. This is a shape check, not an equality check. An out written as effects is read for the events it passes on: [["elb", event]] and [["return", { "event": ... }]] pass the event, [["return", [...]]] passes each item, and ["return", false], message.ack, response or vendor calls pass nothing.

  • No compatible pair is an error.
  • A connection is only checked when both steps declare examples. If either step has no examples key, nothing is checked and no warning is produced.
  • When both steps declare examples but one side has no usable value, the connection produces a warning. An upstream out counts when it passes on at least one event; empty values such as out: [] are skipped. A downstream in counts unless its example sets command.
  • Other chains, such as a source's or transformer's before, a destination's next or collector.next, and steps without these fields are not checked.
  • Each @walkeros/transformer-validate step's own examples agree with its linked contract and settings (see Validate); examples of other steps are not checked against contracts.

Using examples in tests​

Extract examples from your flow config and use them as test fixtures:

import { readFileSync } from 'fs';
import { startFlow } from '@walkeros/collector';
import { destinationGtag } from '@walkeros/web-destination-gtag';

const flow = JSON.parse(readFileSync('flow.json', 'utf-8'));
const ga4 = flow.flows.default.destinations.ga4;
const examples: Record<string, { in: { name: string }; mapping?: unknown; out: unknown[][] }> = ga4.examples;

it.each(Object.entries(examples))('ga4 destination: %s', async (name, example) => {
  // Record every gtag() call instead of loading the real gtag.js
  const calls: unknown[][] = [];
  const env = { window: { dataLayer: [], gtag: (...args: unknown[]) => calls.push(['gtag', ...args]) } };
  const [entity, action] = example.in.name.split(' ');

  const { elb } = await startFlow({
    destinations: { ga4: { code: destinationGtag, config: { ...ga4.config, mapping: { [entity]: { [action]: example.mapping } } }, env } },
  });
  await elb(example.in);

  // calls[0] is the gtag('config', ...) call the destination makes on init
  expect(calls.slice(1)).toEqual(example.out);
});

This pattern means your flow config is the single source of truth for both documentation and tests.

Package-level vs flow-level examples​

Examples exist at two levels:

LevelWherePurpose
PackagewalkerOS.json in the npm package (via dev.ts)Generic examples showing what the package handles
FlowInline on step references in flow.jsonSpecific examples for your actual data and mappings

Package examples are generic and ship with the npm package. They show the package's capabilities with sample data. The MCP tools package_get and flow_examples read them from the package's walkerOS.json on the CDN.

Flow examples are specific to your setup. They use your real event names, your actual data properties, and your configured mappings. In the MCP flow_examples tool, a step's flow examples take precedence over its package's examples; a step without flow examples falls back to the package's examples.

How packages ship examples

Packages export examples through the dev.ts convention:

// src/dev.ts
export * as schemas from './schemas';
export * as examples from './examples';

These are included in the package's walkerOS.json metadata and discoverable via CDN, following the same { in, out } format used in flow configs.

A package with several exports (for example destinationBigQuery and destinationPubSub in @walkeros/server-destination-gcp) also exports exportExamples, a map from every export name, the default included, to that export's examples. The CLI, MCP and walkerOS.json pick the entry for the step's import:

// src/dev.ts
export const exportExamples = {
  destinationBigQuery: bigqueryExamples,
  destinationPubSub: pubsubExamples,
};

Such a package also exports exportSchemas, the same map for schemas: every export name, the default included, to that export's settings (and mapping or setup where it has them). walkeros validate and MCP package_get use the entry for the step's import. The build fails when either map does not list exactly the exports in package.json walkerOS.exports, or an exportSchemas entry has no settings:

// src/dev.ts
export const exportSchemas = {
  destinationBigQuery: { settings: bigquery.settings, mapping: bigquery.mapping },
  destinationPubSub: { settings: pubsub.settings, mapping: pubsub.mapping, setup: pubsub.setup },
};

In the complete example​

Every step of flow-complete.json carries examples. The web GA4 destination's set:

/flows/web/destinations/ga4/examples
{
  "purchase": { … },
  "purchaseWithoutCoupon": { … },
  "productView": { … },
  "consentUpdate": { … }
}
examples are the contract of a step: this in gives this out, checked by simulation. Guide chapter step-envelope

Next steps​

  • CLI: Bundle and simulate flows with examples
  • Flow: Learn the full flow configuration format
  • Mapping: Transform events between steps
  • Event model: Understand the walkerOS event structure
💡 Need implementation support?
elbwalker offers hands-on support: setup review, measurement planning, destination mapping, and live troubleshooting. Book a 2-hour session (€399)