Skip to content

Releases: elbwalker/walkerOS

walkerOS v4.2.1

18 Jun 13:26
5ffd9d1

Choose a tag to compare

Changes

The BigQuery destination now applies config.credentials to the Storage Write
client that performs event writes, not just the query client. Event writes from
the configured service account now succeed on non-Google Cloud runtimes instead
of failing with a credentials error. Both clients resolve credentials the same
way, so a destination always authenticates as a single identity.

walkeros deploy now waits long enough to cover a full server deploy by
default, so a slow but healthy deploy is no longer aborted early and reported as
a failure. Each run sends a fresh idempotency key, so retrying after a failure
starts a new deploy instead of replaying the previous result. Failures print a
stable, machine-readable error code (with a Retry-After hint on rate limits),
and deploy create no longer prints an empty token placeholder.

The managed flow runner now retries its bundle, config, and secret fetches on
transient failures (timeouts, network errors, 5xx) with bounded, jittered
backoff capped well inside the container health window, and the secret fetch is
now bounded by a timeout. A brief outage while a flow container starts no longer
hard-fails the run.

The managed flow runner now reports its recent errors and recent log output in
its heartbeat, so deployed flows can surface runtime errors and logs in the app
without any external log tooling. Secrets are redacted before leaving the
runner.

walkeros run reads two new environment variables. WALKEROS_OBSERVE_LEVEL
sets the runtime's baseline telemetry level (off, standard, or trace).
WALKEROS_CONFIG_FROZEN (1 or true) serves the bundle as an immutable
snapshot: secrets are still injected at boot, but config hot-swap and heartbeat
are disabled.

All four simulate functions (simulateSource, simulateTransformer,
simulateCollector, simulateDestination) accept a new data option to run an
existing bundle with updated configuration values, without rebundling. The new
buildDataPayload, classifyStepProperties, and containsCodeMarkers exports
build and inspect that payload. Destination simulation results now include
mappingKey, the entity-action key of the matched mapping rule.

Enforce consent gating on destination initialization. A destination that
declares a consent requirement is never initialized while that consent is
denied, including the path that flushes queued on (consent) signals.
Initialization is now fail-closed: it requires an affirmative consent decision
from the caller, so a destination cannot load or send under denied consent.

Trace-level telemetry now carries the inbound event on every pipeline hop, so
per-step observers can show what each collector, transformer, and destination
actually received. The destination's outbound frame now reports the delivered
event as its payload, and the raw delivery response moves to meta.response.

Simulation step results gain an optional mappingKey field reporting the
entity-action key of the mapping rule a destination matched during simulation.
The field is additive and present only when a rule matched.

getId now draws from the platform's cryptographic random source
(crypto.getRandomValues) when available, with unbiased character sampling and
a Math.random fallback. Session and device ids generated by the session source
are now longer for a much wider collision margin.

The deploy_manage tool now matches its real behavior: deploy honors wait,
delete removes an active deployment, and list accepts cursor and limit
for pagination. A failed deployment surfaces its error reason so an assistant
can report why a deploy did not succeed.

The collector now stamps a run-scoped trace id (event.source.trace) and a
per-run sequence number (event.source.count) onto every event, minted fresh on
each run. These group all events of a page load or run and are preserved
unchanged when events are forwarded from web to server, giving a stable
correlation id across the pipeline. Adds getTraceId, and getSpanId now uses
the cryptographic random source.

Fix session start being dropped when the collector starts with run: false
and no consent requirement. Without a consent rule the source emitted during
init, before the collector was allowed, so the event never reached destinations.
The emit now waits for the run lifecycle, matching the browser source's page
view timing, so it lands reliably once the collector runs.

Add an optional async option to the source config (Source.Config.async) for
respond-first acknowledgement on response-producing server sources. The express
source now reads config.async (default true): a 2xx response means the event
was accepted, not yet delivered.

The runner registers its process-error guards before startup and degrades its
readiness check after repeated out-of-band errors, so a wedged container is
recycled instead of silently hot-looping. Heartbeats now flush immediately on a
new error and on shutdown, persist errors to disk so a failure cause survives a
restart, and report their configured interval.

Add a per-destination circuit breaker that skips a destination after consecutive
transport failures and probes once after a cooldown, so a persistently failing
destination stops retrying on every event. Out-of-band reportError calls are
routed to the dead-letter queue (when an event is in hand) or counted as
connection errors and surfaced in status.

