Eventvisor

Migration guides

Migrating from pre-release packages to v1

This guide covers the intentional breaking changes between Eventvisor 0.x packages and the stable v1 release. It includes project definitions, CLI workflows, JavaScript and React applications, and custom modules.


Migrate in stages

Treat the project and its consuming applications as two parts of the migration:

  1. Prepare applications: upgrade the SDK and modules, update custom module code, and deploy application changes that can consume the current datafile.
  2. Upgrade the Eventvisor project: update the CLI, add Targets, migrate test expectations, then lint, test, build, and deploy the v1 datafiles.

Generated datafiles still use schema version 1. The v1 SDK can consume existing 0.x datafiles, which makes it possible to upgrade applications before changing the project output.

Do not assume that a 0.x SDK understands v1-only definitions or corrected runtime semantics. Upgrade every consuming application before deploying datafiles that depend on the new sampling, validation failure, condition, effect, or module behaviour described below.

Review sampling before rollout

Sampling evaluation was corrected in v1. Any project using sample should compare the old and new behaviour in a staging application before publishing v1 datafiles. A configured percentage now represents the documented percentage.


Project and CLI

Upgrade the CLI Breaking

Update the project dependency:

Command
$ npm install --save @eventvisor/cli@1

Eventvisor v1 CLI development and execution require Node.js 24 or newer.

Targets replace tag datafiles Breaking

In 0.x, every configured tag generated a datafile such as eventvisor-tag-web.json. In v1, tags are selection metadata and only named Targets generate normal datafiles.

Create at least one Target in every project or Set:

Before
eventvisor.config.js
module.exports = {
tags: ["web", "backend"],
};
Before
datafiles/
├── eventvisor-tag-web.json
└── eventvisor-tag-backend.json
After
targets/web.yml
description: Web application
tag: web
After
targets/backend.yml
description: Backend services
tag: backend
After
datafiles/
├── eventvisor-web.json
└── eventvisor-backend.json

Update application URLs and deployment scripts to use the new filenames. Targets can also include and exclude entity keys with glob-like patterns. Eventvisor retains referenced attributes, destinations, effects, quarantine destinations, and other runtime dependencies.

Selective commands accept repeatable --target and --tag options. When both are present, the filters use AND semantics.

State directory configuration removed Breaking

The unused statesDirectoryPath configuration property and states/ entity type have been removed. Effect state remains supported and lives inside each effect definition.

Remove statesDirectoryPath from eventvisor.config.js. No effect state migration is required.

Reusable Schemas New

Shared JSON Schema structures can now live in schemas/ and be referenced by events, attributes, nested properties, and array items:

schemas/identifier.yml
description: Stable identifier
type: string
minLength: 1
events/order_completed.yml
description: Order completed
type: object
properties:
orderId:
schema: identifier
required:
- orderId

References are resolved and inlined during linting, code generation, and datafile building. SDK datafiles do not contain a separate Schema collection.

Sets and promotions New

Sets let one repository contain independent definition trees. They can model release environments or other isolated projects:

eventvisor.config.js
module.exports = {
sets: true,
promotionFlows: [
{ from: "development", to: "staging" },
{ from: "staging", to: "production" },
],
};

Use Promotions to preview and apply dependency-aware changes between Sets. Existing projects do not need to enable Sets.

Built in parsers moved to their own package Internal

YAML and JSON parsing now comes from @eventvisor/parsers, which is installed through the CLI. Existing parser: "yml" and parser: "json" configuration continues to work.

Custom parsers must now implement both parse and stringify. The stringify function is needed for promotion and other editorial writes.

Catalog development workflow Changed

Running the Catalog without a subcommand now exports, serves, watches project files, and reloads connected browser pages:

Command
$ npx eventvisor catalog

Use catalog export for a one-time static export and catalog serve to serve an existing export. Browser routing is the default. Pass --hash-router only when a static host cannot serve index.html for application routes.

The v1 Catalog adds reusable Schemas, Targets, dependency usage, expanded matrix tests, entity history, and Set switching.

New inspection commands New

The CLI now includes commands for inspecting and exercising a project:

Command
$ npx eventvisor list event
$ npx eventvisor info event page_view
$ npx eventvisor find-usage attribute userId
$ npx eventvisor simulate page_view --value='{"url":"https://example.com"}'
$ npx eventvisor benchmark page_view -n 1000000

Use find-usage --unused-attributes --unused-schemas --unused-destinations to find known unused definitions.


Project definitions

Events must resolve to objects Breaking

Every event must now resolve to type: object, either directly or through a reusable Schema. Add the type where old definitions relied on an implicit object shape:

events/page_view.yml
description: Page view
type: object
properties:
url:
type: string

When an event shape changes incompatibly, create a new event key. Eventvisor deliberately does not add a separate event version field.

Object properties are strict Breaking

