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

# Collector commands

The collector provides a core event processing engine that manages destinations, consent, user data, and custom properties. Commands are executed through the `elb` function, which the collector also exposes as `collector.elb`.

Unknown walker commands log a warning and return `{ ok: false }`. Commands like `walker init` are browser-specific and handled by the browser source before they reach the collector.

note

For browser-specific commands like DOM initialization and elbLayer communication, see [Browser Source Commands](/preview/pr-749/docs/sources/web/browser/commands.md).

## destination[​](#destination "Direct link to destination")

Add destinations to the collector. The recommended approach is to configure destinations during initialization with `startFlow()`:

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

const { collector, elb } = await startFlow({
  destinations: {
    gtag: {
      code: destinationGtag,
      config: {
        settings: { /* custom config */ },
      },
    },
  },
});
```

tip

For dynamic scenarios requiring runtime destination addition, use `elb('walker destination')` command or `collector.addDestination()`. See destination-specific documentation for configuration options.

## consent[​](#consent "Direct link to consent")

Set consent states on the collector. Names are free-form; common groups are functional, analytics, and marketing. Values are booleans, and `true` means consent is granted. A command only updates the keys it contains.

```javascript
elb('walker consent', { marketing: true, analytics: true });
```

When none of the keys in a destination's `config.consent` is granted, the destination stops receiving events. Its events wait in a queue instead, and once consent is granted during the same run, the queued events are delivered in order.

info

Learn more about [consent management](/preview/pr-749/docs/guides/consent.md) in detail.

## on[​](#on "Direct link to on")

Add event listeners to the collector. They get called when specific events occur like `run` or `consent` changes.

```javascript
elb('walker on', { type, rules });
```

`rules` depends on `type` and can also be an array for multiple listeners at once. Every callback receives `(data, context)` where `context` exposes `collector` and `logger`.

### Callback signatures[​](#callback-signatures "Direct link to Callback signatures")

| Action  | Callback signature                                       |
| ------- | -------------------------------------------------------- |
| consent | `{ [key]: (consent, context) => void \| Promise<void> }` |
| ready   | `(_, context) => void \| Promise<void>`                  |
| run     | `(_, context) => void \| Promise<void>`                  |
| session | `(session, context) => void \| Promise<void>`            |
| user    | `(user, context) => void \| Promise<void>`               |
| (other) | `(data, context) => void \| Promise<void>`               |

The `context` object has the same shape for every action:

| Field       | Type                 | Description                              |
| ----------- | -------------------- | ---------------------------------------- |
| `collector` | `Collector.Instance` | Active collector, use for `push`/`queue` |
| `logger`    | `Logger.Instance`    | Use for `info`/`warn`/`error`/`debug`    |

### run[​](#run "Direct link to run")

With each `run`, the on-event will be called. Use `context.collector` to access the running instance.

```javascript
elb('walker on', {
type: 'run',
rules: function (_, context) {
console.log('run with', { instance: context.collector });
},
});
```

Every time the `run` command is called, the function will be executed:

```javascript
// Setup collector with browser source
const { collector } = await startFlow({ run: true });
// Output: run with { instance: { ... } }
elb('walker run');
// Output: run with { instance: { ... } }
```

### consent[​](#consent-1 "Direct link to consent")

A consent handler is called with `consent` as the first argument and `context` (carrying `collector` and `logger`) as the second. `rules` is an object keyed by consent name.

```javascript
function onConsent(consent, context) {
console.log('consent with', { consent, instance: context.collector });
if (consent.marketing) elb('walker user', readFromStorage());
}