Add an optional reportError callback to the step context so any source,
transformer, store, or destination can report an out-of-band error (for example
from an SDK's event emitter) into the pipeline's failure handling. Add an
optional per-destination breaker config to skip a destination after repeated
transport failures.

Capture BigQuery Storage Write stream errors so a broken writer routes events to
the dead-letter queue instead of crashing the process, and re-open a broken
writer automatically on the next event.

Use the official Usercentrics events (UC_UI_INITIALIZED, UC_UI_CMP_EVENT) and
consent getters so a returning visitor's prior choice is applied on load and
first-visit defaults stay suppressed under explicitOnly. The configurable
eventName data-layer setting is removed; the source now uses the always-emitted
official events. Fixes consent-change events being dropped on the current
Usercentrics Web CMP.

Published Packages

walkerOS v4.2.0

09 Jun 22:32
7aa67ca

Choose a tag to compare

Changes

The bundled API contract (spec.json and the generated api.gen.d.ts) now
covers the full served surface, adding typed paths for service accounts,
invitations, billing, deployments and their sub-resources, custom domains,
entitlements, LLM settings, chat sessions, MCP tokens, runners, and the package
catalog. No runtime behavior change; clients gain accurate types for these
endpoints.

Batched destination delivery now reports failures. A batch push that fails
(including BigQuery row errors) is routed to the dead-letter buffer and counted
as failed instead of being silently dropped, and graceful shutdown waits for
in-flight batches to finish. Also fixes a shutdown timer that could delay
process exit, and makes a zero millisecond batch wait (batch: 0) correctly
enable batching.

The BigQuery destination now creates the timing column as FLOAT64 instead of
INT64. Event timing carries sub-second decimal precision, which was previously
truncated to whole numbers on write. Existing tables keep their column type;
alter it to FLOAT64 to preserve precision going forward.

The single-instance guard is now scoped to the window instead of the module, so
loading the tag more than once on the same page is inert rather than
re-initializing. A second load no longer re-binds DOM triggers, re-adopts the
event layer, or surfaces an error to the host page.

walkeros run now accepts a .tar.gz/.tgz flow archive (URL or local file):
it extracts the bundle and its node_modules/ and runs it, so server flows with
external step packages resolve them at runtime. walkeros bundle -o flow.tar.gz
packs a server bundle directory into that archive. Web single-file bundles do
not support archive output.

Browser flow bundles are now emitted as an IIFE so all internal code stays
inside a private scope. Previously the bundled helper functions could leak onto
the global window object and collide with other scripts on the page, such as
Google Analytics or a consent manager. Server bundles are unchanged and still
emit ESM.

CodeBox now memoizes its toolbar and tabs so a validation-marker update repaints
only the error and warning badges instead of re-rendering the whole editor. This
removes the visible editor flicker when content or markers change rapidly.

Lifecycle dispatch now fails closed when called with a non-collector argument.
If an internal function is ever reached by foreign code (for example a global
name collision), it returns quietly instead of throwing, so it can no longer
break the surrounding page.

Promote chain, route shape, and reference scanner helpers to the public surface
so app and tooling can resolve transformer chains, probe route shapes, and
discover $flow. references without reaching into internal modules:

  • collector: re-export walkChain (resolve a transformer chain start into the
    ordered step IDs) and extractTransformerNextMap (read static next-links from
    a Transformer.Transformers map).
  • core: re-export isRouteArray and isRouteConfigEntry (the canonical shape
    probes for Transformer.Route) and add scanFlowRefs(value, into?), which
    walks any value (string, object, array) and returns every $flow.<name>
    reference found, including refs nested inside $code: snippets.

Consent and state-gated source reactions, such as the session source's session
start, now fire reliably regardless of source init order or whether state
arrives before or after run. The collector delivers each state change exactly
once per subscriber, so sources no longer need to re-fire on repeat collector
notifications of the same state.

Destinations now batch every event when you set config.batch, with no '* *'
wildcard mapping rule needed. A bare number sets the debounce wait;
{ wait, size, age } tunes the window. Rule-level batch still overrides per
event type, and pending batches now flush on shutdown.
Migration: if you previously set config.batch alongside a single non-wildcard
rule batch, config.batch only capped that rule before; it now batches all of
the destination's events. To batch only specific events, drop config.batch and
set batch on those rules.

Add an optional, strictly-typed config.credentials field to destinations,
stores, and sources. Service-account credentials now configure under
config.credentials, validated per package and resolved from $env. The
package-specific settings.credentials still works but is deprecated, so move
credentials to config.credentials. The raw settings.<sdk> passthrough (e.g.
settings.bigquery) is unchanged.

Source-level mapping examples for the dataLayer source now key on the prefix as
entity and the gtag action as action: mapping.<prefix>.<action>. The shipped
examples/mapping.ts, the comprehensive flow-complete.json example, and the
related docs reflect the convention, including the special-cased actions
consent, config, and set whose trailing token is dropped by the
entity/action split.

Refresh runtime dependencies to their latest majors: Express 5, Commander 15,
better-sqlite3 12, @libsql/client 0.17, Google Cloud functions-framework 5,
mixpanel 0.22, and jsdom 29. No public API changes; installs now pull the
current versions of these SDKs.

Internal type-safety cleanup: removed unsafe casts around browser globals and
env mocks by typing each destination's Env and reading globals through
getEnv<Env>(env). No behavior change.

The built bundle now preserves its leading "use client" directive, so Next.js
treats the package as a client boundary. The minifier could previously strip it,
which broke server components that import the package at build time.

Fix $flow reference scanning to match the resolver's name grammar, so names
with leading digits or hyphens no longer produce false-positive references.

FlowState records can now carry an optional platform field
('web' | 'server') identifying the runtime that produced the state. Observers
can use it alongside flowId to correlate telemetry across web and server
runtimes of the same flow.

getEnv is now generic over a destination's Env. Passing your env type
(getEnv<Env>(env)) returns window/document as the real DOM globals merged
with your declared SDK shape, so destinations no longer need to cast browser
globals (as Window/as Document) at the call site.

getHashServer now accepts an algorithm option (sha256 default, or md5),
so destinations can request either digest from the shared util instead of
calling Node crypto directly. Criteo's email hashing composes this util for its
md5, sha256, and sha256_md5 forms. No behavior change for existing SHA-256
callers.

Documentation fix: server source config.ingest examples now use the map
operator with direct request field paths instead of a bare object. A bare object
like { url: 'req.url' } is silently inert, so the ingest stayed empty and
downstream ingest.* fields never resolved. Affects package hints, READMEs, the
core source type docs, and the bundled CLI example.

Bundle skeletons now expose each package's dev exports through a lazy loader.
Production deploy bundles drop it entirely, so a shipped walker.js never
carries the dev schema graph, while in-process simulate and push inline the dev
exports so they resolve on a minimal runtime without the source packages
installed alongside. This fixes a browser deploy bundle that could fail to build
or retain dev schemas, and web simulation that could not find the dev exports.

The MCP now loads flows by ID, requires the flow_simulate step parameter it
always enforced, and adds a diagnostics tool reporting client and CLI versions
plus backend reachability. Package discovery returns a complete catalog with a
warning when a source degrades, instead of silently caching partial results, and
returned flow configs are round-trip safe (structural values stay literal). The
demo source can now be simulated as a source step; the CLI also exports
VERSION and resolveAppUrl and clears a deleted default project.

The MCP flow_simulate and flow_bundle tools now accept a cloud flow id as
configPath, so you can simulate or bundle a saved flow without a manual file
round-trip, and repeated simulations reuse a prebuilt bundle for faster runs.
Loading or fetching a flow with no default project set now returns a clear "no
default project" error, and flow_examples surfaces a referenced package's
shipped examples when a step has none inline. Bundle stats now report the real
total bundle size and list package names instead of a per-package estimate, and
the GA4 transformer documents its wiring contract via package hints.

Step examples are no longer bundled into production output. They were
accidentally exported from the production entry of these packages and pulled
into bundled JS. Examples remain available via the package ./dev subpath for
simulation and testing.

Add the Piano Analytics web destination, forwarding events to the official Piano
pa SDK via sendEvent.

Preview creation can now target a deployed version: pass
source: { kind: 'deployment-version', deploymentVersionId } to createPreview
(CLI) or the MCP flow_manage preview_create action to preview what's live
instead of the flow's draft. Deleting a preview no longer errors on the empty
204 No Content response and resolves to a confirmation record.

Request caching now persists structured HTTP responses, including binary bodies
(Buffer, Uint8Array, ArrayBuffer), to byte/string store backends
(filesystem, S3, GCS, in-memory) and honors TTL. Previously, caching a response
could crash the process or never populate, and entries never expired. Cached
values now round-trip safely (binary bodies decode back as a Buffer) and
expire correctly instead of serving stale content after a redeploy.

A source or destination with a require gate now activates reliably from the
collector's current recorded state, regardless of source init order or wh...

Read more

walkerOS v4.1.1

27 May 10:21
0a4dd58

Choose a tag to compare

Changes

The simulate functions (simulateSource, simulateTransformer,
simulateDestination) now return the unified Simulation.Result shape with
captured events and intercepted calls, instead of the internal push result.
PushResult no longer carries the simulate-only captured, usage, and
perDestination fields.

Rename the contract inheritance key from extends to extend for consistency
with the rest of the flow config vocabulary. Contracts that inherit from another
named contract now use "extend": "<name>".

Add the d8a.tech web destination for a GA4-compatible and warehouse-native
analytics platform.

Export the $-ref completion builders (getVariableCompletions,
getEnvCompletions, getStoreCompletions, getFlowCompletions,
getSecretCompletions) and the CompletionEntry type from the package barrel
so custom inputs can reuse them outside Monaco.

Export getMappingPathCompletions and getContractCompletions so custom form
inputs can seed contract-driven path and $contract reference suggestions
outside Monaco. Add allowedRefKinds (the cursor-scoped $-ref gate, now
covering $contract) and getJsonPathAtOffset, and gate the open $
completion fallback by the same scope rule so Monaco offers only the ref kinds
valid at the cursor.

Add Google Ads Enhanced Conversions support. Set enhancedConversions on the
ads settings to map event fields to Google's user_data, sent before each
conversion.

The project, flow, and deployment list operations now accept optional cursor
and limit arguments and return a nextCursor to fetch the next page. Listing
without these arguments is unchanged and returns all results. In the MCP, the
project_manage, flow_manage, and deploy_manage tools expose cursor and
limit on their list action.

Add extend and remove to mapping rules. extend deep-merges a partial rule
onto a package-shipped default (a null value clears an inherited field);
remove strips fields from the produced payload. Rules without either keyword
keep the existing replace behavior.

Send the Conversions API access token in an Authorization: Bearer header
instead of the URL query string, so the credential no longer appears in server,
proxy, or APM request logs.

The collector exposes observers: Set<ObserverFn> so any subscriber can watch
every step of the pipeline. Each source, transformer, destination, and store
call emits a FlowState record with timings, mapping match, consent state, and
skip reasons. createTelemetryObserver from @walkeros/core batches emissions
to an HTTP endpoint, and the CLI runtime picks up the traceUntil flag from its
heartbeat so trace mode toggles take effect without a redeploy.

Fix latent type errors in Panel props and attribute tree builder, and enable
typecheck script so the package participates in CI type coverage.

New server-side bot and AI-agent detection transformer. Annotates events with
user.botScore, user.agentScore, and optionally user.agentProduct (the
matched user-agent, e.g. 'ChatGPT-User'). It wraps isbot and a curated
AI-agent UA map. Annotate-only — events are never dropped; destinations filter
via mapping.

Web requests sent via fetch now support a configurable timeout, defaulting to 10
seconds. A stalled network request is aborted instead of hanging indefinitely.

The browser source now releases its DOM event listeners, pulse intervals, and
wait timeouts when the source is destroyed. This prevents memory growth and
avoids duplicate events when the source is torn down or re-initialized.

Published Packages

walkerOS v4.1.0

21 May 11:51
296450d

Choose a tag to compare

Changes

Cache reads through checkCache are now correct against async stores
(filesystem, Redis, any store with an async get). Previously a custom async
store could silently miss the cache.
checkCache returns a Promise. External callers must add await.

The BigQuery Storage Write API data plane now authenticates from
settings.bigquery (e.g. keyFilename, credentials) instead of always
falling back to Application Default Credentials. Service-account auth configured
for setup now also applies to event ingestion.

Fix getByPath silently returning undefined for objects created in a different
realm (Node http.IncomingMessage, vm contexts, worker threads, iframes). The
internal instanceof Object guard is replaced with a cross-realm-safe check, so
dot-notation paths now extract fields from native request objects and other
cross-realm sources.

Collector and destination buffers are now size-bounded with FIFO drop-oldest
eviction. Defaults: collector queueMax: 1000, destination queueMax: 1000,
destination dlqMax: 100. Set either knob to override per scope. Drop counts
surface in collector.status.dropped and collector.status.destinations[id].

Fix resolveContracts so a child contract that uses extends inherits the
parent's tagging when it does not redeclare it. Previously the parent's
tagging was silently dropped, which corrupted contract version tracking for
anyone building on a base contract.

Flow v4 routing & cache cleanup.
Cache:

  • cache.full is renamed to cache.stop. Search-and-replace.
  • cacheRule.match is now optional. Omitted means always-match. The literal
    '*' is dropped from the schema and the TypeScript types; compileMatcher
    still tolerates the string at runtime for migration.
  • New cache.namespace?: string field. Omit to write keys directly to the
    store. Same store + same key + same namespace = same cache entry.
  • Implicit per-step namespace prefixes (s:, t:, d:) are removed. If you
    relied on them to separate same-keyed caches across
    sources/transformers/destinations using the same store, set cache.namespace
    explicitly.
    Routing:
  • Unified recursive Route type. A Route is string | Route[] | RouteConfig.
  • New case operator replaces the legacy Route[] first-match shape. The
    legacy shape is compiled as an implicit { case: [...] } for runtime
    compatibility, but new configs should use case explicitly.
  • RouteConfig is a disjoint union enforced at the TypeScript type level via
    never fields: a single RouteConfig sets at most one of next / case. A
    bare { match } is a gate (pass-through when the match fires, fall-through
    when it fails). JSON Schema validation currently emits anyOf and does not
    enforce disjointness at runtime — see follow-up notes.
  • Sequence sugar (next: [A, B, C]) is preserved.
    Path:
  • A transformer entry with no code is a path — a code-less passthrough. The
    engine synthesizes (e) => ({ event: e }). Use paths to name and share
    before chains across destinations. Validation: a path must declare at least
    one of package, before, next, or cache.
    Schema & tooling:
  • Updated Zod schemas (cache, route, matcher).
  • Updated MCP tool descriptions and resource references.
  • Updated flow_validate to enforce the new constraints (EMPTY_TRANSFORMER
    error code added).
    Migration: Hard cut at the schema/type level. Configs using cache.full
    will fail validation — rename to stop. Configs using match: "*" will fail
    validation — omit match. Configs using Route[] first-match still work at
    runtime (compiled as implicit case) but new configs should use case
    explicitly.
    $schema: "v4" is preserved. No version bump.

Breaking: code: "<exportName>" is no longer accepted on any step. Replace
with import: "<exportName>" alongside package.
New: Every step (source, transformer, destination, store) accepts
import?: string. With package, it selects a named export. package alone
still loads the default export. Inline code stays
code: { push, type?, init? }. Empty steps are valid no-ops. flow_validate
and the CLI bundler raise OBSOLETE_CODE_STRING on the legacy shape with a
precise rename hint.

Pass-through transformer steps + closed-schema validation.
Validation: validateTransformerEntry in @walkeros/core is now the single
source of truth. Bundler, flow_validate, and collector runtime all delegate.
Closed schema: unknown top-level keys are errors. code + package together is
a CONFLICT.
Pass-through steps: A transformer entry with no code and no package is
valid; the collector synthesizes its push. Three variants:

  • before/next chain only (named hop)
  • cache only (e.g. dedup)
  • mapping only (event-to-event transform via Mapping.Config)
    Mapping at the transformer position: new mapping?: Mapping.Config field on
    Transformer.Config / InitTransformer. Same shape as
    Destination.Config.mapping, event-to-event semantic. data / silent are
    ignored at the transformer position with a one-time warning.
    Engine tag: synthesized instance now uses type: 'pass' (was 'path').
    Hard cut.
    Runtime fixes:
  • compileNext handles mixed-shape next arrays (["a", { case }]) via a new
    'sequence' variant.
  • A destination's before referencing a pass-through transformer now walks that
    transformer's own before / next.
  • cache.stop: true at a pre-collector transformer halts the pipeline (matches
    cache.mdx).
    Migration: Typo keys on a step now fail validation.
    instance.type === 'path' consumers must read 'pass'. runTransformerChain
    consumers should branch on the new stopped flag.

Remove unused legacy fields batchFn and batched from Mapping.Rule. Batch
state lives on the destination via BatchRegistry, never on mapping rules. No
runtime impact.

Route grammar: rename case to one (first-match dispatch) and add many
(all-match parallel fan-out, pre-collector only). many terminates the main
chain and is rejected at post-collector positions (destination.before,
destination.next); use multiple destinations for post-collector fan-out.
RouteCaseConfig is renamed to RouteOneConfig; no aliases.

Source.Context no longer exposes setIngest or setRespond. Server sources
handling concurrent inbound requests must call
context.withScope(rawScope, respond, body) to bind per-request ingest and
respond. Browser and other single-scope sources keep working without changes.

Status.dropped is now keyed by stepId, so operators can see at a glance which
step dropped events. Read with status.dropped["collector"]?.queue or
status.dropped["destination.<id>"]?.queue / .dlq, or build the key with the
new stepId() helper exported from @walkeros/core. Breaking change: the
previous flat shape (status.dropped.queue / .queuePush / .dlq) and the
per-destination dropped field on DestinationStatus are removed.

Add Flow.Store.cache for store-level caching: read-through + write-through
wrapper with single-flight dedup, recursive composition via cache.store, and
per-wrapper counters. CacheRule is now a discriminated union
(EventCacheRule | StoreCacheRule); schema rejects inert fields in store
contexts.
Built-in __cache upgraded with LRU, maxEntries: 10000, batched eviction, and
active TTL sweep.
Breaking: @walkeros/store-memory is removed. Its logic is absorbed into
__cache. Migration: drop the store declaration, or omit cache.store to use
the built-in tier. flow_validate flags legacy references.

Add @walkeros/transformer-ga4: GA4 Measurement Protocol v2 decoder transformer
with default mappings for 33 standard events. Server-side use via
source-express in the before chain.
Also: fix collector to preserve fan-out in source.before chains. Previously,
when a before-transformer returned an array of events, only the first survived.
This enables vendor-protocol decoders (GA4, Segment, Snowplow, etc.) to fan a
batched request into N walkerOS events.

Internal pipeline failures in mapping, source startup, transformer init, and
destination init now log via the scoped logger and increment
collector.status.failed. Previously silent. User-supplied callbacks (mapping
condition/fn/validate, on subscriptions) log on throw but do not affect
status.failed. A source whose init() throws now stays
config.init === false instead of being marked initialized.

Add typed accessors Source.getSource, Destination.getDestination,
Transformer.getTransformer, Store.getStore. Each takes a collector and a
step id and returns the registered instance with its declared generic recovered,
replacing the Elb.Fn-collapsed shape that the bag's index signature exposes on
read.
Callers (mainly tests and integrations that invoke a step's raw push through
the collector) no longer need as any / as (rawData: X) => ... casts at this
boundary. Each helper throws <Kind> not found: <id> for unknown ids. No
runtime behavior change.

Add validateJsonSchema / validateEventsJsonSchema exports for step-level
validation config, promote the validate and no-many route schemas to direct
exports, and add optional nodeType / subPath cursor fields to
IntelliSenseContext for context-scoped autocomplete.

Add step-level validate? primitive on every walkerOS step. validate: is a
declarative description of validation intent, like cache or consent.
Consumers decide how to enforce.
Restructure Flow.ContractRule to a uniform
{ extends?, tagging?, description?, events?, schema? } shape. A single
agnostic JSON Schema replaces the typed section fields (globals, context,
custom, user, consent); standard event field names live inside
schema.properties.<name>. extends resolves schema via additive deep-merge.
Contracts are a description and governance concept: tooling, MCP, and humans
read them. Runtime enforcement is the consumer's cal...

Read more

walkerOS v4.0.2

11 May 17:54
36d0a47

Choose a tag to compare

Changes

Move from packages/web/destinations/demo to packages/destinations/demo so
the package can serve web and server flows from a single location, alongside
packages/transformers/. The published package name and exports are unchanged;
only the in-repo path moves. walkerOS.platform widens from ["web"] to
["web", "server"]. No consumer code changes required.

Rename routing types: NextRule to Route, Next to RouteSpec (Zod schemas
and MatcherNext* IDs renamed in step). Widen Flow.*.before/next to
RouteSpec so Route[] conditional routing type-checks at the JSON layer. Fix
the CLI bundler dropping Route[] data via a narrowing cast on the inline path.
Hard cut, no aliases; flow.json shape unchanged.

Published Packages

walkerOS v4.0.1

11 May 09:34
a1ec50e

Choose a tag to compare

Changes

Fix events being silently dropped when posted via navigator.sendBeacon. The
browser forces Content-Type: text/plain;charset=UTF-8 for beacon requests even
when the payload is JSON, which previously caused the express middleware to skip
body parsing and the GCP Cloud Functions handler to treat the body as an opaque
string, both falling through to an empty-event push. Express now accepts
text/plain bodies through express.json(), and the Cloud Functions handler
attempts JSON.parse on string bodies before classifying the request.

BigQuery destination: migrate from legacy tabledata.insertAll to the BigQuery
Storage Write API (~2x cheaper at volume, 2 TiB/month free tier), add the
setup() lifecycle for one-shot dataset and table provisioning via
walkeros setup destination.bigquery, and implement pushBatch so the
collector's batch: <ms> mapping setting actually batches into a single
appendRows call.
Breaking changes:

  • The 15-column table schema is now using walkerOS event v4 schema.
    Run walkeros setup destination.bigquery to provision the dataset and table
    with day partitioning on timestamp and clustering on (name, entity, action).

@walkeros/cli: Server bundles now use @vercel/nft to trace dependencies and copy
only files actually used into dist/node_modules/. Pacote remains the install
layer (driven by flow.json's config.bundle.packages field; users do not run npm
install for step packages). The walkerOS.bundle.external annotation field on
package manifests is no longer recognized (deprecation warning if seen). The
flow..config.bundle.external sub-field on flow configs is also no longer
supported (warned and stripped during load). The
flow..config.bundle.traceInclude field is the escape hatch for cases nft
cannot statically trace. Server output is always a directory: dist/{flow.mjs,
package.json, node_modules/}. Default output filename changed from bundle.mjs to
flow.mjs. The runtime image expects /app/flow/flow.mjs. flow.json schema is
unchanged (still v4); only @walkeros/cli bumps. Migration: see
https://walkeros.io/docs/migrate/cli-4x.
@walkeros/server-destination-gcp: removed obsolete walkerOS.bundle.external
annotation from package manifest. nft handles externalization automatically. No
behavior change for consumers.

Bundler honors walkerOS.bundle.external declared in step-package package.json
files. Listed packages are externalized from the ESM bundle and the bundler
always installs them (plus their full transitive deps) into
<outputDir>/node_modules/ via pacote — no npm install shell-out, no manual
deploy step. When externals is empty, output remains a single bundle.mjs
(backward compatible). When non-empty, output is a self-contained directory:
bundle.mjs, package.json, package-lock.json, node_modules/.
The bundler reads npm config (registry, scope tokens) from .npmrc,
parallelizes manifest fetches with retry, atomically stages each package
extraction (no half-populated node_modules/ on failure), and reuses the
closure resolution from the existing collectAllSpecs BFS so peerDependencies
are honored.
Hard-errors when:

  • A package in the install closure declares a pre/install/postinstall script
    (pacote.extract does not run them).
  • A step package names an external in walkerOS.bundle.external but does not
    list it in dependencies or peerDependencies.
  • Two step packages declare the same external and the resolved version does not
    satisfy all consumers' constraints.
    Warns (not errors) when:
  • Bundle output contains unresolved __dirname / __filename references (with
    package attribution by hit count).
  • A step package's walkerOS.bundle.* block contains unknown keys (typo guard).
    New sibling export downloadPackagesWithResolution returns both the package
    paths and the full ResolutionResult. Existing downloadPackages keeps its
    return shape unchanged.

Surface destination init errors in logs at ERROR level. Previously, two layers
swallowed errors silently: the gcp destination's init catch only logged for
isNotFound errors and re-threw everything else without logging; the collector
wrapped destinationInit with tryCatchAsync (no onError), which silently
returned undefined on a thrown error and treated the destination as
not-initialized. Combined effect: a real init failure (e.g., the recent
streamType regression in BigQuery Storage Write API call) showed only
[gcp-bigquery] init in DEBUG logs and nothing else, regardless of log level.
Now: gcp's init catch logs every error at ERROR before re-throwing (with
consistent error: context key), AND the collector logs at ERROR via
logger.scope(destType).error('Destination init threw', { error }) if init
throws or rejects. Failures are never silent. Mocks updated to enforce the new
shapes; tests cover both sync-throw and async-rejection variants.

