> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Using Step Examples

## Overview[​](#overview "Direct link to Overview")

Step examples are structured `{ in, out }` pairs (with optional `mapping` for destinations) that define the expected input/output behavior of each step in a flow. They serve as:

* **Test fixtures** for automated `it.each` testing
* **Simulation input** for `walkeros push --simulate` (pass an example's `in` as `--event`; the CLI does not compare the result against `out`)
* **MCP context** for AI-assisted development
* **Documentation** showing real-world usage

Every source, transformer, and destination can ship step examples alongside its code.

## The Three Type Zones[​](#the-three-type-zones "Direct link to The Three Type Zones")

Each step in a flow sits at a boundary between arbitrary external formats and the walkerOS event model:

```
           Source              Transformer           Destination

  ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐

  │  in: arbitrary   │   │  in: walkerOS   │   │  in: walkerOS   │

  │  (HTTP req, DOM) │   │      Event      │   │      Event      │

  │        │         │   │        │         │   │        │         │

  │        ▼         │   │        ▼         │   │        ▼         │

  │  out: walkerOS   │   │  out: walkerOS  │   │  out: arbitrary  │

  │      Event       │   │   Event | false │   │  (gtag, API)     │

  └─────────────────┘   └─────────────────┘   └─────────────────┘



  arbitrary ──────────── walkerOS.Event ──────────── arbitrary
```

* **Source** converts platform-specific input into a walkerOS event
* **Transformer** receives and returns walkerOS events (or `false` to filter)
* **Destination** converts a walkerOS event into vendor-specific output

## The `in`/`out`/`trigger` Format[​](#the-inouttrigger-format "Direct link to the-inouttrigger-format")

Every step example is an object with `in` (input), `out` (array of observable effects), and optional `trigger` (how to invoke):

```
type StepEffect = readonly [callable: string, ...args: unknown[]];

type StepOut = readonly StepEffect[];



interface StepExample {

  in: unknown; // Platform-specific input

  out: StepOut; // Array of effect tuples - see shape rules below

  trigger?: { type?: string; options?: unknown }; // How to invoke

  mapping?: unknown; // Destination mapping rule

  command?: 'config' | 'consent' | 'user' | 'run'; // Route to walker command

}
```

`out` is **always an array of effect tuples**, even for a single effect. Each tuple is `[callable, ...args]`. The first element is the callable's public name (the SDK function users would write). Remaining elements are the arguments.

### Callable conventions[​](#callable-conventions "Direct link to Callable conventions")

| Component type                 | Callable                                | Notes                                                                                   |
| ------------------------------ | --------------------------------------- | --------------------------------------------------------------------------------------- |
| Destination (SDK function)     | `'gtag'`, `'fbq'`, `'ttq.track'`        | Public SDK name. Dotted paths render literally.                                         |
| Destination (method on global) | `'analytics.track'`, `'dataLayer.push'` | Method notation.                                                                        |
| Destination (HTTP)             | `'fetch'`, `'sendServer'`               | The actual call users would make. NOT `env.*` prop names - those are internal plumbing. |
| Source                         | `'elb'`                                 | The walker public push API.                                                             |
| Transformer                    | `'return'`                              | Reserved keyword. Renders as `return <value>` (no parens).                              |

`'return'` is reserved. Don't use it for anything else.

Empty `out: []` means the step produced no observable effect (filtered input, transformer passthrough, validator rejection). Reserved only for cases where the destination/source deliberately emits nothing.

### Source Step Example - Server (Express)[​](#source-step-example---server-express "Direct link to Source Step Example - Server (Express)")

`in` is an HTTP request shape, `out` is a tuple of the `elb()` call the source makes:

```
export const checkoutPost: Flow.StepExample = {

  trigger: { type: 'POST' },

  in: {

    method: 'POST',

    path: '/collect',

    body: { name: 'order complete', data: { id: 'ORD-123', total: 149.97 } },

  },

  out: [

    ['elb', { name: 'order complete', data: { id: 'ORD-123', total: 149.97 } }],

  ],

};
```

### Source Step Example - Commands (CMP, session)[​](#source-step-example---commands-cmp-session "Direct link to Source Step Example - Commands (CMP, session)")

A source that issues walker commands records them in its own call shape: the first argument verbatim, callable `'elb'`. Commands come first, in call order, then the events. A CMP source:

```
out: [['elb', 'walker consent', { functional: true, marketing: true }]],
```

The session source (`env.command('user' | 'session', ...)`, then its event):

```
out: [

  ['elb', 'user', { session: 's3ss10n-id', device: 'd3v1c3-id' }],

  ['elb', 'session', { id: 's3ss10n-id', isStart: true /* ... */ }],

  ['elb', { name: 'session start', data: { id: 's3ss10n-id' /* ... */ } }],

],
```

A source simulation (`walkeros push --simulate source.X`, MCP `flow_simulate`) returns exactly these calls: `call elb("walker consent",{...})`. The collector's own commands (starting consent from `--consent`, globals, custom, shutdown) and the wiring commands `on`, `hook`, `destination` are never part of a source's `out`. A consent-gated source needs `--consent` (MCP `state.consent`) to emit anything.

### Source Step Example - Browser[​](#source-step-example---browser "Direct link to Source Step Example - Browser")

`in` is an HTML string, `out` is a tuple of the `elb()` call:

```
export const clickEvent: Flow.StepExample = {

  trigger: { type: 'click', options: 'button' },

  in: '<button data-elb="cta" data-elb-cta="label:Sign Up" data-elbaction="click:click">Sign Up</button>',

  out: [

    [

      'elb',

      {

        name: 'cta click',

        data: { label: 'Sign Up' },

        trigger: 'click',

        entity: 'cta',

        action: 'click',

      },

    ],

  ],

};
```

### Transformer Step Example[​](#transformer-step-example "Direct link to Transformer Step Example")

`in` is a walkerOS event, `out` is a `['return', value]` tuple. An empty array means the transformer passed the event through unchanged:

```
export const step = {

  orderPasses: {

    in: { name: 'order complete', data: { id: 'ORD-123' } },

    out: [['return', { name: 'order complete', data: { id: 'ORD-123' } }]],

  },

  debugFiltered: {

    in: { name: 'debug test', data: { message: 'noise' } },

    out: [['return', false]], // Transformer rejects

  },

  passthrough: {

    in: { name: 'page view' },

    out: [], // No modification

  },

};
```

### Destination Step Example[​](#destination-step-example "Direct link to Destination Step Example")

`in` is a walkerOS event, `mapping` is the mapping rule that transforms it, `out` is an array of call tuples - one per observable effect the destination produces. Multi-call events (e.g., GA4 + Ads + GTM for a single walker event) flatten into a single array in execution order:

```
import type { Flow } from '@walkeros/core';

import { getEvent } from '@walkeros/core';



export const purchase: Flow.StepExample = {

  in: getEvent('order complete', { timestamp: 1700000000 }),

  mapping: {

    name: 'Purchase',

    data: { map: { value: 'data.total', currency: { value: 'EUR' } } },

  },

  out: [

    [

      'fbq',

      'track',

      'Purchase',

      { value: 555, currency: 'EUR' },

      { eventID: '1700000000-gr0up-1' },

    ],

  ],

};
```

Multi-tool example (one walker event produces GA4, Ads, GTM in order):

```
out: [

  ['gtag', 'event', 'purchase', { transaction_id: 'o1', value: 555 }],

  ['gtag', 'event', 'conversion', { send_to: 'AW-123', value: 555 }],

  ['dataLayer.push', { event: 'purchase', ecommerce: { ... } }],

],
```

Each export is a self-contained `Flow.StepExample` - no intermediate variables, no `all` aggregation. The `mapping` field ties the mapping rule to the example so tests can register it dynamically: `{ [event.entity]: { [event.action]: example.mapping } }`.

Consumers iterate all examples via `Object.entries(examples.step)` - `export * as step` exposes every named export directly.

### Command Step Example[​](#command-step-example "Direct link to Command Step Example")

Some destinations need to respond to walker commands (`walker consent`, `walker user`, `walker run`), not events. Set the `command` field to route `in` through `elb('walker <command>', in)` instead of pushing it as an event:

```
export const consentGranted: Flow.StepExample = {

  command: 'consent',

  in: { marketing: true, functional: true },

  out: [

    'consent',

    'update',

    { ad_storage: 'granted', analytics_storage: 'granted' },

  ],

};
```

Supported commands: `config`, `consent`, `user`, `run`.

* When `command` is absent (default), `in` is pushed as a regular event via `elb(in)`.

* When `command` is set, `mapping` is **not** applied - commands don't flow through event mapping.

* The `out` format is destination-specific. For gtag it's `[action, subAction, params]` matching `gtag(...)` calls.

* Simulate a command example with `--command <name>` (MCP: `command`): the destination simulation runs `collector.command(name, in)` instead of a push and records the calls it makes. Add `--consent` when the destination needs starting consent; it applies first, then the command:

  ```
  walkeros push flow.json -e '{"functional":true,"marketing":true}' \

    --simulate destination.ga4 --command consent
  ```

## Writing Examples[​](#writing-examples "Direct link to Writing Examples")

### Best Practices[​](#best-practices "Direct link to Best Practices")

1. **Use camelCase names** -- descriptive of the scenario (`checkoutPost`, `debugFiltered`, `pageViewBasic`)
2. **Cover happy path and edge cases** -- include at least one positive and one negative case for transformers
3. **Use realistic data** -- real-looking event names, IDs, and values
4. **Keep examples minimal** -- only include fields relevant to the step's logic; omit empty/default-value fields (`context: {}`, `nested: []`, `user: {}`, etc.)
5. **Export from `examples/step.ts`** -- follow the existing `dev.ts` structure
6. **Title and describe public examples** -- add `title` (2-5 words) and `description` (one short sentence, 10-25 words) for every example that should appear in docs. Mark test-only fixtures with `public: false`.

### File Structure[​](#file-structure "Direct link to File Structure")

```
// src/examples/step.ts - only Flow.StepExample exports, nothing else

import type { Flow } from '@walkeros/core';

import { getEvent } from '@walkeros/core';



export const purchase: Flow.StepExample = {

  in: getEvent('order complete', { timestamp: 1700000000 }),

  mapping: { name: 'Purchase', data: { map: { value: 'data.total' } } },

  out: ['track', 'Purchase', { value: 555 }, { eventID: '1700000000-gr0up-1' }],

};



// src/examples/index.ts

export * as env from './env';

export * as step from './step';
```

The file exports **only** `Flow.StepExample` objects. No intermediate variables, no `all`, no `config`. Consumers iterate via `Object.entries(examples.step)`.

### Rule: examples live only on the `./dev` subpath[​](#rule-examples-live-only-on-the-dev-subpath "Direct link to rule-examples-live-only-on-the-dev-subpath")

Examples (and schemas) must be exported **only** from `src/dev.ts` (the `./dev` package subpath), never from a production entry (`src/index.ts` / the package main). Production bundles tree-shake on the import path: if `examples` is reachable from the main entry, the bundler inlines every example (and its `getEvent` fixtures) into the shipped JS, bloating consumer bundles with test-only data. Importing them from `./dev` keeps them available for simulation and testing while leaving production output clean.

A `no-restricted-syntax` ESLint rule enforces this, so a `export ... examples` from a production entry fails lint.

### Multi-export packages: `exportExamples` and `exportSchemas`[​](#multi-export-packages-exportexamples-and-exportschemas "Direct link to multi-export-packages-exportexamples-and-exportschemas")

A package whose `package.json` `walkerOS.exports` lists more than one export adds a map of EVERY export, the default included, to `src/dev.ts`:

```
export const exportExamples = {

  destinationBigQuery: bigqueryExamples,

  destinationPubSub: pubsubExamples,

};
```

The step's export name comes from `resolveExportName` (exported from `@walkeros/cli`): the step's `import`, else `bundle.packages[pkg].imports[0]`, else the default. When the module has the map, `map[exportName]` is used and a missing name resolves to nothing (never a fallback to `examples`); without a map, `examples` is used. `selectDevExamples(devModule, exportName)` implements this. The CLI simulate, MCP `flow_examples` and `package_get`, core `cdn.ts` and the tsup `walkerOS.json` (`exportExamples` key) all follow it. Existing named exports (`pubsubExamples`, `sqsExamples`, ...) stay for back compat.

The same package also exports `exportSchemas`, every export to that export's schemas in the shape of a single-export `schemas` (`settings` required, `mapping`, `setup` where they exist). Reuse the very objects behind `schemas.settings` for the default entry, so a `toBe` test pins it:

```
export const exportSchemas = {

  destinationBigQuery: {

    settings: bigquerySchemas.settings,

    mapping: bigquerySchemas.mapping,

  },

  destinationPubSub: {

    settings: pubsubSchemas.settings,

    mapping: pubsubSchemas.mapping,

    setup: pubsubSchemas.setup,

  },

};
```

`walkeros validate` and MCP `package_get` pick the entry for the step's export with the same rule. The tsup build (`buildDev`) fails when either map does not list exactly the `walkerOS.exports` names, when an `exportSchemas` entry has no `settings`, and when any source, transformer, destination or store package publishes no `schemas.settings` (a package without settings publishes `z.object({})`). Prefixed keys in `schemas` (`pubsubSettings`, ...) stay.

### Mock env and `simulation` paths[​](#mock-env-and-simulation-paths "Direct link to mock-env-and-simulation-paths")

`src/examples/env.ts` exports `push` (the mock env simulate injects) and `simulation` (the call paths to record). Simulate refuses a package destination whose export has no `push` mock ("No mock env for ..."), so it never calls the real vendor.

* A `simulation` path names the call that CARRIES the vendor request (`sendServer`, `fetch`, `call:PubSub.topic.publishMessage`, `call:JSONWriter.appendRows`, `call:AWS.SNSClient.send`, `call:fs.createWriteStream.write`), never only a constructor.

* **`init` is not the request.** Do not record an `init` call (it carries the API key and sends nothing); a path may pass THROUGH init's result (`call:Mixpanel.init.track`). Amplitude and Mixpanel record their tracking calls, not `init`.

* Grammar (`parseCallPath`, shared by the recorder): an optional `call:` prefix, then `.`-separated segments; an empty segment makes the path unresolvable. The one recorder is `observeEnv(env, paths)` in `@walkeros/core` (collector `wrapEnv` and web-core `getEnv` delegate to it). It returns a Proxy view; nothing on the env objects or prototypes is mutated.

* Mid-path resolution: on an object, a segment is looked up as a property. After a function, a segment is the function's own property or a parent class's static; anything else (including `call`, `bind`, `name`, `length`) navigates the call result (or the constructed instance, for `new`). Frozen objects and frozen call results are recorded through a view; only a static on a frozen function stays unresolved. A mid-path Promise (async factory) is followed with `.then`. Only the leaf call is recorded: `fn` is the path without `call:`, `args` the leaf call's arguments. `instanceof`, statics and `#private` fields keep working, and a chain returning `this` records once.

* Pin every list in the package tests with the async `expectSimulationResolves(examples.env)` from `@walkeros/core/dev`: it walks each path through the `push` mock (constructing, calling, awaiting) and fails on a missing segment or a non-function leaf.

  ```
  import { expectSimulationResolves } from '@walkeros/core/dev';

  it('declares simulation paths that resolve', () =>

    expectSimulationResolves(examples.env));
  ```

* Not recordable: anonymous values such as SQLite prepared-statement rows.

* Sources: a source package that declares `simulation` runs on its mock client in a source simulation, and those calls are recorded. SQS records `call:AWS.SQSClient.send` (one receive per 100 ms while running); Pub/Sub pull records `call:PubSub.subscription` (messages arrive as events). Their module-mock tests import `moduleMockEnv`, the same env without the client.

## Metadata: title, description, public[​](#metadata-title-description-public "Direct link to Metadata: title, description, public")

Every `Flow.StepExample` accepts three optional metadata fields that control how it surfaces in docs and MCP output:

* **`title?: string`** - overrides the default camelCase-to-spaced heading in website docs. Keep it short (2-5 words), human-readable.
* **`description?: string`** - one short sentence (10-25 words) rendered above each example in docs and surfaced in MCP `flow_examples` output. Explains what the example demonstrates, not how.
* **`public?: boolean`** - defaults to `true`. When `false`, the example is excluded from the website docs render and from default MCP `flow_examples` output. It still runs in tests and remains callable via CLI/MCP `flow_simulate`.

```
export const purchase: Flow.StepExample = {

  title: 'Purchase',

  description:

    'A completed order mapped to the Meta Pixel Purchase standard event.',

  in: getEvent('order complete', { timestamp: 1700000000 }),

  mapping: { name: 'Purchase', data: { map: { value: 'data.total' } } },

  out: [['fbq', 'track', 'Purchase', { value: 555, currency: 'EUR' }]],

};



// Internal fixture - runs in tests, hidden from docs and default MCP output.

export const debugFiltered: Flow.StepExample = {

  public: false,

  in: { name: 'debug test', data: { message: 'noise' } },

  out: [['return', false]],

};
```

### When to mark `public: false`[​](#when-to-mark-public-false "Direct link to when-to-mark-public-false")

**Mark `public: false` when:**

* The example is a transformer rejection (`out: [['return', false]]`) that only proves filtering works.
* It's an error-path or malformed-input case.
* It's a redundant variant kept only for regression coverage.
* It's an internal fixture used by cross-package tests.

**Keep public (omit the field) when:**

* It's the happy path.
* It teaches a distinct mapping or usage pattern.
* It's the only example for some entity/action the package handles.

## Source Trigger Metadata[​](#source-trigger-metadata "Direct link to Source Trigger Metadata")

Source step examples can include a `trigger` field for simulation:

```
{

  in: '<button data-elb="cta">Sign Up</button>',

  trigger: { type: 'click' },

  out: { name: 'cta click', data: { label: 'Sign Up' } }

}
```

When simulating via CLI or MCP, the step example maps to `SourceInput`:

* `in` -> `content` (the actual source input)
* `trigger` -> `trigger` (which mechanism to fire)

Destination and transformer examples don't use `trigger`.

## Simulating with Step Examples[​](#simulating-with-step-examples "Direct link to Simulating with Step Examples")

Use `--simulate <kind>.<name>` to target a specific step, then provide the input with `--event`. For a source step, that is `SourceInput` (`{ content, trigger? }`):

```
# Simulate a source step with trigger metadata

walkeros push flow.json --simulate source.browser --event '{"content":"<html>...","trigger":{"type":"click"}}'
```

The CLI does not compare the result against `out`. For each simulated step it prints the matched mapping key and every recorded call (`--json`: under `simulations`), and exits 1 only when the push fails with an error; a mapping that produces the wrong output still exits 0. Assert on `out` in your own tests (see Testing with Examples below). Add `--ingest` to give a transformer, collector or destination step a request context, `--consent` for the collector consent it starts from, `--command` for a command example, and `--page-url` for the page of a web source (default: the trigger's `options.url`, else `http://localhost`). See [using-cli](/preview/pr-749/skills/walkeros-using-cli.md).

The MCP `flow_examples` tool returns `trigger` metadata alongside `in`/`out`, and `mapping` for destination examples, giving full visibility into how input events are transformed to vendor-specific output.

## Validating Examples[​](#validating-examples "Direct link to Validating Examples")

Cross-step example validation is included automatically when validating a flow. It does not run any step:

```
# Validate flow config including step example compatibility

walkeros validate flow.json
```

For steps connected through `next` or a destination's `before` chain, flow validation checks that:

* At least one upstream `out` is structurally compatible with a downstream `in`: same type; two objects share at least half of the keys of the object with fewer keys; any two arrays are compatible regardless of content. No compatible pair is an error. This is a shape check, not an equality check.
* A connection is only checked when both steps declare `examples`; a step without an `examples` key produces no warning.
* When both steps declare `examples` but one side has no usable value, the connection produces a warning. An upstream `out` counts when it is a non-empty array, string, or object (such as a walkerOS event); `out: false` and empty values are skipped. A downstream `in` counts unless its example sets `command`.
* A StepOut `out` (every entry an array with a string head) contributes the events it passes on: `elb` gives `args[0]` when it is an object; `return` gives `a.event` for `{ event }`, `a` for a bare event, each item for an array (fan-out); `false`, `message.ack`, `message.nack`, `response`, `respond` and vendor calls give nothing. A StepOut with no events counts as no usable `out`.
* Contracts bind only where a `@walkeros/transformer-validate` step links them, as at runtime. Each validate step's own examples run through that step's resolved `settings.contract`, `mode`, `format` and `output`, and the verdict must agree with its `out` (valid: passed on; invalid: `["return", false]` in `strict`, passed on with the `isValid` flag false in `pass`). A disagreement is `CONTRACT_VIOLATION` (warning, error with `--strict`). No other example is checked against the config-level `contract` block, and a flow without a validate step gets no contract checks.

## Testing with Examples[​](#testing-with-examples "Direct link to Testing with Examples")

The primary use of step examples is automated testing with `it.each`.

### Source Tests (createTrigger)[​](#source-tests-createtrigger "Direct link to Source Tests (createTrigger)")

Sources export `createTrigger` from their examples. It follows the unified `Trigger.CreateFn` interface: receives `Collector.InitConfig`, lazily starts the flow, and returns a trigger function that simulates real-world invocations.

Each package implements createTrigger differently:

* **Browser:** Injects HTML into DOM, starts flow, dispatches native events
* **Express:** Boots real HTTP server, sends `fetch()` requests

Use a spy destination to capture events:

```
import type { Destination, WalkerOS } from '@walkeros/core';

import { sourceExpress } from '@walkeros/server-source-express';

import { examples } from '@walkeros/server-source-express/dev';



describe('Step Examples', () => {

  it.each(Object.entries(examples.step))('%s', async (name, example) => {

    const events: WalkerOS.Event[] = [];

    const spy: Destination.Instance = {

      type: 'spy',

      config: { init: true },

      push: jest.fn((event) => {

        events.push(JSON.parse(JSON.stringify(event)));

      }),

    };



    const instance = await examples.createTrigger({

      consent: { functional: true },

      sources: {

        express: { code: sourceExpress, config: { settings: { port: 0 } } },

      },

      destinations: { spy: { code: spy } },

    });



    await instance.trigger(example.trigger?.type)(example.in);



    const found = events.find((e) => e.name === example.out.name);

    expect(found).toBeDefined();

  });

});
```

**Browser source note:** The browser source's elbLayer processes events via a detached promise chain (fire-and-forget). For interactive triggers (click, submit), poll until events arrive:

```
while (!events.length) await Promise.resolve();
```

### CMP Source Tests (Consent Assertion)[​](#cmp-source-tests-consent-assertion "Direct link to CMP Source Tests (Consent Assertion)")

CMP sources push `walker consent` commands, not regular events. Assert on collector consent state instead of using a spy destination:

```
const instance = await examples.createTrigger({

  consent: {},

  sources: {

    usercentrics: {

      code: sourceUsercentrics,

      config: { settings: {} },

    },

  },

});



await instance.trigger(

  example.trigger?.type,

  example.trigger?.options,

)(example.in);



// Yield for detached elb('walker consent') chain

while (!Object.keys(instance.flow!.collector.config.consent || {}).length)

  await Promise.resolve();



expect(instance.flow!.collector.config.consent).toEqual(

  expect.objectContaining(expected),

);
```

### Server Function Handler Tests (collector.sources)[​](#server-function-handler-tests-collectorsources "Direct link to Server Function Handler Tests (collector.sources)")

Server function handlers (fetch, AWS Lambda, GCP CloudFunction) don't own servers. Their `createTrigger` accesses the source instance from `collector.sources` after `startFlow` and calls `source.push()` with platform-native types:

```
function findSource(collector) {

  for (const source of Object.values(collector.sources || {})) {

    if (source.type === 'fetch') return source;

  }

}



// In trigger:

const source = findSource(flow.collector);

const response = await source.push(request);
```

### Destination Functional Tests[​](#destination-functional-tests "Direct link to Destination Functional Tests")

For destinations, use `startFlow` + `elb()` to run events through the real collector pipeline. This verifies the full flow including mapping:

```
import { startFlow } from '@walkeros/collector';

import { clone } from '@walkeros/core';

import { examples } from './dev';



describe('Step Examples', () => {

  it.each(Object.entries(examples.step))('%s', async (name, example) => {

    const event = example.in as WalkerOS.Event;

    const mapping = example.mapping as Rule | undefined;



    const mockFn = jest.fn();

    const env = clone(examples.env.push);

    env.window.fbq = mockFn;



    const dest = jest.requireActual('.').default;

    const { elb } = await startFlow({});



    // Build mapping config from event entity/action

    const mappingConfig = mapping

      ? { [event.entity]: { [event.action]: mapping } }

      : undefined;



    elb('walker destination', {

      code: { ...dest, env },

      config: {

        settings: { pixelId: '1234567890' },

        mapping: mappingConfig,

      },

    });



    await elb(event);

    expect(mockFn).toHaveBeenLastCalledWith(...(example.out as unknown[]));

  });

});
```

### Transformer Tests[​](#transformer-tests "Direct link to Transformer Tests")

For transformers that can return `false`:

```
import { examples } from '../dev';



describe('transformer', () => {

  it.each(Object.entries(examples.step))(

    '%s',

    async (name, { in: input, out: expected }) => {

      const result = await transformer.push(input, context);

      if (expected === false) {

        expect(result).toBe(false);

      } else {

        expect(result).toEqual(expected);

      }

    },

  );

});
```

## Checklist[​](#checklist "Direct link to Checklist")

When adding step examples to a package or flow:

* [ ] Create `src/examples/step.ts` with `Flow.StepExample` exports
* [ ] Export from `src/examples/index.ts`
* [ ] Use camelCase, descriptive example names
* [ ] Include at least one happy-path example
* [ ] For transformers, include a `false` (filtered) case
* [ ] Add `it.each` test using step examples
* [ ] Verify examples compile: `npm run build`
* [ ] Run tests: `npm run verify:touched -- <pkg>` (L1)
* [ ] Add `title` and `description` to every public example.
* [ ] Mark internal/test-only examples with `public: false`.
* [ ] `examples/env.ts` exports `push` and a `simulation` list naming the request call, pinned with `expectSimulationResolves(examples.env)`.
* [ ] Multi-export package: `exportExamples` and `exportSchemas` in `dev.ts` list every export (the build fails otherwise).
* [ ] In a flow file, add examples sparingly: they are mainly internal and for demos (for example a client flow's source with `public: false` examples showing its default behaviour).

## Related Skills[​](#related-skills "Direct link to Related Skills")

* [testing-strategy](/preview/pr-749/skills/walkeros-testing-strategy.md) - Testing patterns and env mocking
* [understanding-flow](/preview/pr-749/skills/walkeros-understanding-flow.md) - Architecture and data flow
* [using-cli](/preview/pr-749/skills/walkeros-using-cli.md) - CLI simulate and validate commands
* [create-destination](/preview/pr-749/skills/walkeros-create-destination.md) - Destination creation workflow
* [create-source](/preview/pr-749/skills/walkeros-create-source.md) - Source creation workflow
* [create-transformer](/preview/pr-749/skills/walkeros-create-transformer.md) - Transformer creation workflow