elb('walker on', {
type: 'consent',
rules: { marketing: onConsent },
});
```

`onConsent` runs whenever a `walker consent` command contains `marketing`, whether the value is `true` or `false` and whether it changed. It receives only the values of that command, so check the value inside the handler:

```javascript
elb('walker consent', { functional: true }); // Won't trigger the onConsent function
elb('walker consent', { marketing: true }); // Will trigger the onConsent function
elb('walker consent', { marketing: false }); // Will also trigger the onConsent function
```

When registered, and when the collector starts, the handler receives the full consent state and runs if `marketing` is set. No handler is called before the collector runs.

## user[​](#user "Direct link to user")

Set user identification data for the collector. There are three levels: user (company's internal ID), device (longer-term identifier), and session (temporary identification).

```javascript
elb('walker user', { id: 'us3r', device: 'c00k13', session: 's3ss10n' });
```

User IDs are added to each event.

```javascript
{
"event": "entity action",
"user": {
"id": "us3r",
"device": "c00k13",
"session": "s3ss10n"
}
// other properties omitted
}
```

warning

Use fully anonymized & arbitrary IDs by default and check your options with persistent user IDs with your data protection officer.

tip

Learn more about identification and [user stitching](/preview/pr-749/docs/guides/user-stitching.md)

You can also set user identity declaratively from the DOM with the `data-elbuser` attribute. See the browser source's [HTML attributes](/preview/pr-749/docs/sources/web/browser/tagging/html-attributes.md#user).

## custom[​](#custom "Direct link to custom")

Set custom properties that are added to each event processed by the collector.

```javascript
elb('walker custom', { key: 'value' });
```

## globals[​](#globals "Direct link to globals")

Set global properties that are added to each event processed by the collector.

```javascript
elb('walker globals', { key: 'value' });
```

These values are the base for every event the collector processes, including events from server sources and any source with no DOM to read. A browser source adds what it finds in `data-elbglobals` on top, and those values win per key. Defaults that should survive a `walker run` belong in the collector's `globalsStatic` config instead: it seeds the base at startup, and a run that supplies its own globals resets the base to it. An event carries the globals known when it was created, so a global set after an event is captured does not reach one already waiting, whether it waits for consent, for a destination's `require` gate, or for a destination to be added at all.

A transformer that runs before the collector, in a source's `next` chain, sees the event before enrichment and therefore before the base globals are merged.

## State delivery[​](#state-delivery "Direct link to State delivery")

State commands (`consent`, `user`, `globals`, `custom`) set values on the collector. The collector records every state change immediately, even when it arrives before `run`. Side-effecting delivery to subscribers (the `on` callbacks above, source `on` handlers, and destination `on` handlers) happens at or after `run`: when the collector starts, it delivers the current state once to every subscriber that has not yet seen it.

This gives five practical guarantees:

* **Exactly-once.** Each subscriber is invoked once per state change. The collector tracks what each subscriber has already received, so re-running or re-registering never double-fires a reaction.
* **Order-independent.** A consent-gated reaction fires correctly whether the state was set before or after `run`, and regardless of the order in which sources initialize. You don't have to set state and start the collector in a particular sequence.
* **`require` is a timing hint, not a correctness dependency.** A source's `require` only delays when its first `on` delivery lands. A dependent source reacts to state correctly whether or not it declares `require`.
* **Before the first push.** A destination's `on` handler receives the current state before any event is delivered to it. The collector waits for an async handler to finish, up to the destination's `config.timeout`, before it delivers events. If the handler does not finish in that time, or fails, the destination's events stay in its queue instead of going out without the state, until a later delivery of the state finishes. Those held events are discarded at the next `walker run`. Lifecycle handlers such as `on('run')` are awaited too, bounded by the same timeout; one that does not finish holds no events. Handlers of different destinations run independently, so a slow handler in one destination does not delay events to other destinations.
* **Commands wait for handlers.** An async destination `on` handler must settle: until it settles or reaches `config.timeout`, that destination receives no events. `elb` commands and `startFlow` resolve only after the destinations' `on` handlers for that command have settled or timed out; a command sent while a handler is still busy with an earlier state update resolves at once, and its update is applied right after the running one, while that destination's events wait for it.

This means sources don't need their own deduplication for state deliveries: the collector enforces exactly-once.

### Pre-run events[​](#pre-run-events "Direct link to Pre-run events")

The same record-immediately, deliver-at-run rule applies to events. An event pushed while the collector has not run yet is held in a bounded FIFO buffer instead of being discarded, and replayed once the collector starts:

```javascript
const { collector, elb } = await startFlow({ run: false, destinations: { ... } });