Fix walkeros bundle failing on Windows when stage 2 import paths contained
backslashes that JS parsed as escape sequences.

Declare @google-cloud/bigquery-storage as a bundle external via
walkerOS.bundle.external. Fixes __dirname is not defined in ES module scope
when bundling a flow that uses BigQuery Storage Write API. The bundler's closure
walker pulls in the transitive gRPC stack (@grpc/grpc-js,
@grpc/proto-loader, protobufjs, google-gax) automatically via
bigquery-storage's own dependencies and peerDependencies, so only the one
entry needs to be declared. Requires @walkeros/cli >= the version shipping the
bundler-externals feature.

Add Pub/Sub sub-destination to the GCP server package. Publishes walkerOS events
to a Pub/Sub topic with optional per-key ordering and dynamic attributes, plus
idempotent topic provisioning via walkeros setup destination.<id>. EU region
default for at-rest storage. Three auth modes: ADC, service account JSON,
pre-configured client.

Adopt the setup lifecycle for one-shot Kafka topic provisioning. Operators can
now run walkeros setup destination.<id> to create topics idempotently with
explicit numPartitions, replicationFactor, and configEntries. Drift on
partition count, replication factor, and config entries emits warnings without
auto-mutating. Optional Confluent Schema Registry binding registers a schema and
(optionally) sets the per-subject compatibility level.
No safe defaults. Kafka topic creation requires cluster-specific decisions.
The boolean form setup: true is rejected with an error listing the required
fields. Only the object form
(setup: { numPartitions, replicationFactor, ... }) is valid. This is the
canonical example of the "no safe default" pattern in the walkerOS
create-destination skill.
When the topic is missing at runtime, push() now logs an actionable error
pointing at walkeros setup destination.<id>.