Object payloads reject properties that are not declared in properties. During gradual adoption, opt out explicitly at the appropriate object boundary:

type: object
additionalProperties: true

Remove additionalProperties: true after all producers follow the declared contract.

Validation failure policies New

Invalid events use the project default, which is drop. During a migration you can deliver the original payload with validation details:

eventvisor.config.js
module.exports = {
onValidationFailure: "deliverWithWarning",
};

Or quarantine invalid events to one destination:

events/order_completed.yml
onValidationFailure:
action: quarantine
destination: invalidEvents

An event-level policy overrides the project default. Quarantine bypasses normal destinations and effects.

Sampling follows the documented scale Fix

Sampling percentages and ranges now correctly use the 0 through 100 scale with up to three decimal places. For example, percentage: 10 selects approximately ten per cent of bucket keys.

The stable MurmurHash bucketing algorithm and bucket assignments have not changed. The v1 fixes are in evaluation around those buckets:

  • a percentage now compares against the correct 100,000 bucket scale
  • a range is start inclusive and end exclusive
  • conditional sample arrays use the first matching rule
  • a missing or empty bucket key fails closed

Pre-release sampling did not consistently honour these rules. Measure representative traffic with simulate, project tests, and a staging application before rollout.

not uses implicit AND semantics Breaking

Direct children of not are treated as an implicit AND. This expression means “not all of these conditions match”:

not:
- attribute: country
operator: equals
value: NL
- attribute: plan
operator: equals
value: premium

To preserve the pre-release “none of these match” behaviour, wrap the children in one explicit or group before deploying v1:

not:
- or:
- attribute: country
operator: equals
value: NL
- attribute: plan
operator: equals
value: premium

The explicit group has the same meaning in both 0.x and v1 SDKs, which helps during a gradual application rollout.

Empty and, or, and not groups are rejected by linting and fail closed at runtime.

Portable conditions Breaking

Regular expressions use a portable subset shared by JavaScript and Java SDKs. Only unique g, i, m, and s flags are accepted. Lookaround, named or noncapturing groups, backreferences, inline modes, atomic groups, and possessive quantifiers are rejected.

Values used by before and after must be complete ISO 8601 date and time strings with a timezone. Invalid regular expressions, dates, and semantic versions fail closed.

Existence checks treat null as defined. Membership operators consistently support string, number, boolean, and null values.

Sources and transforms are type-safe Changed

Direct source, attribute, state, effect, payload, and lookup fields accept one source or a nonempty ordered array. A definition cannot set several direct source fields at once.

Transform options are validated according to their transform type. The append transform is now available. For increment and decrement with a target, the current target value is the numeric input and value is the operand.

Unsafe object paths and values that cannot be represented safely in transport payloads are rejected.

Effect errors stop by default Breaking

In 0.x, a failed effect step could continue unless continueOnError was explicitly false. In v1, a failed handler stops the remaining steps by default.

Set continueOnError: true only where later steps are safe and meaningful after a failure:

steps:
- handler: audit.record
continueOnError: true
- handler: notification.send

Effect handlers that track another Eventvisor event should use the module API track() function. It carries the active effect chain and prevents recursive effect cycles.


Tests

Pipeline acceptance has a dedicated expectation Breaking

expectedToContinue has been replaced by expectedToBeTracked:

Before
expectedToBeValid: true
expectedToContinue: true
After
expectedToBeValid: true
expectedToBeTracked: true

expectedToBeValid checks the event payload schema. expectedToBeTracked checks whether required attributes, validation policy, conditions, and sampling accepted the event through the complete governance pipeline.

Assertions must test behaviour Breaking

Every test spec needs at least one assertion. Each assertion needs a meaningful action and expectation. Filters that match no specs or assertions now fail instead of reporting a false passing result.

The unused event assertion at field and unsupported expectedToBeBatched and expectedBatchedCount fields have been removed. Use expectedBody, expectedBodies, and assertAfter for destination behaviour.

Matrix assertions New

Assertions can define a matrix whose Cartesian product is executed by both the tester and Catalog:

event: page_view
assertions:
- description: tracks on ${{ device }} in ${{ country }}
matrix:
device: [mobile, desktop]
country: [NL, DE]
track:
device: ${{ device }}
withAttributes:
country: ${{ country }}
expectedToBeTracked: true

A matrix must contain at least one key and every key must contain at least one value.


JavaScript SDK

Upgrade the SDK and modules Breaking

Command
$ npm install --save @eventvisor/sdk@1

Upgrade every installed @eventvisor/module-* package to the v1 major at the same time. React applications should also install @eventvisor/react@1.

Factory and options renamed Breaking

Before
import {
createInstance,
type InstanceOptions,
} from "@eventvisor/sdk";
const options: InstanceOptions = { datafile };
const eventvisor = createInstance(options);
After
import {
createEventvisor,
type EventvisorOptions,
} from "@eventvisor/sdk";
const options: EventvisorOptions = { datafile };
const eventvisor = createEventvisor(options);