await elb('walker consent', { essential: true });
await elb({ name: 'page view' }); // held, not delivered yet

await elb('walker run'); // 'page view' is delivered now
```

* **Replayed in push order.** Held events reach destinations in the order they were pushed, after the run-barrier state delivery, so each one carries the consent, user, globals and custom state that is current at `run`.
* **The pipeline runs once, at replay.** Mapping, transformer chains and enrichment are applied when the event is replayed, not when it is held.
* **Exactly-once.** A second `run` does not re-deliver events an earlier run already replayed.
* **Bounded.** The buffer holds at most `queueMax` events (default `1000`, shared with the destination replay buffer) and drops the oldest entries on overflow. Each hold and drop is logged at `debug` level.

Commands are unaffected: they continue to apply immediately, as described above.

## hook[​](#hook "Direct link to hook")

Hooks customize the default behavior of the collector. Available hooks include `Push`, `DestinationInit`, `DestinationPush`, `StoreGet`, `StoreSet`, and `StoreDelete`. Hooks allow for validation, manipulation, or cancellation of default behavior.

Add hooks to the collector to customize or enhance default processing.

```javascript
elb('walker hook', { name: '<moment>', fn: hookFn });
```

### Moments[​](#moments "Direct link to Moments")

The overall function execution order is as follows:

1. prePush
2. preDestinationInit
3. postDestinationInit
4. preDestinationPush or preDestinationPushBatch
5. postDestinationPush or postDestinationPushBatch
6. postPush

Others are:

* preCommand / postCommand
* preTransformerInit / postTransformerInit
* preTransformerPush / postTransformerPush
* preStoreGet / postStoreGet
* preStoreSet / postStoreSet
* preStoreDelete / postStoreDelete

### Function signatures[​](#function-signatures "Direct link to Function signatures")

In general, `params` will be prefixed as a parameter, containing `fn` which is the original function and `result` for the post-hooks. A post-hook's return value replaces the result, so return `params.result` to keep it. Use the following function signatures:

```javascript
// Push
function prePush(params, event, options) {
return params.fn(event, options);
}
function postPush(params, event, options) {
console.log('default return result', params.result);
return params.result;
}

// DestinationInit
function preDestinationInit(params, context) {
return params.fn(context);
}
function postDestinationInit(params, context) {
console.log('default return result', params.result);
return params.result;
}

// DestinationPush
function preDestinationPush(params, event, context) {
return params.fn(event, context);
}
function postDestinationPush(params, event, context) {
return params.result;
}

// DestinationPushBatch
function preDestinationPushBatch(params, batch, context) {
return params.fn(batch, context);
}
function postDestinationPushBatch(params, batch, context) {
return params.result;
}

// StoreGet
function preStoreGet(params, key) {
return params.fn(key);
}
function postStoreGet(params, key) {
return params.result;
}

// StoreSet
function preStoreSet(params, key, value, ttl) {
return params.fn(key, value, ttl);
}
function postStoreSet(params, key, value, ttl) {
return params.result;
}

// StoreDelete
function preStoreDelete(params, key) {
return params.fn(key);
}
function postStoreDelete(params, key) {
return params.result;
}
```

### Adding a hook[​](#adding-a-hook "Direct link to Adding a hook")

Add hooks during collector initialization or via the `hook` command:

```javascript
// Add hooks during initialization
const { collector } = await startFlow({
hooks: {
prePush: (params, ...args) => {
window.elbTimer = Date.now();
return params.fn(...args);
},
},
});

// Add hooks via command
elb('walker hook', {
name: 'postPush',
fn: function (params, ...args) {
console.log('walker exec time', Date.now() - window.elbTimer);
return params.result;
},
});

elb({ name: 'page view' });

// Output:
// walker exec time 1
```