Add an optional setup lifecycle to destinations, sources, and stores.
Each package may now implement setup?: SetupFn to provision external resources
(BigQuery datasets and tables, Pub/Sub topics and subscriptions, SQLite tables,
webhook registrations, etc.). Setup is triggered only by the new
walkeros setup <kind>.<name> CLI command, never automatically by the runtime,
push, or deploy. Idempotency, ordering, and error semantics are the package's
responsibility; the framework provides the type slot, the CLI invocation, and a
resolveSetup(value, defaults) helper.
LifecycleContext<C, E> is the new shared context type used by both setup and
destroy. DestroyContext remains as a deprecated type alias for one minor
cycle. The Types bundle on Destination, Source, and Store gains a
5th/6th/4th positional slot for setup options; existing aliases compile
unchanged because the slot defaults to unknown.
Config<T>.setup?: boolean | SetupOptions<T> is added across all three kinds
and validated by the corresponding Zod ConfigSchema plus the flow component
schemas in @walkeros/core/schemas/flow.ts.
CLI:

  • walkeros setup <kind>.<name> runs a single component's setup() function.
  • <kind> is source, destination, or store (transformers have no
    provisioning).
  • --config <path> (default ./flow.json), --flow <name> for multi-flow
    configs, plus standard --json / --verbose / --silent.
  • Exit 0 on success or skip; non-zero on failure. Skip narration covers three
    cases: no setup() on the package, config.setup === false, or
    config.setup unset.
  • When the package's setup() returns a non-undefined value, the CLI emits it
    as JSON on stdout for jq piping.