Use the exported Eventvisor type when passing the instance through application code.

DatafileReader, managers, evaluators, the logger factory, and other internal classes are no longer public. Import only from the package root.

Operations are asynchronous Breaking

The old fire-and-forget methods and separate async aliases have become one Promise based API:

Before
eventvisor.setAttribute("userId", "123");
await eventvisor.setAttributeAsync("userId", "123");
eventvisor.track("page_view", payload);
await eventvisor.trackAsync("page_view", payload);
After
await eventvisor.setAttribute("userId", "123");
await eventvisor.track("page_view", payload);

track, setAttribute, removeAttribute, setDatafile, removeModule, flush, and close are asynchronous. Public operations are serialized in call order, including calls made before readiness.

Datafiles merge by default Soft breaking

In 0.x, setDatafile() replaced the active datafile. In v1, it merges incoming attributes, events, destinations, and effects by default:

await eventvisor.setDatafile(additionalDatafile);

Pass true to retain the old replacement behaviour:

await eventvisor.setDatafile(nextDatafile, true);

The method accepts a parsed object or JSON string. Parse failures keep the active datafile and report a diagnostic containing Could not parse datafile. The datafile_set event now contains { replaced }.

Modules use a focused API Breaking

Rename Module to EventvisorModule and registerModule() to addModule():

Before
import type { Module } from "@eventvisor/sdk";
const module: Module = {
name: "warehouse",
async transport(options, deps) {
deps.logger.info("sending");
},
};
eventvisor.registerModule(module);
After
import type { EventvisorModule } from "@eventvisor/sdk";
const module: EventvisorModule = {
name: "warehouse",
setup(api) {
api.reportDiagnostic({
level: "info",
code: "warehouse_ready",
message: "Warehouse module is ready",
details: {},
});
},
async transport(options, api) {
// Send options.payload.
},
};
const remove = eventvisor.addModule(module);
await remove?.();

The module API exposes getRevision, onDiagnostic, reportDiagnostic, and cycle-safe track. Modules can implement setup, flush, and close lifecycle functions. Duplicate names and lifecycle failures are reported through diagnostics.

Diagnostics replace custom logging Breaking

The logger option and public createLogger API have been removed. Use logLevel, onDiagnostic, or a later diagnostic subscription:

const eventvisor = createEventvisor({
datafile,
logLevel: "warn",
onDiagnostic(diagnostic) {
observability.capture(diagnostic);
},
});
const unsubscribe = eventvisor.onDiagnostic(handleDiagnostic);

Diagnostics contain a stable code, level, message, details, and optional module or error information. Error diagnostics also emit the SDK error event.

Delivery and lifecycle Changed

Selected destination transports start in parallel and receive the active datafile revision. track() waits for each selected transport attempt to settle, but transport modules decide whether that attempt means immediate delivery or queue acceptance.

Use await eventvisor.flush() to ask queueing modules to attempt buffered work. close() waits for readiness, flushes modules, closes their resources, and clears SDK event listeners and diagnostic subscriptions.

The new @eventvisor/module-http provides bounded batching and retries. The new @eventvisor/module-beacon handles browser page lifecycle delivery on a best effort basis.

Pixel scripts are disabled by default Breaking

The Pixel module still injects ordinary markup, but ignores script elements unless application code opts in:

createPixelModule({
allowScripts: true,
nonce: () => window.__cspNonce,
});

Only enable scripts when the datafile publishing path is trusted and protected. Review the security guide and apply a restrictive Content Security Policy.


React

React hooks now use explicit Eventvisor names:

Before
import {
useInstance,
isReady,
useEventvisor,
} from "@eventvisor/react";
After
import {
useEventvisorInstance,
useEventvisorReady,
useEventvisor,
} from "@eventvisor/react";

useEventvisorAttribute and useEventvisorAttributes provide reactive attribute values. Hooks now throw a clear error outside EventvisorProvider, reset readiness when the provider instance changes, and return stable bound operations for an unchanged instance.


Verification checklist

Before deploying v1 datafiles:

  1. Upgrade every consuming SDK and module package.
  2. Rewrite direct multi-child not groups with an explicit or when preserving the 0.x meaning.
  3. Review every sampling rule against the corrected percentage and range behaviour.
  4. Add at least one Target to every project or Set.
  5. Update datafile URLs from eventvisor-tag-<tag>.json to eventvisor-<target>.json.
  6. Remove statesDirectoryPath and obsolete test fields.
  7. Update custom modules and React hook imports.
  8. Run project linting and tests.
  9. Build every Target and inspect the generated file list.
  10. Exercise representative events with simulate and a staging application.
Command
$ npx eventvisor lint
$ npx eventvisor test
$ npx eventvisor build

For Set projects, repeat focused checks for every release lane before promotion or deployment.

Previous
Cloudflare Pages