Repository navigation
Changes
Server flows skip consent-denied events by default
(collector.consentDenied: 'skip'; an authored value wins). A flow's own
logger level, format and handler apply under runneros. walkeros setup
fails with the reason when a step's init throws or returns false. Inline
on, pushBatch and destroy reach the bundle. A flow's custom log handler
gets a no-op originalHandler.
walkeros validate reports unknown keys, mapping keys with no effect, values
without an operator, malformed references and batch without pushBatch as
errors (MAPPING_NO_OP replaces the warning TRANSFORMER_MAPPING_NO_OP), and
bundle, push, simulate, setup stop on them before building. An unknown
settings key is an error where the package closes its schema.
The CLI authenticates with a token only: create one under Account > Automation
tokens and set WALKEROS_TOKEN. walkeros auth login, logout and token are
removed, with the stored session and their exports. An old sign-in in the config
file is no longer read, and walkeros api refuses an Authorization header.
Browser and wrap bundles import from @walkeros/web-core, which ships with the
CLI, so no extra package is downloaded.
@walkeros/collector/dev adds runExamples, which runs step examples on a real
collector, and conformance, a test kit with profiles (http, sdk-env,
web-sdk, vendor-global, socket, transformer, source) that checks the
failure contract of a destination, source, transformer or store and reports
every broken item at once.
Failures are explicit. A failing init, an open circuit breaker, a destination
held past one retry window, a full pre-run queue and a throwing transformer
answer failed or ok: false with a reason; consent waits stay queued. The
collector writes one ERROR line per cause and minute, with a suppressed count,
and never the event payload.
A held event keeps the consent it was admitted with: when its destination's hold
ends it is delivered even if the collector's consent changed in the meantime. A
mapping value problem now logs once per mapping rule instead of once for all
rules, so a second rule's problem is no longer hidden behind the first.
An event that a rule with ignore: true drops is reported as skipped, not
delivered: it is no longer under done, does not move count, and counts in
skipped. A source answers { ok: true, dropped: true }. New walker flush
sends every pending destination batch at once and leaves the collector running.
A destination or transformer init that does not settle within the step's
timeout (destination config.timeout, default 10 seconds; transformers use the
default) now fails the waiting pushes with code timeout instead of leaving
them unanswered. The next push retries the init.
A destination whose config sets batch without a pushBatch, or a mapping key
with no effect at its position, logs one ERROR when it registers and still runs.
A connection error that lost no event logs at WARN instead of ERROR.
New consentDenied: 'skip' makes a server collector skip an event that a
destination's consent does not allow, instead of queueing it for a grant that
never comes. It counts in skipped and logs at DEBUG, never as an error. A
destination without init is initialized without one, so its on handlers run.
Transformers accept config.timeout in milliseconds (default 10000). It bounds
init and each push; a push that does not settle fails the event with code
timeout instead of leaving the push unanswered.
The web bundle walker.js carries the full failure contract, the circuit
breaker included. It grows from 35,181 bytes to 39,411 bytes, measured as gzip
-9.
A web Observe config takes slot: pageObserveSlot() from @walkeros/web-core;
without it one error line says so and Observe stays off. The conformance kit's
browser profiles take page from @walkeros/web-core/dev. Observed web bundles
need this CLI version.
The lint rules walkeros/await-send and walkeros/no-silent-return are errors:
a send that is not awaited, or a bare return in a destination's push or
pushBatch, fails lint. ESM builds of server-only packages define require, so
their CommonJS SDKs load, and tests are no longer published.
The presets set lib and types and enforce the platform: web and neutral code
are held to ES2020 APIs, and the new @walkeros/config/eslint/neutral and the
platform bans in eslint/web and eslint/node keep each package on its own
platform. npm run check:platform checks every package.
logger.throw only throws and no longer writes a log line; the collector writes
the one failure line. A rule condition that throws fails the event instead of
falling through. A mapping value without an operator is dropped and reported.
The wildcard lookup tries exact, entity *, * action, then * *, and falls
through a tier whose conditions all fail.
A JSON log line above 32 KiB now truncates its message as well as its context
and sets messageTruncated: true, so one oversized line can no longer break a
log pipeline. Mapping problems report under the step mapping.<rule>, one line
per rule.
logger.format: 'json' prints one JSON object per line on server flows, with
severity, message, time, scope and context; errors and warnings go to
stderr. Web flows reject it. A log handler receives the cause code, status,
retryable flag and suppressed count as structured fields, so lines can be
forwarded without parsing.
Tag Mode's moin and the preview activator browserSwapActivator (with
SwapActivatorConfig and SESSION_STORAGE_KEY) moved to @walkeros/web-core,
and isElementOrDocument is removed. serializeStoreValue,
deserializeStoreValue and StoreCodecError moved to @walkeros/core/node.
getFlowSettings reads $env only from the env you pass it, never from
process.env. Errors keep cause in every browser.
getEvent returns the same event on every run: a fixed timestamp and an id
derived from the event name. It accepts only standard event names, so a typo no
longer compiles; createEvent builds any other event and uses the same fixed
default timestamp. order complete carries the order amount as data.value
instead of data.total, and the package examples read data.value.
New StepError carries a code, an HTTP status and retryable;
acknowledge(result) tells a producer to ack, retry or reject a message, and
reportCause writes one failure line per cause. SendResponse is { data }.
An init of a destination, transformer or store can no longer return false:
throw a StepError, and untyped false fails with not-ready.
Flow validation now rejects unknown keys in step config, destination and store
entries, the collector block, inline code and contract rules, mapping keys
with no effect at their position, values without an operator or with two
producers, and malformed whole-value references. Inline code may define
pushBatch, on and destroy. Strict $flow treats an empty value as
unresolved.
A 2xx Data Manager answer whose body is not a JSON object is reported as a
transport failure ("Data Manager answered with an unreadable body").
The error for an empty $flow reference names only the setting to fill in; it
no longer suggests a walkeros deploy command.
$flow references now accept flow names with a hyphen, such as
$flow.tracking-server.url.
The walkeros://reference/packages resource answers
{ catalog, count, warnings? } instead of a bare array, so a failed npm search
appears as a warning and a partial catalog is never read as an empty one.
Tag Mode's loader now hears the app's call on sites that block messages from
origins they do not know.
Auth header records skip undefined values.
A flow's logger.level and logger.format apply to the collector's lines, and
the format to the runner's own lines; --verbose still forces DEBUG. A flow's
logger.level, DEBUG included, applies to the collector's lines without
--verbose. flow handler error logs at ERROR, a rejected heartbeat at WARN,
and Node process warnings go through the logger at WARN.
sendServer now throws a StepError instead of resolving { ok: false }:
transport with the status and body, timeout, or config for an invalid URL.
It runs on the global fetch, and GET and HEAD requests send no body.
Batches by default (config.batch 200 events or 10 seconds) through
pushBatch; each event fails, with the status, when Amplitude does not answer
with 2xx, instead of counting as delivered. A single push waits for its answer
too.
A group name list that holds a non-string is skipped with a DEBUG line naming
the call and the mapping key instead of being sent to Amplitude, so malformed
group data is never delivered.
settings.url is required: without it the destination fails to start with a
config error. Requests run on fetch, and a request that fails, times out or
gets an error status is reported as a failed push.
Failures are reported with the AWS error name as code, the HTTP status and a
correct retryable flag, so a missing stream, denied access or missing
credentials is no longer retried. Messages name the stream or topic, region and
fix, not AWS's text. Settings schemas require a stream name (Firehose) or topic
ARN or name (SNS).
A failed insert carries its ClickHouse code or network error and, for a missing
table or too many parts, the fix in its message. The destination no longer logs
a line of its own, and the server's text stays out of the message. Rules with
silent: true insert nothing.
Each push waits until its line is written; a failed open or write fails the push
instead of a warning, and a stream error no longer crashes the process. A
dynamic filename that resolves to nothing fails with missing-input. Rules with
silent: true write nothing.
BigQuery opens its writer at the first push instead of at startup; a missing
dataset or table fails that push with the setup hint. BigQuery and Pub/Sub
failures are reported once by the collector instead of being logged twice, and
their messages name codes, not row data.
An append that the BigQuery server rejects (quota, permission, a dropped table)
now fails the push instead of counting as delivered. Pub/Sub retries only the
error codes its SDK retries.
settings.batch and settings.batchSize are removed: batch with the
destination's config.batch, which sends through HubSpot's batch API in chunks
of 500 and reports each event's own outcome. An event without email or objectId
fails with missing-input, and a failed HubSpot call fails the push with its
status.
The producer connects at the first push, so a flow starts without a reachable
broker. A failed send fails the push instead of being logged and counted
delivered; an unknown topic is a config error with the setup hint. Rules with
silent: true send nothing.
A send is retried only when kafkajs marks the error as retriable. Deterministic
failures, such as an unknown topic or a rejected message, are final and fail the
push once instead of being retried.
An event that Pinterest reports as failed in its HTTP 200 answer now fails the
push instead of counting as delivered, with the dead-letter queue and one ERROR
line. Rules with silent: true send nothing.
Events that no rule maps, unknown methods, invalid arguments and a missing page
URL fail with missing-input; use an ignore: true rule to keep an event out
without a failure. Piwik PRO's own 429 warning is replaced by the collector's
line with the status.
settings.include is removed; it had no effect, use config.include. Events
batch by default (config.batch 20 events or 10 seconds) through pushBatch; a
failed send fails its events and lands them in the dead-letter queue instead of
counting as delivered.
A failed XADD fails the push and lands in the dead-letter queue instead of being
logged and counted delivered. Connection errors are reported through the
collector, and shutdown no longer waits on a disconnected client. Rules with
silent: true append nothing.
Batches by default (config.batch 20 events or 10 seconds) through pushBatch;
each event's own send decides its outcome, so a failed send fails that event
instead of counting as delivered, and no longer causes an unhandled promise
rejection. An event without userId or anonymousId fails with missing-input.
Batches by default (config.batch 15 events or 10 seconds) through pushBatch;
each event's own delivery decides its outcome, so a failed delivery fails that
event and lands in the dead-letter queue instead of counting as delivered. An
event without userId or anonymousId fails with missing-input.
reply_broadcast is sent only together with a thread. A broadcast flag set on a
message that has no thread to reply to is no longer passed on to Slack, so the
message is posted as an ordinary message.
A constraint error on insert is final and not retried, and a missing table named
by a rule's table setting is a config error. The legacy schema: 'auto'
also creates the rule tables.
The table check and the prepared insert run at the first push, so a flow starts
without querying the database. A missing table fails the push with the setup
hint, and a failed INSERT fails the push instead of being logged and counted
delivered.
A request that TikTok answers with HTTP 200 and a non-zero code now fails the
push instead of counting as delivered; rate limits and server errors are
retryable, other rejections are not. Rules with silent: true send nothing.
A silent rule whose identify, group or similar mapping resolves to nothing sends
nothing, counts as delivered and writes one DEBUG line naming the call and
mapping key, never the value. HubSpot's warning and Mixpanel's error for this
case are now DEBUG, because nothing was lost.
A failed request is reported as a failed push (dead-letter queue, one ERROR line
with the status) instead of counting as delivered or being logged at debug.
Events a destination cannot send, such as a missing user identifier, fail with
missing-input. Rules with silent: true send no event of their own.
Retries stay inside the destination's config.timeout: a quarter of it per
attempt, at most two retries, and a value set in the settings wins. RudderStack
gains a timeout setting. Events carry event.id as the vendor's dedup id
(PostHog on capture only), so at-least-once delivery drops duplicates.
Failures carry the vendor's HTTP status, so a 4xx other than 429 is not retried
and a 429 or 5xx is. Data Manager's settings.logLevel is removed; it was never
read.
The Request body rejected debug line carries only the status and content type,
never a body sample or the parser message. In respond-first mode a later
per-event failure logs at DEBUG, because the collector's ERROR line is the
signal.
A per-event "not processed" answer in a batch logs at DEBUG, because the
collector's ERROR line is the signal. A push that throws inside a batch logs an
ERROR and answers Internal server error, never the error text.
A push that failed for a reason a resend cannot fix (a missing setting, a 4xx, a
destination that is not ready) is acknowledged: SQS deletes the message, Pub/Sub
acks it, HTTP sources answer 200. Only retryable failures nack or answer 500, or
207 for a batch, with the fixed text Event was not processed.
A nack for a retryable failure logs at DEBUG, because the collector's ERROR line
reports the cause once per minute instead of one line per message. Transient
Pub/Sub stream errors and SQS receive errors that the SDK or loop retries log at
WARN instead of ERROR.
A path that is a directory is a miss, like a missing file, instead of a failed
read. A read that fails for another reason, such as a permission error, still
throws and is reported by the collector.
A read, write or delete that fails (403, 5xx, network) now throws instead of
looking like a missing key, and a stored value that cannot be decoded throws
too. The collector reports a failed read as a miss with one error line. S3 and
Sheets check their bucket or spreadsheet at the first operation instead of at
startup.
settings.salt is required; without it the transformer does not start. An event
whose IP, user agent and fields all resolve empty fails with missing-input
instead of getting the hash every other visitor gets.
A request with more events than settings.maxEvents is dropped whole and now
writes an ERROR line, no longer a WARN, because those events are lost. Raise
maxEvents if your own server-to-server calls send larger batches.
An event without a GA4 request in its ingest now fails with missing-input
instead of being dropped at debug level, so a source missing config.ingest
shows as an error. A request that cannot be decoded fails the event with code
transformer.
GA4 purchase and refund hits now decode the order amount into data.value
instead of data.total, matching the standard order complete event. A mapping
that reads data.total from these events must read data.value now.
loadVendor injects the script into the document that env provides, falling
back to the page's own, and fails with vendor-not-ready when there is no
document at all. The new @walkeros/web-core/dev entry exports
mockScriptDocument() for examples and tests that run without a browser.
@walkeros/web-core exports moin, browserSwapActivator and
pageObserveSlot, and @walkeros/web-core/dev exports page for the
conformance tests of web steps.
sendWeb now throws a StepError instead of resolving { ok: false }:
transport with the status for an error answer or a network error, timeout,
or beacon-rejected. A fetch skips keepalive past the browser's 64 KiB limit.
New loadVendor loads a vendor script once per page and reports
vendor-not-loaded, vendor-not-ready or vendor-timeout.
Amplitude loads each plugin package only when that plugin is enabled. The
package now imports cleanly under Node and tsx, so simulation and tooling no
longer fail on a browser-only plugin that the flow never uses.
Without Session Replay, Experiment or Guides & Surveys settings only Amplitude
analytics starts; enabling one still starts all three. Options now reach the SDK
(autocapture: false was lost before). A missing apiKey is a config error
and a failed init is retried by the next push.
A failed request (an error status, a network error, a timeout, a beacon the
browser refuses) is reported as a failed push instead of counting as delivered.
Without settings.url the destination fails with a config error. Batches over
64 KiB are no longer sent with keepalive.
Without settings.consent, a consent-gated destination grants
analytics_Storage only and denies both categories on a revocation.
ad_Storage follows only settings.consent, so a grant no longer switches
advertising storage on by itself.
A GA4 parameter whose name is empty, or collides with another after
normalization, is dropped with one ERROR line per cause and the event is still
sent; before, it logged a warning per event and the later value overwrote the
earlier. gtag.js and GTM containers load through walkerOS.
Validation rejects disablePageviewAutocapture, disableSessionReplay and
ingestServer: the classic Heap SDK never read them (use heapConfig). Events
pushed before Heap loads now reach it, consent commands no longer stall the
destination, and a missing appId is a config error.
An event that no rule maps fails with missing-input instead of a warning. An
invalid goalId sends the hit without the goal and logs one ERROR per cause. A
consent revocation sends forgetConsentGiven, stopping link tracking too, and
silent rules send only their goal.
Events no longer vanish while the Piano script loads: pushes wait for it, and
fail visibly when it is blocked or missing. A missing site or collectDomain
is a config error, and rules with silent: true send no event.
Events that no rule maps fail with missing-input instead of a warning; use an
ignore: true rule to keep an event out without a failure. A blocked ppms.js
fails later events visibly.
cdnURL now reaches Analytics.js; before it was ignored, so a proxy was
bypassed. A missing apiKey is a config error, a failed Analytics.js load
fails later events, and a consent-gated destination loads Analytics.js on its
first event after the grant.
On a revocation, activity tracking and the link, form, button and YouTube
plugins switch off and come back on a grant. Error tracking and FocalMeter have
no off switch, so a revocation fails visibly (consent-not-applied). With
settings.tracker, URL plugins fail at init with a config error.
A destination with config.consent now tells the vendor about consent changes:
Meta calls fbq('consent', 'revoke' | 'grant'), Matomo forgetConsentGiven and
setConsentGiven, Clarity sends ConsentV2, FullStory stops and starts capture,
and Piwik PRO turns off the link tracking it enabled.
A consent handler decides on the whole consent state against the destination's
own config.consent and acts only when a change touches its required keys.
Optimizely keeps its client across a revoke and a grant, instead of closing it.
A silent rule whose identify, group or similar mapping resolves to nothing sends
nothing, counts as delivered and writes one DEBUG line naming the call and
mapping key, never the value. An identity or group that is already current
writes nothing.
Settings accept SDK options that the schema does not list and pass them through
to the SDK, so a newer SDK option no longer fails validation. Amplitude events
from a setup with plugins no longer carry the amplitude-ts-unified library
prefix.
Validation rejects skipOdp (Optimizely) and pageViewTracking (Snowplow):
neither was read, so they had no effect. Snowplow's settings schema now lists
every setting the code reads, with collectorUrl required, so a typo in a
setting name now fails validation.
A failed vendor call, a missing required setting (config) and an event that
cannot be sent (missing-input) are reported as failed pushes, with the
dead-letter queue and one ERROR line, instead of counting as delivered. Rules
with silent: true send no event. Settings schemas reject unknown keys.
Web destinations that load a vendor script inject it through the document of
their env, so simulation and server-side rendering work without a browser
page. A page still gets the script in its own document by default, and a missing
document fails the push with vendor-not-ready.
An event that no rule maps fails with missing-input in Snowplow; add an
ignore: true rule for events you do not send. Optimizely fails events from
visitors without the configured userId with missing-input instead of
dropping them.
Vendor scripts load through walkerOS: a blocked or slow script fails later
events visibly (vendor-not-loaded, vendor-timeout) instead of losing them.
Pushes made while the script loads wait for it, and each script is injected once
per page.
New: the source sends walker flush when the page goes away (pagehide, or the
document turning hidden), so batched events are delivered. A failing elbLayer
entry now logs one ERROR naming its event, never its data, and counts as failed.
Works without an injected window: the source reads the page's own, so the
documented integration captures dataLayer.push calls in integrated and bundled
mode alike, with no env needed. In tests, env.window replaces it.
Published Packages
- @walkeros/cli
- @walkeros/collector
- @walkeros/config
- @walkeros/core
- @walkeros/explorer
- @walkeros/mcp
- @walkeros/runner
- @walkeros/server-core
- @walkeros/server-destination-amplitude
- @walkeros/server-destination-api
- @walkeros/server-destination-aws
- @walkeros/server-destination-bing
- @walkeros/server-destination-clickhouse
- @walkeros/server-destination-criteo
- @walkeros/server-destination-customerio
- @walkeros/server-destination-datamanager
- @walkeros/server-destination-file
- @walkeros/server-destination-gcp
- @walkeros/server-destination-hubspot
- @walkeros/server-destination-kafka
- @walkeros/server-destination-klaviyo
- @walkeros/server-destination-linkedin
- @walkeros/server-destination-meta
- @walkeros/server-destination-mixpanel
- @walkeros/server-destination-mparticle
- @walkeros/server-destination-pinterest
- @walkeros/server-destination-piwikpro
- @walkeros/server-destination-posthog
- @walkeros/server-destination-reddit
- @walkeros/server-destination-redis
- @walkeros/server-destination-rudderstack
- @walkeros/server-destination-segment
- @walkeros/server-destination-slack
- @walkeros/server-destination-snapchat
- @walkeros/server-destination-sqlite
- @walkeros/server-destination-tiktok
- @walkeros/server-destination-twitter
- @walkeros/server-source-aws
- @walkeros/server-source-express
- @walkeros/server-source-fetch
- @walkeros/server-source-gcp
- @walkeros/server-store-fs
- @walkeros/server-store-gcs
- @walkeros/server-store-s3
- @walkeros/server-store-sheets
- @walkeros/server-transformer-fingerprint
- @walkeros/transformer-ga4
- @walkeros/web-core
- @walkeros/web-destination-amplitude
- @walkeros/web-destination-api
- @walkeros/web-destination-clarity
- @walkeros/web-destination-d8a
- @walkeros/web-destination-fullstory
- @walkeros/web-destination-gtag
- @walkeros/web-destination-heap
- @walkeros/web-destination-hotjar
- @walkeros/web-destination-linkedin
- @walkeros/web-destination-matomo
- @walkeros/web-destination-meta
- @walkeros/web-destination-mixpanel
- @walkeros/web-destination-optimizely
- @walkeros/web-destination-piano
- @walkeros/web-destination-pinterest
- @walkeros/web-destination-piwikpro
- @walkeros/web-destination-plausible
- @walkeros/web-destination-posthog
- @walkeros/web-destination-segment
- @walkeros/web-destination-snowplow
- @walkeros/web-destination-tiktok
- @walkeros/web-source-browser
- @walkeros/web-source-datalayer