Source lifecycle redesign: factory + eager init + collector-gated on()
Source factories must now be side-effect-free. The collector calls
Instance.init() on each source eagerly after all factories register. require
no longer gates code execution. It gates on(type) delivery (events queue in
Instance.queueOn until the source is started, then replay).
collector.pending.sources has been removed; per-source state lives on
Source.Instance (queueOn) and Source.Config (init, require).
Migration: any source factory with side effects (queue draining, walker command
emission, listener attachment) should move those into the returned Instance's
optional init method. Tests asserting on collector.pending.sources should
read collector.sources[id] and inspect config.init / config.require
instead.
Fixes the elbLayer queue replay clobbering fresh consent/user state,
late-activated sources missing walker run, and inter-source require chains
racing when a non-required source's init fired a state-mutating walker command
before later require-gated sources had been registered.

Add the setup() lifecycle. Run walkeros setup destination.<name> to create
the events table with the canonical walkerOS Event v4 schema and apply pragmas
(journal_mode=WAL, synchronous=NORMAL, foreign_keys=ON,
temp_store=MEMORY). Setup is idempotent and detects drift via
PRAGMA table_info (logs WARN setup.drift, never auto-mutat...

Read more

walkerOS v4.0.0

04 May 14:32
3764cc0

Choose a tag to compare

Changes

Remove dead bundleRemote() and add OpenAPI drift detection.
Breaking changes:

  • Removed bundleRemote() export from @walkeros/cli. The corresponding
    /api/bundle endpoint was removed from the walkerOS app on 2026-04-08, so
    this function had been silently broken in production for ~3 weeks. Local
    bundling via bundle() is unaffected.
  • Removed remote and content options from the MCP flow_bundle tool. The
    tool now bundles locally only.
    New:
  • Added npm run -w @walkeros/cli validate:openapi-spec script that diffs the
    checked-in packages/cli/openapi/spec.json against the live app's OpenAPI
    document. Detects drift between the walkerOS-side type contract and the actual
    API. Wired into PR-time CI, daily cron, and a pre-commit lint-staged hook. All
    layers are gated on a WALKEROS_APP_URL secret and skip silently when unset,
    so the change ships safely without configuration. To activate: set
    WALKEROS_APP_URL in repo secrets pointing to a deployed app instance.

Fix walkeros push deadlock for web flows whose destinations await real timers
during init.
Previously, async-drain timer interception captured every setTimeout into a
pending map and only fired them via a post-fn flush. If a destination's init
awaited one of those captured timers (e.g.,
@walkeros/web-destination-amplitude's engagement plugin awaits a 10s
setTimeout to give up on a CDN script load), init never resolved,
await collector.push deadlocked, and Node exited with
Detected unsettled top-level await (exit 13).
A drain pump now runs alongside fn(flowModule) for non---simulate runs: each
tick fires every captured non-cleared timer using a real setImmediate
reference. Timers fire in delay-ascending order, intervals re-register, callback
errors are reported via console.warn. Bounded by max-iterations (1000) and
wall-clock (30s) caps.
--simulate <step> continues to use the post-fn flush path so snapshot
ordering remains stable.
Behavior change (edge case): a destination using setTimeout for retry backoff
under walkeros push (real, non-simulate) now sees its timer fire instantly.
This was already the documented contract for --simulate <step> snapshots; it
now extends to real push for consistency.

Validate device-code and token responses from the auth server with Zod schemas
at the trust boundary in login/index.ts. Malformed responses now surface as
structured errors instead of being trusted into config writes or browser
launches. Replaces the hand-rolled type guards for Items 1 and 3 of the cli-auth
feedback review. No public API change.

Explicit opt-in anonymous usage telemetry for CLI and MCP. Telemetry is off by
default; users opt in with walkeros telemetry enable and out with
walkeros telemetry disable. No persistent identifier is written before opt-in.
No ingest endpoint ships in this release: opting in records consent locally;
emission begins when a managed endpoint is released. The data contract lives at
packages/cli/src/telemetry/flow.json.

Add flowId filter to CLI listDeployments and redesign the MCP
deploy_manage tool around it.
CLI (@walkeros/cli):

  • listDeployments({ projectId?, type?, status?, flowId? }) now forwards
    flowId as a query parameter to GET /api/projects/{id}/deployments.
  • New helper deleteDeploymentByFlowId({ projectId?, flowId, slug? }) deletes
    the active deployment for a flow, surfacing a DeploymentAmbiguityError (code
    MULTIPLE_DEPLOYMENTS, with a details[] list) when a flow has more than one
    active deployment and no slug was supplied.
    MCP (@walkeros/mcp) breaking:
  • deploy_manage's get, delete, and list actions now take
    { projectId?, flowId, slug? }. The old id parameter has been removed.
    flowId is required for get/delete and optional for list. Soft-deleted
    deployments are always excluded.
  • When a flow has multiple active deployments and slug is not provided,
    get/delete return a MULTIPLE_DEPLOYMENTS error with a details[] list
    of { slug, type, status, updatedAt } entries so the caller can pick one.
    deploy action is unchanged.

Event model v4: breaking changes to the Event, Source, and Entity shapes.

  • event.id is now a W3C span_id (16 lowercase hex chars), generated by the
    collector. Reference: W3C Trace Context (W3C Recommendation, January 2020).
  • event.version, event.group, event.count are removed.
  • source.type is now the source kind (e.g. browser, gtag, mcp, cli).
    New source.platform holds the runtime (web | server | app | ...).
  • source.id and source.previous_id are removed.
  • Browser source now sets source.url and source.referrer.
  • MCP source sets source.tool per emission. CLI source sets source.command.
  • Entity.nested and Entity.context are now optional. Root event.nested and
    event.context remain required.
  • Each source self-registers via TypeScript module augmentation of SourceMap
    in @walkeros/core.
  • App-side coordination (/workspaces/developer/app) is a follow-up plan, not
    part of this release. Telemetry from v4 CLI/MCP will not validate against the
    existing app schema until that follow-up ships.
  • Mapping.Rule.skip is renamed to Mapping.Rule.silent. Customer flow.json
    configs using skip: true in mapping rules must rename to silent: true.
    Hard cut: no legacy alias, the field is gone.

Add CodeDiff atom and CodeDiffBox molecule — read-only, theme-aware Monaco
DiffEditor wrappers for side-by-side / inline code diff viewing. CodeDiffBox
mirrors CodeBox's API (header, actions, traffic lights, footer) and adds an
opt-in summary strip, split/inline toggle, and copy button. Supports any Monaco
language; walkerOS $var: / $secret: decorations are applied to both sides
automatically.

IntelliSense improvements for flow.json editors:

  • Chain references (next / before) now autocomplete in all forms: scalar,
    inline array, multi-line array, and Route[] inner next. Previously only the
    scalar form triggered.
  • $store. completions, hover, and validation added. Fed by a new optional
    stores field on IntelliSenseContext; the flow extractor collects store IDs
    from the active flow.
  • $env. completions and hover added. Optional envNames inventory on
    IntelliSenseContext enables validation; when absent, $env. still gets a
    generic hover.
  • $contract. completion now only triggers when the cursor starts a new string
    value, matching runtime semantics (whole-string refs only).
  • package completion detection is JSON-path aware — multi-line "package":
    values now surface completions.
  • Variables and definitions are collected at config / flow / step levels with
    correct cascade priority (step > flow > config).
  • Markers validate chain references in all forms via a JSON walk instead of a
    scalar-only regex.
  • Internals now import the shared REF_* regex constants from @walkeros/core
    — single source of truth, no inline duplicates.

PropertyTable — responsive card-view fallback via CSS container queries
(triggered below 420px), graceful empty-state rendering with an optional
emptyMessage prop (default: "No specific properties available."), and improved
column-width handling so the Description column no longer forces horizontal
overflow in narrow containers.

Add Monaco IntelliSense for $flow.X cross-flow references in Code/CodeBox.
Completion offers known sibling flow names from the parsed flow document, hover
describes the resolved target, decorations style matches the other reference
prefixes, and unknown flow names emit a warning marker. Re-export REF_FLOW
from @walkeros/core so consumers can build inline regex tooling without
reaching into the subpath.

Flow v4: type redesign and cross-flow references.
Breaking changes:

  • Renamed Flow.Settings (single-flow shape) to Flow. The new Flow.Settings
    is the arbitrary kv-bag inside Flow.Config (matches Destination.Settings
    semantics).
  • Renamed Flow.Config (root file shape) to Flow.Json.
  • Removed Flow.Web and Flow.Server. Replaced by
    config.platform: 'web' | 'server' (a string discriminator).
  • Renamed Flow.InlineCode to Flow.Code.
  • Renamed Flow.SourceReference / DestinationReference /
    TransformerReference / StoreReference to Flow.Source / Destination /
    Transformer / Store (Reference suffix dropped).
  • Renamed Flow.ContractEntry to Flow.ContractRule.
  • Lifted bundle and platform fields into the per-flow config block.
  • flow.json version bumped from 3 to 4. v3 input is rejected (no compat
    shim).
    New:
  • $flow.X.Y reference resolves to flows.X.config.Y in the same file. Useful
    for linking a web flow's API destination to a server flow's deployed URL
    without duplicating values.
  • Per-flow Flow.Config block: { platform, url, settings, bundle }.
  • walkeros validate warns on unresolved $flow.X.Y (use --strict to error).
    walkeros bundle and walkeros deploy always error on unresolved refs.
  • See docs/migrating/v3-to-v4.mdx on the website for the manual migration
    steps. No automated codemod is shipped.

Add flowCanvasResult helper + FlowCanvasToolResult / FlowCanvasPayload /
SuggestionTile types for UI-renderable tool outputs. flow_manage actions
get / create / update now return a kind: 'flow-canvas' payload with
optional suggestion tiles so chat clients can render the flow graph inline.

Split @walkeros/mcp into a library entry (server factory plus ToolClient
abstraction) and a thin stdio binary. The package now exports
createWalkerOSMcpServer, HttpToolClient, createStreamableHttpHandler,
TOOL_DEFINITIONS, and the ToolClient interface so host applications can
mount walkerOS MCP tools over HTTP (e.g. from a Next.js Route Handler) or
consume them directly from non-MCP runtimes (e.g. Vercel AI SDK adapters). The
walkeros-mcp stdio binary is unchanged for end ...

Read more

walkerOS v3.4.1

21 Apr 17:22

Choose a tag to compare

Changes

Add walkeros previews {list|get|create|delete} commands for managing preview
bundles. create supports --flow <name> or --settings-id <id> to target a
flow settings entry, and --url <siteUrl> to produce a ready-to-open activation
URL. Use --open to launch it in your default browser.

Add preview.list, preview.get, preview.create, and preview.delete
actions to the api tool. When siteUrl is provided to preview.create, the
response includes a ready-to-open activationUrl and deactivationUrl.

Preview preflight now self-heals when a preview bundle is deleted. Instead of
injecting the preview script directly and letting it 404, the preflight does a
fetch(HEAD) first. If the bundle is missing, it clears the elbPreview cookie
and loads the production walker, so visitors never see silent analytics
breakage.

Step examples can now carry a title, description, and public flag.
Non-public examples stay hidden from the docs and AI tools so first-time
visitors see only the canonical ones.

useHooks now isolates hook failures. A pre-hook that throws no longer crashes
the pipeline — the wrapped function is called directly and a warning is logged.
A post-hook that throws leaves the original result in place. Added optional 4th
logger parameter so warnings route through the walkerOS Logger (falls back to
console.warn when no logger is provided). All collector call sites now pass
collector.logger.

Wrap localStorage/sessionStorage/cookie operations in try/catch. Storage
access in private browsing (Safari), sandboxed iframes, or when quota is
exceeded throws SecurityError/QuotaExceededError — previously these crashed
the event pipeline at the call site. Reads now return empty, writes return empty
and do not persist, deletes are silently ignored.

Published Packages

walkerOS v3.4.0

21 Apr 07:46
fbec5aa

Choose a tag to compare

Changes

Add target option to bundle():
cdn | cdn-skeleton | runner | simulate | push. Replaces
buildOverrides.skipWrapper (deprecated) to stop dev schemas leaking into
production CDN bundles. Stage 2 entry generators gain platform option and
inject env.window/env.document for browser targets, fixing window.elbLayer
in deployed walker.js.

Clients now send User-Agent, X-WalkerOS-Client, and
X-WalkerOS-Client-Version on every request to the walkerOS app. When the app
returns 426 Upgrade Required, the CLI prints the required version + upgrade
instruction and exits with code 2; the MCP surfaces the same info in tool
errors. Set WALKEROS_CLIENT_TYPE=runner to have the CLI binary identify as a
long-lived runner instead of an interactive CLI (used by the runtime image so
runners are distinguishable from interactive sessions).

<CodeBox> and <LiveCode> now run with Monaco configured to target: ES2022,
module: ESNext, moduleDetection: 'force', and a registered ambient
declarations file exposing walkerOS runtime globals (elb, getMappingEvent,
getMappingValue). Mapping snippets can be plain object literals or top-level
await calls — no import / export boilerplate required — while keeping full
IntelliSense via the existing @walkeros/core type registration.
<LiveCode> now renders its result panel as JSON (it's always vendor output,
regardless of the input language) and its config panel as JSON (it's always
data). Only the input panel respects the language prop.

Add CodeView (Shiki-backed read-only code display) with matching Box frame,
plus a CodeStatic atom as the underlying highlighter. Also suppress the Monaco
loader's {type: 'cancelation'} unhandled rejections globally via a single
window-level listener, fixing the dev-console noise that fired on every unmount
of a <CodeBox> consumer.

Add TransformerSchemas and StoreSchemas namespaces with ConfigSchema /
configJsonSchema exports. Mirrors the existing DestinationSchemas /
SourceSchemas pattern so every component type has a documented Config schema
available via @walkeros/core/dev.
Reconcile pre-existing drift between TS Config interfaces and their Zod schemas:

  • DestinationSchemas.ConfigSchema now includes before, next, cache,
    disabled, mock, include (matching Destination.Config) and drops
    phantom onError / onLog fields that were never wired in the TS type or
    consumed at runtime.
  • SourceSchemas.ConfigSchema now includes disabled and drops a phantom
    onError field.
  • MappingSchemas.ConfigSchema now includes include, matching
    Mapping.Config (the base Source.Config extends).
  • CollectorSchemas.ConfigSchema now includes logger and drops phantom
    verbose / onError / onLog fields not present in Collector.Config.
  • CollectorSchemas.InitConfigSchema now includes transformers, stores,
    hooks, matching Collector.InitConfig.
    Add a compile-time drift guard at
    packages/core/src/schemas/__tests__/config-drift.test-d.ts. Any future
    divergence between a Config TS interface and its Zod schema fails tsc --noEmit
    (already wired into the project's typecheck script and CI). Keys-only check;
    value types may still differ for recursive or generic-slot fields where Zod
    cannot express TS-side precision.

Add FullStory web destination with trackEvent, setIdentity, setProperties
(user/page), consent start/shutdown, and clean teardown via @fullstory/browser
SDK v2.

Add Heap web destination (product analytics, identity, consent)

Add Hotjar web destination (session replay, heatmaps, surveys)

Add Matomo web destination (self-hosted/cloud privacy-first analytics) via _paq
command queue with support for page views, custom events, ecommerce, goals, site
search, content tracking, and custom dimensions

Harden the Monaco / CodeBox integration. Fix moduleDetection (Force), add
<LiveCode> configLanguage prop, guard ScriptTarget.ES2022 fallback, warn
on loader.init() failures in dev, drop dead code. No API change for existing
callers.

Add Optimizely Feature Experimentation web destination with conversion tracking
via trackEvent(), revenue/value event tags, user context management with
attribute targeting, and consent-based client lifecycle.

Add Microsoft Advertising (Bing UET CAPI) server destination

Add Criteo Events API server destination

Add server-side Customer.io destination with Track, Identify, Page View,
Transactional Messaging (sendEmail/sendPush), and Customer Lifecycle management
(destroy/suppress/merge) via customerio-node SDK. Auto-fallback to
trackAnonymous() for anonymous visitors, state-diffed identify to avoid
redundant calls.

Add @walkeros/server-destination-file: local filesystem sink for walkerOS
server flows. Appends events to a file as JSONL (default), TSV, or CSV with
per-event filename resolution via the standard Mapping.Value DSL (tenant
sharding via key, daily rotation via $code: in fn). Opens one
WriteStream per resolved filename and keeps it open until destroy(). No
third-party SDK — uses node:fs built-ins.

Add server-side HubSpot CRM destination with custom event tracking via
events.send API, contact upsert via CRM API with state-based dedup, optional
batch mode (up to 500 events/flush), defaultProperties for attribution, and
graceful shutdown with queue flush.

Add server-side Apache Kafka destination via kafkajs. Supports JSON
serialization, configurable compression (gzip, snappy, lz4, zstd), per-rule
topic and message key overrides, SASL/SSL authentication (Confluent Cloud, AWS
MSK, SCRAM, OAuthBearer), and graceful producer shutdown via destroy().

Add server-side Klaviyo marketing automation destination with event tracking via
EventsApi.createEvent() and profile management via
ProfilesApi.createOrUpdateProfile(). Supports revenue tracking with
value/valueCurrency, ecommerce metric name mapping, and identify state diffing
to avoid redundant upserts.

Add server-side mParticle CDP destination. Events are packaged into batches and
POSTed to the regional mParticle pod via the HTTP Events API using Basic auth
(apiKey / apiSecret). Supports user identities, user attributes, consent state,
pod selection, and environment targeting.

Add Reddit Conversions API server destination

Add server-side Redis Streams destination via ioredis. Supports XADD append with
auto-generated entry IDs, JSON and flat serialization modes, approximate and
exact MAXLEN trimming, per-rule stream key overrides, env-injected client
pattern for testing, and graceful shutdown via client.quit().

Add server-side RudderStack CDP destination with full Segment Spec support
(Track, Identify, Group, Page, Screen, Alias) via @rudderstack/rudder-sdk-node
SDK. Includes graceful shutdown via flush(), identity resolution per-call, alias
support for identity merging, and state diffing for identify/group.

Add Snapchat Conversions API server destination.

Add @walkeros/server-destination-sqlite: server destination that persists
walkerOS events to SQLite. One destination, two drivers behind a single
interface: better-sqlite3 for local files and :memory:, @libsql/client for
remote Turso / libSQL / sqld. Driver is auto-selected from the connection URL.
Both SDKs are optional peer dependencies. Auto-creates a canonical events table
on init (opt-out via schema: 'manual'), caches a prepared INSERT, closes the
connection on destroy(). Per-rule mapping.settings.table override supported.

Add X (Twitter) Conversions API server destination

Migrate every step example in every walkerOS package to the standardized
[callable, ...args][] shape introduced in @walkeros/core. Every step
example's out is now an array of effect tuples whose first element is the
callable's public SDK name ('gtag', 'analytics.track', 'fbq',
'dataLayer.push', 'sendServer', 'fetch', 'trackClient.track',
'amplitude.track', 'fs.writeFile', 'producer.send', 'client.xadd',
'client.send', 'dataset.table.insert', etc.). Source examples use 'elb' as
the callable; transformer examples use the reserved 'return' keyword; store
examples use store-operation callables ('get', 'set'). Tests capture real
calls on each component's spy and assert against example.out directly — the
hardcoded PACKAGE_CALLS registry in the app is no longer consulted (emptied;
plan #3 removes it structurally).

Introduce the standardized StepExample.out shape: [callable, ...args][]
where each tuple is a function call (first element is the callable name) or a
['return', value] tuple for transformer-style returns. Every effect is
self-describing; docs and tools can render it uniformly without a per-package
registry.
Ship the shared formatOut renderer from @walkeros/core for docs + app. Also
exports StepEffect and StepOut types. Migrate
@walkeros/web-destination-gtag to the new shape as the canary — its multi-tool
outputs (GA4 + Ads + GTM) now flatten into a single array of gtag(...) and
dataLayer.push(...) tuples in observed execution order. Remaining destination
packages ship the old shape until the bulk migration (separate plan).

Published Packages

Read more

walkerOS v3.3.1

15 Apr 18:55
b69b105

Choose a tag to compare

Changes

Collector auto-generated destination keys now use lowercase letters only (a-z,
length 5) instead of base-36 (0-9a-z, length 4). getId gains an optional
charset parameter; default behavior is unchanged so session IDs and other
existing callers stay bit-for-bit identical.

Force collector.run=true during push and simulate so flows with run:false work
in CLI

Fix infinite recursion when registering on('consent', ...) handlers. The
collector's on() helper previously re-broadcast to all source on handlers,
causing self-re-registering consent handlers to recurse unbounded and crash the
tab. on() now fires only the newly-registered callback against current state.

Fix release pipeline to embed the correct __VERSION__ in published packages.

Fix race in source cache MISS wrapper: applyUpdate promise was
fire-and-forget, so a source fallback (e.g. express GIF default) could win
createRespond's first-call-wins race on the first request. wrappedPush now
awaits the pending update before returning.

Published Packages