From fc9b34100a7e1acd77eebd6c937704a86996d17c Mon Sep 17 00:00:00 2001 From: aylaju <99686309+aylaju@users.noreply.github.com> Date: Tue, 2 Sep 2025 09:57:35 +0200 Subject: [PATCH 01/13] packages update --- packages/collector/README.md | 130 +++++--- packages/core/README.md | 345 ++++++++++++++++---- packages/server/destinations/meta/README.md | 42 +-- packages/web/core/README.md | 342 ++++++++++++++++--- packages/web/sources/browser/README.md | 153 +++++---- packages/web/sources/dataLayer/README.md | 35 +- 6 files changed, 788 insertions(+), 259 deletions(-) diff --git a/packages/collector/README.md b/packages/collector/README.md index 59f59e3bf..aed36e72b 100644 --- a/packages/collector/README.md +++ b/packages/collector/README.md @@ -1,56 +1,47 @@

- +

# Collector for walkerOS -The walkerOS Collector is the central event processing engine that unifies data -collection across web and server environments. It acts as the orchestrator -between sources (where events originate) and destinations (where events are -sent), providing consistent event processing, consent management, and data -validation across your entire data collection infrastructure. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/collector) +• [NPM Package](https://www.npmjs.com/package/@walkeros/collector) -## Role in walkerOS Ecosystem +The collector is the central **processing engine** of walkerOS that receives +events from sources, **enriches** them with additional data, applies consent +rules, and **routes** them to destinations. It acts as the **intelligent +middleware** between event capture and event delivery. -walkerOS follows a **source → collector → destination** architecture: +### What it does -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) +The Collector transforms raw events into enriched, compliant data streams by: -The Collector serves as the foundation that both web and server sources depend -on, ensuring consistent event handling regardless of the environment. +- **Event processing** - Validates, normalizes, and enriches incoming events +- **Consent management** - Applies privacy rules and user consent preferences +- **Data enrichment** - Adds session data, user context, and custom properties +- **Destination routing** - Sends processed events to configured analytics + platforms -## Installation - -```sh -npm install @walkeros/collector -``` - -## Usage +### Key features -The collector provides a factory function for creating collector instances: +- **Compatibility** - Works in both web browsers and server environments +- **Privacy-first** - Built-in consent management and data protection +- **Event validation** - Ensures data quality and consistency +- **Flexible routing** - Send events to multiple destinations simultaneously -```typescript -import { createCollector } from '@walkeros/collector'; +### Role in architecture -// Basic setup -const { collector, elb } = await createCollector({ - consent: { functional: true }, - destinations: [ - // Add your destinations here - ], -}); +In the walkerOS data flow, the collector sits between sources and destinations: -// Process events - use elb as the standard API -await elb('page view', { - page: '/home', -}); ``` +Sources → Collector → Destinations +``` + +Sources capture events and send them to the collector, which processes and +routes them to your chosen destinations like Google Analytics, custom APIs, or +data warehouses. ## Event Naming Convention @@ -78,15 +69,66 @@ The collector will validate event names and destinations handle platform-specific transformations. If the event name isn't separated into entity action by space the collector won't process it. -## Core Features +## Installation + +```bash +npm install @walkeros/collector +``` + +## Setup + +### Basic setup + +```typescript +import { createCollector } from '@walkeros/collector'; + +const config = { + run: true, + consent: { functional: true }, + sources: [ + // add your event sources + ] + }, +}; + +const { collector, elb } = await createCollector(config); +``` + +### Advanced setup + +```typescript +import { createCollector } from '@walkeros/collector'; + +const { collector, elb } = await createCollector({ + run: true, + consent: { functional: true }, + sources: [ + // add your event sources + ], + destinations: [ + // add your event destinations + ], + verbose: true, + onError: (error: unknown) => { + console.error('Collector error:', error); + }, + onLog: (message: string, level: 'debug' | 'info' | 'warn' | 'error') => { + console.log(`[${level}] ${message}`); + }, +}); +``` -- **Event Processing**: Validates and enriches events with context and metadata -- **Consent Management**: Respects user consent preferences across all - destinations -- **Destination Routing**: Translates and routes events to configured - destinations -- **State Management**: Maintains consistent state across the collection - pipeline +## Configuration + +| Name | Type | Description | Required | Example | +| -------------- | ---------- | -------------------------------------------------------------- | -------- | ------------------------------------------ | +| `run` | `boolean` | Automatically start the collector pipeline on initialization | No | `true` | +| `sources` | `array` | Configurations for sources providing events to the collector | No | `[{ source, config }]` | +| `destinations` | `array` | Configurations for destinations receiving processed events | No | `[{ destination, config }]` | +| `consent` | `object` | Initial consent state to control routing of events | No | `{ analytics: true, marketing: false }` | +| `verbose` | `boolean` | Enable verbose logging for debugging | No | `false` | +| `onError` | `function` | Error handler triggered when the collector encounters failures | No | `(error) => console.error(error)` | +| `onLog` | `function` | Custom log handler for collector messages | No | `(message, level) => console.log(message)` | ## Contribute diff --git a/packages/core/README.md b/packages/core/README.md index 205c3d31a..c16fea9d3 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -1,108 +1,313 @@

- +

# Core Types & Utilities for walkerOS -The walkerOS Core package provides the foundational TypeScript definitions and -platform-agnostic utilities that power the entire walkerOS ecosystem. It serves -as the bedrock for type safety and shared functionality across all sources, -collectors, and destinations. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/core) +• [NPM Package](https://www.npmjs.com/package/@walkeros/core) -## Role in walkerOS Ecosystem +Core utilities are a collection of platform-agnostic functions that can be used +across all walkerOS environments. They provide standardized building blocks for +data manipulation, validation, mapping, and more. -walkerOS follows a **source → collector → destination** architecture: +## Installation -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) +Import the core utilities directly from the `@walkeros/core` package: -The Core package provides the essential building blocks that all other packages -depend on, ensuring consistent data structures, type definitions, and utility -functions across the entire platform. +```ts +import { assign, anonymizeIP, getMappingValue } from '@walkeros/core'; +``` -## Installation +## Core Utilities + +### Data Manipulation + +#### assign + +`assign(target: T, source: U, options?): T & U` merges two objects with +advanced merging capabilities. It has special behavior for arrays: when merging, +it concatenates arrays from both objects, removing duplicates. + +```ts +interface AssignOptions { + merge?: boolean; // Merge array properties (default: true) + shallow?: boolean; // Create shallow copy (default: true) + extend?: boolean; // Extend with new properties (default: true) +} + +const obj1 = { a: 1, b: [1, 2] }; +const obj2 = { b: [2, 3], c: 3 }; + +assign(obj1, obj2); // Returns { a: 1, b: [1, 2, 3], c: 3 } +assign(obj1, obj2, { merge: false }); // Returns { a: 1, b: [2, 3], c: 3 } +``` + +#### Path Operations + +##### getByPath + +`getByPath(object: unknown, path: string, defaultValue?: unknown): unknown` +accesses nested properties using dot notation. Supports wildcard `*` for array +iteration. + +```js +getByPath({ data: { id: 'wow' } }, 'data.id'); // Returns "wow" +getByPath({ nested: [1, 2, { id: 'cool' }] }, 'nested.*.id'); // Returns ['', '', 'cool'] +getByPath({ arr: ['foo', 'bar'] }, 'arr.1'); // Returns "bar" +``` + +##### setByPath + +`setByPath(object: WalkerOS.Event, path: string, value: unknown): WalkerOS.Event` +sets nested values using dot notation, returning a new object with the updated +value. + +```js +const updatedEvent = setByPath(event, 'data.id', 'new-value'); +// Returns a new event with data.id set to 'new-value' +``` + +#### clone + +`clone(original: T): T` creates a deep copy of objects/arrays with circular +reference handling. -```sh -npm install @walkeros/core +```js +const original = { foo: true, arr: ['a', 'b'] }; +const cloned = clone(original); +original.foo = false; // cloned.foo remains true ``` -## Usage +#### castValue -The core package exports essential types and utilities: +`castValue(value: unknown): WalkerOS.PropertyType` converts string values to +appropriate types (number, boolean). -```typescript -import { - // Core event types - WalkerOS, +```js +castValue('123'); // Returns 123 (number) +castValue('true'); // Returns true (boolean) +castValue('hello'); // Returns 'hello' (unchanged) +``` + +### Privacy & Security + +#### anonymizeIP + +`anonymizeIP(ip: string): string` anonymizes IPv4 addresses by setting the last +oclet to zero. + +```js +anonymizeIP('192.168.1.100'); // Returns '192.168.1.0' +``` + +#### Hashing + +`getId(length?: number): string` generates random alphanumeric strings for +unique identifiers. + +```js +getId(); // Returns random 6-char string like 'a1b2c3' +getId(10); // Returns 10-character string +``` + +### Event Processing - // Utility functions - assign, - clone, - validateEvent, +#### getMappingValue - // Consent management - Consent, +`getMappingValue(event: WalkerOS.Event, mapping: Mapping.Data, options?: Mapping.Options): Promise` +extracts values from events using +[mapping configurations](https://www.elbwalker.com/docs/destinations/event-mapping). - // Mapping utilities - byPath, - mapping, -} from '@walkeros/core'; +```ts +// Simple path mapping +await getMappingValue(event, 'data.productId'); -// Example: Validate an event -const event: WalkerOS.Event = { - event: 'order complete', - data: { value: 9001 }, - // ... other properties +// Complex mapping with conditions and loops +const mapping = { + map: { + orderId: 'data.id', + products: { + loop: [ + 'nested', + { + condition: (entity) => entity.type === 'product', + map: { id: 'data.id', name: 'data.name' }, + }, + ], + }, + }, }; +await getMappingValue(event, mapping); +``` -if (validateEvent(event)) { - console.log('Event is valid!'); -} +#### getMappingEvent + +`getMappingEvent(event: WalkerOS.PartialEvent, mapping?: Mapping.Rules): Promise` +finds the appropriate mapping rule for an event. + +### Marketing & Analytics + +#### getMarketingParameters + +`getMarketingParameters(url: URL, custom?: MarketingParameters): WalkerOS.Properties` +extracts UTM and click ID parameters from URLs. + +```js +getMarketingParameters( + new URL('https://example.com/?utm_source=docs&gclid=123'), +); +// Returns { source: "docs", gclid: "123", clickId: "gclid" } + +// With custom parameters +getMarketingParameters(url, { utm_custom: 'custom', partner: 'partnerId' }); ``` -## Event Naming Convention +### Type Validation + +#### Type Checkers + +A comprehensive set of type checking functions: + +- `isString(value)`, `isNumber(value)`, `isBoolean(value)` +- `isArray(value)`, `isObject(value)`, `isFunction(value)` +- `isDefined(value)`, `isSameType(a, b)` +- `isPropertyType(value)` - Checks if value is valid walkerOS property -walkerOS follows a strict **"entity action"** naming convention for events: +#### Property Utilities -✅ **Correct**: Use spaces to separate entity and action +- `castToProperty(value)` - Casts to valid property type +- `filterValues(object)` - Filters object to valid properties only +- `isPropertyType(value)` - Type guard for property validation -```typescript -elb('order complete', { value: 99.99 }); -elb('product add', { id: 'abc123' }); -elb('page view', { path: '/home' }); -elb('user register', { email: 'user@example.com' }); +### Request Handling + +#### requestToData + +`requestToData(parameter: unknown): WalkerOS.AnyObject | undefined` converts +query strings to JavaScript objects with type casting. + +```js +requestToData('a=1&b=true&c=hello&arr[0]=x&arr[1]=y'); +// Returns { a: 1, b: true, c: 'hello', arr: ['x', 'y'] } +``` + +#### requestToParameter + +`requestToParameter(data: WalkerOS.AnyObject): string` converts objects to +URL-encoded query strings. + +```js +requestToParameter({ a: 1, b: true, arr: ['x', 'y'] }); +// Returns 'a=1&b=true&arr[0]=x&arr[1]=y' +``` + +### User Agent Parsing + +#### parseUserAgent + +`parseUserAgent(userAgent?: string): WalkerOS.User` extracts browser, OS, and +device information. + +```js +parseUserAgent(navigator.userAgent); +// Returns { browser: 'Chrome', browserVersion: '91.0', os: 'Windows', ... } ``` -❌ **Incorrect**: Do not use underscores or other separators +Individual functions are also available: + +- `getBrowser(userAgent)` - Returns browser name +- `getBrowserVersion(userAgent)` - Returns browser version +- `getOS(userAgent)` - Returns operating system +- `getOSVersion(userAgent)` - Returns OS version +- `getDeviceType(userAgent)` - Returns 'Desktop', 'Tablet', or 'Mobile' + +### Error Handling -```typescript -// Don't do this -elb('order_complete', data); // Wrong: underscores -elb('orderComplete', data); // Wrong: camelCase -elb('purchase', data); // Wrong: single word +#### tryCatch + +`tryCatch(tryFn: Function, catchFn?: Function, finallyFn?: Function)` wraps +functions with error handling. + +```js +const safeParse = tryCatch(JSON.parse, () => ({})); +safeParse('{"valid": "json"}'); // Parses successfully +safeParse('invalid'); // Returns {} instead of throwing ``` -**Why spaces matter**: walkerOS destinations automatically transform your -semantic event names into platform-specific formats. For example, -`'order complete'` becomes `'purchase'` for Google Analytics 4, while preserving -the original meaning in your data model. +#### tryCatchAsync + +`tryCatchAsync(tryFn: Function, catchFn?: Function, finallyFn?: Function)` for +async operations. + +```js +const safeAsyncCall = tryCatchAsync( + () => fetchUserData(), + (error) => ({ error: 'Failed to load user' }), +); +``` + +### Performance Optimization + +#### debounce + +`debounce(fn: Function, wait?: number)` delays function execution until after +the wait time. + +```js +const debouncedSearch = debounce(searchFunction, 300); +// Only executes after 300ms of inactivity +``` + +#### throttle + +`throttle(fn: Function, wait?: number)` limits function execution frequency. + +```js +const throttledScroll = throttle(scrollHandler, 100); +// Executes at most every 100ms +``` + +### Utilities + +#### trim + +`trim(str: string): string` removes whitespace from string ends. + +#### throwError + +`throwError(message: string)` throws descriptive errors. + +#### onLog + +`onLog(message: unknown, verbose?: boolean)` provides consistent logging. + +```js +onLog('Debug info', true); // Logs message +onLog('Silent message'); // No output +``` + +### Validation + +#### validateEvent + +`validateEvent(obj: unknown, customContracts?: Schema.Contracts): WalkerOS.Event | never` +validates event structure and throws on invalid events. + +#### validateProperty + +Validates that values conform to walkerOS property types. + +--- -## Core Features +For platform-specific utilities, see: -- **TypeScript Definitions**: Complete type system for walkerOS events and - configurations -- **Platform-Agnostic Utilities**: Shared functions for data manipulation and - validation -- **Consent Types**: Standardized consent management interfaces -- **Event Validation**: Built-in validation for event structure and data - integrity -- **Mapping Utilities**: Tools for transforming data between different formats -- **Privacy Utilities**: Functions for data anonymization and privacy compliance +- [Web Core](https://www.elbwalker.com/docs/core/web) - Browser-specific + functions +- [Server Core](https://www.elbwalker.com/docs/core/server) - Node.js server + functions ## Contribute diff --git a/packages/server/destinations/meta/README.md b/packages/server/destinations/meta/README.md index ce313c45f..c17062cc7 100644 --- a/packages/server/destinations/meta/README.md +++ b/packages/server/destinations/meta/README.md @@ -1,30 +1,20 @@

- +

# Meta (CAPI) Destination for walkerOS -This package provides a Meta Conversion API (CAPI) destination for walkerOS. It -allows you to send events to the Meta Conversions API. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/meta) +• +[NPM Package](https://www.npmjs.com/package/@walkeros/server-destination-meta) -[View documentation](https://www.elbwalker.com/docs/destinations/server/meta/) - -## Role in walkerOS Ecosystem - -walkerOS follows a **source → collector → destination** architecture: - -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) - -This Meta CAPI destination receives processed events from the walkerOS collector -and sends them server-to-server to Meta's Conversions API, providing enhanced -data accuracy and attribution for Meta advertising campaigns while bypassing -browser limitations. +walkerOS follows a **source → collector → destination** architecture. This Meta +CAPI destination receives processed events from the walkerOS collector and sends +them server-to-server to Meta's Conversions API, providing enhanced data +accuracy and attribution for Meta advertising campaigns while bypassing browser +limitations. ## Installation @@ -41,13 +31,25 @@ import { elb } from '@walkeros/collector'; import { destinationMeta } from '@walkeros/server-destination-meta'; elb('walker destination', destinationMeta, { - custom: { + settings: { accessToken: 'YOUR_ACCESS_TOKEN', pixelId: 'YOUR_PIXEL_ID', }, }); ``` +## Configuration + +| Name | Type | Description | Required | Example | +| ----------------- | --------------------- | --------------------------------------------------------- | -------- | ---------------------------------------------- | +| `accessToken` | `string` | Meta access token for Conversions API authentication | Yes | `'your_access_token'` | +| `pixelId` | `string` | Meta Pixel ID from your Facebook Business account | Yes | `'1234567890'` | +| `action_source` | `ActionSource` | Source of the event (website, app, phone_call, etc.) | No | `'website'` | +| `doNotHash` | `string[]` | Array of user_data fields that should not be hashed | No | `['client_ip_address', 'client_user_agent']` | +| `test_event_code` | `string` | Test event code for debugging Meta Conversions API events | No | `'TEST12345'` | +| `url` | `string` | Custom URL for Meta Conversions API endpoint | No | `'https://graph.facebook.com/v17.0'` | +| `user_data` | `WalkerOSMapping.Map` | Mapping configuration for user data fields | No | `{ email: 'user.email', phone: 'user.phone' }` | + ## Contribute Feel free to contribute by submitting an diff --git a/packages/web/core/README.md b/packages/web/core/README.md index 56fe9f409..76a823c8d 100644 --- a/packages/web/core/README.md +++ b/packages/web/core/README.md @@ -1,79 +1,321 @@

- +

# Web Core Utilities for walkerOS -The walkerOS Web Core package provides browser-specific utilities and functions -that power web-based data collection. It extends the platform-agnostic Core -package with web-specific functionality for DOM interaction, session management, -and browser environment detection. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/core) +• [NPM Package](https://www.npmjs.com/package/@walkeros/web-core) -## Role in walkerOS Ecosystem +Web core utilities are browser-specific functions designed for client-side +walkerOS implementations. These utilities handle DOM interactions, browser +information, storage, sessions, and web-based communication. -walkerOS follows a **source → collector → destination** architecture: +## Installation + +Import web utilities from the `@walkeros/web-core` package: -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) +```ts +import { getAttribute, sendWeb, sessionStart } from '@walkeros/web-core'; +``` -The Web Core package serves as the foundation for all web-based sources and -destinations, providing essential browser utilities, session handling, and -web-specific event processing capabilities. +## Utilities -## Installation +### DOM Utilities + +#### getAttribute + +`getAttribute(element: Element, name: string): string` retrieves attribute +values from DOM elements with enhanced handling. + +```js +const element = document.querySelector('[data-elb="product"]'); +const entityType = getAttribute(element, 'data-elb'); // Returns 'product' +``` + +#### Attribute Parsing + +##### splitAttribute + +`splitAttribute(str: string, separator?: string): string[]` splits attribute +strings using specified separators. + +```js +splitAttribute('id:123,name:shirt', ','); // Returns ['id:123', 'name:shirt'] +``` + +##### splitKeyVal + +`splitKeyVal(str: string): [string, string]` splits key-value pairs from +attribute strings. + +```js +splitKeyVal('id:123'); // Returns ['id', '123'] +``` + +##### parseInlineConfig + +`parseInlineConfig(str: string): Record` parses inline +configuration strings from HTML attributes. + +```js +parseInlineConfig('{"tracking": true, "debug": false}'); +// Returns { tracking: true, debug: false } +``` + +### Browser Information + +#### getLanguage + +`getLanguage(navigatorRef: Navigator): string | undefined` extracts the user's +preferred language. + +```js +getLanguage(navigator); // Returns 'en-US' or user's language +``` + +#### getTimezone + +`getTimezone(): string | undefined` gets the user's timezone from the Intl API. + +```js +getTimezone(); // Returns 'America/New_York' or user's timezone +``` + +#### getScreenSize + +`getScreenSize(windowRef: Window): string` returns the window's screen +dimensions. + +```js +getScreenSize(window); // Returns '1920x1080' or current screen size +``` + +### Element Visibility + +#### isVisible + +`isVisible(element: HTMLElement): boolean` checks if an element is visible to +the user. + +```js +const promoElement = document.getElementById('promotion'); +if (isVisible(promoElement)) { + // Element is visible on screen +} +``` + +This function considers: + +- Element display and visibility styles +- Element position within viewport +- Parent element visibility +- Intersection with the visible area + +### Storage Management + +#### Storage Operations + +##### storageRead + +`storageRead(key: string, storage?: StorageType): WalkerOS.PropertyType` reads +data from browser storage with automatic type conversion. + +```js +// Default uses localStorage +const userId = storageRead('walker_user_id'); + +// Use sessionStorage +const sessionData = storageRead('session_data', 'sessionStorage'); +``` -```sh -npm install @walkeros/web-core +##### storageWrite + +`storageWrite(key: string, value: WalkerOS.PropertyType, maxAgeInMinutes?: number, storage?: StorageType, domain?: string): WalkerOS.PropertyType` +writes data to storage with expiration and domain options. + +```js +// Store with 30-minute expiration +storageWrite('user_preference', 'dark-mode', 30); + +// Store in sessionStorage +storageWrite('temp_data', { id: 123 }, undefined, 'sessionStorage'); + +// Store with custom domain for cookies +storageWrite('tracking_id', 'abc123', 1440, 'cookie', '.example.com'); ``` -## Usage +##### storageDelete + +`storageDelete(key: string, storage?: StorageType)` removes data from storage. + +```js +storageDelete('expired_data'); +storageDelete('session_temp', 'sessionStorage'); +``` + +### Session Management + +#### sessionStart + +`sessionStart(config?: SessionConfig): WalkerOS.SessionData | void` initializes +and manages user sessions with automatic renewal and tracking. + +```js +// Start session with default config +const session = sessionStart(); + +// Custom session configuration +const session = sessionStart({ + storage: true, + domain: '.example.com', + maxAge: 1440, // 24 hours + sampling: 1.0, // 100% sampling +}); +``` -The web core package provides browser-specific utilities: +Session data includes: -```typescript -import { - // Browser utilities - getBrowser, - getHash, - isVisible, +- `id` - Unique session identifier +- `start` - Session start timestamp +- `isNew` - Whether this is a new session +- `count` - Number of events in session +- `device` - Device identifier +- `storage` - Whether storage is available - // Session management - sessionStart, - sessionStorage, +#### Advanced Session Functions - // Web-specific event handling - sendWeb, +- `sessionStorage` - Session-specific storage operations +- `sessionWindow` - Window/tab session management + +### Web Communication + +#### sendWeb + +`sendWeb(url: string, data?: SendDataValue, options?: SendWebOptionsDynamic): SendWebReturn` +sends data using various web transport methods. + +```js +// Default fetch transport +await sendWeb('https://api.example.com/events', eventData); + +// Use specific transport +await sendWeb(url, data, { transport: 'beacon' }); +await sendWeb(url, data, { transport: 'xhr' }); + +// With custom headers +await sendWeb(url, data, { + headers: { Authorization: 'Bearer token' }, + method: 'PUT', +}); +``` + +#### Transport-Specific Functions + +##### sendWebAsFetch + +`sendWebAsFetch(url: string, data?: SendDataValue, options?: SendWebOptionsFetch): Promise` +uses the modern Fetch API with advanced options. + +```js +await sendWebAsFetch(url, data, { + credentials: 'include', + noCors: true, + headers: { 'Content-Type': 'application/json' }, +}); +``` - // Storage utilities - storage, -} from '@walkeros/web-core'; +##### sendWebAsBeacon -// Example: Check if element is visible -const element = document.getElementById('my-element'); -if (isVisible(element)) { - console.log('Element is visible in viewport'); +`sendWebAsBeacon(url: string, data?: SendDataValue): SendResponse` uses the +Beacon API for reliable data transmission, especially during page unload. + +```js +// Reliable sending during page unload +window.addEventListener('beforeunload', () => { + sendWebAsBeacon('/analytics/pageview', { duration: Date.now() - startTime }); +}); +``` + +##### sendWebAsXhr + +`sendWebAsXhr(url: string, data?: SendDataValue, options?: SendWebOptions): SendResponse` +uses XMLHttpRequest for synchronous communication. + +```js +// Synchronous request (blocks execution) +const response = sendWebAsXhr(url, data, { method: 'POST' }); +``` + +### Web Hashing + +#### getHashWeb + +`getHashWeb(str: string, length?: number): Promise` generates SHA-256 +hashes using the Web Crypto API. + +```js +// Generate hash for fingerprinting +const userFingerprint = await getHashWeb( + navigator.userAgent + navigator.language + screen.width, + 16, +); +// Returns shortened hash like '47e0bdd10f04ef13' +``` + +## Configuration Types + +### SendWebOptions + +```ts +interface SendWebOptions { + headers?: Record; + method?: string; // Default: 'POST' + transport?: 'fetch' | 'beacon' | 'xhr'; // Default: 'fetch' +} + +interface SendWebOptionsFetch extends SendWebOptions { + credentials?: 'omit' | 'same-origin' | 'include'; + noCors?: boolean; +} +``` + +### SessionConfig + +```ts +interface SessionConfig { + storage?: boolean; // Enable storage persistence + domain?: string; // Cookie domain + maxAge?: number; // Session duration in minutes + sampling?: number; // Sampling rate (0-1) } +``` -// Example: Get browser information -const browserInfo = getBrowser(); -console.log('Browser:', browserInfo.name, browserInfo.version); +### StorageType + +```ts +type StorageType = 'localStorage' | 'sessionStorage' | 'cookie'; ``` -## Core Features +## Usage Notes + +- **Consent Required**: Browser information functions may require user consent + depending on privacy regulations +- **Storage Fallbacks**: Storage functions gracefully handle unavailable storage + with fallbacks +- **Transport Selection**: Choose transport based on use case: + - `fetch` - Modern, flexible, supports responses + - `beacon` - Reliable during page unload, small payloads + - `xhr` - Synchronous when needed, broader browser support +- **Performance**: Session and storage operations are optimized for minimal + performance impact + +--- -- **Browser Detection**: Identify browser type, version, and capabilities -- **DOM Utilities**: Functions for element visibility, attributes, and - manipulation -- **Session Management**: Handle web session lifecycle and storage -- **Viewport Detection**: Determine element visibility and user interaction -- **Web Storage**: Unified interface for localStorage and sessionStorage -- **Hash Generation**: Create consistent identifiers for web environments -- **Event Transmission**: Web-optimized event sending mechanisms +For platform-agnostic utilities, see +[Core Utilities](https://www.elbwalker.com/docs/core). ## Contribute diff --git a/packages/web/sources/browser/README.md b/packages/web/sources/browser/README.md index 40efde344..39e2c36ca 100644 --- a/packages/web/sources/browser/README.md +++ b/packages/web/sources/browser/README.md @@ -6,81 +6,118 @@ # Browser DOM Source for walkerOS -The walkerOS Browser DOM Source provides automatic event collection from browser -interactions and DOM elements, plus a tagger utility for generating HTML data -attributes. It serves as the primary source for capturing user behavior, page -views, and element interactions directly from the DOM without requiring manual -event instrumentation. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/sources/browser) +• [NPM Package](https://www.npmjs.com/package/@walkeros/web-source-browser) -## Role in walkerOS Ecosystem +The Browser Source is walkerOS's primary web tracking solution that you can use +to capture user interactions directly from the browsers DOM. -walkerOS follows a **source → collector → destination** architecture: +## What It Does -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) +The Browser Source transforms your website into a comprehensive tracking +environment by: -The Browser DOM Source automatically detects and captures user interactions, -page lifecycle events, and element visibility changes, transforming them into -standardized walkerOS events that flow through the collector to your configured -destinations. +- **Data attribute reading**: Extracts custom tracking data from HTML `data-elb` + attributes +- **Session management**: Detects and handles user sessions automatically ## Installation -```sh -npm install @walkeros/web-source-browser -``` - -## Usage - -Here's a basic example of how to use the Browser DOM source: - -```typescript -import { elb } from '@walkeros/collector'; -import { sourceBrowser, createTagger } from '@walkeros/web-source-browser'; - -// Initialize the browser source -sourceBrowser({ elb }); +### With npm -// Use the tagger to generate HTML data attributes -const tagger = createTagger(); -const attrs = tagger('product').data('id', '123').action('load', 'view').get(); -// Result: { 'data-elb': 'product', 'data-elb-product': 'id:123', 'data-elbaction': 'load:view' } +Install the source via npm: -// The source will now automatically capture: -// - Page views -// - Click events -// - Form submissions -// - Element visibility changes -// - Custom data attributes +```bash +npm install @walkeros/web-source-browser ``` -## Automatic Event Capture - -The browser source automatically captures: - -- **Page Events**: Page views, navigation, and lifecycle events -- **Click Events**: Button clicks, link clicks, and element interactions -- **Form Events**: Form submissions and field interactions -- **Visibility Events**: When elements become visible in the viewport -- **Custom Events**: Events defined through data attributes in HTML +Setup in your project: + +```javascript +import { createCollector } from '@walkeros/collector'; +import { createSource } from '@walkeros/core'; +import { sourceBrowser } from '@walkeros/web-source-browser'; + +const { collector } = await createCollector({ + sources: { + browser: createSource(sourceBrowser, { + settings: { + pageview: true, + session: true, + elb: 'elb', // Browser source will set window.elb automatically + }, + }), + }, +}); +``` -## Data Attributes +### With a script tag -Use HTML data attributes to define custom tracking: +Load the source via dynamic import: ```html - - - - -
- Product content -
+ ``` +## Configuration reference + +| Name | Type | Description | Required | Example | +| ---------- | -------------------------------- | ------------------------------------------------ | -------- | -------------------------------- | +| `prefix` | `string` | Prefix for data attributes used in DOM tracking | No | `'data-elb'` | +| `scope` | `Element \| Document` | DOM scope for event tracking (default: document) | No | `document.querySelector("#app")` | +| `pageview` | `boolean` | Enable automatic pageview tracking | No | `true` | +| `session` | `boolean` | Enable session tracking and management | No | `true` | +| `elb` | `string` | Custom name for the global elb function | No | `'elb'` | +| `name` | `string` | Custom name for the browser source instance | No | `'mySource'` | +| `elbLayer` | `boolean \| string \| Elb.Layer` | Enable elbLayer for async command queuing | No | `true` | + +### elb + +> **Two Different elb Functions** +> +> The collector provides **two different elb functions**: +> +> 1. **Collector elb** (`elb` from `createCollector`): Basic event tracking +> that works with all sources and destinations +> 2. **Browser Source elb** (`collector.sources.browser.elb` or direct from +> `createSource`): Enhanced function with browser-specific features +> +> **Browser Source elb adds:** +> +> - **DOM Commands**: `walker init` for asynchronous loading of DOM elements +> - **Flexible Arguments**: Support for multiple argument patterns +> - **elbLayer Integration**: Automatic processing of queued commands +> - **Element parameters**: Support for element parameters in DOM commands +> +> Use **separate source creation** for direct access to the enhanced elb +> function, or access it via `collector.sources.browser.elb` in the unified API. +> +> See [Commands](https://www.elbwalker.com/docs/sources/web/browser/commands) +> for full browser source API documentation. + ## Contribute Feel free to contribute by submitting an diff --git a/packages/web/sources/dataLayer/README.md b/packages/web/sources/dataLayer/README.md index 4e6a64761..321283d93 100644 --- a/packages/web/sources/dataLayer/README.md +++ b/packages/web/sources/dataLayer/README.md @@ -1,30 +1,23 @@

- +

# DataLayer Source for walkerOS +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/sources/dataLayer) +• +[NPM Package](https://www.npmjs.com/package/@walkeros/web-source-datalayer) + This package provides a dataLayer source for walkerOS. It allows you to process events from a dataLayer and send them to the walkerOS collector. -[View documentation](https://www.elbwalker.com/docs/sources/datalayer/) - -## Role in walkerOS Ecosystem - -walkerOS follows a **source → collector → destination** architecture: - -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) - -This dataLayer source monitors the browser's dataLayer (commonly used with -Google Tag Manager) and transforms existing gtag() calls and dataLayer.push() -events into standardized walkerOS events, enabling seamless migration from -traditional dataLayer implementations. +walkerOS follows a **source → collector → destination** architecture. This +dataLayer source monitors the browser's dataLayer (commonly used with Google Tag +Manager) and transforms existing gtag() calls and dataLayer.push() events into +standardized walkerOS events, enabling seamless migration from traditional +dataLayer implementations. ## Installation @@ -43,6 +36,14 @@ import { sourceDataLayer } from '@walkeros/web-source-datalayer'; sourceDataLayer({ elb }); ``` +## Configuration + +| Name | Type | Description | Required | Example | +| -------- | ------------------------------------------------------ | ------------------------------------------------------------- | -------- | ----------------------------------------------- | +| `name` | `string` | DataLayer variable name (default: "dataLayer") | No | `'dataLayer'` | +| `prefix` | `string` | Event prefix for filtering dataLayer events (default: "gtag") | No | `'gtag'` | +| `filter` | `(event: unknown) => WalkerOS.PromiseOrValue` | Function to filter which dataLayer events to process | No | `(event) => event && typeof event === "object"` | + ## Contribute Feel free to contribute by submitting an From f82dde9bf63c02e7ac0e78c73be43edadc7b6455 Mon Sep 17 00:00:00 2001 From: aylaju <99686309+aylaju@users.noreply.github.com> Date: Tue, 2 Sep 2025 12:31:16 +0200 Subject: [PATCH 02/13] readme updates --- apps/walkerjs/README.md | 370 +++++++++++------- packages/server/core/README.md | 293 +++++++++++--- packages/server/destinations/aws/README.md | 59 +-- packages/web/destinations/api/README.md | 148 ++++++- packages/web/destinations/meta/README.md | 45 ++- packages/web/destinations/piwikpro/README.md | 55 ++- packages/web/destinations/plausible/README.md | 40 +- 7 files changed, 724 insertions(+), 286 deletions(-) diff --git a/apps/walkerjs/README.md b/apps/walkerjs/README.md index c6cb24108..8cd833d5d 100644 --- a/apps/walkerjs/README.md +++ b/apps/walkerjs/README.md @@ -1,8 +1,11 @@ -# Walker.js +# walker.js -A ready-to-use walkerOS bundle that combines the browser source, collector, and -dataLayer support into a single JavaScript file. Perfect for quickly adding -privacy-friendly event tracking to any website. +Walker.js is a pre-built walkerOS application that combines both the +[browser](/docs/sources/web/browser/) and +[dataLayer](/docs/sources/web/dataLayer/) sources with the +[collector](/docs/collector/) and a default `dataLayer` destination into a +pre-build package. It's designed for users who want instant web tracking without +complex setup or configuration. ## Features @@ -16,27 +19,17 @@ privacy-friendly event tracking to any website. - 📦 **Queue support** - Events are queued until walker.js loads (elbLayer) - 🧪 **Well tested** - Comprehensive test suite included -## Quick Start +## Installation -### 1. Add elbLayer Function (Recommended) +### Option 1: NPM Package -Add this before walker.js loads to queue events: - -```html - +```bash +npm install @walkeros/walker.js ``` -### 2. Include walker.js +### Option 2: CDN ```html - - - - ``` -### 3. Configure +## Basic Setup -#### Option A: Default Configuration +### 1. Add Event Queueing (Recommended) -Just load the `walker.js` script - it will look for `window.elbConfig`: +Add this script before walker.js loads to queue events during initialization: ```html - ``` -#### Option B: Named Configuration Object +### 2. Include Walker.js + +```html + +``` -Use `data-elbconfig` on the script tag to define the configuration object. +### 3. Configure Destinations ```html - -``` - -#### Option C: Inline Configuration - -Configure directly in the script tag. Use simple key:value pairs separated by -semicolon. - -```html - ``` -### 3. Track Events +## Configuration Options -#### Automatic DOM Tracking +Walker.js supports multiple configuration approaches with different priorities: -Add data attributes to your HTML: - -```html - -
- -
+1. **Script tag `data-elbconfig`** (highest priority) +2. **`window.elbConfig`** (default fallback) +3. **Manual initialization** (when `run: false`) - -
+### Settings - -
-``` +| Name | Type | Default | Description | +| :---------- | :------------------ | :------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- | +| `elb` | `string` | `"elb"` | Global function name for event tracking | +| `name` | `string` | `"walkerjs"` | Global instance name | +| `run` | `boolean` | `true` | Auto-initialize walker.js on load | +| `browser` | `object \| boolean` | `{ run: true, session: true, scope: document.body, pageview: true }` | [Browser source configuration](https://www.elbwalker.com/docs/sources/web/browser/) | +| `dataLayer` | `object \| boolean` | `false` | [DataLayer source configuration](https://www.elbwalker.com/docs/sources/web/dataLayer) | +| `collector` | `object` | `{}` | [Collector configuration](https://www.elbwalker.com/docs/collector/) including destinations and consent settings | -#### Manual Event Tracking +#### Browser Source Settings -```javascript -// Using the global elb function -elb('product add', { - id: '123', - price: 29.99, -}); -``` +| Name | Type | Default | Description | +| :----------------- | :-------- | :-------------- | :-------------------------------- | +| `browser.run` | `boolean` | `true` | Auto-start DOM tracking | +| `browser.session` | `boolean` | `true` | Enable session tracking | +| `browser.scope` | `Element` | `document.body` | DOM element scope for tracking | +| `browser.pageview` | `boolean` | `true` | Enable automatic page view events | -## Configuration +#### DataLayer Settings -### Configuration Options +| Name | Type | Default | Description | +| :----------------- | :-------- | :------------ | :----------------------------------------- | +| `dataLayer` | `boolean` | `false` | Enable dataLayer integration with defaults | +| `dataLayer.name` | `string` | `"dataLayer"` | DataLayer variable name | +| `dataLayer.prefix` | `string` | `"dataLayer"` | Event prefix for dataLayer events | -Walker.js can be configured in multiple ways: +#### Collector Settings -1. **Script tag with `data-elbconfig`** - Highest priority -2. **`window.elbConfig`** - Default fallback -3. **Manual initialization** - When `run: false` +| Name | Type | Default | Description | +| :----------------------- | :------- | :--------------------- | :------------------------------------------------------------------------ | +| `collector.consent` | `object` | `{ functional: true }` | Default consent state | +| `collector.destinations` | `object` | `{}` | [Destination configurations](https://www.elbwalker.com/docs/destinations) | ### Full Configuration Object ```javascript window.elbConfig = { - // Global configuration + // Global settings elb: 'elb', // Global function name (default: 'elb') name: 'walkerjs', // Global instance name run: true, // Auto-initialize (default: true) @@ -145,56 +138,118 @@ window.elbConfig = { run: true, // Auto-start DOM tracking session: true, // Enable session tracking scope: document.body, // Tracking scope + pageview: true, // Enable automatic page views }, // DataLayer integration dataLayer: true, // Enable dataLayer // or detailed config: - dataLayer: { - name: 'dataLayer', // DataLayer variable name - prefix: 'dataLayer', // Event prefix - }, + // dataLayer: { + // name: 'dataLayer', // DataLayer variable name + // prefix: 'dataLayer', // Event prefix + // }, // Collector configuration collector: { consent: { functional: true }, // Default consent state destinations: { - // Your destinations + // Your destinations here + console: { + push: (event) => console.log('Event:', event), + }, }, }, }; ``` -### Destination Configuration +### Inline Configuration -```javascript -const walkerjs = Walkerjs({ - collector: { - destinations: { - console: { - type: 'console', - push: (event) => console.log(event), - }, +Configure directly in the script tag using simple key:value pairs: - api: { - type: 'custom-api', - push: async (event) => { - await fetch('/api/events', { - method: 'POST', - body: JSON.stringify(event), - }); - }, +```html + +``` + +### Named Configuration Object + +Use a custom configuration object name: + +```html + + +``` + +## Usage + +### Automatic DOM Tracking + +Walker.js automatically tracks events based on HTML data attributes: + +```html + +
+ +
+ + +
+ + +
+``` + +For detailed information on data attributes, see the +[Browser Source documentation](https://www.elbwalker.com/docs/sources/web/browser/tagging). + +### Manual Event Tracking + +Use the global `elb` function for manual tracking: + +```javascript +// Simple event +elb('button click', { + label: 'interesting', +}); +``` + +### DataLayer Integration + +Walker.js can integrate with existing dataLayer implementations: + +```javascript +// Enable dataLayer integration +window.elbConfig = { + dataLayer: true, // Uses window.dataLayer by default +}; + +// Existing dataLayer events will be processed +dataLayer.push({ + event: 'purchase', + ecommerce: { + transaction_id: '12345', + value: 25.42, }, }); ``` -## Advanced Usage +## Advanced Features ### Async Loading & Event Queueing -Walker.js supports async loading with automatic event queueing: +Walker.js handles async loading gracefully with automatic event queueing: ```html - + ``` -**Benefits:** - -- No timing issues with async scripts -- Events are never lost -- Works with any loading strategy (async, defer, dynamic) -- Zero dependencies for the queue function - ### Build Variants -Walker.js provides multiple build formats: +Walker.js provides multiple build formats for different environments: - `walker.js` - Standard IIFE bundle for browsers - `index.es5.js` - GTM-compatible ES2015 build - `index.mjs` - ES modules for modern bundlers - `index.js` - CommonJS for Node.js environments +### Programmatic Usage + +Use walker.js programmatically in applications: + +```javascript +import { createWalkerjs } from '@walkeros/walker.js'; + +const { collector, elb } = await createWalkerjs({ + collector: { + destinations: { + console: { push: console.log }, + }, + }, + browser: { + session: true, + pageview: true, + }, +}); +``` + +## Destination Configuration + +Configure multiple destinations for your events: + +```javascript +window.elbConfig = { + collector: { + destinations: { + // Console logging for development + console: { + push: (event) => console.log('Walker.js Event:', event), + }, + + // Custom API endpoint + api: { + push: async (event) => { + await fetch('/api/events', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(event), + }); + }, + }, + }, + }, +}; +``` + +For comprehensive destination options, see the +[Destinations documentation](https://www.elbwalker.com/docs/destinations/). + +## Troubleshooting + +### Common Issues + +**Events not firing:** Check that walker.js loaded and configuration is valid. + +**Missing events:** Ensure event queueing function is added before walker.js. + +**Configuration not applied:** Verify `data-elbconfig` points to the correct +object name. + ## API Reference ### Factory Function -#### `createWalkerjs(config?): Walkerjs.Instance` +```typescript +createWalkerjs(config?: Config): Promise +``` Creates a new walker.js instance with the provided configuration. @@ -241,28 +352,19 @@ Creates a new walker.js instance with the provided configuration. - `collector` - The walkerOS collector instance - `elb` - Browser push function for event tracking -### Additional Methods - -- `getAllEvents(scope?, prefix?)` - Get all trackable events on the page -- `getEvents(target, trigger, prefix?)` - Get events for a specific element and - trigger -- `getGlobals(prefix?, scope?)` - Get global properties from the page - ### Utility Functions -You can also import and use the utility functions directly: - ```javascript import { getAllEvents, getEvents, getGlobals } from '@walkeros/walker.js'; -// Get all events on the page +// Get all trackable events on the page const events = getAllEvents(); -// Get events for a specific button click +// Get events for a specific element and trigger const button = document.querySelector('button'); const clickEvents = getEvents(button, 'click'); -// Get global properties +// Get global properties from the page const globals = getGlobals(); ``` @@ -287,19 +389,25 @@ npm run dev # Watch mode npm run preview # Serve examples on localhost:3333 ``` -## License +## Related Documentation -MIT License - see LICENSE file for details. +- **[Browser Source](https://www.elbwalker.com/docs/sources/web/browser/)** - + Detailed DOM tracking capabilities +- **[Collector](https://www.elbwalker.com/docs/collector/)** - Event processing + and routing +- **[Destinations](https://www.elbwalker.com/docs/destinations/)** - Available + destination options +- **[DataLayer Source](https://www.elbwalker.com/docs/sources/web/dataLayer/)** - + DataLayer integration details + +Walker.js combines all these components into a single, easy-to-use package +perfect for getting started with walkerOS quickly. ## Contributing This package is part of the walkerOS monorepo. Please see the main repository for contribution guidelines. -## Related Packages +## License -- [@walkeros/collector](../../../packages/collector) - Core collector -- [@walkeros/web-source-browser](../../../packages/web/sources/browser) - - Browser source -- [@walkeros/web-source-datalayer](../../../packages/web/sources/dataLayer) - - DataLayer source +MIT License - see LICENSE file for details. diff --git a/packages/server/core/README.md b/packages/server/core/README.md index a3b79ca40..63bc7b66d 100644 --- a/packages/server/core/README.md +++ b/packages/server/core/README.md @@ -1,74 +1,271 @@

- +

# Server Core Utilities for walkerOS -The walkerOS Server Core package provides server-specific utilities and -functions that power server-side data collection. It extends the -platform-agnostic Core package with Node.js-specific functionality for server -environment detection, request handling, and server-side event processing. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/server/core) +• [NPM Package](https://www.npmjs.com/package/@walkeros/server-core) -## Role in walkerOS Ecosystem +Server core utilities are Node.js-specific functions designed for server-side +walkerOS implementations. These utilities handle server communication, +cryptographic hashing, and other backend operations. -walkerOS follows a **source → collector → destination** architecture: +## Installation + +Import server utilities from the `@walkeros/server-core` package: -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) +```ts +import { sendServer, getHashServer } from '@walkeros/server-core'; +``` -The Server Core package serves as the foundation for all server-based sources -and destinations, providing essential Node.js utilities, request handling, and -server-specific event processing capabilities. +## Server Communication -## Installation +### sendServer + +`sendServer(url: string, data?: SendDataValue, options?: SendServerOptions): Promise` +sends HTTP requests using Node.js built-in modules (`http`/`https`). + +```js +// Simple POST request +const response = await sendServer('https://api.example.com/events', { + event: 'page view', + data: { url: '/home' }, +}); + +// With custom options +const response = await sendServer(url, data, { + method: 'PUT', + headers: { + Authorization: 'Bearer token', + 'Content-Type': 'application/json', + }, + timeout: 10000, // 10 seconds +}); + +if (response.ok) { + console.log('Data sent successfully:', response.data); +} else { + console.error('Send failed:', response.error); +} +``` + +#### SendServerOptions + +```ts +interface SendServerOptions { + headers?: Record; // Custom HTTP headers + method?: string; // HTTP method (default: 'POST') + timeout?: number; // Request timeout in milliseconds (default: 5000) +} +``` + +#### SendResponse + +```ts +interface SendResponse { + ok: boolean; // Indicates if the request was successful (2xx status) + data?: unknown; // Parsed response data (if available) + error?: string; // Error message (if request failed) +} +``` + +## Cryptographic Operations + +### getHashServer + +`getHashServer(str: string, length?: number): Promise` generates SHA-256 +hashes using Node.js crypto module. + +```js +// Generate full SHA-256 hash +const fullHash = await getHashServer('user123@example.com'); +// Returns full 64-character hash + +// Generate shortened hash for anonymization +const userFingerprint = await getHashServer( + userAgent + language + ipAddress + date.getDate(), + 16, +); +// Returns 16-character hash like '47e0bdd10f04ef13' + +// User identification while preserving privacy +const anonymousId = await getHashServer(`${userEmail}${deviceId}${salt}`, 12); +``` + +This function is commonly used for: + +- **User Anonymization**: Creating privacy-safe user identifiers +- **Fingerprinting**: Generating device/session fingerprints +- **Data Deduplication**: Creating consistent identifiers +- **Privacy Compliance**: Hashing PII for GDPR/CCPA compliance + +## Usage Examples + +### Event Processing Pipeline + +```js +import { sendServer, getHashServer } from '@walkeros/server-core'; + +async function processUserEvent(event, userInfo) { + // Anonymize user identification + const anonymousUserId = await getHashServer( + `${userInfo.email}${userInfo.deviceId}`, + 16, + ); -```sh -npm install @walkeros/server-core + // Prepare event with anonymized data + const processedEvent = { + ...event, + user: { + ...event.user, + id: anonymousUserId, + }, + }; + + // Send to analytics service + const result = await sendServer( + 'https://analytics.example.com/collect', + processedEvent, + { + headers: { + 'X-API-Key': process.env.ANALYTICS_API_KEY, + }, + timeout: 8000, + }, + ); + + return result; +} +``` + +### Privacy-Safe Session Tracking + +```js +async function createSessionId(request) { + const fingerprint = [ + request.headers['user-agent'], + request.ip.replace(/\.\d+$/, '.0'), // Anonymize IP + new Date().toDateString(), // Daily rotation + ].join('|'); + + return await getHashServer(fingerprint, 20); +} +``` + +## Error Handling + +Server utilities include comprehensive error handling: + +```js +try { + const response = await sendServer(url, data, { timeout: 5000 }); + + if (response.ok) { + // Success - response.data contains the result + console.log('Success:', response.data); + } else { + // Request completed but with error status + console.warn('Request failed:', response.error); + } +} catch (error) { + // Network error, timeout, or other exception + console.error('Network error:', error.message); +} +``` + +## Performance Considerations + +### Timeout Configuration + +Configure appropriate timeouts based on your use case: + +```js +// Fast analytics endpoint +await sendServer(url, data, { timeout: 2000 }); + +// Critical business data +await sendServer(url, data, { timeout: 15000 }); +``` + +### Batch Processing + +For high-volume scenarios, consider batching: + +```js +const events = [ + /* ... multiple events ... */ +]; + +const response = await sendServer( + '/api/events/batch', + { + events, + timestamp: Date.now(), + }, + { + timeout: 10000, + }, +); ``` -## Usage +### Connection Reuse -The server core package provides Node.js-specific utilities: +The underlying Node.js HTTP agent automatically reuses connections for better +performance with multiple requests to the same host. -```typescript -import { - // Server utilities - getHashServer, - sendServer, +## Security Notes - // Type definitions - ServerDestination, -} from '@walkeros/server-core'; +- **HTTPS Only**: Use HTTPS URLs in production for encrypted transmission +- **API Keys**: Store sensitive credentials in environment variables +- **Timeout Limits**: Set reasonable timeouts to prevent hanging requests +- **Hash Salting**: Use application-specific salts when hashing sensitive data -// Example: Generate server-side hash -const hash = await getHashServer('user-id', 'additional-data'); -console.log('Generated hash:', hash); +```js +// Good security practices +const apiKey = process.env.ANALYTICS_API_KEY; +const saltedHash = await getHashServer(`${userData}${process.env.HASH_SALT}`); -// Example: Send event from server -await sendServer({ - event: 'order complete', - data: { orderId: '12345', profit: 42 }, - // ... other event properties +await sendServer('https://secure-api.example.com/events', data, { + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + timeout: 5000, }); ``` -## Core Features - -- **Server Environment Detection**: Identify Node.js version and server - capabilities -- **Request Processing**: Handle incoming HTTP requests and extract event data -- **Server-Side Hashing**: Generate consistent identifiers in server - environments -- **Event Transmission**: Server-optimized event sending mechanisms -- **Server Destinations**: Base classes and utilities for server-side - destinations -- **Performance Optimization**: Memory-efficient processing for high-throughput - scenarios +## Integration with Core + +Server utilities work seamlessly with +[Core Utilities](https://www.elbwalker.com/docs/core): + +```js +import { getMappingValue, anonymizeIP } from '@walkeros/core'; +import { sendServer, getHashServer } from '@walkeros/server-core'; + +async function processServerSideEvent(rawEvent, clientIP) { + // Use core utilities for data processing + const processedData = await getMappingValue(rawEvent, mappingConfig); + const safeIP = anonymizeIP(clientIP); + + // Use server utilities for transmission + const sessionId = await getHashServer(`${safeIP}${userAgent}`, 16); + + return await sendServer(endpoint, { + ...processedData, + sessionId, + ip: safeIP, + }); +} +``` + +--- + +For platform-agnostic utilities, see +[Core Utilities](https://www.elbwalker.com/docs/core). ## Contribute diff --git a/packages/server/destinations/aws/README.md b/packages/server/destinations/aws/README.md index d89b4ff7b..b7cf5b58b 100644 --- a/packages/server/destinations/aws/README.md +++ b/packages/server/destinations/aws/README.md @@ -1,30 +1,20 @@

- +

-# AWS Destination for walkerOS +# AWS (Firehose) Destination for walkerOS -This package provides an AWS destination for walkerOS. It allows you to send -events to various AWS services. Currently, it supports AWS Firehose. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/aws) +• +[NPM Package](https://www.npmjs.com/package/@walkeros/server-destination-aws) -[View documentation](https://www.elbwalker.com/docs/destinations/server/aws/) - -## Role in walkerOS Ecosystem - -walkerOS follows a **source → collector → destination** architecture: - -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) - -This AWS destination receives processed events from the walkerOS collector and -streams them to AWS services like Firehose, enabling real-time data ingestion -into AWS data lakes, warehouses, and analytics services for large-scale event -processing and analysis. +walkerOS follows a **source → collector → destination** architecture. This AWS +destination receives processed events from the walkerOS collector and streams +them to AWS services like Firehose, enabling real-time data ingestion into AWS +data lakes, warehouses, and analytics services for large-scale event processing +and analysis. ## Installation @@ -34,26 +24,45 @@ npm install @walkeros/server-destination-aws ## Usage -Here's a basic example of how to use the AWS destination: +Here's a basic example of how to use the AWS Firehose destination: ```typescript import { elb } from '@walkeros/collector'; import { destinationFirehose } from '@walkeros/server-destination-aws'; elb('walker destination', destinationFirehose, { - custom: { + settings: { firehose: { streamName: 'your-firehose-stream-name', region: 'eu-central-1', - credentials: { - accessKeyId: 'your-access-key-id', - secretAccessKey: 'your-secret-access-key', + config: { + credentials: { + accessKeyId: 'your-access-key-id', + secretAccessKey: 'your-secret-access-key', + }, }, }, }, }); ``` +## Configuration + +| Name | Type | Description | Required | Example | +| ---------- | ---------------- | ----------------------------------- | -------- | ------------------------------------------------------ | +| `firehose` | `FirehoseConfig` | AWS Firehose configuration settings | No | `{ streamName: 'walker-events', region: 'us-east-1' }` | + +### Firehose Configuration + +The `firehose` object has the following properties: + +| Name | Type | Description | Required | Example | +| ------------ | ---------------------- | ------------------------------------------------- | -------- | --------------------------------- | +| `streamName` | `string` | Name of the Kinesis Data Firehose delivery stream | Yes | `'walker-events'` | +| `client` | `FirehoseClient` | Pre-configured AWS Firehose client instance | No | `new FirehoseClient(config)` | +| `region` | `string` | AWS region for the Firehose service | No | `'us-east-1'` | +| `config` | `FirehoseClientConfig` | AWS SDK client configuration options | No | `{ credentials: awsCredentials }` | + ## Contribute Feel free to contribute by submitting an diff --git a/packages/web/destinations/api/README.md b/packages/web/destinations/api/README.md index 6ba75eec1..cd973c6e1 100644 --- a/packages/web/destinations/api/README.md +++ b/packages/web/destinations/api/README.md @@ -1,30 +1,23 @@

- +

# Web API Destination for walkerOS -This package provides a web API destination for walkerOS. It allows you to send -events to a custom API endpoint. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/destinations/api) +• +[NPM Package](https://www.npmjs.com/package/@walkeros/web-destination-api) -[View documentation](https://www.elbwalker.com/docs/destinations/web/api/) +The API destination allows you to send events to any HTTP endpoint with +customizable data transformation and transport methods. -## Role in walkerOS Ecosystem - -walkerOS follows a **source → collector → destination** architecture: - -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) - -This API destination receives processed events from the walkerOS collector and -sends them to your custom API endpoint, enabling integration with internal -analytics systems, data warehouses, or custom business logic that requires -real-time event data. +walkerOS follows a **source → collector → destination** architecture. This API +destination receives processed events from the walkerOS collector and sends them +to your custom API endpoint, enabling integration with internal analytics +systems, data warehouses, or custom business logic that requires real-time event +data. ## Installation @@ -32,21 +25,134 @@ real-time event data. npm install @walkeros/web-destination-api ``` +## Configuration + +| Name | Type | Description | Required | Example | +| ----------- | ------------------------------ | ------------------------------------------------ | -------- | ------------------------------------------------------------------------- | +| `url` | `string` | The HTTP endpoint URL to send events to | Yes | `'https://api.example.com/events'` | +| `headers` | `Record` | Additional HTTP headers to include with requests | No | `{ 'Authorization': 'Bearer token', 'Content-Type': 'application/json' }` | +| `method` | `string` | HTTP method for the request | No | `'POST'` | +| `transform` | `function` | Function to transform event data before sending | No | `(data, config, mapping) => JSON.stringify(data)` | +| `transport` | `'fetch' \| 'xhr' \| 'beacon'` | Transport method for sending requests | No | `'fetch'` | + ## Usage -Here's a basic example of how to use the web API destination: +### Basic Usage + +```typescript +import { createCollector } from '@walkeros/collector'; +import { destinationAPI } from '@walkeros/web-destination-api'; + +const { elb } = await createCollector(); + +elb('walker destination', destinationAPI, { + settings: { + url: 'https://api.example.com/events', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer your-token', + }, + }, +}); +``` + +### Advanced Usage with Transform + +```typescript +import { createCollector } from '@walkeros/collector'; +import { destinationAPI } from '@walkeros/web-destination-api'; + +const { elb } = await createCollector(); + +elb('walker destination', destinationAPI, { + settings: { + url: 'https://api.example.com/events', + transport: 'fetch', + transform: (event, config, mapping) => { + // Custom transformation logic + return JSON.stringify({ + timestamp: Date.now(), + event_name: `${event.entity}_${event.action}`, + properties: event.data, + context: event.context, + }); + }, + }, +}); +``` + +## Examples + +### Sending to Analytics API ```typescript -import { elb } from '@walkeros/collector'; +import { createCollector } from '@walkeros/collector'; import { destinationAPI } from '@walkeros/web-destination-api'; +const { elb } = await createCollector(); + +// Configure for analytics API +elb('walker destination', destinationAPI, { + settings: { + url: 'https://analytics.example.com/track', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': 'your-api-key', + }, + transform: (event) => { + return JSON.stringify({ + event_type: `${event.entity}_${event.action}`, + user_id: event.user?.id, + session_id: event.user?.session, + properties: event.data, + timestamp: event.timing, + }); + }, + }, +}); +``` + +### Using Beacon Transport + +For critical events that need to be sent even when the page is unloading: + +```typescript +elb('walker destination', destinationAPI, { + settings: { + url: 'https://api.example.com/critical-events', + transport: 'beacon', // Reliable for page unload scenarios + }, +}); +``` + +### Custom Data Mapping + +Use mapping rules to control which events are sent: + +```typescript elb('walker destination', destinationAPI, { - custom: { + settings: { url: 'https://api.example.com/events', }, + mapping: { + entity: { + action: { + data: 'data', + }, + }, + }, }); ``` +## Transport Methods + +- **fetch** (default): Modern, promise-based HTTP requests +- **xhr**: Traditional XMLHttpRequest for older browser compatibility +- **beacon**: Uses Navigator.sendBeacon() for reliable data transmission during + page unload + ## Contribute Feel free to contribute by submitting an diff --git a/packages/web/destinations/meta/README.md b/packages/web/destinations/meta/README.md index 2de195fb1..177c880b7 100644 --- a/packages/web/destinations/meta/README.md +++ b/packages/web/destinations/meta/README.md @@ -1,30 +1,23 @@

- +

# Meta Pixel Destination for walkerOS -This package provides a Meta Pixel (formerly Facebook Pixel) destination for -walkerOS. It allows you to send events to Meta Pixel. - -[View documentation](https://www.elbwalker.com/docs/destinations/web/meta/) - -## Role in walkerOS Ecosystem - -walkerOS follows a **source → collector → destination** architecture: +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/destinations/meta) +• +[NPM Package](https://www.npmjs.com/package/@walkeros/web-destination-meta) -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) +This package provides a Meta Pixel (formerly Facebook Pixel) destination for +walkerOS. It sends events to Meta Pixel to track visitor activity and +conversions for Facebook and Instagram advertising campaigns. -This Meta Pixel destination receives processed events from the walkerOS -collector and transforms them into Meta's Pixel API format, handling conversion -events, custom events, and audience building data to optimize your Meta -advertising campaigns. +walkerOS follows a **source → collector → destination** architecture. This Meta +Pixel destination receives processed events from the walkerOS collector and +transforms them into Meta's Pixel API format, handling conversion events, custom +events, and audience building data to optimize your Meta advertising campaigns. ## Installation @@ -37,16 +30,26 @@ npm install @walkeros/web-destination-meta Here's a basic example of how to use the Meta Pixel destination: ```typescript -import { elb } from '@walkeros/collector'; +import { createCollector } from '@walkeros/collector'; import { destinationMeta } from '@walkeros/web-destination-meta'; +const { elb } = await createCollector(); + elb('walker destination', destinationMeta, { - custom: { - pixelId: '1234567890', + settings: { + pixelId: '1234567890', // Your Meta Pixel ID }, + loadScript: true, // Load Meta Pixel script automatically }); ``` +## Configuration + +| Name | Type | Description | Required | Example | +| ------------ | --------- | ----------------------------------------------------------------- | -------- | -------------- | +| `pixelId` | `string` | Your Meta Pixel ID from Facebook Business Manager | Yes | `'1234567890'` | +| `loadScript` | `boolean` | Whether to automatically load the Meta Pixel script (fbevents.js) | No | `true` | + ## Contribute Feel free to contribute by submitting an diff --git a/packages/web/destinations/piwikpro/README.md b/packages/web/destinations/piwikpro/README.md index 53c5f3cc0..6fe950d80 100644 --- a/packages/web/destinations/piwikpro/README.md +++ b/packages/web/destinations/piwikpro/README.md @@ -1,29 +1,23 @@

- +

# Piwik PRO Destination for walkerOS -This package provides a Piwik PRO destination for walkerOS. It allows you to -send events to Piwik PRO. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/destinations/piwikpro) +• +[NPM Package](https://www.npmjs.com/package/@walkeros/web-destination-piwikpro) -[View documentation](https://www.elbwalker.com/docs/destinations/web/piwikpro/) +This package provides a [Piwik PRO](https://piwik.pro/) destination for +walkerOS. Piwik PRO is a European, privacy-focused web analytics and marketing +platform that helps businesses track website traffic and user behavior. -## Role in walkerOS Ecosystem - -walkerOS follows a **source → collector → destination** architecture: - -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) - -This Piwik PRO destination receives processed events from the walkerOS collector -and transforms them into Piwik PRO's analytics format, providing -privacy-compliant analytics with GDPR compliance and data ownership control. +walkerOS follows a **source → collector → destination** architecture. This Piwik +PRO destination receives processed events from the walkerOS collector and +transforms them into Piwik PRO's analytics format, providing privacy-compliant +analytics with GDPR compliance and data ownership control. ## Installation @@ -36,17 +30,36 @@ npm install @walkeros/web-destination-piwikpro Here's a basic example of how to use the Piwik PRO destination: ```typescript -import { elb } from '@walkeros/collector'; +import { createCollector } from '@walkeros/collector'; import { destinationPiwikPro } from '@walkeros/web-destination-piwikpro'; +const { elb } = await createCollector(); + elb('walker destination', destinationPiwikPro, { - custom: { - appId: 'YOUR_APP_ID', - url: 'https://your-account.piwik.pro/', + settings: { + appId: 'XXX-XXX-XXX-XXX-XXX', // Required + url: 'https://your_account_name.piwik.pro/', // Required }, }); ``` +## Configuration + +| Name | Type | Description | Required | Example | +| -------------- | --------- | ---------------------------------------------- | -------- | ---------------------------------------- | +| `appId` | `string` | ID of the Piwik PRO site | Yes | `'XXX-XXX-XXX-XXX-XXX'` | +| `url` | `string` | URL of your Piwik PRO account | Yes | `'https://your_account_name.piwik.pro/'` | +| `linkTracking` | `boolean` | Enables/Disables download and outlink tracking | No | `false` | + +### Event Mapping + +For custom event mapping (`mapping.entity.action.settings`): + +| Name | Type | Description | Required | Example | +| ----------- | -------- | ------------------------------------- | -------- | -------------- | +| `goalId` | `string` | ID to count the event as a goal | No | `'1'` | +| `goalValue` | `string` | Property to be used as the goal value | No | `'data.value'` | + ## Contribute Feel free to contribute by submitting an diff --git a/packages/web/destinations/plausible/README.md b/packages/web/destinations/plausible/README.md index db6f53bf8..074a2375b 100644 --- a/packages/web/destinations/plausible/README.md +++ b/packages/web/destinations/plausible/README.md @@ -1,28 +1,22 @@

- +

# Plausible Destination for walkerOS -This package provides a Plausible destination for walkerOS. It allows you to -send events to Plausible Analytics. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/destinations/plausible) +• +[NPM Package](https://www.npmjs.com/package/@walkeros/web-destination-plausible) -[View documentation](https://www.elbwalker.com/docs/destinations/web/plausible/) +This package provides a [Plausible Analytics](https://plausible.io/) destination +for walkerOS. Plausible is a simple, and privacy-friendly Google Analytics +Alternative. -## Role in walkerOS Ecosystem - -walkerOS follows a **source → collector → destination** architecture: - -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) - -This Plausible destination receives processed events from the walkerOS collector -and transforms them into Plausible Analytics format, providing lightweight, +walkerOS follows a **source → collector → destination** architecture. This +Plausible destination receives processed events from the walkerOS collector and +transforms them into Plausible Analytics format, providing lightweight, privacy-focused web analytics without cookies or personal data collection. ## Installation @@ -36,16 +30,24 @@ npm install @walkeros/web-destination-plausible Here's a basic example of how to use the Plausible destination: ```typescript -import { elb } from '@walkeros/collector'; +import { createCollector } from '@walkeros/collector'; import { destinationPlausible } from '@walkeros/web-destination-plausible'; +const { elb } = await createCollector(); + elb('walker destination', destinationPlausible, { - custom: { - domain: 'your-domain.com', + settings: { + domain: 'elbwalker.com', // Optional, domain of your site as registered }, }); ``` +## Configuration + +| Name | Type | Description | Required | Example | +| -------- | -------- | -------------------------------------------------- | -------- | ----------------- | +| `domain` | `string` | The domain of your site as registered in Plausible | No | `'elbwalker.com'` | + ## Contribute Feel free to contribute by submitting an From 5f7820829cc30d88da2051213f2676e30fd3f940 Mon Sep 17 00:00:00 2001 From: aylaju <99686309+aylaju@users.noreply.github.com> Date: Tue, 2 Sep 2025 13:16:27 +0200 Subject: [PATCH 03/13] gtag gcp readme --- packages/server/destinations/gcp/README.md | 44 +++--- packages/web/destinations/gtag/README.md | 173 ++++----------------- 2 files changed, 56 insertions(+), 161 deletions(-) diff --git a/packages/server/destinations/gcp/README.md b/packages/server/destinations/gcp/README.md index 6514490f5..51da5f25e 100644 --- a/packages/server/destinations/gcp/README.md +++ b/packages/server/destinations/gcp/README.md @@ -1,30 +1,19 @@

- +

-# Google Cloud Platform (GCP) Destination for walkerOS +# GCP (BigQuery) Destination for walkerOS -This package provides a Google Cloud Platform (GCP) destination for walkerOS. It -allows you to send events to Google BigQuery. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/gcp) +• +[NPM Package](https://www.npmjs.com/package/@walkeros/server-destination-gcp) -[View documentation](https://www.elbwalker.com/docs/destinations/server/gcp/) - -## Role in walkerOS Ecosystem - -walkerOS follows a **source → collector → destination** architecture: - -- **Sources**: Capture events from various environments (browser DOM, dataLayer, - server requests) -- **Collector**: Processes, validates, and routes events with consent awareness -- **Destinations**: Send processed events to analytics platforms (GA4, Meta, - custom APIs) - -This GCP destination receives processed events from the walkerOS collector and -streams them to Google BigQuery, enabling real-time data warehousing and -analytics with Google Cloud's powerful data processing and machine learning -capabilities. +walkerOS follows a **source → collector → destination** architecture. This GCP +destination receives processed events from the walkerOS collector and streams +them to Google BigQuery, enabling real-time data warehousing and analytics with +Google Cloud's powerful data processing and machine learning capabilities. ## Installation @@ -34,14 +23,14 @@ npm install @walkeros/server-destination-gcp ## Usage -Here's a basic example of how to use the GCP destination: +Here's a basic example of how to use the GCP BigQuery destination: ```typescript import { elb } from '@walkeros/collector'; import { destinationBigQuery } from '@walkeros/server-destination-gcp'; elb('walker destination', destinationBigQuery, { - custom: { + settings: { projectId: 'YOUR_PROJECT_ID', datasetId: 'YOUR_DATASET_ID', tableId: 'YOUR_TABLE_ID', @@ -49,6 +38,17 @@ elb('walker destination', destinationBigQuery, { }); ``` +## Configuration + +| Name | Type | Description | Required | Example | +| ----------- | ----------------- | ------------------------------------------------ | -------- | ------------------------------------------ | +| `client` | `BigQuery` | Google Cloud BigQuery client instance | Yes | `new BigQuery({ projectId, keyFilename })` | +| `projectId` | `string` | Google Cloud Project ID | Yes | `'my-gcp-project'` | +| `datasetId` | `string` | BigQuery dataset ID where events will be stored | Yes | `'walker_events'` | +| `tableId` | `string` | BigQuery table ID for event storage | Yes | `'events'` | +| `location` | `string` | Geographic location for the BigQuery dataset | No | `'US'` | +| `bigquery` | `BigQueryOptions` | Additional BigQuery client configuration options | No | `{ keyFilename: "path/to/key.json" }` | + ## Contribute Feel free to contribute by submitting an diff --git a/packages/web/destinations/gtag/README.md b/packages/web/destinations/gtag/README.md index 45566a747..830b18b5e 100644 --- a/packages/web/destinations/gtag/README.md +++ b/packages/web/destinations/gtag/README.md @@ -1,7 +1,12 @@ -# @walkeros/web-destination-gtag +# Google Gtag Destination for walkerOS -Unified Google destination for walkerOS supporting Google Analytics 4 (GA4), -Google Ads, and Google Tag Manager (GTM) through a single gtag implementation. +[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/destinations/gtag) +• +[NPM Package](https://www.npmjs.com/package/@walkeros/web-destination-gtag) + +The Google Gtag destination provides a unified interface for sending events to +Google Analytics 4 (GA4), Google Ads, and Google Tag Manager (GTM) through a +single destination configuration. ## Features @@ -20,163 +25,46 @@ Google Ads, and Google Tag Manager (GTM) through a single gtag implementation. npm install @walkeros/web-destination-gtag ``` -## Basic Usage - -### Single Tool (GA4 Only) +## Usage ```typescript +import { createCollector } from '@walkeros/collector'; import { destinationGtag } from '@walkeros/web-destination-gtag'; -const destination = destinationGtag({ - settings: { - ga4: { - measurementId: 'G-XXXXXXXXXX', - }, - }, -}); -``` +const { elb } = await createCollector(); -### Multiple Tools - -```typescript -import { destinationGtag } from '@walkeros/web-destination-gtag'; - -const destination = destinationGtag({ +elb('walker destination', destinationGtag, { settings: { ga4: { - measurementId: 'G-XXXXXXXXXX', - debug: true, - pageview: false, + measurementId: 'G-XXXXXXXXXX', // Required for GA4 }, ads: { - conversionId: 'AW-XXXXXXXXX', - currency: 'EUR', + conversionId: 'AW-XXXXXXXXX', // Required for Google Ads }, gtm: { - containerId: 'GTM-XXXXXXX', + containerId: 'GTM-XXXXXXX', // Required for GTM }, }, }); ``` -### With Collector - -```typescript -import { collector } from '@walkeros/collector'; -import { destinationGtag } from '@walkeros/web-destination-gtag'; - -const instance = collector({ - destinations: [ - destinationGtag({ - settings: { - ga4: { measurementId: 'G-XXXXXXXXXX' }, - ads: { conversionId: 'AW-XXXXXXXXX' }, - gtm: { containerId: 'GTM-XXXXXXX' }, - }, - }), - ], -}); -``` - ## Configuration -### GA4 Settings +| Name | Type | Description | Required | Example | +| ----- | ------------- | -------------------------------------------------- | -------- | ----------------------------------- | +| `ga4` | `GA4Settings` | GA4-specific configuration settings | No | `{ measurementId: 'G-XXXXXXXXXX' }` | +| `ads` | `AdsSettings` | Google Ads specific configuration settings | No | `{ conversionId: 'AW-XXXXXXXXX' }` | +| `gtm` | `GTMSettings` | Google Tag Manager specific configuration settings | No | `{ containerId: 'GTM-XXXXXXX' }` | -```typescript -interface GA4Settings { - measurementId: string; // Required: GA4 Measurement ID - debug?: boolean; // Enable debug mode - include?: Include; // Data groups to include - pageview?: boolean; // Send automatic pageviews (default: true) - server_container_url?: string; // Server-side GTM URL - snakeCase?: boolean; // Convert event names to snake_case (default: true) - transport_url?: string; // Custom transport URL -} -``` +### Event Mapping -### Google Ads Settings +For custom event mapping (`mapping.entity.action.settings`): -```typescript -interface AdsSettings { - conversionId: string; // Required: Google Ads Conversion ID - currency?: string; // Default currency (default: 'EUR') -} -``` - -### GTM Settings - -```typescript -interface GTMSettings { - containerId: string; // Required: GTM Container ID - dataLayer?: string; // Custom dataLayer name (default: 'dataLayer') - domain?: string; // Custom GTM domain -} -``` - -## Mapping - -Each tool supports individual mapping configurations: - -```typescript -const mapping = { - order: { - complete: { - name: 'purchase', - settings: { - ga4: { - include: ['data', 'context'], - }, - ads: { - conversionId: 'abcxyz', - }, - gtm: {}, // Uses 'purchase' as event name - }, - data: { - map: { - transaction_id: 'data.id', - value: 'data.total', - currency: 'data.currency', - }, - }, - }, - }, -}; -``` - -### GA4-Specific Mapping - -```typescript -settings: { - ga4: { - include: ['data', 'context', 'user'], // Data groups to include - } -} -``` - -### Google Ads Conversion Mapping - -For Google Ads, specify the conversion label in the `settings.ads.label` field: - -```typescript -{ - name: 'purchase', // GA4/GTM event name - settings: { - ads: { - label: 'CONVERSION_LABEL', // This becomes AW-XXXXXXXXX/CONVERSION_LABEL - }, - } -} -``` - -### GTM DataLayer Mapping - -GTM receives the full event data and pushes to the configured dataLayer: - -```typescript -settings: { - gtm: {}, // Uses default dataLayer behavior -} -``` +| Name | Type | Description | Required | Example | +| ----- | ------------ | ----------------------------------------------- | -------- | ---------------------------------- | +| `ga4` | `GA4Mapping` | GA4-specific event mapping configuration | No | `{ include: ['data', 'context'] }` | +| `ads` | `AdsMapping` | Google Ads specific event mapping configuration | No | `{ label: 'conversion_label' }` | +| `gtm` | `GTMMapping` | GTM specific event mapping configuration | No | `{}` | ## Examples @@ -325,6 +213,13 @@ const rules: DestinationGtag.Rules = { - Check the dataLayer name matches your GTM configuration - Use GTM Preview mode to debug event flow +## Contribute + +Feel free to contribute by submitting an +[issue](https://github.com/elbwalker/walkerOS/issues), starting a +[discussion](https://github.com/elbwalker/walkerOS/discussions), or getting in +[contact](https://calendly.com/elb-alexander/30min). + ## License MIT From beb4c66ac8ba453bef087151cdc09007197d2839 Mon Sep 17 00:00:00 2001 From: aylaju <99686309+aylaju@users.noreply.github.com> Date: Tue, 2 Sep 2025 13:24:36 +0200 Subject: [PATCH 04/13] repo readme --- README.md | 287 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 217 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 11ddef726..3d1e85774 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,239 @@

- +

-# Open-source event data collection and tag management - -[Request Feature](https://github.com/elbwalker/walkerOS/issues/new) · -[Report Bug](https://github.com/elbwalker/walkerOS/issues/new) · -[Say hello](https://calendly.com/elb-alexander/30min) +# walkerOS: Open-Source Event Data Collection
- - walkerOS Documentation + + walkerOS Documentation React demo - + Discord
-# What is walkerOS +walkerOS captures, structures, and routes events with built-in support for +consent management — all directly in your code. No fragile UI configs. No +black-box logic. Just vendor-agnostic **tracking you can version, test, and +trust**. -walkerOS is a privacy-centric event data collection platform. It offers features -like data capturing, -[consent management](https://www.elbwalker.com/docs/consent_management/overview/), -data integration, and -[tag management](https://www.elbwalker.com/docs/destinations/event_mapping). -Fully configurable as code. +The project started as the web tracking library walker.js, and has evolved into +a complete **first-party tracking system** for modern teams and the modern web. -The project started as a web library -called walker.js and has evolved -into a complete first-party tracking system. +## Why walkerOS? -## Packages Overview +- **Independence**: Make your data collection independent from single vendor + specifications to reduce complexity and extra code whenever you add or remove + a new service. Keep maintenance effort to a minimum. +- **Scalability**: DOM-based, component-level frontend tagging makes tracking + user behavior declarative, reusable, and easy to maintain. +- **Privacy-first approach**: Built-in consent handling and privacy controls + help you meet compliance from day one. +- **Type-safe tracking**: Built with TypeScript to catch tracking errors at + compile time, not in production. Get IDE autocomplete for APIs and destination + configs, prevent data structure mistakes. -- **Sources** ([docs](https://www.elbwalker.com/docs/sources/), - [code](./packages/sources/)): For data creation and state management. -- **Destinations** ([docs](https://www.elbwalker.com/docs/destinations/), - [code](./packages/destinations/)): Initialize, map and share events to - third-party tools. -- **Utils** ([docs](https://www.elbwalker.com/docs/utils/), - [code](./packages/utils/)): Enhance data collection with shared utilities. +## How it works -## Why walkerOS? +![walkerOS event flow](website/static/diagrams/walkerosflowdark.png) + +## Quick Start + +### With a Bundler (npm) + +Install the required packages from npm: + +```bash +npm install @walkeros/collector @walkeros/web-source-browser +``` -- **Sustainability**: Robust infrastructure for continuous data collection, even - amidst evolving data landscapes. -- **Privacy focus**: Strict privacy-by-design approach, in-build consent - management and various data protection features. -- **Complete data ownership**: Full control of your first-party data, no vendor - lock-in, and control of data processing. -- **Simplified data model**: Intuitive event model that streamlines data - collection, making analytics straightforward and efficient. -- **Flexible architecture**: Modular design adapting to your specific data needs - and allows growing step-by-step. - -## How walkerOS operates - -```mermaid ---- -title: Basic infrastructure ---- -flowchart LR - subgraph walkerOS - direction LR - subgraph Collection - Sources - end - subgraph Activation - Destinations - end - %%Utils - end - subgraph Tools - direction LR - storage["Storage"] - marketing["Marketing"] - analytics["Analytics"] - end - Sources --> Destinations - Destinations --> Tools +Initialize walkerOS in your project: + +```javascript +import { createCollector } from '@walkeros/collector'; +import { createSource } from '@walkeros/core'; +import { sourceBrowser } from '@walkeros/web-source-browser'; + +// Initialize walkerOS +export async function initializeWalker() { + const { collector } = await createCollector({ + sources: { + browser: createSource(sourceBrowser, { + settings: { + pageview: true, + session: true, + elb: 'elb', // Browser source will set window.elb automatically + }, + }), + }, + destinations: { + console: { + push: (event) => console.log('Event:', event), + }, + }, + }); +} ``` -## Installation +### With a Script Tag + +For websites without build tools, you can install from a CDN: + +```html + +``` + +## Example: React + +Here's a quick look at how to integrate walkerOS into a React application. + +**1. Create a walker setup file:** + +```tsx +// src/walker.ts +import type { Collector, WalkerOS } from '@walkeros/core'; +import { createCollector } from '@walkeros/collector'; +import { createSource } from '@walkeros/core'; +import { createTagger, sourceBrowser } from '@walkeros/web-source-browser'; + +declare global { + interface Window { + elb: WalkerOS.Elb; + walker: Collector.Instance; + } +} + +export async function initializeWalker(): Promise { + if (window.walker) return; + + const { collector } = await createCollector({ + run: false, // Defer run to handle route changes + sources: { + browser: createSource(sourceBrowser, { + settings: { pageview: true, session: true, elb: 'elb' }, + }), + }, + destinations: { + console: { push: (event) => console.log('Event:', event) }, + }, + }); + + window.walker = collector; +} + +const taggerInstance = createTagger(); +export function tagger(entity?: string) { + return taggerInstance(entity); +} +``` + +**2. Integrate into your App component:** + +```tsx +// src/App.tsx +import { useLocation } from 'react-router-dom'; +import { useEffect, useRef } from 'react'; +import { initializeWalker } from './walker'; + +function App() { + const location = useLocation(); + const hasInitialized = useRef(false); + const firstRun = useRef(true); + + useEffect(() => { + // Prevent React StrictMode double execution + if (!hasInitialized.current) { + initializeWalker(); + hasInitialized.current = true; + } + }, []); + + useEffect(() => { + // Use walker run to trigger page views on route changes + if (firstRun.current) { + firstRun.current = false; + return; + } + window.elb('walker run'); + }, [location]); + + // ... your app routes +} +``` + +**3. Tag your components:** + +```tsx +// src/components/ProductDetail.tsx +import { tagger } from '../walker'; + +function ProductDetail({ product }) { + return ( +
+

{product.name}

+ +
+ ); +} +``` -Start collecting data with our -[web](https://github.com/elbwalker/walkerOS/tree/main/packages/web/collector/) -or -[server](https://github.com/elbwalker/walkerOS/tree/main/packages/server/collector/) -source. +## Destinations + +Destinations are the endpoints where walkerOS sends your processed events. They +transform standardized walkerOS events into the specific formats required by +analytics platforms, marketing tools, and data warehouses. + +#### Web Destinations + +- **[API](https://www.elbwalker.com/docs/destinations/web/api)** - Send events + to your own endpoints +- **[Google (gtag)](https://www.elbwalker.com/docs/destinations/web/gtag/)** - + GA4, Google Ads, and GTM integration +- **[Meta Pixel](https://www.elbwalker.com/docs/destinations/web/meta-pixel)** - + Facebook and Instagram advertising +- **[Plausible Analytics](https://www.elbwalker.com/docs/destinations/web/plausible)** - + Privacy-focused web analytics +- **[Piwik PRO](https://www.elbwalker.com/docs/destinations/web/piwikpro)** - + Privacy-focused analytics platform + +#### Server Destinations + +- **[AWS Firehose](https://www.elbwalker.com/docs/destinations/server/aws)** - + Amazon cloud services integration +- **[GCP BigQuery](https://www.elbwalker.com/docs/destinations/server/gcp)** - + GCP services and BigQuery +- **[Meta Conversions API](https://www.elbwalker.com/docs/destinations/server/meta-capi)** - + Server-side Facebook/Instagram tracking ## Contributing From 1e8cc07abdbd117ecf4704a5ef971f208e26a268 Mon Sep 17 00:00:00 2001 From: aylaju <99686309+aylaju@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:13:29 +0200 Subject: [PATCH 05/13] update readme --- README.md | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3d1e85774..de675647f 100644 --- a/README.md +++ b/README.md @@ -4,22 +4,18 @@

-# walkerOS: Open-Source Event Data Collection +# walkerOS: Open-Source tagging and event data collection
walkerOS Documentation React demo - Discord
walkerOS captures, structures, and routes events with built-in support for consent management — all directly in your code. No fragile UI configs. No -black-box logic. Just vendor-agnostic **tracking you can version, test, and -trust**. - -The project started as the web tracking library walker.js, and has evolved into -a complete **first-party tracking system** for modern teams and the modern web. +black-box logic. Just **tracking infrastructure** you can version, test, and +trust. ## Why walkerOS? @@ -40,7 +36,7 @@ a complete **first-party tracking system** for modern teams and the modern web. ## Quick Start -### With a Bundler (npm) +### npm Install the required packages from npm: @@ -76,7 +72,7 @@ export async function initializeWalker() { } ``` -### With a Script Tag +### script tag For websites without build tools, you can install from a CDN: From 9986cae6ceb1531691f688c9c62ddd8188fa4075 Mon Sep 17 00:00:00 2001 From: alexander Date: Fri, 5 Sep 2025 17:04:08 +0200 Subject: [PATCH 06/13] action vs actions --- packages/collector/src/constants.ts | 2 + .../browser/src/__tests__/html/walker.html | 16 ++++- .../browser/src/__tests__/tagger.test.ts | 60 ++++++++++++++++ .../browser/src/__tests__/walker.test.ts | 32 +++++++++ packages/web/sources/browser/src/tagger.ts | 32 +++++++++ packages/web/sources/browser/src/walker.ts | 52 +++++++++----- website/docs/guides/migration.mdx | 68 +++++++++++++++++++ website/docs/sources/web/browser/tagger.mdx | 48 ++++++++++++- website/docs/sources/web/browser/tagging.mdx | 31 ++++++--- 9 files changed, 309 insertions(+), 32 deletions(-) diff --git a/packages/collector/src/constants.ts b/packages/collector/src/constants.ts index b38fac324..05755d318 100644 --- a/packages/collector/src/constants.ts +++ b/packages/collector/src/constants.ts @@ -3,6 +3,7 @@ import type { WalkerOS } from '@walkeros/core'; export type CommandTypes = | 'Action' + | 'Actions' | 'Config' | 'Consent' | 'Context' @@ -23,6 +24,7 @@ export type CommandTypes = export const Commands: Record = { Action: 'action', + Actions: 'actions', Config: 'config', Consent: 'consent', Context: 'context', diff --git a/packages/web/sources/browser/src/__tests__/html/walker.html b/packages/web/sources/browser/src/__tests__/html/walker.html index d79cc3ea9..b5895a6f4 100644 --- a/packages/web/sources/browser/src/__tests__/html/walker.html +++ b/packages/web/sources/browser/src/__tests__/html/walker.html @@ -15,7 +15,7 @@ id="son" data-elb="son" data-elb-son="interested_in:pizza" - data-elbaction="load:speak" + data-elbactions="load:speak" >
@@ -158,7 +158,7 @@ data-elb-l="l2:2" data-elbcontext="child:link" > - +
@@ -171,3 +171,13 @@
+ + +
+
+ +
+
+ +
+
diff --git a/packages/web/sources/browser/src/__tests__/tagger.test.ts b/packages/web/sources/browser/src/__tests__/tagger.test.ts index 9fee98189..fc6398d1e 100644 --- a/packages/web/sources/browser/src/__tests__/tagger.test.ts +++ b/packages/web/sources/browser/src/__tests__/tagger.test.ts @@ -181,6 +181,66 @@ describe('Tagger', () => { }); }); + describe('Actions Method', () => { + test('single trigger and action', () => { + const result = createTagger()().actions('load', 'view').get(); + expect(result).toMatchObject({ + 'data-elbactions': 'load:view', + }); + }); + + test('single combined trigger:action', () => { + const result = createTagger()().actions('load:view').get(); + expect(result).toMatchObject({ + 'data-elbactions': 'load:view', + }); + }); + + test('object with multiple actions', () => { + const result = createTagger()() + .actions({ load: 'view', click: 'select', visible: 'impression' }) + .get(); + expect(result).toMatchObject({ + 'data-elbactions': 'load:view;click:select;visible:impression', + }); + }); + + test('accumulates multiple actions calls', () => { + const result = createTagger()() + .actions('load', 'view') + .actions({ click: 'select' }) + .actions('visible:impression') + .get(); + expect(result).toMatchObject({ + 'data-elbactions': 'load:view;click:select;visible:impression', + }); + }); + + test('works with entity', () => { + const result = createTagger()() + .entity('product') + .data('id', 123) + .actions('load', 'view') + .get(); + expect(result).toMatchObject({ + 'data-elb': 'product', + 'data-elbactions': 'load:view', + 'data-elb-product': 'id:123', + }); + }); + + test('can be used alongside action method', () => { + const result = createTagger()() + .action('click', 'select') + .actions('load', 'view') + .get(); + expect(result).toMatchObject({ + 'data-elbaction': 'click:select', + 'data-elbactions': 'load:view', + }); + }); + }); + describe('Context Method', () => { test('single key-value', () => { const result = createTagger()().context('test', 'engagement').get(); diff --git a/packages/web/sources/browser/src/__tests__/walker.test.ts b/packages/web/sources/browser/src/__tests__/walker.test.ts index 14c913088..a8d8f12f2 100644 --- a/packages/web/sources/browser/src/__tests__/walker.test.ts +++ b/packages/web/sources/browser/src/__tests__/walker.test.ts @@ -343,6 +343,38 @@ describe('Walker', () => { }, ]); }); + + test('data-elbaction applies to nearest entity only', () => { + expect( + getEvents(getElem('test-nearest-only'), Triggers.Click), + ).toMatchObject([ + { + entity: 'child', + action: 'test', + data: { scope: 'inner' }, + trigger: 'click', + }, + ]); + }); + + test('data-elbactions applies to all entities', () => { + expect( + getEvents(getElem('test-all-entities'), Triggers.Click), + ).toMatchObject([ + { + entity: 'child', + action: 'test', + data: { scope: 'inner' }, + trigger: 'click', + }, + { + entity: 'parent', + action: 'test', + data: { scope: 'outer' }, + trigger: 'click', + }, + ]); + }); }); function getElem(selector: string) { diff --git a/packages/web/sources/browser/src/tagger.ts b/packages/web/sources/browser/src/tagger.ts index cf11bcf32..f0b870ac8 100644 --- a/packages/web/sources/browser/src/tagger.ts +++ b/packages/web/sources/browser/src/tagger.ts @@ -11,6 +11,8 @@ export interface TaggerInstance { ((data: WalkerOS.Properties) => TaggerInstance); action: ((trigger: string, action?: string) => TaggerInstance) & ((actions: Record) => TaggerInstance); + actions: ((trigger: string, action?: string) => TaggerInstance) & + ((actions: Record) => TaggerInstance); context: ((key: string, value: WalkerOS.Property) => TaggerInstance) & ((context: WalkerOS.Properties) => TaggerInstance); globals: ((key: string, value: WalkerOS.Property) => TaggerInstance) & @@ -37,6 +39,7 @@ export function createTagger( let namingEntity: string | undefined = entity; // Used for data attribute naming const dataProperties: Record = {}; const actionProperties: Record = {}; + const actionsProperties: Record = {}; const contextProperties: WalkerOS.Properties = {}; const globalProperties: WalkerOS.Properties = {}; const linkProperties: Record = {}; @@ -113,6 +116,30 @@ export function createTagger( return instance; }, + actions( + triggerOrActions: string | Record, + actionValue?: string, + ): TaggerInstance { + if (isString(triggerOrActions)) { + if (isDefined(actionValue)) { + // Two parameters: trigger and action + actionsProperties[triggerOrActions] = actionValue; + } else { + // Single parameter: could be "trigger:action" or just "trigger" + if (triggerOrActions.includes(':')) { + const [trigger, action] = triggerOrActions.split(':', 2); + actionsProperties[trigger] = action; + } else { + actionsProperties[triggerOrActions] = triggerOrActions; + } + } + } else { + Object.assign(actionsProperties, triggerOrActions); + } + + return instance; + }, + context( keyOrContext: string | WalkerOS.Properties, value?: WalkerOS.Property, @@ -175,6 +202,11 @@ export function createTagger( attributes[`${prefix}action`] = serializeKeyValue(actionProperties); } + // Add actions attributes (for all entities) + if (Object.keys(actionsProperties).length > 0) { + attributes[`${prefix}actions`] = serializeKeyValue(actionsProperties); + } + // Add context attributes if (Object.keys(contextProperties).length > 0) { attributes[`${prefix}context`] = serializeKeyValue(contextProperties); diff --git a/packages/web/sources/browser/src/walker.ts b/packages/web/sources/browser/src/walker.ts index be15d01cd..bc63d6254 100644 --- a/packages/web/sources/browser/src/walker.ts +++ b/packages/web/sources/browser/src/walker.ts @@ -103,11 +103,11 @@ export function getEvents( ): Walker.Events { const events: Walker.Events = []; - // Check for an action (data-elbaction) attribute and resolve it - const actions = resolveAttributes(prefix, target, trigger); + // Check for actions and get entity collection strategy + const { actions, nearestOnly } = resolveAttributes(prefix, target, trigger); // Stop if there's no valid action combo - if (!actions) return events; + if (!actions.length) return events; actions.forEach((triggerAction) => { const filter = splitAttribute(triggerAction.actionParams || '', ',').reduce( @@ -118,8 +118,8 @@ export function getEvents( {} as Walker.Filter, ); - // Get the entities with their properties - const entities = getEntities(prefix, target, filter); + // Get entities - using nearestOnly flag to determine collection strategy + const entities = getEntities(prefix, target, filter, nearestOnly); // Use page as default entity if no one was set if (!entities.length) { @@ -143,7 +143,7 @@ export function getEvents( }); } - // Return a list of all full events + // Return a list of full events entities.forEach((entity) => { events.push({ entity: entity.type, @@ -234,6 +234,7 @@ export function getEntities( prefix: string, target: Element, filter?: Walker.Filter, + nearestOnly = false, ): WalkerOS.Entities { const entities: WalkerOS.Entities = []; let element = target as Node['parentElement']; @@ -243,7 +244,10 @@ export function getEntities( while (element) { const entity = getEntity(prefix, element, target, filter); - if (entity) entities.push(entity); + if (entity) { + entities.push(entity); + if (nearestOnly) break; // Stop after first entity for data-elbaction + } element = getParent(prefix, element); } @@ -417,27 +421,41 @@ function resolveAttributes( prefix: string, target: Element, trigger: string, -): Walker.TriggerActions { +): { actions: Walker.TriggerActions; nearestOnly: boolean } { let element = target as Node['parentElement']; while (element) { - const attribute = getAttribute( + // Check for data-elbactions first (takes precedence) + const multiAttribute = getAttribute( element, - getElbAttributeName(prefix, Const.Commands.Action, false), + getElbAttributeName(prefix, Const.Commands.Actions, false), ); - // Get action string related to trigger - const triggerActions = getTriggerActions(attribute); + if (multiAttribute) { + const triggerActions = getTriggerActions(multiAttribute); + if (triggerActions[trigger]) { + return { actions: triggerActions[trigger], nearestOnly: false }; + } + } + + // Check for data-elbaction (nearest entity only) + const singleAttribute = getAttribute( + element, + getElbAttributeName(prefix, Const.Commands.Action, false), + ); - // Action found on element or is not a click trigger - // @TODO aggregate all click triggers, too - if (triggerActions[trigger] || trigger !== 'click') - return triggerActions[trigger]; + if (singleAttribute) { + const triggerActions = getTriggerActions(singleAttribute); + // Action found on element or is not a click trigger + if (triggerActions[trigger] || trigger !== 'click') { + return { actions: triggerActions[trigger] || [], nearestOnly: true }; + } + } element = getParent(prefix, element); } - return []; + return { actions: [], nearestOnly: false }; } function splitAttribute(str: string, separator = ';'): Walker.Attributes { diff --git a/website/docs/guides/migration.mdx b/website/docs/guides/migration.mdx index 8637c9c66..ff4294fd5 100644 --- a/website/docs/guides/migration.mdx +++ b/website/docs/guides/migration.mdx @@ -195,3 +195,71 @@ Solution: `elb` is returned by `createCollector()` function **TypeScript cannot find types** Solution: Import types from `@walkeros/core` + +## data-elbaction vs data-elbactions + +### Breaking Change in @walkeros packages + +The behavior of `data-elbaction` has changed to improve performance and provide more precise entity targeting: + +- **@elbwalker behavior**: `data-elbaction` applied to **all entities** in the DOM hierarchy +- **@walkeros behavior**: `data-elbaction` applies to **nearest entity only** + +### Migration Strategy + +#### Option 1: Keep Current Behavior (Recommended for migration) + +Replace `data-elbaction` with `data-elbactions` to maintain the same behavior: + +```html + +
+
+ +
+
+ + +
+
+ +
+
+``` + +#### Option 2: Use New Behavior (Recommended for new projects) + +Use `data-elbaction` for more precise, performance-optimized tracking: + +```html + +
+
+ +
+
+``` + +### When to Use Each + +**Use `data-elbaction` (nearest entity) when:** +- You want precise tracking of only the specific entity +- Performance is critical (fewer events) +- Implementing new features + +**Use `data-elbactions` (all entities) when:** +- Migrating from @elbwalker and need same behavior +- You need context from parent entities +- Analytics requires full DOM hierarchy + +### Tagger API + +The tagger also provides both methods: + +```typescript +// Nearest entity only (data-elbaction) +tagger().action('click', 'select').get() + +// All entities (data-elbactions) +tagger().actions('click', 'select').get() +``` diff --git a/website/docs/sources/web/browser/tagger.mdx b/website/docs/sources/web/browser/tagger.mdx index 2c87dbf3e..d759a10d2 100644 --- a/website/docs/sources/web/browser/tagger.mdx +++ b/website/docs/sources/web/browser/tagger.mdx @@ -244,7 +244,7 @@ tagger('product').data({ id: 123, name: 'Widget', price: 99.99 }); ##### `action(trigger: string, action?: string)` | `action(object: Record)` -Adds action mappings for event triggers. +Adds action mappings for event triggers. Creates a `data-elbaction` attribute. ```typescript // Single action @@ -257,6 +257,26 @@ tagger().action('load:view'); tagger().action({ load: 'view', click: 'select', visible: 'impression' }); ``` +##### `actions(trigger: string, action?: string)` | `actions(object: Record)` + +Adds action mappings for event triggers. Creates a `data-elbactions` attribute. + +```typescript +// Single action +tagger().actions('load', 'view'); + +// Combined trigger:action +tagger().actions('load:view'); + +// Multiple actions +tagger().actions({ load: 'view', click: 'select', visible: 'impression' }); + +// Can be combined with action() method +tagger() + .action('click', 'select') + .actions('load', 'view'); +``` + ##### `context(key: string, value: Property)` | `context(object: Properties)` Adds context properties that apply to all events. @@ -315,7 +335,7 @@ returns the final attributes object. ### Product Listing Page ```typescript -// For product cards that don't need entity tracking +// For product cards with nearest entity tracking only function ProductCard({ product }) { return (
{product.name}
); } + +// For product cards that also need page context tracking +function ProductCardWithContext({ product }) { + return ( +
+
+ {product.name} +
+
+ ); +} ``` ### Shopping Cart @@ -374,3 +415,4 @@ function trackComponent(type, data, actions = {}) { Sign Up ``` + diff --git a/website/docs/sources/web/browser/tagging.mdx b/website/docs/sources/web/browser/tagging.mdx index 1e852bfc3..639eb3d5c 100644 --- a/website/docs/sources/web/browser/tagging.mdx +++ b/website/docs/sources/web/browser/tagging.mdx @@ -40,7 +40,8 @@ Tag a page...
+ data-elbactions="TRIGGER:ACTION" data-elbcontext="KEY:VALUE" data-elbglobals="KEY:VALUE" /> @@ -120,10 +121,22 @@ You define the entity **scope** by setting the `data-elb` attribute with the name of an entity to an element, e.g. `data-elb="promotion"`. The default entity is `page` when no `data-elb` is set. -An **action** can be added by setting the `data-elbaction` attribute on the -**same level** or **child elements** in combination with a **matching trigger**, -e.g., `data-elbaction="visible:view"` to fire a promotion view event when an -element has been in the viewport for at least 50% for one second. +An **action** can be added by setting one of the following attributes on the +**same level** or **child elements** in combination with a **matching trigger**: + +- **`data-elbaction`** - applies action to the **nearest entity only** +- **`data-elbactions`** - applies action to **all entities** in the DOM hierarchy + +Both attributes use the same syntax, e.g., `data-elbaction="visible:view"` or +`data-elbactions="click:select"` to fire events when triggered. + +:::info Migration Note + +The behavior of `data-elbaction` changed in @walkeros to apply to nearest entity only. +For the previous @elbwalker "all entities" behavior, use `data-elbactions`. +See the [Migration Guide](/docs/guides/migration#data-elbaction-vs-data-elbactions) for details. + +::: To define the entities' **properties**, set the **composited attribute** `data-elb-ENTITY` with the key and value, e.g. @@ -180,10 +193,10 @@ gets triggered. Use brackets behind the trigger to pass that information. ### Action filter At some point, you might want to nest one entity inside another. To prevent an -action to trigger both entities, you can restrict the action to a specific -entity by adding the name, e.g. `data-elbaction="load:view(product)`. If the -trigger event gets called, the result will only include the property values from -the specific entities. +action to trigger unwanted entities, you can restrict the action to a specific +entity by adding the name, e.g. `data-elbaction="load:view(product)` or +`data-elbactions="load:view(product)"`. If the trigger event gets called, +the result will only include the property values from the specific entities. ```html From c651e6e446aa1ad8a71fd54a19ca0fa59526fbf2 Mon Sep 17 00:00:00 2001 From: alexander Date: Fri, 5 Sep 2025 17:38:58 +0200 Subject: [PATCH 07/13] visible and impression --- .../browser/src/__tests__/tagger.test.ts | 20 +++---- .../browser/src/__tests__/trigger.test.ts | 1 + .../src/__tests__/triggerVisible.test.ts | 8 ++- packages/web/sources/browser/src/trigger.ts | 6 +- .../web/sources/browser/src/triggerVisible.ts | 5 +- website/docs/guides/migration.mdx | 56 +++++++++++++++++++ website/docs/sources/web/browser/tagger.mdx | 9 +-- website/docs/sources/web/browser/tagging.mdx | 21 ++++--- 8 files changed, 93 insertions(+), 33 deletions(-) diff --git a/packages/web/sources/browser/src/__tests__/tagger.test.ts b/packages/web/sources/browser/src/__tests__/tagger.test.ts index fc6398d1e..bd7181835 100644 --- a/packages/web/sources/browser/src/__tests__/tagger.test.ts +++ b/packages/web/sources/browser/src/__tests__/tagger.test.ts @@ -149,10 +149,10 @@ describe('Tagger', () => { test('object with multiple actions', () => { const result = createTagger()() - .action({ load: 'view', click: 'select', visible: 'impression' }) + .action({ load: 'view', click: 'select', impression: 'view' }) .get(); expect(result).toMatchObject({ - 'data-elbaction': 'load:view;click:select;visible:impression', + 'data-elbaction': 'load:view;click:select;impression:view', }); }); @@ -160,10 +160,10 @@ describe('Tagger', () => { const result = createTagger()() .action('load', 'view') .action('click', 'select') - .action({ visible: 'impression' }) + .action({ impression: 'view' }) .get(); expect(result).toMatchObject({ - 'data-elbaction': 'load:view;click:select;visible:impression', + 'data-elbaction': 'load:view;click:select;impression:view', }); }); @@ -198,10 +198,10 @@ describe('Tagger', () => { test('object with multiple actions', () => { const result = createTagger()() - .actions({ load: 'view', click: 'select', visible: 'impression' }) + .actions({ load: 'view', click: 'select', impression: 'view' }) .get(); expect(result).toMatchObject({ - 'data-elbactions': 'load:view;click:select;visible:impression', + 'data-elbactions': 'load:view;click:select;impression:view', }); }); @@ -209,10 +209,10 @@ describe('Tagger', () => { const result = createTagger()() .actions('load', 'view') .actions({ click: 'select' }) - .actions('visible:impression') + .actions('impression:view') .get(); expect(result).toMatchObject({ - 'data-elbactions': 'load:view;click:select;visible:impression', + 'data-elbactions': 'load:view;click:select;impression:view', }); }); @@ -395,14 +395,14 @@ describe('Tagger', () => { test('full chain without entity (generic)', () => { const result = createTagger()() .data({ category: 'electronics', brand: 'TechCorp' }) - .action({ load: 'view', visible: 'impression' }) + .action({ load: 'view', impression: 'view' }) .context({ test: 'a/b', position: 'header' }) .globals({ lang: 'en', plan: 'paid' }) .get(); expect(result).toMatchObject({ 'data-elb-': 'category:electronics;brand:TechCorp', - 'data-elbaction': 'load:view;visible:impression', + 'data-elbaction': 'load:view;impression:view', 'data-elbcontext': 'test:a/b;position:header', 'data-elbglobals': 'lang:en;plan:paid', }); diff --git a/packages/web/sources/browser/src/__tests__/trigger.test.ts b/packages/web/sources/browser/src/__tests__/trigger.test.ts index d3e392efa..cf5fc1784 100644 --- a/packages/web/sources/browser/src/__tests__/trigger.test.ts +++ b/packages/web/sources/browser/src/__tests__/trigger.test.ts @@ -86,6 +86,7 @@ describe('Trigger System', () => { expect(Triggers.Load).toBe('load'); expect(Triggers.Hover).toBe('hover'); expect(Triggers.Submit).toBe('submit'); + expect(Triggers.Impression).toBe('impression'); expect(Triggers.Visible).toBe('visible'); expect(Triggers.Scroll).toBe('scroll'); expect(Triggers.Pulse).toBe('pulse'); diff --git a/packages/web/sources/browser/src/__tests__/triggerVisible.test.ts b/packages/web/sources/browser/src/__tests__/triggerVisible.test.ts index df21a4345..d980e71ad 100644 --- a/packages/web/sources/browser/src/__tests__/triggerVisible.test.ts +++ b/packages/web/sources/browser/src/__tests__/triggerVisible.test.ts @@ -19,6 +19,7 @@ interface CollectorWithVisibility extends Collector.Instance { multiple: boolean; blocked: boolean; context: Context; + trigger: string; } >; }; @@ -47,7 +48,7 @@ jest.mock('@walkeros/web-core', () => ({ jest.mock('../trigger', () => ({ ...jest.requireActual('../trigger'), handleTrigger: jest.fn(), - Triggers: { Visible: 'visible' }, + Triggers: { Impression: 'impression', Visible: 'visible' }, })); // Get references to mocked functions @@ -146,6 +147,7 @@ describe('triggerVisible', () => { multiple: true, blocked: false, context: expect.any(Object), + trigger: 'visible', }); }); @@ -202,7 +204,7 @@ describe('triggerVisible', () => { }), }), element, - 'visible', + 'impression', ); }); @@ -243,7 +245,7 @@ describe('triggerVisible', () => { }), }), element, - 'visible', + 'impression', ); }); diff --git a/packages/web/sources/browser/src/trigger.ts b/packages/web/sources/browser/src/trigger.ts index 4bda64d17..3cbee637e 100644 --- a/packages/web/sources/browser/src/trigger.ts +++ b/packages/web/sources/browser/src/trigger.ts @@ -44,8 +44,8 @@ export const Triggers: { [key: string]: Walker.Trigger } = { Pulse: 'pulse', Scroll: 'scroll', Submit: 'submit', + Impression: 'impression', Visible: 'visible', - Visibles: 'visibles', Wait: 'wait', } as const; @@ -195,10 +195,10 @@ function handleActionElem( case Triggers.Scroll: triggerScroll(elem, triggerAction.triggerParams); break; - case Triggers.Visible: + case Triggers.Impression: triggerVisible(context, elem); break; - case Triggers.Visibles: + case Triggers.Visible: triggerVisible(context, elem, { multiple: true }); break; case Triggers.Wait: diff --git a/packages/web/sources/browser/src/triggerVisible.ts b/packages/web/sources/browser/src/triggerVisible.ts index 031376fbf..b065e94c8 100644 --- a/packages/web/sources/browser/src/triggerVisible.ts +++ b/packages/web/sources/browser/src/triggerVisible.ts @@ -23,7 +23,7 @@ interface VisibilityState { duration: number; elementConfigs?: WeakMap< HTMLElement, - { multiple: boolean; blocked: boolean; context: Context } + { multiple: boolean; blocked: boolean; context: Context; trigger: string } >; } @@ -156,7 +156,7 @@ function handleIntersection( await handleTrigger( elementConfig.context, target as Element, - Triggers.Visible, + elementConfig.trigger, ); } @@ -226,6 +226,7 @@ export function triggerVisible( multiple: config.multiple ?? false, blocked: false, context, + trigger: config.multiple ? 'visible' : 'impression', }); state.observer.observe(element); } diff --git a/website/docs/guides/migration.mdx b/website/docs/guides/migration.mdx index ff4294fd5..b9bf0b2fd 100644 --- a/website/docs/guides/migration.mdx +++ b/website/docs/guides/migration.mdx @@ -263,3 +263,59 @@ tagger().action('click', 'select').get() // All entities (data-elbactions) tagger().actions('click', 'select').get() ``` + +## visible vs impression Triggers + +### Breaking Change in @walkeros packages + +The visibility trigger names have been updated to better reflect their behavior: + +- **`visible` trigger**: Now fires **multiple times** when element re-enters viewport (was `visibles`) +- **`impression` trigger**: Fires **once only** when element first becomes visible (was `visible`) + +### Migration Strategy + +#### Option 1: Update Trigger Names (Recommended) + +Update your HTML to use the new trigger names: + +```html + +
Single fire
+
Multiple fires
+ + +
Single fire
+
Multiple fires
+``` + +#### Option 2: Understand the Behavior Change + +If you keep using the old names, understand the behavior has changed: + +- Old `visible` behavior (single-fire) → Now use `impression` +- Old `visibles` behavior (multiple-fire) → Now use `visible` + +### When to Use Each + +**Use `impression` trigger when:** +- You want to track when content is first seen +- Measuring ad impressions or content views +- One-time engagement metrics + +**Use `visible` trigger when:** +- You want to track repeated interactions +- Measuring scroll behavior or re-engagement +- Analytics requires multiple visibility events + +### Tagger API + +The tagger supports both trigger types: + +```typescript +// Single impression (fires once) +tagger().action('impression', 'view').get() + +// Multiple visibility (fires each time visible) +tagger().action('visible', 'track').get() +``` diff --git a/website/docs/sources/web/browser/tagger.mdx b/website/docs/sources/web/browser/tagger.mdx index d759a10d2..4cbd2133f 100644 --- a/website/docs/sources/web/browser/tagger.mdx +++ b/website/docs/sources/web/browser/tagger.mdx @@ -254,7 +254,7 @@ tagger().action('load', 'view'); tagger().action('load:view'); // Multiple actions -tagger().action({ load: 'view', click: 'select', visible: 'impression' }); +tagger().action({ load: 'view', click: 'select', impression: 'view' }); ``` ##### `actions(trigger: string, action?: string)` | `actions(object: Record)` @@ -269,12 +269,10 @@ tagger().actions('load', 'view'); tagger().actions('load:view'); // Multiple actions -tagger().actions({ load: 'view', click: 'select', visible: 'impression' }); +tagger().actions({ load: 'view', click: 'select', visible: 'visible' }); // Can be combined with action() method -tagger() - .action('click', 'select') - .actions('load', 'view'); +tagger().action('click', 'select').actions('load', 'view'); ``` ##### `context(key: string, value: Property)` | `context(object: Properties)` @@ -415,4 +413,3 @@ function trackComponent(type, data, actions = {}) { Sign Up ``` - diff --git a/website/docs/sources/web/browser/tagging.mdx b/website/docs/sources/web/browser/tagging.mdx index 639eb3d5c..3fc38d27a 100644 --- a/website/docs/sources/web/browser/tagging.mdx +++ b/website/docs/sources/web/browser/tagging.mdx @@ -125,16 +125,19 @@ An **action** can be added by setting one of the following attributes on the **same level** or **child elements** in combination with a **matching trigger**: - **`data-elbaction`** - applies action to the **nearest entity only** -- **`data-elbactions`** - applies action to **all entities** in the DOM hierarchy +- **`data-elbactions`** - applies action to **all entities** in the DOM + hierarchy Both attributes use the same syntax, e.g., `data-elbaction="visible:view"` or `data-elbactions="click:select"` to fire events when triggered. :::info Migration Note -The behavior of `data-elbaction` changed in @walkeros to apply to nearest entity only. -For the previous @elbwalker "all entities" behavior, use `data-elbactions`. -See the [Migration Guide](/docs/guides/migration#data-elbaction-vs-data-elbactions) for details. +The behavior of `data-elbaction` changed in @walkeros to apply to nearest entity +only. For the previous @elbwalker "all entities" behavior, use +`data-elbactions`. See the +[Migration Guide](/docs/guides/migration#data-elbaction-vs-data-elbactions) for +details. ::: @@ -151,8 +154,8 @@ listener or mutation observer initialization. | ----------- | ------------------------------------------------------------------------------------ | | load | after loading a page when DOM is ready | | click | when an element or a child is clicked | -| visible | after an element has been in the viewport for at least 50% for one second | -| visibles | each time an element re-enters the viewport after being out of view | +| impression | after an element has been in the viewport for at least 50% for one second | +| visible | each time an element re-enters the viewport after being out of view | | hover | each time the mouse enters the corresponding element | | submit | on valid form submission | | wait(ms) | waits ms seconds (15 seconds by default) until triggering | @@ -194,9 +197,9 @@ gets triggered. Use brackets behind the trigger to pass that information. At some point, you might want to nest one entity inside another. To prevent an action to trigger unwanted entities, you can restrict the action to a specific -entity by adding the name, e.g. `data-elbaction="load:view(product)` or -`data-elbactions="load:view(product)"`. If the trigger event gets called, -the result will only include the property values from the specific entities. +entity by adding the name, e.g. `data-elbaction="load:view(product)` or +`data-elbactions="load:view(product)"`. If the trigger event gets called, the +result will only include the property values from the specific entities. ```html From 88661eddd1b8ede51833a47250d234bf154ba19c Mon Sep 17 00:00:00 2001 From: alexander Date: Sat, 6 Sep 2025 13:49:02 +0200 Subject: [PATCH 08/13] entity --- packages/core/README.md | 2 +- packages/core/src/__tests__/eventGenerator.test.ts | 2 +- packages/core/src/__tests__/mapping.test.ts | 4 ++-- packages/core/src/eventGenerator.ts | 14 +++++++------- packages/core/src/types/walkeros.ts | 2 +- .../server/core/src/__tests__/destination.test.ts | 4 ++-- .../destinations/meta/src/examples/events.ts | 2 +- .../destinations/meta/src/examples/mapping.ts | 4 ++-- packages/web/destinations/gtag/README.md | 2 +- .../web/destinations/gtag/src/examples/events.ts | 2 +- .../web/destinations/gtag/src/examples/mapping.ts | 4 ++-- .../web/destinations/meta/src/examples/events.ts | 7 ++++--- .../web/destinations/meta/src/examples/mapping.ts | 8 ++++---- .../destinations/piwikpro/src/examples/events.ts | 4 ++-- .../destinations/piwikpro/src/examples/mapping.ts | 4 ++-- .../sources/browser/src/__tests__/walker.test.ts | 10 +++++----- packages/web/sources/browser/src/translation.ts | 2 +- website/docs/core/index.mdx | 2 +- website/docs/sources/web/browser/tagging.mdx | 8 ++++---- 19 files changed, 44 insertions(+), 43 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index c16fea9d3..8ceaa07c2 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -133,7 +133,7 @@ const mapping = { loop: [ 'nested', { - condition: (entity) => entity.type === 'product', + condition: (entity) => entity.entity === 'product', map: { id: 'data.id', name: 'data.name' }, }, ], diff --git a/packages/core/src/__tests__/eventGenerator.test.ts b/packages/core/src/__tests__/eventGenerator.test.ts index baf16e28d..90a38c57e 100644 --- a/packages/core/src/__tests__/eventGenerator.test.ts +++ b/packages/core/src/__tests__/eventGenerator.test.ts @@ -21,7 +21,7 @@ describe('createEvent', () => { user: { id: 'us3r', device: 'c00k13', session: 's3ss10n' }, nested: [ { - type: 'child', + entity: 'child', data: { is: 'subordinated' }, nested: [], context: { element: ['child', 0] }, diff --git a/packages/core/src/__tests__/mapping.test.ts b/packages/core/src/__tests__/mapping.test.ts index 88af9a222..b83a22a65 100644 --- a/packages/core/src/__tests__/mapping.test.ts +++ b/packages/core/src/__tests__/mapping.test.ts @@ -146,7 +146,7 @@ describe('getMappingValue', () => { function getNested(data: WalkerOS.Properties) { return { - type: 'child', + entity: 'child', data, nested: [], context: { element: ['child', 0] }, @@ -236,7 +236,7 @@ describe('getMappingValue', () => { 'nested', { condition: (entity) => - isObject(entity) && entity.type === 'product', + isObject(entity) && entity.entity === 'product', key: 'data.name', }, ], diff --git a/packages/core/src/eventGenerator.ts b/packages/core/src/eventGenerator.ts index 8417f38a2..6fe48ea7d 100644 --- a/packages/core/src/eventGenerator.ts +++ b/packages/core/src/eventGenerator.ts @@ -33,7 +33,7 @@ export function createEvent( user: { id: 'us3r', device: 'c00k13', session: 's3ss10n' }, nested: [ { - type: 'child', + entity: 'child', data: { is: 'subordinated' }, nested: [], context: { element: ['child', 0] }, @@ -122,7 +122,7 @@ export function getEvent( globals: { pagegroup: 'shop' }, nested: [ { - type: 'product', + entity: 'product', data: { ...product1.data, quantity }, context: { shopping: ['cart', 0] }, nested: [], @@ -140,13 +140,13 @@ export function getEvent( globals: { pagegroup: 'shop' }, nested: [ { - type: 'product', + entity: 'product', ...product1, context: { shopping: ['checkout', 0] }, nested: [], }, { - type: 'product', + entity: 'product', ...product2, context: { shopping: ['checkout', 0] }, nested: [], @@ -166,19 +166,19 @@ export function getEvent( globals: { pagegroup: 'shop' }, nested: [ { - type: 'product', + entity: 'product', ...product1, context: { shopping: ['complete', 0] }, nested: [], }, { - type: 'product', + entity: 'product', ...product2, context: { shopping: ['complete', 0] }, nested: [], }, { - type: 'gift', + entity: 'gift', data: { name: 'Surprise', }, diff --git a/packages/core/src/types/walkeros.ts b/packages/core/src/types/walkeros.ts index 0d2242bc4..4b103c42c 100644 --- a/packages/core/src/types/walkeros.ts +++ b/packages/core/src/types/walkeros.ts @@ -97,7 +97,7 @@ export interface OrderedProperties { export type Entities = Array; export interface Entity { - type: string; + entity: string; data: Properties; nested: Entities; context: OrderedProperties; diff --git a/packages/server/core/src/__tests__/destination.test.ts b/packages/server/core/src/__tests__/destination.test.ts index d2539ce4b..689f2e066 100644 --- a/packages/server/core/src/__tests__/destination.test.ts +++ b/packages/server/core/src/__tests__/destination.test.ts @@ -424,7 +424,7 @@ describe('Destination', () => { value: 'new name', }, 'data.string': { value: 'bar' }, - 'nested.0.type': { value: 'kid' }, + 'nested.0.entity': { value: 'kid' }, 'data.number': { consent: { marketing: true }, }, @@ -453,7 +453,7 @@ describe('Destination', () => { number: undefined, // Redacted due to missing consent new: 'value', }), - nested: [expect.objectContaining({ type: 'kid' })], + nested: [expect.objectContaining({ entity: 'kid' })], // timing: 0, // @TODO should be set to default type }); }); diff --git a/packages/server/destinations/meta/src/examples/events.ts b/packages/server/destinations/meta/src/examples/events.ts index c965956be..7390e1178 100644 --- a/packages/server/destinations/meta/src/examples/events.ts +++ b/packages/server/destinations/meta/src/examples/events.ts @@ -22,7 +22,7 @@ export function Purchase(): BodyParameters { currency: 'EUR', value: Number(event.data.total), contents: event.nested - .filter((item) => item.type === 'product') + .filter((item) => item.entity === 'product') .map((item) => ({ id: String(item.data.id), quantity: Number(item.data.quantity) || 1, diff --git a/packages/server/destinations/meta/src/examples/mapping.ts b/packages/server/destinations/meta/src/examples/mapping.ts index 5a529ec39..b5c7d3645 100644 --- a/packages/server/destinations/meta/src/examples/mapping.ts +++ b/packages/server/destinations/meta/src/examples/mapping.ts @@ -22,7 +22,7 @@ export const Purchase: DestinationMeta.Rule = { 'nested', { condition: (entity) => - isObject(entity) && entity.type === 'product', + isObject(entity) && entity.entity === 'product', map: { id: 'data.id', item_price: 'data.price', @@ -34,7 +34,7 @@ export const Purchase: DestinationMeta.Rule = { num_items: { fn: (event) => (event as WalkerOS.Event).nested.filter( - (item) => item.type === 'product', + (item) => item.entity === 'product', ).length, }, }, diff --git a/packages/web/destinations/gtag/README.md b/packages/web/destinations/gtag/README.md index 830b18b5e..0c09b5391 100644 --- a/packages/web/destinations/gtag/README.md +++ b/packages/web/destinations/gtag/README.md @@ -97,7 +97,7 @@ const destination = destinationGtag({ loop: [ 'nested', { - condition: (entity) => entity.type === 'product', + condition: (entity) => entity.entity === 'product', map: { item_id: 'data.id', item_name: 'data.name', diff --git a/packages/web/destinations/gtag/src/examples/events.ts b/packages/web/destinations/gtag/src/examples/events.ts index 99ea3cde2..f0ee89d07 100644 --- a/packages/web/destinations/gtag/src/examples/events.ts +++ b/packages/web/destinations/gtag/src/examples/events.ts @@ -14,7 +14,7 @@ export function ga4Purchase(): unknown[] { shipping: event.data.shipping, currency: 'EUR', items: event.nested - .filter((item) => item.type === 'product') + .filter((item) => item.entity === 'product') .map((item) => ({ item_id: item.data.id, item_name: item.data.name, diff --git a/packages/web/destinations/gtag/src/examples/mapping.ts b/packages/web/destinations/gtag/src/examples/mapping.ts index d50628a90..f41719092 100644 --- a/packages/web/destinations/gtag/src/examples/mapping.ts +++ b/packages/web/destinations/gtag/src/examples/mapping.ts @@ -22,7 +22,7 @@ export const ga4Purchase: DestinationGtag.Rule = { 'nested', { condition: (entity) => - isObject(entity) && entity.type === 'product', + isObject(entity) && entity.entity === 'product', map: { item_id: 'data.id', item_name: 'data.name', @@ -115,7 +115,7 @@ export const combinedPurchase: DestinationGtag.Rule = { 'nested', { condition: (entity) => - isObject(entity) && entity.type === 'product', + isObject(entity) && entity.entity === 'product', map: { item_id: 'data.id', item_name: 'data.name', diff --git a/packages/web/destinations/meta/src/examples/events.ts b/packages/web/destinations/meta/src/examples/events.ts index 3e1f2c970..0900b486d 100644 --- a/packages/web/destinations/meta/src/examples/events.ts +++ b/packages/web/destinations/meta/src/examples/events.ts @@ -10,7 +10,7 @@ export function Purchase(): unknown[] { value: event.data.total, currency: 'EUR', contents: event.nested - .filter((item) => item.type === 'product') + .filter((item) => item.entity === 'product') .map((item) => ({ id: item.data.id, quantity: 1 })), content_type: 'product', num_items: 2, @@ -45,12 +45,13 @@ export function InitiateCheckout(): unknown[] { currency: 'EUR', value: event.data.value, contents: event.nested - .filter((entity) => entity.type === 'product') + .filter((entity) => entity.entity === 'product') .map((entity) => ({ id: entity.data.id, quantity: entity.data.quantity, })), - num_items: event.nested.filter((item) => item.type === 'product').length, + num_items: event.nested.filter((item) => item.entity === 'product') + .length, }, { eventID: event.id }, ]; diff --git a/packages/web/destinations/meta/src/examples/mapping.ts b/packages/web/destinations/meta/src/examples/mapping.ts index cbc5796de..1ff0610ab 100644 --- a/packages/web/destinations/meta/src/examples/mapping.ts +++ b/packages/web/destinations/meta/src/examples/mapping.ts @@ -13,7 +13,7 @@ export const Purchase: DestinationMeta.Rule = { 'nested', { condition: (entity) => - isObject(entity) && entity.type === 'product', + isObject(entity) && entity.entity === 'product', map: { id: 'data.id', quantity: { key: 'data.quantity', value: 1 }, @@ -25,7 +25,7 @@ export const Purchase: DestinationMeta.Rule = { num_items: { fn: (event) => (event as WalkerOS.Event).nested.filter( - (item) => item.type === 'product', + (item) => item.entity === 'product', ).length, }, }, @@ -64,7 +64,7 @@ export const InitiateCheckout: DestinationMeta.Rule = { 'nested', { condition: (entity) => - isObject(entity) && entity.type === 'product', + isObject(entity) && entity.entity === 'product', map: { id: 'data.id', quantity: { key: 'data.quantity', value: 1 }, @@ -75,7 +75,7 @@ export const InitiateCheckout: DestinationMeta.Rule = { num_items: { fn: (event) => (event as WalkerOS.Event).nested.filter( - (item) => item.type === 'product', + (item) => item.entity === 'product', ).length, }, }, diff --git a/packages/web/destinations/piwikpro/src/examples/events.ts b/packages/web/destinations/piwikpro/src/examples/events.ts index b0f22dedb..278988b80 100644 --- a/packages/web/destinations/piwikpro/src/examples/events.ts +++ b/packages/web/destinations/piwikpro/src/examples/events.ts @@ -20,7 +20,7 @@ export function ecommerceOrder(): unknown[] { return [ [ 'ecommerceOrder', - event.nested.filter((item) => item.type === 'product').map(getProduct), + event.nested.filter((item) => item.entity === 'product').map(getProduct), { orderId: event.data.id, grandTotal: event.data.total, @@ -58,7 +58,7 @@ export function ecommerceCartUpdate(): unknown[] { return [ [ 'ecommerceCartUpdate', - event.nested.filter((item) => item.type === 'product').map(getProduct), + event.nested.filter((item) => item.entity === 'product').map(getProduct), event.data.value, { currencyCode: 'EUR' }, ], diff --git a/packages/web/destinations/piwikpro/src/examples/mapping.ts b/packages/web/destinations/piwikpro/src/examples/mapping.ts index 07f7dd386..7b36770f5 100644 --- a/packages/web/destinations/piwikpro/src/examples/mapping.ts +++ b/packages/web/destinations/piwikpro/src/examples/mapping.ts @@ -23,7 +23,7 @@ export const ecommerceOrder: DestinationPiwikPro.Rule = { 'nested', { condition: (entity) => - isObject(entity) && entity.type === 'product', + isObject(entity) && entity.entity === 'product', map: productMap, }, ], @@ -94,7 +94,7 @@ export const ecommerceCartUpdate: DestinationPiwikPro.Rule = { 'nested', { condition: (entity) => - isObject(entity) && entity.type === 'product', + isObject(entity) && entity.entity === 'product', map: productMap, }, ], diff --git a/packages/web/sources/browser/src/__tests__/walker.test.ts b/packages/web/sources/browser/src/__tests__/walker.test.ts index a8d8f12f2..952a2699d 100644 --- a/packages/web/sources/browser/src/__tests__/walker.test.ts +++ b/packages/web/sources/browser/src/__tests__/walker.test.ts @@ -39,13 +39,13 @@ describe('Walker', () => { data: { label: 'grandmother' }, trigger: Triggers.Load, nested: [ - { type: 'son', data: { interested_in: 'pizza' } }, + { entity: 'son', data: { interested_in: 'pizza' } }, { - type: 'daughter', + entity: 'daughter', data: { status: 'hungry' }, - nested: [{ type: 'baby', data: { status: 'infant' } }], + nested: [{ entity: 'baby', data: { status: 'infant' } }], }, - { type: 'baby', data: { status: 'infant' } }, + { entity: 'baby', data: { status: 'infant' } }, ], }, ]); @@ -291,7 +291,7 @@ describe('Walker', () => { parent: ['link', 1], entity: ['link', 2], }, - nested: [{ type: 'n', data: { k: 'v' } }], + nested: [{ entity: 'n', data: { k: 'v' } }], }, ]); }); diff --git a/packages/web/sources/browser/src/translation.ts b/packages/web/sources/browser/src/translation.ts index e5aa2b84d..bdd17b311 100644 --- a/packages/web/sources/browser/src/translation.ts +++ b/packages/web/sources/browser/src/translation.ts @@ -68,7 +68,7 @@ export function translateToCoreCollector( const entityObj = getEntities( settings.prefix || 'data-elb', elemParameter, - ).find((obj) => obj.type === entity); + ).find((obj) => obj.entity === entity); if (entityObj) { if (dataIsElem) eventData = entityObj.data; eventContext = entityObj.context; diff --git a/website/docs/core/index.mdx b/website/docs/core/index.mdx index d13b5760a..9d38eb773 100644 --- a/website/docs/core/index.mdx +++ b/website/docs/core/index.mdx @@ -133,7 +133,7 @@ const mapping = { loop: [ 'nested', { - condition: (entity) => entity.type === 'product', + condition: (entity) => entity.entity === 'product', map: { id: 'data.id', name: 'data.name' }, }, ], diff --git a/website/docs/sources/web/browser/tagging.mdx b/website/docs/sources/web/browser/tagging.mdx index 3fc38d27a..5d2bf8a8d 100644 --- a/website/docs/sources/web/browser/tagging.mdx +++ b/website/docs/sources/web/browser/tagging.mdx @@ -537,13 +537,13 @@ This example will lead to the following event on load: "event": "mother view", "data": { "label": "caring" }, "nested": [ - { "type": "son", "data": { "age": 23 } }, + { "entity": "son", "data": { "age": 23 } }, { - "type": "daughter", + "entity": "daughter", "data": { "age": 32 }, - "nested": [{ "type": "baby", "data": { "status": "infant" } }], + "nested": [{ "entity": "baby", "data": { "status": "infant" } }], }, - { "type": "baby", "data": { "status": "infant" } }, + { "entity": "baby", "data": { "status": "infant" } }, ], // other properties omitted } From 01501a1c3e80ea66a7bda137ef739796761b7755 Mon Sep 17 00:00:00 2001 From: alexander Date: Sat, 6 Sep 2025 14:27:19 +0200 Subject: [PATCH 09/13] more type to entity --- .../gcp/src/bigquery/__tests__/index.test.ts | 2 +- packages/web/sources/browser/src/walker.ts | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/server/destinations/gcp/src/bigquery/__tests__/index.test.ts b/packages/server/destinations/gcp/src/bigquery/__tests__/index.test.ts index 97a029a15..84502a60e 100644 --- a/packages/server/destinations/gcp/src/bigquery/__tests__/index.test.ts +++ b/packages/server/destinations/gcp/src/bigquery/__tests__/index.test.ts @@ -88,7 +88,7 @@ describe('Server Destination BigQuery', () => { custom: '{"completely":"random"}', user: '{"id":"us3r","device":"c00k13","session":"s3ss10n"}', nested: - '[{"type":"child","data":{"is":"subordinated"},"nested":[],"context":{"element":["child",0]}}]', + '[{"entity":"child","data":{"is":"subordinated"},"nested":[],"context":{"element":["child",0]}}]', trigger: 'test', timing: 3.14, group: 'gr0up', diff --git a/packages/web/sources/browser/src/walker.ts b/packages/web/sources/browser/src/walker.ts index bc63d6254..9a0b0484d 100644 --- a/packages/web/sources/browser/src/walker.ts +++ b/packages/web/sources/browser/src/walker.ts @@ -123,20 +123,20 @@ export function getEvents( // Use page as default entity if no one was set if (!entities.length) { - const type = 'page'; + const entity = 'page'; // Only use explicit page properties and ignore generic properties - const entitySelector = `[${getElbAttributeName(prefix, type)}]`; + const entitySelector = `[${getElbAttributeName(prefix, entity)}]`; // Get matching properties from the element and its parents const [data, context] = getThisAndParentProperties( target, entitySelector, prefix, - type, + entity, ); entities.push({ - type, // page + entity, // page data, // Consider only upper data nested: [], // Skip nested in this faked page case context, @@ -146,7 +146,7 @@ export function getEvents( // Return a list of full events entities.forEach((entity) => { events.push({ - entity: entity.type, + entity: entity.entity, action: triggerAction.action, data: entity.data, trigger, @@ -261,15 +261,15 @@ function getEntity( origin?: Element, filter?: Walker.Filter, ): WalkerOS.Entity | null { - const type = getAttribute(element, getElbAttributeName(prefix)); + const entity = getAttribute(element, getElbAttributeName(prefix)); // It's not a (valid) entity element or should be filtered - if (!type || (filter && !filter[type])) return null; + if (!entity || (filter && !filter[entity])) return null; const scopeElems = [element]; // All related elements const dataSelector = `[${getElbAttributeName( prefix, - type, + entity, )}],[${getElbAttributeName(prefix, '')}]`; // [data-elb-entity,data-elb-] const linkName = getElbAttributeName(prefix, Const.Commands.Link, false); // data-elblink @@ -279,7 +279,7 @@ function getEntity( origin || element, dataSelector, prefix, - type, + entity, ); // Add linked elements (data-elblink) @@ -315,7 +315,7 @@ function getEntity( propertyElems.forEach((child) => { // Eventually override closer properties genericData = assign(genericData, getElbValues(prefix, child, '')); - data = assign(data, getElbValues(prefix, child, type)); + data = assign(data, getElbValues(prefix, child, entity)); }); // Merge properties with the hierarchy generic > data > parent @@ -333,7 +333,7 @@ function getEntity( ); }); - return { type, data, context, nested }; + return { entity, data, context, nested }; } function getParent(prefix: string, elem: HTMLElement): HTMLElement | null { From c3e9f421d4f380ade7dbc57232cbcfb0ac5d5d4a Mon Sep 17 00:00:00 2001 From: alexander Date: Sat, 6 Sep 2025 15:07:16 +0200 Subject: [PATCH 10/13] removed quickstarts --- .../src/__tests__/advanced-examples.test.ts | 110 ------- .../src/__tests__/collector/basic.test.ts | 26 -- .../src/__tests__/ga4-complete.test.ts | 17 -- .../src/__tests__/server-destinations.test.ts | 50 ---- .../src/__tests__/setup-advanced.ts | 66 ----- apps/quickstart/src/__tests__/setup.ts | 11 - .../__tests__/walkerjs-with-sources.test.ts | 9 - .../quickstart/src/__tests__/walkerjs.test.ts | 17 -- .../web-destinations-complete.test.ts | 71 ----- .../src/__tests__/web-destinations.test.ts | 45 --- .../src/__tests__/web-sources.test.ts | 29 -- apps/quickstart/src/collector/basic.ts | 65 ----- apps/quickstart/src/consent/management.ts | 195 ------------- .../src/mappings/custom-functions.ts | 154 ---------- .../src/performance/batch-processing.ts | 268 ------------------ .../quickstart/src/server-destinations/aws.ts | 93 ------ .../quickstart/src/server-destinations/gcp.ts | 33 --- .../src/server-destinations/meta-capi.ts | 46 --- apps/quickstart/src/walkerjs/basic.ts | 15 - apps/quickstart/src/walkerjs/with-sources.ts | 55 ---- apps/quickstart/src/web-browser/basic.ts | 24 -- apps/quickstart/src/web-dataLayer/basic.ts | 10 - apps/quickstart/src/web-destinations/api.ts | 79 ------ .../web-destinations/custom-destination.ts | 142 ---------- .../src/web-destinations/ga4-complete.ts | 78 ----- .../src/web-destinations/gtag-ads.ts | 63 ---- .../src/web-destinations/gtag-complete.ts | 116 -------- .../src/web-destinations/meta-pixel.ts | 84 ------ .../src/web-destinations/piwikpro.ts | 114 -------- .../src/web-destinations/plausible.ts | 52 ---- package-lock.json | 20 +- package.json | 5 +- 32 files changed, 3 insertions(+), 2159 deletions(-) delete mode 100644 apps/quickstart/src/__tests__/advanced-examples.test.ts delete mode 100644 apps/quickstart/src/__tests__/collector/basic.test.ts delete mode 100644 apps/quickstart/src/__tests__/ga4-complete.test.ts delete mode 100644 apps/quickstart/src/__tests__/server-destinations.test.ts delete mode 100644 apps/quickstart/src/__tests__/setup-advanced.ts delete mode 100644 apps/quickstart/src/__tests__/setup.ts delete mode 100644 apps/quickstart/src/__tests__/walkerjs-with-sources.test.ts delete mode 100644 apps/quickstart/src/__tests__/walkerjs.test.ts delete mode 100644 apps/quickstart/src/__tests__/web-destinations-complete.test.ts delete mode 100644 apps/quickstart/src/__tests__/web-destinations.test.ts delete mode 100644 apps/quickstart/src/__tests__/web-sources.test.ts delete mode 100644 apps/quickstart/src/collector/basic.ts delete mode 100644 apps/quickstart/src/consent/management.ts delete mode 100644 apps/quickstart/src/mappings/custom-functions.ts delete mode 100644 apps/quickstart/src/performance/batch-processing.ts delete mode 100644 apps/quickstart/src/server-destinations/aws.ts delete mode 100644 apps/quickstart/src/server-destinations/gcp.ts delete mode 100644 apps/quickstart/src/server-destinations/meta-capi.ts delete mode 100644 apps/quickstart/src/walkerjs/basic.ts delete mode 100644 apps/quickstart/src/walkerjs/with-sources.ts delete mode 100644 apps/quickstart/src/web-browser/basic.ts delete mode 100644 apps/quickstart/src/web-dataLayer/basic.ts delete mode 100644 apps/quickstart/src/web-destinations/api.ts delete mode 100644 apps/quickstart/src/web-destinations/custom-destination.ts delete mode 100644 apps/quickstart/src/web-destinations/ga4-complete.ts delete mode 100644 apps/quickstart/src/web-destinations/gtag-ads.ts delete mode 100644 apps/quickstart/src/web-destinations/gtag-complete.ts delete mode 100644 apps/quickstart/src/web-destinations/meta-pixel.ts delete mode 100644 apps/quickstart/src/web-destinations/piwikpro.ts delete mode 100644 apps/quickstart/src/web-destinations/plausible.ts diff --git a/apps/quickstart/src/__tests__/advanced-examples.test.ts b/apps/quickstart/src/__tests__/advanced-examples.test.ts deleted file mode 100644 index 6ecf13fae..000000000 --- a/apps/quickstart/src/__tests__/advanced-examples.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { - setupCustomDestination, - trackCustomDestinationEvents, -} from '../web-destinations/custom-destination'; -import { - setupConsentManagement, - handleConsentChoice, - trackConsentedEvents, -} from '../consent/management'; -import { - setupCustomMappingFunctions, - trackCustomMappedEvents, -} from '../mappings/custom-functions'; -import { - setupBatchProcessing, - simulateHighVolumeTracking, -} from '../performance/batch-processing'; - -// Mock fetch for the custom destination -global.fetch = jest.fn().mockResolvedValue({ - ok: true, - status: 200, - statusText: 'OK', -}); - -describe('Advanced Examples', () => { - describe('Custom Destination', () => { - it('creates collector with custom destination', async () => { - const { collector, elb } = await setupCustomDestination(); - expect(collector).toBeDefined(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks custom destination events without errors', async () => { - const { collector, elb } = await setupCustomDestination(); - await expect(trackCustomDestinationEvents(elb)).resolves.not.toThrow(); - }); - }); - - describe('Consent Management', () => { - it('creates collector with consent setup', async () => { - const { collector, elb } = await setupConsentManagement(); - expect(collector).toBeDefined(); - expect(collector.allowed).toBe(false); // Initially disabled - expect(elb).toBeDefined(); - }); - - it('handles consent choices', async () => { - const { collector, elb } = await setupConsentManagement(); - - // Test accept consent - await expect( - handleConsentChoice(collector, 'accept'), - ).resolves.not.toThrow(); - expect(collector.allowed).toBe(true); - - // Test reject consent - await expect( - handleConsentChoice(collector, 'reject'), - ).resolves.not.toThrow(); - expect(collector.allowed).toBe(false); - - // Test custom consent - await expect( - handleConsentChoice(collector, 'customize', { - analytics: true, - advertising: false, - functional: true, - }), - ).resolves.not.toThrow(); - expect(collector.allowed).toBe(true); - }); - - it('tracks consented events without errors', async () => { - const { collector, elb } = await setupConsentManagement(); - await expect(trackConsentedEvents(elb)).resolves.not.toThrow(); - }); - }); - - describe('Custom Mapping Functions', () => { - it('creates collector with custom mappings', async () => { - const { collector, elb } = await setupCustomMappingFunctions(); - expect(collector).toBeDefined(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks events with custom mappings without errors', async () => { - const { collector, elb } = await setupCustomMappingFunctions(); - await expect(trackCustomMappedEvents(elb)).resolves.not.toThrow(); - }); - }); - - describe('Batch Processing', () => { - it('creates collector with batch processing', async () => { - const { collector, elb } = await setupBatchProcessing(); - expect(collector).toBeDefined(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('simulates high-volume tracking without errors', async () => { - const { collector, elb } = await setupBatchProcessing(); - // Test that we can call the elb function without errors - // Skip the full simulation to avoid timeout issues in tests - await expect(elb('test event', { test: true })).resolves.not.toThrow(); - }); - }); -}); diff --git a/apps/quickstart/src/__tests__/collector/basic.test.ts b/apps/quickstart/src/__tests__/collector/basic.test.ts deleted file mode 100644 index 35294fdb0..000000000 --- a/apps/quickstart/src/__tests__/collector/basic.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - setupCollector, - setupCollectorWithConfig, - trackPageView, - trackUserAction, -} from '../../collector/basic'; - -describe('Collector Basic Examples', () => { - it('creates basic collector', async () => { - const { collector, elb } = await setupCollector(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('creates collector with console destination', async () => { - const { collector, elb } = await setupCollectorWithConfig(); - expect(collector.destinations.console).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks events without errors', async () => { - const { collector, elb } = await setupCollector(); - await expect(trackPageView(elb)).resolves.not.toThrow(); - await expect(trackUserAction(elb)).resolves.not.toThrow(); - }); -}); diff --git a/apps/quickstart/src/__tests__/ga4-complete.test.ts b/apps/quickstart/src/__tests__/ga4-complete.test.ts deleted file mode 100644 index 515675816..000000000 --- a/apps/quickstart/src/__tests__/ga4-complete.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - setupGA4Complete, - trackGA4Events, -} from '../web-destinations/ga4-complete'; - -describe('GA4 Complete Example', () => { - it('creates collector instance', async () => { - const { collector, elb } = await setupGA4Complete(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks all GA4 events without errors', async () => { - const { elb } = await setupGA4Complete(); - await expect(trackGA4Events(elb)).resolves.not.toThrow(); - }); -}); diff --git a/apps/quickstart/src/__tests__/server-destinations.test.ts b/apps/quickstart/src/__tests__/server-destinations.test.ts deleted file mode 100644 index edb1e54bd..000000000 --- a/apps/quickstart/src/__tests__/server-destinations.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { - setupAWSFirehose, - trackServerEvents, -} from '../server-destinations/aws'; -import { setupGCPPubSub, publishToGCP } from '../server-destinations/gcp'; -import { - setupMetaCAPI, - trackServerConversions, -} from '../server-destinations/meta-capi'; - -describe('Server Destination Examples', () => { - describe('AWS Firehose', () => { - it('creates collector for AWS', async () => { - const { collector, elb } = await setupAWSFirehose(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks server events without errors', async () => { - const { collector, elb } = await setupAWSFirehose(); - await expect(trackServerEvents(elb)).resolves.not.toThrow(); - }); - }); - - describe('GCP Pub/Sub', () => { - it('creates collector for GCP', async () => { - const { collector, elb } = await setupGCPPubSub(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('publishes to GCP without errors', async () => { - const { collector, elb } = await setupGCPPubSub(); - await expect(publishToGCP(elb)).resolves.not.toThrow(); - }); - }); - - describe('Meta CAPI', () => { - it('creates collector for Meta CAPI', async () => { - const { collector, elb } = await setupMetaCAPI(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks server conversions without errors', async () => { - const { collector, elb } = await setupMetaCAPI(); - await expect(trackServerConversions(elb)).resolves.not.toThrow(); - }); - }); -}); diff --git a/apps/quickstart/src/__tests__/setup-advanced.ts b/apps/quickstart/src/__tests__/setup-advanced.ts deleted file mode 100644 index af35bd150..000000000 --- a/apps/quickstart/src/__tests__/setup-advanced.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Setup file for advanced examples tests - -// Mock DOM environment -const mockElement = { - src: '', - async: false, - onload: null as (() => void) | null, -}; - -const mockDocument = { - createElement: jest.fn().mockReturnValue(mockElement), - head: { - appendChild: jest.fn(), - }, - referrer: 'https://google.com', - addEventListener: jest.fn(), - removeEventListener: jest.fn(), -}; - -const mockWindow = { - location: { - hostname: 'example.com', - }, - elb: jest.fn(), -}; - -const mockNavigator = { - userAgent: - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', -}; - -const mockPerformance = { - timing: { - navigationStart: 1000, - loadEventEnd: 2000, - domContentLoadedEventEnd: 1500, - }, - getEntriesByType: jest - .fn() - .mockReturnValue([{ name: 'first-contentful-paint', startTime: 800 }]), - now: jest.fn().mockReturnValue(1000), -}; - -// Mock fetch -const mockFetch = jest.fn().mockResolvedValue({ - ok: true, - status: 200, - statusText: 'OK', - json: async () => ({ success: true }), -}); - -// Apply mocks to global -Object.defineProperty(global, 'document', { value: mockDocument }); -Object.defineProperty(global, 'window', { value: mockWindow }); -Object.defineProperty(global, 'navigator', { value: mockNavigator }); -Object.defineProperty(global, 'performance', { value: mockPerformance }); -Object.defineProperty(global, 'fetch', { value: mockFetch }); - -export { - mockElement, - mockDocument, - mockWindow, - mockNavigator, - mockPerformance, - mockFetch, -}; diff --git a/apps/quickstart/src/__tests__/setup.ts b/apps/quickstart/src/__tests__/setup.ts deleted file mode 100644 index bc656c41f..000000000 --- a/apps/quickstart/src/__tests__/setup.ts +++ /dev/null @@ -1,11 +0,0 @@ -import '@testing-library/jest-dom'; - -declare global { - interface Window { - dataLayer: unknown; - } -} - -if (typeof window !== 'undefined') { - window.dataLayer = []; -} diff --git a/apps/quickstart/src/__tests__/walkerjs-with-sources.test.ts b/apps/quickstart/src/__tests__/walkerjs-with-sources.test.ts deleted file mode 100644 index 79376a520..000000000 --- a/apps/quickstart/src/__tests__/walkerjs-with-sources.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { trackWithWalkerSources } from '../walkerjs/with-sources'; - -describe('Walker.js with Sources', () => { - it('tracks events without errors', async () => { - const mockElb = jest.fn(); - await expect(trackWithWalkerSources(mockElb)).resolves.not.toThrow(); - expect(mockElb).toHaveBeenCalled(); - }); -}); diff --git a/apps/quickstart/src/__tests__/walkerjs.test.ts b/apps/quickstart/src/__tests__/walkerjs.test.ts deleted file mode 100644 index ae5e803b4..000000000 --- a/apps/quickstart/src/__tests__/walkerjs.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { setupWalkerJS, initWalkerJS } from '../walkerjs/basic'; - -describe('Walker.js Examples', () => { - beforeEach(() => { - document.head.innerHTML = ''; - }); - - it('creates walker.js script element', () => { - const script = setupWalkerJS(); - expect(script.src).toContain('walker.js'); - expect(script.async).toBe(true); - }); - - it('initializes walker.js without errors', () => { - expect(() => initWalkerJS()).not.toThrow(); - }); -}); diff --git a/apps/quickstart/src/__tests__/web-destinations-complete.test.ts b/apps/quickstart/src/__tests__/web-destinations-complete.test.ts deleted file mode 100644 index cd4f28ff6..000000000 --- a/apps/quickstart/src/__tests__/web-destinations-complete.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { - setupGtagComplete, - trackGtagEvents, -} from '../web-destinations/gtag-complete'; -import { - setupPiwikPro, - trackPiwikProEvents, -} from '../web-destinations/piwikpro'; -import { - setupPlausible, - trackPlausibleEvents, -} from '../web-destinations/plausible'; -import { setupAPIDestination, trackAPIEvents } from '../web-destinations/api'; - -describe('Complete Web Destination Examples', () => { - describe('Gtag Complete', () => { - it('creates collector instance', async () => { - const { collector, elb } = await setupGtagComplete(); - expect(collector).toBeDefined(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks gtag events without errors', async () => { - const { collector, elb } = await setupGtagComplete(); - await expect(trackGtagEvents(elb)).resolves.not.toThrow(); - }); - }); - - describe('PiwikPro', () => { - it('creates collector instance', async () => { - const { collector, elb } = await setupPiwikPro(); - expect(collector).toBeDefined(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks PiwikPro events without errors', async () => { - const { collector, elb } = await setupPiwikPro(); - await expect(trackPiwikProEvents(elb)).resolves.not.toThrow(); - }); - }); - - describe('Plausible', () => { - it('creates collector instance', async () => { - const { collector, elb } = await setupPlausible(); - expect(collector).toBeDefined(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks Plausible events without errors', async () => { - const { collector, elb } = await setupPlausible(); - await expect(trackPlausibleEvents(elb)).resolves.not.toThrow(); - }); - }); - - describe('API with Mapping', () => { - it('creates collector instance', async () => { - const { collector, elb } = await setupAPIDestination(); - expect(collector).toBeDefined(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks API events without errors', async () => { - const { collector, elb } = await setupAPIDestination(); - await expect(trackAPIEvents(elb)).resolves.not.toThrow(); - }); - }); -}); diff --git a/apps/quickstart/src/__tests__/web-destinations.test.ts b/apps/quickstart/src/__tests__/web-destinations.test.ts deleted file mode 100644 index 59b589875..000000000 --- a/apps/quickstart/src/__tests__/web-destinations.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - setupGoogleAds, - trackAdsConversions, -} from '../web-destinations/gtag-ads'; -import { - setupMetaPixel, - trackMetaEvents, -} from '../web-destinations/meta-pixel'; -import { setupAPIDestination } from '../web-destinations/api'; - -describe('Web Destination Examples', () => { - describe('Google Ads', () => { - it('creates collector for Google Ads', async () => { - const { collector, elb } = await setupGoogleAds(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks conversions without errors', async () => { - const { collector, elb } = await setupGoogleAds(); - await expect(trackAdsConversions(elb)).resolves.not.toThrow(); - }); - }); - - describe('Meta Pixel', () => { - it('creates collector for Meta Pixel', async () => { - const { collector, elb } = await setupMetaPixel(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('tracks Meta events without errors', async () => { - const { collector, elb } = await setupMetaPixel(); - await expect(trackMetaEvents(elb)).resolves.not.toThrow(); - }); - }); - - describe('API Destination', () => { - it('creates basic API collector', async () => { - const { collector, elb } = await setupAPIDestination(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - }); -}); diff --git a/apps/quickstart/src/__tests__/web-sources.test.ts b/apps/quickstart/src/__tests__/web-sources.test.ts deleted file mode 100644 index 6da96c7d6..000000000 --- a/apps/quickstart/src/__tests__/web-sources.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - setupBrowserTracking, - setupBrowserWithConsole, -} from '../web-browser/basic'; -import { setupDataLayer } from '../web-dataLayer/basic'; - -describe('Web Source Examples', () => { - describe('Browser Source', () => { - it('creates collector', async () => { - const { collector, elb } = await setupBrowserTracking(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - - it('creates collector with console', async () => { - const { collector, elb } = await setupBrowserWithConsole(); - expect(collector.destinations.console).toBeDefined(); - expect(elb).toBeDefined(); - }); - }); - - describe('DataLayer Source', () => { - it('creates collector', async () => { - const { collector, elb } = await setupDataLayer(); - expect(collector.push).toBeDefined(); - expect(elb).toBeDefined(); - }); - }); -}); diff --git a/apps/quickstart/src/collector/basic.ts b/apps/quickstart/src/collector/basic.ts deleted file mode 100644 index 146775eec..000000000 --- a/apps/quickstart/src/collector/basic.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { createSource } from '@walkeros/core'; -import { sourceBrowser } from '@walkeros/web-source-browser'; -import type { Collector, WalkerOS } from '@walkeros/core'; - -export async function setupCollector(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - // Single big config file - basic setup - const trackingConfig = { - run: true, - sources: { - browser: createSource(sourceBrowser, { - settings: { - scope: document.body, - }, - }), - }, - }; - - const { collector, elb } = await createCollector(trackingConfig); - return { collector, elb }; -} - -export async function setupCollectorWithConfig(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - // Single big config file with console destination - const trackingConfig = { - run: true, - sources: { - browser: createSource(sourceBrowser, { - settings: { - scope: document.body, - }, - }), - }, - destinations: { - console: { - type: 'console', - push: (event: WalkerOS.Event) => console.log('Event:', event), - config: {}, - }, - }, - }; - - const { collector, elb } = await createCollector(trackingConfig); - return { collector, elb }; -} - -export async function trackPageView(elb: WalkerOS.Elb): Promise { - await elb('page view', { - title: 'Home Page', - path: '/', - }); -} - -export async function trackUserAction(elb: WalkerOS.Elb): Promise { - await elb('button click', { - id: 'cta-button', - text: 'Get Started', - }); -} diff --git a/apps/quickstart/src/consent/management.ts b/apps/quickstart/src/consent/management.ts deleted file mode 100644 index 4646764f3..000000000 --- a/apps/quickstart/src/consent/management.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { destinationGtag } from '@walkeros/web-destination-gtag'; -import { destinationMeta } from '@walkeros/web-destination-meta'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupConsentManagement(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector({ - destinations: { - gtag: { - ...destinationGtag, - config: { - settings: { - ga4: { measurementId: 'G-XXXXXXXXXX' }, - }, - mapping: { - // Map consent events - walker: { - consent: { - name: 'consent_update', - data: { - map: { - analytics_storage: 'analytics_storage', - ad_storage: 'ad_storage', - }, - }, - }, - }, - }, - }, - }, - meta: { - ...destinationMeta, - config: { - settings: { - pixelId: 'YOUR_PIXEL_ID', - }, - mapping: { - // Map consent events for Meta - walker: { - consent: { - name: 'consent_granted', - data: { - map: { - consent_type: 'ad_storage', - }, - }, - }, - }, - }, - }, - }, - }, - }); - - // Initially disable all tracking until consent is given - collector.allowed = false; - - return { collector, elb }; -} - -export async function handleConsentChoice( - collector: Collector.Instance, - consentType: 'accept' | 'reject' | 'customize', - customConsent?: { - analytics: boolean; - advertising: boolean; - functional: boolean; - }, -): Promise { - let consentState: WalkerOS.Consent = {}; - - switch (consentType) { - case 'accept': - // User accepts all tracking - consentState = { - functional: true, - analytics: true, - marketing: true, - ad_storage: true, - analytics_storage: true, - ad_user_data: true, - ad_personalization: true, - }; - collector.allowed = true; - break; - - case 'reject': - // User rejects all non-essential tracking - consentState = { - functional: true, // Essential cookies only - analytics: false, - marketing: false, - ad_storage: false, - analytics_storage: false, - ad_user_data: false, - ad_personalization: false, - }; - collector.allowed = false; - break; - - case 'customize': - // User customizes consent preferences - if (customConsent) { - consentState = { - functional: true, // Always required - analytics: customConsent.analytics, - marketing: customConsent.advertising, - ad_storage: customConsent.advertising, - analytics_storage: customConsent.analytics, - ad_user_data: customConsent.advertising, - ad_personalization: customConsent.advertising, - }; - collector.allowed = - customConsent.analytics || customConsent.advertising; - } - break; - } - - // Update consent state - await collector.push({ - event: 'walker consent', - data: consentState, - context: {}, - globals: {}, - custom: {}, - user: {}, - nested: [], - consent: {}, - id: '', - trigger: '', - entity: 'walker', - action: 'consent', - timestamp: Date.now(), - timing: 0, - group: '', - count: 0, - version: { source: '0.0.7', tagging: 0 }, - source: { type: 'collector', id: '', previous_id: '' }, - }); - - console.log(`Consent updated: ${consentType}`, consentState); -} - -export async function trackConsentedEvents(elb: WalkerOS.Elb): Promise { - // This event will only be sent if consent allows it - await elb('page view', { - title: 'Consent Demo Page', - category: 'demo', - }); - - // Marketing events require marketing consent - await elb('product view', { - id: 'demo-product', - name: 'Consent Example Product', - price: 29.99, - }); - - // Functional events (like error tracking) might always be allowed - await elb('error occurred', { - type: 'javascript', - message: 'Demo error for testing', - severity: 'low', - }); -} - -// Simulate consent banner interaction -export async function simulateConsentBanner( - elb: WalkerOS.Elb, - collector: Collector.Instance, -): Promise { - console.log('🍪 Consent banner shown'); - - // Simulate user clicking "Accept All" - setTimeout(async () => { - console.log('✅ User accepted all cookies'); - await handleConsentChoice(collector, 'accept'); - - // Now tracking events will be sent - await trackConsentedEvents(elb); - }, 1000); - - // Alternative: Simulate custom consent - // setTimeout(async () => { - // console.log('⚙️ User customized consent'); - // await handleConsentChoice(collector, 'customize', { - // analytics: true, - // advertising: false, - // functional: true, - // }); - // await trackConsentedEvents(elb); - // }, 1000); -} diff --git a/apps/quickstart/src/mappings/custom-functions.ts b/apps/quickstart/src/mappings/custom-functions.ts deleted file mode 100644 index a3fcfae00..000000000 --- a/apps/quickstart/src/mappings/custom-functions.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { destinationGtag } from '@walkeros/web-destination-gtag'; -import type { WalkerOS, Destination, Collector } from '@walkeros/core'; - -// Custom destination with advanced mapping functions -const advancedMappingDestination: Destination.Instance = { - type: 'advanced-mapping', - - config: {}, - - init() { - console.log('Advanced mapping destination initialized'); - }, - - async push(event, { config }) { - // Apply custom mappings - const mappedData = applyCustomMappings(event); - - console.log('Advanced Mapping Result:', { - original: event, - mapped: mappedData, - }); - }, -}; - -// Custom mapping utility functions -function applyCustomMappings(event: WalkerOS.Event) { - const mapped: Record = {}; - - // Currency formatting function - const formatCurrency = (value: number, currency = 'USD') => { - return new Intl.NumberFormat('en-US', { - style: 'currency', - currency, - }).format(value); - }; - - // Time-based segmentation - const getTimeSegment = () => { - const hour = new Date().getHours(); - if (hour >= 6 && hour < 12) return 'morning'; - if (hour >= 12 && hour < 17) return 'afternoon'; - if (hour >= 17 && hour < 22) return 'evening'; - return 'night'; - }; - - // Apply mappings based on event type - switch (event.entity) { - case 'product': - mapped.item = { - id: event.data.id, - name: event.data.name, - price_formatted: formatCurrency( - typeof event.data.price === 'number' ? event.data.price : 0, - ), - category: - typeof event.data.category === 'string' - ? event.data.category - : 'Uncategorized', - in_stock: typeof event.data.stock === 'number' && event.data.stock > 0, - }; - break; - - case 'order': - mapped.transaction = { - id: event.data.id, - revenue_formatted: formatCurrency( - typeof event.data.total === 'number' ? event.data.total : 0, - ), - is_high_value: - typeof event.data.total === 'number' && event.data.total > 100, - order_day_segment: getTimeSegment(), - }; - break; - - default: - mapped.generic = { - event_type: event.entity, - action: event.action, - timestamp_iso: new Date(event.timestamp).toISOString(), - time_segment: getTimeSegment(), - }; - } - - return mapped; -} - -export async function setupCustomMappingFunctions(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector({ - destinations: { - // Advanced mapping destination - advanced: { - ...advancedMappingDestination, - config: { - settings: {}, - }, - }, - // GA4 with custom mapping - gtag: { - ...destinationGtag, - config: { - settings: { - ga4: { measurementId: 'G-XXXXXXXXXX' }, - }, - mapping: { - product: { - view: { - name: 'view_item', - settings: { ga4: {} }, - data: { - map: { - currency: { value: 'USD' }, - value: 'data.price', - }, - }, - }, - }, - }, - }, - }, - }, - }); - - return { collector, elb }; -} - -export async function trackCustomMappedEvents( - elb: WalkerOS.Elb, -): Promise { - // Track product with rich data - await elb('product view', { - id: 'prod-123', - name: 'Wireless Headphones', - price: 129.99, - category: 'Electronics', - stock: 15, - }); - - // Track order - await elb('order complete', { - id: 'order-456', - total: 259.98, - currency: 'USD', - }); - - // Track custom event - await elb('feature used', { - feature: 'custom-mapping', - success: true, - }); -} diff --git a/apps/quickstart/src/performance/batch-processing.ts b/apps/quickstart/src/performance/batch-processing.ts deleted file mode 100644 index 06509780b..000000000 --- a/apps/quickstart/src/performance/batch-processing.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { destinationAPI } from '@walkeros/web-destination-api'; -import type { WalkerOS, Destination, Collector } from '@walkeros/core'; - -// Custom batching destination for high-performance event processing -const batchingDestination: Destination.Instance = { - type: 'batching', - - config: {}, - - init({ config }) { - const { settings } = config; - - if (!settings || typeof settings !== 'object') { - console.log('Batch destination initialized with default settings'); - return; - } - - // Initialize batch processing - const settingsObj = settings as Record; - const batchSize = - typeof settingsObj.batchSize === 'number' ? settingsObj.batchSize : 10; - const flushInterval = - typeof settingsObj.flushInterval === 'number' - ? settingsObj.flushInterval - : 5000; - const maxWaitTime = - typeof settingsObj.maxWaitTime === 'number' - ? settingsObj.maxWaitTime - : 30000; - - console.log( - `Batch destination initialized: size=${batchSize}, interval=${flushInterval}ms`, - ); - }, - - async pushBatch(events, { config }) { - const { settings } = config; - const batchId = Date.now().toString(36); - - // Handle batch as array - const eventsArray = Array.isArray(events) ? events : []; - console.log( - `📦 Processing batch ${batchId} with ${eventsArray.length} events`, - ); - - try { - // Simulate API call with batched events - const payload = { - batch_id: batchId, - timestamp: new Date().toISOString(), - events: eventsArray.map((event: WalkerOS.Event) => ({ - event_name: event.event, - event_data: event.data, - user_id: - event.user && typeof event.user === 'object' && 'id' in event.user - ? String(event.user.id) - : undefined, - session_id: - event.context?.session && - typeof event.context.session === 'object' && - 'id' in event.context.session - ? String(event.context.session.id) - : undefined, - timestamp: event.timestamp, - })), - metadata: { - source: 'walkerOS-batch', - version: '1.0', - total_events: eventsArray.length, - }, - }; - - // In production, this would be an actual API call - const settingsObj = - settings && typeof settings === 'object' - ? (settings as Record) - : {}; - const endpoint = - typeof settingsObj.endpoint === 'string' ? settingsObj.endpoint : null; - - if (endpoint) { - const headers = - settingsObj.headers && typeof settingsObj.headers === 'object' - ? (settingsObj.headers as Record) - : {}; - - const response = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...headers, - }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - throw new Error(`Batch API failed: ${response.status}`); - } - } - - console.log(`✅ Batch ${batchId} sent successfully`); - return { ok: true }; - } catch (error) { - console.error(`❌ Batch ${batchId} failed:`, error); - throw error; - } - }, - - async push(event, context) { - // Fallback for single events (shouldn't be called when pushBatch is available) - console.log('Single event fallback:', event.event); - }, -}; - -export async function setupBatchProcessing(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector({ - destinations: { - // High-performance batch destination - batch: { - ...batchingDestination, - config: { - settings: { - endpoint: 'https://api.example.com/events/batch', - batchSize: 5, // Small batch for demo - flushInterval: 3000, // 3 seconds - maxWaitTime: 10000, // 10 seconds max wait - headers: { - Authorization: 'Bearer batch-api-token', - 'X-Batch-Source': 'walkerOS', - }, - }, - }, - }, - // Regular API destination for comparison - api_single: { - ...destinationAPI, - config: { - settings: { - url: 'https://api.example.com/events/single', - headers: { - Authorization: 'Bearer single-api-token', - }, - }, - mapping: { - // Send all events to single endpoint - '*': { - '*': { - name: 'tracked_event', - data: { - map: { - event_type: 'event', - event_action: 'action', - properties: 'data', - }, - }, - }, - }, - }, - }, - }, - }, - }); - - return { collector, elb }; -} - -// Simulate high-volume event tracking -export async function simulateHighVolumeTracking( - elb: WalkerOS.Elb, -): Promise { - console.log('🚀 Starting high-volume event simulation...'); - - const eventTypes = [ - { entity: 'page', action: 'view' }, - { entity: 'product', action: 'view' }, - { entity: 'product', action: 'add' }, - { entity: 'button', action: 'click' }, - { entity: 'form', action: 'submit' }, - { entity: 'video', action: 'play' }, - { entity: 'search', action: 'perform' }, - ]; - - const sampleData = [ - { title: 'Homepage', category: 'navigation' }, - { id: 'prod-001', name: 'Product A', price: 29.99 }, - { id: 'prod-002', name: 'Product B', price: 49.99 }, - { label: 'CTA Button', position: 'header' }, - { type: 'newsletter', success: true }, - { id: 'video-123', duration: 120 }, - { query: 'wireless headphones', results: 25 }, - ]; - - // Send events rapidly to trigger batching - for (let i = 0; i < 10; i++) { - const eventType = eventTypes[i % eventTypes.length]; - const data = sampleData[i % sampleData.length]; - - await elb(`${eventType.entity} ${eventType.action}`, { - ...data, - sequence: i + 1, - timestamp: Date.now(), - }); - - // Very small delay for testing - await new Promise((resolve) => setTimeout(resolve, 1)); - } - - console.log('📊 High-volume simulation completed'); -} - -// Performance comparison: batched vs individual requests -export async function comparePerformance(elb: WalkerOS.Elb): Promise { - console.log('⚡ Starting performance comparison...'); - - const events = Array.from({ length: 50 }, (_, i) => ({ - entity: 'performance', - action: 'test', - data: { - test_id: `perf-test-${i}`, - batch_number: Math.floor(i / 10), - sequence: i, - }, - })); - - // Measure batched processing time - const batchStart = performance.now(); - - for (const event of events) { - await elb(`${event.entity} ${event.action}`, event.data); - } - - // Wait for batches to flush - await new Promise((resolve) => setTimeout(resolve, 5000)); - - const batchEnd = performance.now(); - const batchDuration = batchEnd - batchStart; - - console.log(`📈 Performance Results:`); - console.log(` Total events: ${events.length}`); - console.log(` Batch processing time: ${batchDuration.toFixed(2)}ms`); - console.log( - ` Average per event: ${(batchDuration / events.length).toFixed(2)}ms`, - ); -} - -// Monitor batch queue status -export function monitorBatchQueue(collector: Collector.Instance): void { - // Check queue status periodically - const monitor = setInterval(() => { - const queueInfo = { - pending_events: 'Queue monitoring not directly available', - destinations: Object.keys(collector.destinations).length, - timestamp: new Date().toISOString(), - }; - - console.log('📋 Queue Status:', queueInfo); - }, 10000); // Every 10 seconds - - // Clean up after 1 minute - setTimeout(() => { - clearInterval(monitor); - console.log('🛑 Queue monitoring stopped'); - }, 60000); -} diff --git a/apps/quickstart/src/server-destinations/aws.ts b/apps/quickstart/src/server-destinations/aws.ts deleted file mode 100644 index 2d99f3b06..000000000 --- a/apps/quickstart/src/server-destinations/aws.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { createDestination } from '@walkeros/core'; -import { destinationFirehose } from '@walkeros/server-destination-aws'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupAWSFirehose(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - // Single big config file - AWS Firehose server destination - const trackingConfig = { - run: true, - globals: { - environment: 'production', - service: 'api-server', - }, - destinations: { - aws: createDestination(destinationFirehose, { - settings: { - firehose: { - streamName: 'your-firehose-stream', - region: 'us-east-1', - config: { - credentials: { - accessKeyId: process.env.AWS_ACCESS_KEY_ID || '', - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '', - }, - }, - }, - }, - mapping: { - user: { - signup: { - name: 'user_registration', - data: { - map: { - user_id: 'user.id', - email: 'user.email', - signup_source: 'data.source', - plan_type: 'data.plan', - }, - }, - }, - }, - subscription: { - purchase: { - name: 'subscription_created', - data: { - map: { - user_id: 'user.id', - plan: 'data.plan', - amount: 'data.amount', - currency: 'data.currency', - billing_period: 'data.period', - }, - }, - }, - }, - }, - }), - }, - }; - - const { collector, elb } = await createCollector(trackingConfig); - return { collector, elb }; -} - -export async function trackServerEvents(elb: WalkerOS.Elb): Promise { - await elb({ - event: 'user signup', - user: { - id: 'user-123', - email: 'user@example.com', - }, - data: { - plan: 'premium', - source: 'organic', - }, - }); - - await elb({ - event: 'subscription purchase', - user: { - id: 'user-123', - }, - data: { - plan: 'premium', - amount: 99.99, - currency: 'USD', - period: 'monthly', - }, - }); -} diff --git a/apps/quickstart/src/server-destinations/gcp.ts b/apps/quickstart/src/server-destinations/gcp.ts deleted file mode 100644 index 6ff305630..000000000 --- a/apps/quickstart/src/server-destinations/gcp.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupGCPPubSub(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector(); - return { collector, elb }; -} - -export const gcpPubSubConfig = { - settings: { - projectId: 'your-gcp-project', - topicName: 'walkerOS-events', - credentials: { - client_email: process.env.GCP_CLIENT_EMAIL || '', - private_key: process.env.GCP_PRIVATE_KEY || '', - }, - }, -}; - -export async function publishToGCP(elb: WalkerOS.Elb): Promise { - await elb('api request', { - endpoint: '/api/v1/users', - method: 'POST', - }); - - await elb('job complete', { - jobId: 'job-789', - type: 'data-processing', - }); -} diff --git a/apps/quickstart/src/server-destinations/meta-capi.ts b/apps/quickstart/src/server-destinations/meta-capi.ts deleted file mode 100644 index 0737d4cd6..000000000 --- a/apps/quickstart/src/server-destinations/meta-capi.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupMetaCAPI(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector(); - return { collector, elb }; -} - -export const metaCAPIConfig = { - settings: { - pixelId: 'YOUR_PIXEL_ID', - accessToken: process.env.META_ACCESS_TOKEN || '', - test_event_code: process.env.META_TEST_EVENT_CODE, - }, -}; - -export async function trackServerConversions(elb: WalkerOS.Elb): Promise { - await elb({ - event: 'order complete', - user: { - id: 'user-456', - email: 'customer@example.com', - }, - data: { - id: 'order-789', - total: 199.99, - currency: 'USD', - }, - }); - - await elb({ - event: 'form submit', - user: { - id: 'user-789', - email: 'lead@example.com', - phone: '+1234567890', - }, - data: { - type: 'contact', - value: 100, - }, - }); -} diff --git a/apps/quickstart/src/walkerjs/basic.ts b/apps/quickstart/src/walkerjs/basic.ts deleted file mode 100644 index 9c4d91645..000000000 --- a/apps/quickstart/src/walkerjs/basic.ts +++ /dev/null @@ -1,15 +0,0 @@ -export function setupWalkerJS(): HTMLScriptElement { - const script = document.createElement('script'); - script.src = - 'https://cdn.jsdelivr.net/npm/@walkeros/walker.js@latest/dist/index.browser.js'; - script.async = true; - document.head.appendChild(script); - return script; -} - -export function initWalkerJS(): void { - const script = setupWalkerJS(); - script.onload = () => { - console.log('Walker.js loaded'); - }; -} diff --git a/apps/quickstart/src/walkerjs/with-sources.ts b/apps/quickstart/src/walkerjs/with-sources.ts deleted file mode 100644 index f9a9f5d4b..000000000 --- a/apps/quickstart/src/walkerjs/with-sources.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Walker.js bundle includes browser source by default -export async function setupWalkerWithSources(): Promise { - // Load walker.js bundle from CDN - await new Promise((resolve) => { - const script = document.createElement('script'); - script.src = - 'https://cdn.jsdelivr.net/npm/@walkeros/walker.js@latest/dist/index.browser.js'; - script.async = true; - script.onload = () => resolve(); - document.head.appendChild(script); - }); - - // Walker.js automatically initializes with browser source - // which tracks DOM elements with data-elb attributes - return (window as Record).elb; -} - -export async function trackWithWalkerSources(elb: unknown): Promise { - // Type the elb function safely - const elbFn = elb as ( - command: string, - config: Record, - ) => Promise; - - // Configure browser source to track specific attributes - await elbFn('walker config', { - source: { - browser: { - // Track elements with data-track attributes - dataLayer: true, - // Enable click tracking - click: true, - // Enable view tracking (intersection observer) - view: true, - }, - }, - }); - - // Add a destination to see the events - await elbFn('walker destination', { - push: (event: Record) => { - console.log('Walker.js Event:', { - event: event.event, - data: event.data, - trigger: event.trigger, - }); - }, - }); - - // Trigger a custom event - await elbFn('button click', { - label: 'Sign Up', - position: 'header', - }); -} diff --git a/apps/quickstart/src/web-browser/basic.ts b/apps/quickstart/src/web-browser/basic.ts deleted file mode 100644 index 108259f40..000000000 --- a/apps/quickstart/src/web-browser/basic.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupBrowserTracking(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector(); - return { collector, elb }; -} - -export async function setupBrowserWithConsole(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector({ - destinations: { - console: { - push: (event) => console.log('Event:', event), - }, - }, - }); - return { collector, elb }; -} diff --git a/apps/quickstart/src/web-dataLayer/basic.ts b/apps/quickstart/src/web-dataLayer/basic.ts deleted file mode 100644 index 81859d1fd..000000000 --- a/apps/quickstart/src/web-dataLayer/basic.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupDataLayer(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector(); - return { collector, elb }; -} diff --git a/apps/quickstart/src/web-destinations/api.ts b/apps/quickstart/src/web-destinations/api.ts deleted file mode 100644 index 96f70cadf..000000000 --- a/apps/quickstart/src/web-destinations/api.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { createSource, createDestination } from '@walkeros/core'; -import { destinationAPI } from '@walkeros/web-destination-api'; -import { sourceBrowser } from '@walkeros/web-source-browser'; -import type { WalkerOS, Collector, Source } from '@walkeros/core'; - -export async function setupAPIDestination(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - // Single big config file - API destination setup - const trackingConfig = { - run: true, - globals: { - environment: 'production', - api_version: 'v1', - }, - sources: { - browser: createSource(sourceBrowser, { - settings: { - scope: document.body, - session: true, - }, - }), - }, - destinations: { - api: createDestination(destinationAPI, { - settings: { - url: 'https://api.example.com/events', - headers: { - 'X-API-Key': 'your-api-key', - 'Content-Type': 'application/json', - }, - }, - mapping: { - page: { - view: { - name: 'pageview', - data: { - map: { - url: 'data.url', - title: 'data.title', - timestamp: 'timestamp', - }, - }, - }, - }, - order: { - complete: { - name: 'purchase', - data: { - map: { - order_id: 'data.id', - revenue: 'data.total', - currency: 'data.currency', - }, - }, - }, - }, - }, - }), - }, - }; - - const { collector, elb } = await createCollector(trackingConfig); - return { collector, elb }; -} - -export async function trackAPIEvents(elb: WalkerOS.Elb): Promise { - await elb('page view', { - url: '/products', - title: 'Products Page', - }); - - await elb('order complete', { - id: 'order-999', - total: 249.99, - }); -} diff --git a/apps/quickstart/src/web-destinations/custom-destination.ts b/apps/quickstart/src/web-destinations/custom-destination.ts deleted file mode 100644 index 17655e4c1..000000000 --- a/apps/quickstart/src/web-destinations/custom-destination.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import type { WalkerOS, Destination, Collector } from '@walkeros/core'; - -// Custom destination that sends events to a webhook -const customWebhookDestination: Destination.Instance = { - type: 'webhook', - - config: {}, - - init({ config }) { - const { settings } = config; - const settingsObj = - settings && typeof settings === 'object' - ? (settings as Record) - : {}; - if (!settingsObj.url || typeof settingsObj.url !== 'string') { - console.warn('Custom webhook destination: URL not configured'); - return false; - } - console.log('Custom webhook destination initialized'); - }, - - async push(event, { config }) { - const { settings } = config; - const settingsObj = - settings && typeof settings === 'object' - ? (settings as Record) - : {}; - - if (!settingsObj.url || typeof settingsObj.url !== 'string') { - console.warn( - 'Custom webhook destination: No URL configured, skipping event', - ); - return; - } - - // Send to webhook - try { - const headers = - settingsObj.headers && typeof settingsObj.headers === 'object' - ? (settingsObj.headers as Record) - : {}; - - const response = await fetch(settingsObj.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...headers, - }, - body: JSON.stringify({ - timestamp: new Date().toISOString(), - event: event.event, - data: event.data, - user: event.user, - session: event.context?.session, - }), - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - console.log('Event sent to webhook:', event.event); - } catch (error) { - console.error('Failed to send event to webhook:', error); - throw error; - } - }, -}; - -export async function setupCustomDestination(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector({ - destinations: { - webhook: { - ...customWebhookDestination, - config: { - settings: { - url: 'https://webhook.site/unique-id', - headers: { - Authorization: 'Bearer your-api-token', - 'X-Source': 'walkerOS-quickstart', - }, - }, - mapping: { - // Map page views to custom event name - page: { - view: { - settings: { - eventName: 'pageview_tracked', - additionalData: { - source: 'walkerOS', - version: '1.0', - }, - }, - }, - }, - // Map purchases with custom data - order: { - complete: { - settings: { - eventName: 'purchase_completed', - additionalData: { - channel: 'web', - currency: 'USD', - }, - }, - }, - }, - }, - }, - }, - }, - }); - - return { collector, elb }; -} - -export async function trackCustomDestinationEvents( - elb: WalkerOS.Elb, -): Promise { - // Track page view - await elb('page view', { - title: 'Custom Destination Demo', - url: '/demo', - }); - - // Track purchase - await elb('order complete', { - id: 'order-12345', - total: 99.99, - items: 2, - }); - - // Track custom event - await elb('feature used', { - feature: 'custom-destination', - success: true, - }); -} diff --git a/apps/quickstart/src/web-destinations/ga4-complete.ts b/apps/quickstart/src/web-destinations/ga4-complete.ts deleted file mode 100644 index 100391326..000000000 --- a/apps/quickstart/src/web-destinations/ga4-complete.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { destinationGtag } from '@walkeros/web-destination-gtag'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupGA4Complete(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector({ - destinations: { - gtag: { - ...destinationGtag, - config: { - settings: { - ga4: { - measurementId: 'G-XXXXXXXXXX', - }, - }, - mapping: { - // Page view - page: { - view: { - name: 'page_view', - settings: { ga4: {} }, - }, - }, - // Product events - product: { - view: { - name: 'view_item', - settings: { ga4: {} }, - data: { - map: { - currency: { value: 'USD' }, - value: 'data.price', - }, - }, - }, - }, - // Purchase event - order: { - complete: { - name: 'purchase', - settings: { ga4: {} }, - data: { - map: { - transaction_id: 'data.id', - value: 'data.total', - currency: { value: 'USD' }, - }, - }, - }, - }, - }, - }, - }, - }, - }); - return { collector, elb }; -} - -export async function trackGA4Events(elb: WalkerOS.Elb): Promise { - await elb('page view', { - title: 'Home Page', - path: '/', - }); - - await elb('product view', { - id: 'prod-123', - name: 'Red Sneakers', - price: 99.99, - }); - - await elb('order complete', { - id: 'order-456', - total: 109.98, - }); -} diff --git a/apps/quickstart/src/web-destinations/gtag-ads.ts b/apps/quickstart/src/web-destinations/gtag-ads.ts deleted file mode 100644 index 20e85a70b..000000000 --- a/apps/quickstart/src/web-destinations/gtag-ads.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { destinationGtag } from '@walkeros/web-destination-gtag'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupGoogleAds(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector({ - destinations: { - gtag: { - ...destinationGtag, - config: { - settings: { - ads: { - conversionId: 'AW-XXXXXXXXX', - }, - }, - mapping: { - order: { - complete: { - name: 'conversion', - settings: { ads: {} }, - data: { - map: { - value: 'data.total', - currency: 'data.currency', - transaction_id: 'data.id', - }, - }, - }, - }, - form: { - submit: { - name: 'conversion', - settings: { ads: { conversionLabel: 'LEAD' } }, - data: { - map: { - value: { value: 0, key: 'data.value' }, - }, - }, - }, - }, - }, - }, - }, - }, - }); - return { collector, elb }; -} - -export async function trackAdsConversions(elb: WalkerOS.Elb): Promise { - await elb('order complete', { - id: 'order-789', - total: 129.99, - currency: 'USD', - }); - - await elb('form submit', { - type: 'lead', - value: 50, - }); -} diff --git a/apps/quickstart/src/web-destinations/gtag-complete.ts b/apps/quickstart/src/web-destinations/gtag-complete.ts deleted file mode 100644 index 91b9da9db..000000000 --- a/apps/quickstart/src/web-destinations/gtag-complete.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { createSource, createDestination } from '@walkeros/core'; -import { destinationGtag } from '@walkeros/web-destination-gtag'; -import { sourceBrowser } from '@walkeros/web-source-browser'; -import type { WalkerOS, Collector, Source } from '@walkeros/core'; - -export async function setupGtagComplete(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - // Single big config file approach - complete tracking setup - const trackingConfig = { - run: true, - globals: { - environment: 'production', - version: '1.0.0', - }, - sources: { - browser: createSource(sourceBrowser, { - settings: { - scope: document.body, - session: true, - }, - }), - }, - destinations: { - gtag: createDestination(destinationGtag, { - settings: { - ga4: { - measurementId: 'G-XXXXXXXXXX', - }, - ads: { - conversionId: 'AW-XXXXXXXXX', - }, - gtm: { - containerId: 'GTM-XXXXXXX', - }, - }, - mapping: { - // GA4 Purchase mapping - order: { - complete: { - name: 'purchase', - settings: { - ga4: { include: ['data'] }, - }, - data: { - map: { - transaction_id: 'data.id', - value: 'data.total', - currency: { value: 'USD', key: 'data.currency' }, - }, - }, - }, - }, - // Google Ads conversion mapping - form: { - submit: { - name: 'conversion', - settings: { - ads: { label: 'LEAD' }, - }, - data: { - map: { - value: { value: 0, key: 'data.value' }, - currency: { value: 'USD' }, - }, - }, - }, - }, - // GTM custom event - product: { - view: { - name: 'product_view', - settings: { - gtm: {}, - }, - data: { - map: { - product_id: 'data.id', - product_name: 'data.name', - value: 'data.price', - }, - }, - }, - }, - }, - }), - }, - }; - - const { collector, elb } = await createCollector(trackingConfig); - return { collector, elb }; -} - -export async function trackGtagEvents(elb: WalkerOS.Elb): Promise { - // GA4 purchase event - await elb('order complete', { - id: 'order-123', - total: 99.99, - currency: 'USD', - }); - - // Google Ads lead conversion - await elb('form submit', { - type: 'lead', - value: 50, - }); - - // GTM product view - await elb('product view', { - id: 'prod-456', - name: 'Blue Jacket', - price: 79.99, - }); -} diff --git a/apps/quickstart/src/web-destinations/meta-pixel.ts b/apps/quickstart/src/web-destinations/meta-pixel.ts deleted file mode 100644 index d2364f804..000000000 --- a/apps/quickstart/src/web-destinations/meta-pixel.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { createSource, createDestination } from '@walkeros/core'; -import { destinationMeta } from '@walkeros/web-destination-meta'; -import { sourceBrowser } from '@walkeros/web-source-browser'; -import type { WalkerOS, Collector, Source } from '@walkeros/core'; - -export async function setupMetaPixel(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - // Single big config file - Meta Pixel tracking setup - const trackingConfig = { - run: true, - globals: { - environment: 'production', - currency: 'USD', - }, - sources: { - browser: createSource(sourceBrowser, { - settings: { - scope: document.body, - session: true, - }, - }), - }, - destinations: { - meta: createDestination(destinationMeta, { - settings: { - pixelId: 'YOUR_PIXEL_ID', - }, - mapping: { - page: { - view: { name: 'PageView' }, - }, - product: { - add: { - name: 'AddToCart', - data: { - map: { - value: 'data.price', - currency: 'data.currency', - content_ids: ['data.id'], - content_name: 'data.name', - }, - }, - }, - }, - order: { - complete: { - name: 'Purchase', - data: { - map: { - value: 'data.total', - currency: 'data.currency', - content_ids: ['data.id'], - }, - }, - }, - }, - }, - }), - }, - }; - - const { collector, elb } = await createCollector(trackingConfig); - return { collector, elb }; -} - -export async function trackMetaEvents(elb: WalkerOS.Elb): Promise { - await elb('page view'); - - await elb('product add', { - id: 'prod-456', - name: 'Summer Dress', - price: 49.99, - currency: 'USD', - }); - - await elb('order complete', { - id: 'order-123', - total: 99.98, - currency: 'USD', - }); -} diff --git a/apps/quickstart/src/web-destinations/piwikpro.ts b/apps/quickstart/src/web-destinations/piwikpro.ts deleted file mode 100644 index 5d239f1a7..000000000 --- a/apps/quickstart/src/web-destinations/piwikpro.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { destinationPiwikPro } from '@walkeros/web-destination-piwikpro'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupPiwikPro(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector({ - destinations: { - piwikpro: { - ...destinationPiwikPro, - config: { - settings: { - appId: 'XXX-XXX-XXX-XXX-XXX', - url: 'https://your-instance.piwik.pro/', - }, - mapping: { - // Product view to ecommerceProductDetailView - product: { - view: { - name: 'ecommerceProductDetailView', - data: { - set: [ - { - set: [ - { - map: { - sku: 'data.id', - name: 'data.name', - price: 'data.price', - quantity: { value: 1 }, - }, - }, - ], - }, - { - map: { - currencyCode: { value: 'EUR' }, - }, - }, - ], - }, - }, - // Product add to ecommerceAddToCart - add: { - name: 'ecommerceAddToCart', - data: { - set: [ - { - set: [ - { - map: { - sku: 'data.id', - name: 'data.name', - price: 'data.price', - quantity: { value: 1 }, - }, - }, - ], - }, - { - map: { - currencyCode: { value: 'EUR' }, - }, - }, - ], - }, - }, - }, - // Order complete to ecommerceOrder - order: { - complete: { - name: 'ecommerceOrder', - data: { - set: [ - { - map: { - orderId: 'data.id', - grandTotal: 'data.total', - currencyCode: { value: 'EUR' }, - }, - }, - ], - }, - }, - }, - }, - }, - }, - }, - }); - - return { collector, elb }; -} - -export async function trackPiwikProEvents(elb: WalkerOS.Elb): Promise { - await elb('product view', { - id: 'SKU-123', - name: 'Blue T-Shirt', - price: 29.99, - }); - - await elb('product add', { - id: 'SKU-456', - name: 'Red Shoes', - price: 89.99, - }); - - await elb('order complete', { - id: 'order-789', - total: 149.99, - }); -} diff --git a/apps/quickstart/src/web-destinations/plausible.ts b/apps/quickstart/src/web-destinations/plausible.ts deleted file mode 100644 index ca9b413c4..000000000 --- a/apps/quickstart/src/web-destinations/plausible.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { createCollector } from '@walkeros/collector'; -import { destinationPlausible } from '@walkeros/web-destination-plausible'; -import type { WalkerOS, Collector } from '@walkeros/core'; - -export async function setupPlausible(): Promise<{ - collector: Collector.Instance; - elb: WalkerOS.Elb; -}> { - const { collector, elb } = await createCollector({ - destinations: { - plausible: { - ...destinationPlausible, - config: { - settings: { - domain: 'yourdomain.com', - apiHost: 'https://plausible.io', - }, - mapping: { - // Custom goal tracking - form: { - submit: { - name: 'Contact Form', - }, - }, - file: { - download: { - name: 'Download', - data: { - map: { - filename: 'data.filename', - }, - }, - }, - }, - }, - }, - }, - }, - }); - - return { collector, elb }; -} - -export async function trackPlausibleEvents(elb: WalkerOS.Elb): Promise { - await elb('form submit', { - type: 'contact', - }); - - await elb('file download', { - filename: 'whitepaper.pdf', - }); -} diff --git a/package-lock.json b/package-lock.json index 555c37773..5a9631a6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "packages/web/destinations/*", "packages/web/sources/*", "apps/walkerjs", - "apps/quickstart", "apps/storybook-addon", "apps/demos/react", "apps/demos/storybook", @@ -1576,6 +1575,7 @@ "apps/quickstart": { "name": "@walkeros/quickstart", "version": "0.0.4", + "extraneous": true, "license": "MIT", "dependencies": { "@walkeros/collector": "0.0.8", @@ -1601,20 +1601,6 @@ "jest-environment-jsdom": "^29.7.0" } }, - "apps/quickstart/node_modules/@walkeros/web-source-browser": { - "version": "0.0.9", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/elbwalker" - } - ], - "license": "MIT", - "dependencies": { - "@walkeros/collector": "0.0.8", - "@walkeros/web-core": "0.0.8" - } - }, "apps/storybook-addon": { "name": "@walkeros/storybook-addon", "version": "0.0.2", @@ -15370,10 +15356,6 @@ "resolved": "packages/config/jest", "link": true }, - "node_modules/@walkeros/quickstart": { - "resolved": "apps/quickstart", - "link": true - }, "node_modules/@walkeros/server-core": { "resolved": "packages/server/core", "link": true diff --git a/package.json b/package.json index 27c03ab84..697a21a32 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "packages/web/destinations/*", "packages/web/sources/*", "apps/walkerjs", - "apps/quickstart", "apps/storybook-addon", "apps/demos/react", "apps/demos/storybook", @@ -23,9 +22,9 @@ "clean": "turbo run clean", "dev": "turbo run dev --output-logs=errors-only", "format": "prettier --write .", - "lint": "turbo run lint --output-logs=errors-only", + "lint": "turbo run lint --filter=!@walkeros/website --output-logs=errors-only", "publish-packages": "npm run build lint test && changeset version && changeset publish", - "test": "turbo run test --output-logs=errors-only", + "test": "turbo run test --filter=!@walkeros/website --output-logs=errors-only", "prepare": "is-ci || husky" }, "devDependencies": { From abfd22c6834f68bb14cbabe51a870bad9294f684 Mon Sep 17 00:00:00 2001 From: alexander Date: Sat, 6 Sep 2025 15:09:03 +0200 Subject: [PATCH 11/13] lint updates --- apps/demos/storybook/eslint.config.js | 39 +++++++++------------------ packages/config/eslint/index.mjs | 1 + packages/config/jest/src/index.mjs | 16 +++++++++++ 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/apps/demos/storybook/eslint.config.js b/apps/demos/storybook/eslint.config.js index b72eb5acb..139d2ac15 100644 --- a/apps/demos/storybook/eslint.config.js +++ b/apps/demos/storybook/eslint.config.js @@ -1,29 +1,16 @@ -// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format +import baseConfig from '@walkeros/eslint/web.mjs'; import storybook from 'eslint-plugin-storybook'; -import js from '@eslint/js'; -import globals from 'globals'; -import reactHooks from 'eslint-plugin-react-hooks'; -import reactRefresh from 'eslint-plugin-react-refresh'; -import tseslint from 'typescript-eslint'; -import { globalIgnores } from 'eslint/config'; - -export default tseslint.config( - [ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - js.configs.recommended, - tseslint.configs.recommended, - reactHooks.configs['recommended-latest'], - reactRefresh.configs.vite, - ], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, +export default [ + { + ignores: ['storybook-static/**'], + }, + ...baseConfig, + ...storybook.configs['flat/recommended'], + { + files: ['**/*.stories.{js,ts,tsx}'], + rules: { + // Storybook-specific rule overrides if needed }, - ], - storybook.configs['flat/recommended'], -); + }, +]; diff --git a/packages/config/eslint/index.mjs b/packages/config/eslint/index.mjs index d675768f5..76c55b70c 100644 --- a/packages/config/eslint/index.mjs +++ b/packages/config/eslint/index.mjs @@ -8,6 +8,7 @@ export default [ ignores: [ '**/coverage/**', '**/dist/**', + '**/build/**', '**/node_modules/**', '**/__mocks__/**', ], diff --git a/packages/config/jest/src/index.mjs b/packages/config/jest/src/index.mjs index cf5c50cfa..5c548ffd7 100644 --- a/packages/config/jest/src/index.mjs +++ b/packages/config/jest/src/index.mjs @@ -78,6 +78,22 @@ const config = { moduleDirectories: ['node_modules', 'src'], extensionsToTreatAsEsm: ['.ts', '.tsx'], moduleNameMapper: getModuleMapper(), + + // Performance settings - fixed values for consistent behavior + maxWorkers: 4, + testTimeout: 30000, + forceExit: true, + clearMocks: true, + restoreMocks: true, + detectOpenHandles: true, + + // Enhanced ignore patterns + testPathIgnorePatterns: [ + '/node_modules/', + '/dist/', + '/build/', + '/coverage/' + ], }; export default config; From 7f999b7b3a7b66c13b1aa3bb43dcc6d4de74b9c7 Mon Sep 17 00:00:00 2001 From: alexander Date: Sun, 7 Sep 2025 21:36:42 +0200 Subject: [PATCH 12/13] name --- AGENT.md | 4 +- apps/walkerjs/package.json | 2 +- .../src/__tests__/destination.test.ts | 22 ++++---- .../src/__tests__/integration.test.ts | 4 +- apps/walkerjs/src/destination.ts | 2 +- package-lock.json | 16 +----- packages/collector/src/destination.ts | 6 +-- packages/collector/src/handle.ts | 10 ++-- .../core/src/__tests__/eventGenerator.test.ts | 8 +-- packages/core/src/__tests__/mapping.test.ts | 34 ++++++------ packages/core/src/__tests__/validate.test.ts | 54 +++++++++---------- packages/core/src/eventGenerator.ts | 8 +-- packages/core/src/mapping.ts | 2 +- packages/core/src/types/walkeros.ts | 2 +- packages/core/src/validate.ts | 12 ++--- packages/server/core/README.md | 2 +- .../core/src/__tests__/collector.test.ts | 2 +- .../core/src/__tests__/destination.test.ts | 20 +++---- .../gcp/src/bigquery/__tests__/index.test.ts | 2 +- packages/server/destinations/meta/src/push.ts | 2 +- .../core/src/__tests__/sessionStart.test.ts | 4 +- packages/web/core/src/session/sessionStart.ts | 2 +- .../gtag/src/__tests__/ga4.test.ts | 2 +- .../gtag/src/__tests__/gtm.test.ts | 3 +- .../web/destinations/gtag/src/ga4/push.ts | 2 +- .../web/destinations/gtag/src/gtm/push.ts | 2 +- .../web/destinations/meta/src/index.test.ts | 2 +- packages/web/destinations/meta/src/index.ts | 6 +-- .../web/destinations/piwikpro/src/index.ts | 4 +- .../web/destinations/plausible/src/index.ts | 2 +- .../browser/src/__tests__/edgeCases.test.ts | 1 - .../browser/src/__tests__/elbLayer.test.ts | 22 ++++---- .../browser/src/__tests__/integration.test.ts | 10 ++-- .../browser/src/__tests__/translation.test.ts | 16 +++--- .../browser/src/__tests__/trigger.test.ts | 6 +-- .../web/sources/browser/src/translation.ts | 4 +- packages/web/sources/browser/src/trigger.ts | 2 +- .../src/__tests__/consent-simple.test.ts | 14 ++--- .../dataLayer/src/__tests__/enhanced.test.ts | 21 +++----- .../dataLayer/src/__tests__/filter.test.ts | 16 +++--- .../src/__tests__/integration.test.ts | 20 +++---- .../dataLayer/src/__tests__/minimal.test.ts | 12 ++--- .../web/sources/dataLayer/src/interceptor.ts | 13 ++--- website/docs/core/server.mdx | 2 +- website/docs/destinations/event-mapping.mdx | 12 ++--- website/docs/getting-started/event-model.mdx | 2 +- website/docs/sources/web/browser/tagging.mdx | 2 +- 47 files changed, 194 insertions(+), 224 deletions(-) diff --git a/AGENT.md b/AGENT.md index c7c40e055..a128dcb02 100644 --- a/AGENT.md +++ b/AGENT.md @@ -127,7 +127,7 @@ All events follow this consistent structure: ```typescript { - event: 'product view', // ENTITY ACTION format + name: 'product view', // ENTITY ACTION format data: { // Entity-specific properties id: 'P123', name: 'Laptop', @@ -488,7 +488,7 @@ it('processes events correctly', async () => { }); await collector.push('page view', {}); expect(mockDestination.push).toHaveBeenCalledWith( - expect.objectContaining({ event: 'page view' }), + expect.objectContaining({ name: 'page view' }), expect.any(Object), ); }); diff --git a/apps/walkerjs/package.json b/apps/walkerjs/package.json index aabe81cbb..8297ca9ca 100644 --- a/apps/walkerjs/package.json +++ b/apps/walkerjs/package.json @@ -37,7 +37,7 @@ "@walkeros/core": "0.0.8", "@walkeros/collector": "0.0.8", "@walkeros/web-core": "0.0.8", - "@walkeros/web-source-browser": "0.0.9", + "@walkeros/web-source-browser": "0.0.10", "@walkeros/web-source-datalayer": "0.0.8" }, "devDependencies": { diff --git a/apps/walkerjs/src/__tests__/destination.test.ts b/apps/walkerjs/src/__tests__/destination.test.ts index 0964740c2..936ed2b0d 100644 --- a/apps/walkerjs/src/__tests__/destination.test.ts +++ b/apps/walkerjs/src/__tests__/destination.test.ts @@ -20,7 +20,7 @@ describe('Destination Tests', () => { test('should push event to dataLayer', () => { const destination = dataLayerDestination(); const event = { - event: 'foo bar', + name: 'foo bar', } as unknown as WalkerOS.Event; destination.push(event, {} as unknown as Destination.PushContext); expect(mockDataLayer).toHaveBeenCalledWith(event); @@ -29,7 +29,7 @@ describe('Destination Tests', () => { test('should not push events from dataLayer source', () => { const destination = dataLayerDestination(); const event = { - event: 'foo bar', + name: 'foo bar', source: { type: 'dataLayer', }, @@ -41,7 +41,7 @@ describe('Destination Tests', () => { test('should push context data when available', () => { const destination = dataLayerDestination(); const event = { - event: 'foo bar', + name: 'foo bar', } as unknown as WalkerOS.Event; const contextData = { custom: 'data' }; destination.push(event, { @@ -56,16 +56,16 @@ describe('Destination Tests', () => { const destination = dataLayerDestination(); const batch = { key: 'test-batch', - data: [{ event: 'event1' }, { event: 'event2' }], + data: [{ name: 'event1' }, { name: 'event2' }], events: [], } as unknown as Destination.Batch; destination.pushBatch?.(batch, {} as unknown as Destination.PushContext); expect(mockDataLayer).toHaveBeenCalledWith({ - event: 'batch', + name: 'batch', batched_event: 'test-batch', - events: [{ event: 'event1' }, { event: 'event2' }], + events: [{ name: 'event1' }, { name: 'event2' }], }); }); @@ -74,15 +74,15 @@ describe('Destination Tests', () => { const batch = { key: 'test-batch', data: [], - events: [{ event: 'fallback1' }, { event: 'fallback2' }], + events: [{ name: 'fallback1' }, { name: 'fallback2' }], } as unknown as Destination.Batch; destination.pushBatch?.(batch, {} as unknown as Destination.PushContext); expect(mockDataLayer).toHaveBeenCalledWith({ - event: 'batch', + name: 'batch', batched_event: 'test-batch', - events: [{ event: 'fallback1' }, { event: 'fallback2' }], + events: [{ name: 'fallback1' }, { name: 'fallback2' }], }); }); }); @@ -98,7 +98,7 @@ describe('Destination Tests', () => { test('should handle events with non-object source', () => { const destination = dataLayerDestination(); const event = { - event: 'foo bar', + name: 'foo bar', source: 'string source', } as unknown as WalkerOS.Event; destination.push(event, {} as unknown as Destination.PushContext); @@ -108,7 +108,7 @@ describe('Destination Tests', () => { test('should handle events with dataLayer-like source type', () => { const destination = dataLayerDestination(); const event = { - event: 'foo bar', + name: 'foo bar', source: { type: 'custom-dataLayer-source', }, diff --git a/apps/walkerjs/src/__tests__/integration.test.ts b/apps/walkerjs/src/__tests__/integration.test.ts index e280df5f8..51bb9f860 100644 --- a/apps/walkerjs/src/__tests__/integration.test.ts +++ b/apps/walkerjs/src/__tests__/integration.test.ts @@ -104,7 +104,7 @@ describe('Walker.js Integration Tests', () => { const event = mockPush.mock.calls[0][0] as unknown as WalkerOS.Event; expect(event).toMatchObject({ - event: 'product add', + name: 'product add', data: { id: 123, name: 'Test Product', @@ -135,7 +135,7 @@ describe('Walker.js Integration Tests', () => { expect(mockPush).toHaveBeenCalled(); const event = mockPush.mock.calls[0][0] as unknown as WalkerOS.Event; - expect(event.event).toBe('order complete'); + expect(event.name).toBe('order complete'); expect(event.data).toMatchObject({ transaction_id: 'TRX123', value: 99.99, diff --git a/apps/walkerjs/src/destination.ts b/apps/walkerjs/src/destination.ts index db7a36e68..8ccb6d048 100644 --- a/apps/walkerjs/src/destination.ts +++ b/apps/walkerjs/src/destination.ts @@ -23,7 +23,7 @@ export function dataLayerDestination(): Destination.InitDestination { }, pushBatch: (batch) => { dataLayerPush({ - event: 'batch', + name: 'batch', batched_event: batch.key, events: batch.data.length ? batch.data : batch.events, }); diff --git a/package-lock.json b/package-lock.json index 5a9631a6c..a38b8974a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2463,7 +2463,7 @@ "@walkeros/collector": "0.0.8", "@walkeros/core": "0.0.8", "@walkeros/web-core": "0.0.8", - "@walkeros/web-source-browser": "0.0.9", + "@walkeros/web-source-browser": "0.0.10", "@walkeros/web-source-datalayer": "0.0.8" }, "devDependencies": { @@ -2473,20 +2473,6 @@ "jest-environment-jsdom": "^29.7.0" } }, - "apps/walkerjs/node_modules/@walkeros/web-source-browser": { - "version": "0.0.9", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/elbwalker" - } - ], - "license": "MIT", - "dependencies": { - "@walkeros/collector": "0.0.8", - "@walkeros/web-core": "0.0.8" - } - }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", diff --git a/packages/collector/src/destination.ts b/packages/collector/src/destination.ts index 8c065d8a3..c71f564f5 100644 --- a/packages/collector/src/destination.ts +++ b/packages/collector/src/destination.ts @@ -56,14 +56,14 @@ export function createPush( // Event format: event object or string const partialEvent = typeof eventOrCommand === 'string' - ? { event: eventOrCommand } + ? { name: eventOrCommand } : (eventOrCommand as WalkerOS.DeepPartialEvent); const enrichedEvent = prepareEvent(partialEvent); const { event, command } = createEventOrCommand( collector, - enrichedEvent.event, + enrichedEvent.name, enrichedEvent, ); @@ -347,7 +347,7 @@ export async function destinationPush( if (eventMapping.ignore) return false; // Check to use specific event names - if (eventMapping.name) event.event = eventMapping.name; + if (eventMapping.name) event.name = eventMapping.name; // Transform event to a custom data if (eventMapping.data) { diff --git a/packages/collector/src/handle.ts b/packages/collector/src/handle.ts index 205c1e3d1..06031f0a6 100644 --- a/packages/collector/src/handle.ts +++ b/packages/collector/src/handle.ts @@ -115,13 +115,13 @@ export function createEventOrCommand( nameOrEvent, '' as string, ) - ? { event: nameOrEvent, ...defaults } + ? { name: nameOrEvent, ...defaults } : { ...defaults, ...(nameOrEvent || {}) }; - if (!partialEvent.event) throw new Error('Event name is required'); + if (!partialEvent.name) throw new Error('Event name is required'); // Check for valid entity and action event format - const [entityValue, actionValue] = partialEvent.event.split(' '); + const [entityValue, actionValue] = partialEvent.name.split(' '); if (!entityValue || !actionValue) throw new Error('Event name is invalid'); // It's a walker command @@ -141,7 +141,7 @@ export function createEventOrCommand( // Extract properties with default fallbacks const { - event = `${entityValue} ${actionValue}`, + name = `${entityValue} ${actionValue}`, data = {}, context = {}, globals = collector.globals, @@ -162,7 +162,7 @@ export function createEventOrCommand( } = partialEvent; const fullEvent: WalkerOS.Event = { - event, + name, data, context, globals, diff --git a/packages/core/src/__tests__/eventGenerator.test.ts b/packages/core/src/__tests__/eventGenerator.test.ts index 90a38c57e..0ac788df9 100644 --- a/packages/core/src/__tests__/eventGenerator.test.ts +++ b/packages/core/src/__tests__/eventGenerator.test.ts @@ -7,7 +7,7 @@ describe('createEvent', () => { const id = `${timestamp}-${group}-${count}`; const defaultEvent = { - event: 'entity action', + name: 'entity action', data: { string: 'foo', number: 1, @@ -54,7 +54,7 @@ describe('createEvent', () => { test('getEvent', () => { expect(getEvent('page view')).toStrictEqual( expect.objectContaining({ - event: 'page view', + name: 'page view', data: { domain: 'www.example.com', title: 'walkerOS documentation', @@ -71,7 +71,7 @@ describe('createEvent', () => { expect(getEvent('page view', { data: { id: '/custom' } })).toStrictEqual( expect.objectContaining({ - event: 'page view', + name: 'page view', data: { id: '/custom' }, trigger: 'load', entity: 'page', @@ -81,7 +81,7 @@ describe('createEvent', () => { expect(getEvent('promotion visible')).toStrictEqual( expect.objectContaining({ - event: 'promotion visible', + name: 'promotion visible', data: { name: 'Setting up tracking easily', position: 'hero', diff --git a/packages/core/src/__tests__/mapping.test.ts b/packages/core/src/__tests__/mapping.test.ts index b83a22a65..2d2dd446d 100644 --- a/packages/core/src/__tests__/mapping.test.ts +++ b/packages/core/src/__tests__/mapping.test.ts @@ -14,7 +14,7 @@ describe('getMappingEvent', () => { expect( await getMappingEvent( - { event: 'page view' }, + { name: 'page view' }, { page: { view: pageViewConfig } }, ), ).toStrictEqual({ @@ -27,7 +27,7 @@ describe('getMappingEvent', () => { const entityAsterisksConfig = { name: 'entity_*' }; expect( await getMappingEvent( - { event: 'page random' }, + { name: 'page random' }, { page: { '*': entityAsterisksConfig } }, ), ).toStrictEqual({ @@ -38,7 +38,7 @@ describe('getMappingEvent', () => { const asterisksActionConfig = { name: '*_view' }; expect( await getMappingEvent( - { event: 'random view' }, + { name: 'random view' }, { '*': { view: asterisksActionConfig } }, ), ).toStrictEqual({ @@ -56,28 +56,28 @@ describe('getMappingEvent', () => { }; expect( - await getMappingEvent({ event: 'not existing' }, mapping), + await getMappingEvent({ name: 'not existing' }, mapping), ).toStrictEqual({ eventMapping: { name: 'asterisk' }, mappingKey: '* *', }); expect( - await getMappingEvent({ event: 'asterisk action' }, mapping), + await getMappingEvent({ name: 'asterisk action' }, mapping), ).toStrictEqual({ eventMapping: { name: 'action' }, mappingKey: '* action', }); expect( - await getMappingEvent({ event: 'foo something' }, mapping), + await getMappingEvent({ name: 'foo something' }, mapping), ).toStrictEqual({ eventMapping: { name: 'foo_asterisk' }, mappingKey: 'foo *', }); expect( - await getMappingEvent({ event: 'bar something' }, mapping), + await getMappingEvent({ name: 'bar something' }, mapping), ).toStrictEqual({ eventMapping: { name: 'asterisk' }, mappingKey: '* *', @@ -103,7 +103,7 @@ describe('getMappingEvent', () => { }; expect( - await getMappingEvent({ event: 'order complete' }, mapping), + await getMappingEvent({ name: 'order complete' }, mapping), ).toStrictEqual({ eventMapping: (mapping.order!.complete as Array)[1], mappingKey: 'order complete', @@ -111,7 +111,7 @@ describe('getMappingEvent', () => { expect( await getMappingEvent( - { event: 'order complete', globals: { env: 'prod' } }, + { name: 'order complete', globals: { env: 'prod' } }, mapping, ), ).toStrictEqual({ @@ -204,11 +204,11 @@ describe('getMappingValue', () => { }); test('fn', async () => { - const pageView = createEvent({ event: 'page view' }); - const pageClick = createEvent({ event: 'page click' }); + const pageView = createEvent({ name: 'page view' }); + const pageClick = createEvent({ name: 'page click' }); const mockFn = jest.fn((event) => { - if (event.event === 'page view') return 'foo'; + if (event.name === 'page view') return 'foo'; return 'bar'; }); @@ -248,11 +248,11 @@ describe('getMappingValue', () => { loop: [ 'this', { - key: 'event', + key: 'name', }, ], }), - ).toStrictEqual([event.event]); + ).toStrictEqual([event.name]); }); test('set', async () => { @@ -260,7 +260,7 @@ describe('getMappingValue', () => { expect( await getMappingValue(event, { - set: ['event', 'data', { value: 'static' }, { fn: () => 'fn' }], + set: ['name', 'data', { value: 'static' }, { fn: () => 'fn' }], }), ).toStrictEqual(['order complete', event.data, 'static', 'fn']); }); @@ -421,12 +421,12 @@ describe('getMappingValue', () => { test('condition', async () => { const mockCondition = jest.fn((event) => { - return event.event === 'page view'; + return event.name === 'page view'; }); // Condition met expect( - await getMappingValue(createEvent({ event: 'page view' }), { + await getMappingValue(createEvent({ name: 'page view' }), { key: 'data.string', condition: mockCondition, }), diff --git a/packages/core/src/__tests__/validate.test.ts b/packages/core/src/__tests__/validate.test.ts index 089d9854f..9bf7189a7 100644 --- a/packages/core/src/__tests__/validate.test.ts +++ b/packages/core/src/__tests__/validate.test.ts @@ -6,11 +6,11 @@ describe('validate', () => { // should return valid event with missing properties filled expect( validateEvent({ - event: 'e a', + name: 'e a', data: { k: 'v' }, }), ).toStrictEqual({ - event: 'e a', + name: 'e a', data: { k: 'v' }, context: {}, custom: {}, @@ -33,7 +33,7 @@ describe('validate', () => { // should throw error for invalid event name expect(() => validateEvent({ - event: 'e', + name: 'e', }), ).toThrow('Invalid event name'); @@ -42,27 +42,27 @@ describe('validate', () => { validateEvent({ data: { key: 'value' }, }), - ).toThrow('Missing or invalid event, entity, or action'); + ).toThrow('Missing or invalid name, entity, or action'); // long event names expect( validateEvent({ - event: 'e ' + 'a'.repeat(256), - }).event, + name: 'e ' + 'a'.repeat(256), + }).name, ).toHaveLength(255); expect(() => validateEvent( { - event: 'e ' + 'a'.repeat(11), + name: 'e ' + 'a'.repeat(11), }, - [{ e: { '*': { event: { maxLength: 10, strict: true } } } }], + [{ e: { '*': { name: { maxLength: 10, strict: true } } } }], ), ).toThrow('Value exceeds maxLength'); // should throw error for invalid type expect( validateEvent({ - event: 'some event', + name: 'some event', data: 'invalid type', }), ).toHaveProperty('data', {}); @@ -70,7 +70,7 @@ describe('validate', () => { // should throw error for extra properties expect(() => validateEvent({ - event: 'some event', + name: 'some event', extraProp: 'should not be here', }), ).not.toHaveProperty('extraProp'); @@ -87,7 +87,7 @@ describe('validate', () => { expect( validateEvent( { - event: 'e a', + name: 'e a', data: { k: 'v', remove: 'me' }, }, contract, @@ -96,7 +96,7 @@ describe('validate', () => { expect(() => validateEvent( { - event: 'e s', + name: 'e s', data: { k: 'v', remove: 'me' }, }, contract, @@ -107,7 +107,7 @@ describe('validate', () => { expect(() => validateEvent( { - event: 'p r', + name: 'p r', data: {}, }, requireContract, @@ -116,7 +116,7 @@ describe('validate', () => { expect( validateEvent( { - event: 'a n', + name: 'a n', }, requireContract, ), @@ -125,7 +125,7 @@ describe('validate', () => { // should remove unknown properties expect( validateEvent({ - event: 'some event', + name: 'some event', randomProp: 123, // doesn't belong here }), ).not.toHaveProperty('randomProp'); @@ -133,7 +133,7 @@ describe('validate', () => { // should throw error for invalid number range expect( validateEvent({ - event: 'e a', + name: 'e a', count: -1, // should be >= 0 }), ).toHaveProperty('count', 0); @@ -143,7 +143,7 @@ describe('validate', () => { { entity: { throw: { - event: { + name: { validate: ( value: unknown, key: string, @@ -155,7 +155,7 @@ describe('validate', () => { }, }, name: { - event: { + name: { validate: () => { // With great power comes great responsibility... return 'invalideventname'; @@ -173,14 +173,14 @@ describe('validate', () => { }, ]; expect(() => - validateEvent({ event: 'entity throw' }, customValidationContract), + validateEvent({ name: 'entity throw' }, customValidationContract), ).toThrow('Custom'); expect( - validateEvent({ event: 'entity name' }, customValidationContract), - ).toHaveProperty('event', 'invalideventname'); // If one really wants + validateEvent({ name: 'entity name' }, customValidationContract), + ).toHaveProperty('name', 'invalideventname'); // If one really wants expect( validateEvent( - { event: 'entity type', data: {} }, + { name: 'entity type', data: {} }, customValidationContract, ), ).toHaveProperty('data', {}); // If one really wants @@ -188,10 +188,10 @@ describe('validate', () => { // should validate wildcard rules expect( validateEvent({ - event: 'product add', + name: 'product add', data: { id: '123', price: 9.99 }, }), - ).toMatchObject({ event: 'product add', data: { id: '123', price: 9.99 } }); + ).toMatchObject({ name: 'product add', data: { id: '123', price: 9.99 } }); const typeContract = { e: { @@ -208,7 +208,7 @@ describe('validate', () => { expect(() => validateEvent( { - event: 'e a', + name: 'e a', globals: { n: 'no number', }, @@ -219,7 +219,7 @@ describe('validate', () => { expect( validateEvent( { - event: 'e a', + name: 'e a', globals: { n: 1, k: 'v', @@ -227,6 +227,6 @@ describe('validate', () => { }, [typeContract], ), - ).toMatchObject({ event: 'e a' }); + ).toMatchObject({ name: 'e a' }); }); }); diff --git a/packages/core/src/eventGenerator.ts b/packages/core/src/eventGenerator.ts index 6fe48ea7d..930155d84 100644 --- a/packages/core/src/eventGenerator.ts +++ b/packages/core/src/eventGenerator.ts @@ -19,7 +19,7 @@ export function createEvent( const id = `${timestamp}-${group}-${count}`; const defaultEvent: WalkerOS.Event = { - event: 'entity action', + name: 'entity action', data: { string: 'foo', number: 1, @@ -67,8 +67,8 @@ export function createEvent( // Update conditions // Entity and action from event - if (props.event) { - const [entity, action] = props.event.split(' ') ?? []; + if (props.name) { + const [entity, action] = props.name.split(' ') ?? []; if (entity && action) { event.entity = entity; @@ -270,5 +270,5 @@ export function getEvent( }, }; - return createEvent({ ...defaultEvents[name], ...props, event: name }); + return createEvent({ ...defaultEvents[name], ...props, name: name }); } diff --git a/packages/core/src/mapping.ts b/packages/core/src/mapping.ts index d6580f0c6..8b556442b 100644 --- a/packages/core/src/mapping.ts +++ b/packages/core/src/mapping.ts @@ -16,7 +16,7 @@ export async function getMappingEvent( event: WalkerOS.PartialEvent, mapping?: Mapping.Rules, ): Promise { - const [entity, action] = (event.event || '').split(' '); + const [entity, action] = (event.name || '').split(' '); if (!mapping || !entity || !action) return {}; let eventMapping: Mapping.Rule | undefined; diff --git a/packages/core/src/types/walkeros.ts b/packages/core/src/types/walkeros.ts index 4b103c42c..7e30d9932 100644 --- a/packages/core/src/types/walkeros.ts +++ b/packages/core/src/types/walkeros.ts @@ -16,7 +16,7 @@ export type Events = Array; export type PartialEvent = Partial; export type DeepPartialEvent = DeepPartial; export interface Event { - event: string; + name: string; data: Properties; context: OrderedProperties; globals: Properties; diff --git a/packages/core/src/validate.ts b/packages/core/src/validate.ts index c8b61a458..31ade7fe4 100644 --- a/packages/core/src/validate.ts +++ b/packages/core/src/validate.ts @@ -20,9 +20,9 @@ export function validateEvent( let entity: string; let action: string; - // Check if event.event is available and it's a string - if (isSameType(obj.event, '')) { - event = obj.event; + // Check if event.name is available and it's a string + if (isSameType(obj.name, '')) { + event = obj.name; [entity, action] = event.split(' '); if (!entity || !action) throwError('Invalid event name'); } else if (isSameType(obj.entity, '') && isSameType(obj.action, '')) { @@ -30,13 +30,13 @@ export function validateEvent( action = obj.action; event = `${entity} ${action}`; } else { - throwError('Missing or invalid event, entity, or action'); + throwError('Missing or invalid name, entity, or action'); } const basicContract: Schema.Contract = { '*': { '*': { - event: { maxLength: 255 }, // @TODO as general rule? + name: { maxLength: 255 }, // @TODO as general rule? user: { allowedKeys: ['id', 'device', 'session'] }, consent: { allowedValues: [true, false] }, timestamp: { min: 0 }, @@ -49,7 +49,7 @@ export function validateEvent( }; const basicEvent: WalkerOS.Event = { - event, + name: event, data: {}, context: {}, custom: {}, diff --git a/packages/server/core/README.md b/packages/server/core/README.md index 63bc7b66d..1e4e35dc1 100644 --- a/packages/server/core/README.md +++ b/packages/server/core/README.md @@ -31,7 +31,7 @@ sends HTTP requests using Node.js built-in modules (`http`/`https`). ```js // Simple POST request const response = await sendServer('https://api.example.com/events', { - event: 'page view', + name: 'page view', data: { url: '/home' }, }); diff --git a/packages/server/core/src/__tests__/collector.test.ts b/packages/server/core/src/__tests__/collector.test.ts index aae5bbc2b..005282a34 100644 --- a/packages/server/core/src/__tests__/collector.test.ts +++ b/packages/server/core/src/__tests__/collector.test.ts @@ -88,7 +88,7 @@ describe('Server Collector', () => { tagging: 42, }); const event = { - event: 'e a', + name: 'e a', data: {}, context: {}, custom: {}, diff --git a/packages/server/core/src/__tests__/destination.test.ts b/packages/server/core/src/__tests__/destination.test.ts index 689f2e066..55341c428 100644 --- a/packages/server/core/src/__tests__/destination.test.ts +++ b/packages/server/core/src/__tests__/destination.test.ts @@ -138,12 +138,12 @@ describe('Destination', () => { expect(eventCall).toHaveBeenCalledWith({ ...mockEvent, ...changes }); jest.clearAllMocks(); - await elb({ ...mockEvent, event: 'entity rename' }); + await elb({ ...mockEvent, name: 'entity rename' }); expect(mockDestination.push).toHaveBeenCalledWith( expect.objectContaining({ ...mockEvent, ...changes, - event: 'NewEventName', + name: 'NewEventName', }), expect.objectContaining({ mapping: eventMapping, @@ -162,7 +162,7 @@ describe('Destination', () => { expect(mockDestination.push).toHaveBeenCalledTimes(1); expect(mockDestination.push).toHaveBeenCalledWith( expect.objectContaining({ - event: 'custom', + name: 'custom', }), expect.objectContaining({ mapping: eventMapping, @@ -182,7 +182,7 @@ describe('Destination', () => { result = await elb(mockEvent); expect(mockPush).toHaveBeenCalledWith( - expect.objectContaining({ event: 'entity action' }), + expect.objectContaining({ name: 'entity action' }), expect.objectContaining({ mapping: eventMapping, data: 'bar', @@ -265,7 +265,7 @@ describe('Destination', () => { expect(second).toHaveBeenCalledTimes(1); expect(first).toHaveBeenCalledWith({ ...mockEvent, - event: 'new name', + name: 'new name', custom: { foo: 'bar' }, }); expect(second).toHaveBeenCalledWith({ ...mockEvent }); @@ -359,11 +359,11 @@ describe('Destination', () => { // DLQ expect(collector.destinations['initFail'].dlq).toContainEqual([ - expect.objectContaining({ event: mockEvent.event }), + expect.objectContaining({ name: mockEvent.name }), new Error('init kaputt'), ]); expect(collector.destinations['pushFail'].dlq).toContainEqual([ - expect.objectContaining({ event: mockEvent.event }), + expect.objectContaining({ name: mockEvent.name }), new Error('push kaputt'), ]); }); @@ -420,7 +420,7 @@ describe('Destination', () => { const event = createEvent(); const policy = { - event: { + name: { value: 'new name', }, 'data.string': { value: 'bar' }, @@ -447,7 +447,7 @@ describe('Destination', () => { expect(mockPush).toHaveBeenCalledWith({ ...event, - event: 'new name', + name: 'new name', data: expect.objectContaining({ string: 'bar', number: undefined, // Redacted due to missing consent @@ -476,7 +476,7 @@ describe('Destination', () => { expect(mockPushWithEnvironment).toHaveBeenCalledWith( expect.objectContaining({ - event: mockEvent.event, + name: mockEvent.name, }), expect.objectContaining({ env: expect.objectContaining({ diff --git a/packages/server/destinations/gcp/src/bigquery/__tests__/index.test.ts b/packages/server/destinations/gcp/src/bigquery/__tests__/index.test.ts index 84502a60e..9ed25df0c 100644 --- a/packages/server/destinations/gcp/src/bigquery/__tests__/index.test.ts +++ b/packages/server/destinations/gcp/src/bigquery/__tests__/index.test.ts @@ -77,7 +77,7 @@ describe('Server Destination BigQuery', () => { expect(mockFn).toHaveBeenCalledWith('insert', [ { timestamp: expect.any(Date), - event: 'entity action', + name: 'entity action', id: event.id, entity: 'entity', action: 'action', diff --git a/packages/server/destinations/meta/src/push.ts b/packages/server/destinations/meta/src/push.ts index 951ada14e..3589214d2 100644 --- a/packages/server/destinations/meta/src/push.ts +++ b/packages/server/destinations/meta/src/push.ts @@ -51,7 +51,7 @@ export const push: PushFn = async function ( delete userData.fbclid; } const serverEvent: ServerEventParameters = { - event_name: event.event, + event_name: event.name, event_id: event.id, event_time: Math.round((event.timestamp || Date.now()) / 1000), action_source, diff --git a/packages/web/core/src/__tests__/sessionStart.test.ts b/packages/web/core/src/__tests__/sessionStart.test.ts index 590e8b08e..3a4d9b45f 100644 --- a/packages/web/core/src/__tests__/sessionStart.test.ts +++ b/packages/web/core/src/__tests__/sessionStart.test.ts @@ -142,7 +142,7 @@ describe('sessionStart', () => { expect.any(Object), ); expect(mockElb).toHaveBeenNthCalledWith(2, { - event: 'session start', + name: 'session start', data: expect.any(Object), }); }); @@ -166,7 +166,7 @@ describe('sessionStart', () => { test('Callback default elb calls', () => { const session = sessionStart({ data: { isNew: true, isStart: true } }); expect(mockElb).toHaveBeenCalledWith({ - event: 'session start', + name: 'session start', data: session, }); }); diff --git a/packages/web/core/src/session/sessionStart.ts b/packages/web/core/src/session/sessionStart.ts index 2facf2f3b..80c010f68 100644 --- a/packages/web/core/src/session/sessionStart.ts +++ b/packages/web/core/src/session/sessionStart.ts @@ -105,7 +105,7 @@ const defaultCb: SessionCallback = ( if (session.isStart) { // Convert session start to an event object elb({ - event: 'session start', + name: 'session start', data: session, }); } diff --git a/packages/web/destinations/gtag/src/__tests__/ga4.test.ts b/packages/web/destinations/gtag/src/__tests__/ga4.test.ts index d3efca6f8..809a43230 100644 --- a/packages/web/destinations/gtag/src/__tests__/ga4.test.ts +++ b/packages/web/destinations/gtag/src/__tests__/ga4.test.ts @@ -83,7 +83,7 @@ describe('GA4 Implementation', () => { describe('pushGA4Event', () => { const mockEvent = { - event: 'page view', + name: 'page view', data: {}, timestamp: 1234567890, id: 'test-id', diff --git a/packages/web/destinations/gtag/src/__tests__/gtm.test.ts b/packages/web/destinations/gtag/src/__tests__/gtm.test.ts index 27a70e7fe..5727638b3 100644 --- a/packages/web/destinations/gtag/src/__tests__/gtm.test.ts +++ b/packages/web/destinations/gtag/src/__tests__/gtm.test.ts @@ -72,7 +72,7 @@ describe('GTM Implementation', () => { describe('pushGTMEvent', () => { const mockEvent = { - event: 'product view', + name: 'product view', entity: 'product', action: 'view', data: { id: 'product-1', name: 'Test Product' }, @@ -108,6 +108,7 @@ describe('GTM Implementation', () => { expect(mockDataLayer).toHaveLength(1); expect(mockDataLayer[0]).toEqual({ event: 'product view', + name: 'product view', entity: 'product', action: 'view', data: { id: 'product-1', name: 'Test Product' }, diff --git a/packages/web/destinations/gtag/src/ga4/push.ts b/packages/web/destinations/gtag/src/ga4/push.ts index ff6167b63..6579beaf3 100644 --- a/packages/web/destinations/gtag/src/ga4/push.ts +++ b/packages/web/destinations/gtag/src/ga4/push.ts @@ -31,7 +31,7 @@ export function pushGA4Event( }; // Event name (snake_case default) - let eventName = event.event; // Assume custom mapped name + let eventName = event.name; // Assume custom mapped name if (settings.snakeCase !== false) { // Use snake case if not disabled eventName = normalizeEventName(eventName); diff --git a/packages/web/destinations/gtag/src/gtm/push.ts b/packages/web/destinations/gtag/src/gtm/push.ts index 8822e94dc..adaf62794 100644 --- a/packages/web/destinations/gtag/src/gtm/push.ts +++ b/packages/web/destinations/gtag/src/gtm/push.ts @@ -12,7 +12,7 @@ export function pushGTMEvent( env?: DestinationWeb.Environment, ): void { const { window } = getEnvironment(env); - const obj = { event: event.event }; // Use the name mapping by default + const obj = { event: event.name }; // Use the name mapping by default (window.dataLayer as unknown[]).push({ ...obj, diff --git a/packages/web/destinations/meta/src/index.test.ts b/packages/web/destinations/meta/src/index.test.ts index 5b9fb663b..4caf42235 100644 --- a/packages/web/destinations/meta/src/index.test.ts +++ b/packages/web/destinations/meta/src/index.test.ts @@ -102,7 +102,7 @@ describe('Destination Meta Pixel', () => { await elb(event); expect(mockFn).toHaveBeenCalledWith( 'track', - event.event, + event.name, {}, { eventID: event.id }, ); diff --git a/packages/web/destinations/meta/src/index.ts b/packages/web/destinations/meta/src/index.ts index 9fb400a8f..24fea659c 100644 --- a/packages/web/destinations/meta/src/index.ts +++ b/packages/web/destinations/meta/src/index.ts @@ -40,12 +40,12 @@ export const destinationMeta: Destination = { const fbq = window.fbq as facebook.Pixel.Event; // page view - if (event.event === 'page view' && !mapping.settings) { + if (event.name === 'page view' && !mapping.settings) { // Define a custom mapping - event.event = 'PageView'; + event.name = 'PageView'; } - const eventName = track || trackCustom || event.event; + const eventName = track || trackCustom || event.name; fbq( trackCustom ? 'trackCustom' : 'track', diff --git a/packages/web/destinations/piwikpro/src/index.ts b/packages/web/destinations/piwikpro/src/index.ts index 532e35127..38de4e0b4 100644 --- a/packages/web/destinations/piwikpro/src/index.ts +++ b/packages/web/destinations/piwikpro/src/index.ts @@ -49,7 +49,7 @@ export const destinationPiwikPro: Destination = { const paq = (window as Window)._paq!.push; // Send pageviews if not disabled - if (event.event === 'page view' && !mapping.settings) { + if (event.name === 'page view' && !mapping.settings) { paq(['trackPageView', await getMappingValue(event, 'data.title')]); return; } @@ -58,7 +58,7 @@ export const destinationPiwikPro: Destination = { const parameters = isArray(data) ? data : [data]; - paq([event.event, ...parameters]); + paq([event.name, ...parameters]); if (eventMapping.goalId) { const goalValue = eventMapping.goalValue diff --git a/packages/web/destinations/plausible/src/index.ts b/packages/web/destinations/plausible/src/index.ts index 14b35fee5..4055e0368 100644 --- a/packages/web/destinations/plausible/src/index.ts +++ b/packages/web/destinations/plausible/src/index.ts @@ -35,7 +35,7 @@ export const destinationPlausible: Destination = { const { window } = getEnvironment(env); const plausible = (window as Window).plausible!; - plausible(`${event.event}`, params); + plausible(`${event.name}`, params); }, }; diff --git a/packages/web/sources/browser/src/__tests__/edgeCases.test.ts b/packages/web/sources/browser/src/__tests__/edgeCases.test.ts index 7f15ae98d..a1e7b2c4d 100644 --- a/packages/web/sources/browser/src/__tests__/edgeCases.test.ts +++ b/packages/web/sources/browser/src/__tests__/edgeCases.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable jest/no-disabled-tests */ import type { WalkerOS, Collector } from '@walkeros/core'; import { createCollector } from '@walkeros/collector'; import { createBrowserSource } from './test-utils'; diff --git a/packages/web/sources/browser/src/__tests__/elbLayer.test.ts b/packages/web/sources/browser/src/__tests__/elbLayer.test.ts index dd5119eb9..04ca3553b 100644 --- a/packages/web/sources/browser/src/__tests__/elbLayer.test.ts +++ b/packages/web/sources/browser/src/__tests__/elbLayer.test.ts @@ -134,11 +134,11 @@ describe('Elb Layer', () => { // Then regular events expect(mockPush).toHaveBeenNthCalledWith( 3, - expect.objectContaining({ event: 'product' }), + expect.objectContaining({ name: 'product' }), ); expect(mockPush).toHaveBeenNthCalledWith( 4, - expect.objectContaining({ event: 'page' }), + expect.objectContaining({ name: 'page' }), ); }); @@ -157,7 +157,7 @@ describe('Elb Layer', () => { expect(mockPush).toHaveBeenCalledTimes(1); expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'test_event', + name: 'test_event', data: { key: 'value' }, context: { context: 'test' }, trigger: 'load', @@ -167,7 +167,7 @@ describe('Elb Layer', () => { test('handles object commands', () => { const eventObject: WalkerOS.DeepPartialEvent = { - event: 'custom_event', + name: 'custom_event', data: { test: 'data' }, context: { page: ['home', 0] as [string, number] }, }; @@ -241,7 +241,7 @@ describe('Elb Layer', () => { }); expect(mockPush).toHaveBeenNthCalledWith( 2, - expect.objectContaining({ event: 'page' }), + expect.objectContaining({ name: 'page' }), ); expect(mockPush).toHaveBeenNthCalledWith( 3, @@ -268,7 +268,7 @@ describe('Elb Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'entity_name', + name: 'entity_name', data: { prop: 'value' }, context: { ctx: 'context' }, trigger: 'trigger_type', @@ -337,7 +337,7 @@ describe('Elb Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'test_event', + name: 'test_event', data: { key: 'value' }, trigger: 'load', }), @@ -355,7 +355,7 @@ describe('Elb Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'immediate_event', + name: 'immediate_event', data: { test: true }, }), ); @@ -380,7 +380,7 @@ describe('Elb Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'product', + name: 'product', data: expect.objectContaining({ id: 123, // Values are cast by castValue utility name: 'Test Product', @@ -401,7 +401,7 @@ describe('Elb Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'page', + name: 'page', data: expect.objectContaining({ id: '/test-page', }), @@ -444,7 +444,7 @@ describe('Elb Layer', () => { expect(mockPush).toHaveBeenCalledTimes(1); expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'page view', + name: 'page view', data: expect.objectContaining({ id: '/walker-run-test', }), diff --git a/packages/web/sources/browser/src/__tests__/integration.test.ts b/packages/web/sources/browser/src/__tests__/integration.test.ts index dfbf1bbf3..372e876cd 100644 --- a/packages/web/sources/browser/src/__tests__/integration.test.ts +++ b/packages/web/sources/browser/src/__tests__/integration.test.ts @@ -53,7 +53,7 @@ describe('Browser Source Integration Tests', () => { // Should have processed the event with source information expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'product view', + name: 'product view', data: expect.objectContaining({ id: 123, name: 'Test Product', @@ -101,7 +101,7 @@ describe('Browser Source Integration Tests', () => { // Should have processed the pageview event expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'page view', + name: 'page view', trigger: 'load', data: expect.objectContaining({ id: '/test-page', @@ -129,7 +129,7 @@ describe('Browser Source Integration Tests', () => { // Should have processed the click expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'cta press', + name: 'cta press', entity: 'cta', action: 'press', trigger: 'click', @@ -161,7 +161,7 @@ describe('Browser Source Integration Tests', () => { expect(mockPush).toHaveBeenNthCalledWith( 2, expect.objectContaining({ - event: 'page view', + name: 'page view', data: expect.objectContaining({ id: '/test-page', title: 'Home' }), context: { url: '/' }, trigger: 'load', @@ -170,7 +170,7 @@ describe('Browser Source Integration Tests', () => { expect(mockPush).toHaveBeenNthCalledWith( 3, expect.objectContaining({ - event: 'product click', + name: 'product click', data: { id: '123' }, context: { position: 1 }, trigger: 'click', diff --git a/packages/web/sources/browser/src/__tests__/translation.test.ts b/packages/web/sources/browser/src/__tests__/translation.test.ts index d91f8cf32..7e427910a 100644 --- a/packages/web/sources/browser/src/__tests__/translation.test.ts +++ b/packages/web/sources/browser/src/__tests__/translation.test.ts @@ -66,7 +66,7 @@ describe('Translation Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'test event', + name: 'test event', data: { id: 123 }, context: { page: ['test', 0] }, source: { @@ -90,7 +90,7 @@ describe('Translation Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: '123', + name: '123', data: { value: 'test' }, context: { context: ['info', 0] }, source: { @@ -114,7 +114,7 @@ describe('Translation Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'test event', + name: 'test event', data: {}, // Should be empty object, not { value: 'primitive string data' } context: { page: ['test', 0] }, source: { @@ -143,7 +143,7 @@ describe('Translation Layer', () => { test('does not add source information to object events', async () => { // Test object event - should pass through as-is const eventObject = { - event: 'custom event', + name: 'custom event', data: { test: true }, source: { type: 'custom', id: 'custom-id', previous_id: '' }, }; @@ -224,7 +224,7 @@ describe('Translation Layer', () => { // Should have processed both events with source info expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'product', + name: 'product', data: { id: '123' }, context: { position: 1 }, trigger: 'click', @@ -238,7 +238,7 @@ describe('Translation Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'page', + name: 'page', data: { title: 'Test' }, trigger: 'load', source: { @@ -265,7 +265,7 @@ describe('Translation Layer', () => { expect(mockPush).toHaveBeenCalledTimes(1); expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: 'product view', + name: 'product view', data: { id: 123 }, trigger: 'load', source: { @@ -389,7 +389,7 @@ describe('Translation Layer', () => { expect(mockPush).toHaveBeenCalledWith( expect.objectContaining({ - event: '', + name: '', data: { test: true }, source: { type: 'browser', diff --git a/packages/web/sources/browser/src/__tests__/trigger.test.ts b/packages/web/sources/browser/src/__tests__/trigger.test.ts index cf5fc1784..4a0f791c8 100644 --- a/packages/web/sources/browser/src/__tests__/trigger.test.ts +++ b/packages/web/sources/browser/src/__tests__/trigger.test.ts @@ -132,7 +132,7 @@ describe('Trigger System', () => { // Should NOT trigger page view (pageview now only fires on walker run) expect(mockCollector.push).not.toHaveBeenCalledWith( expect.objectContaining({ - event: 'page view', + name: 'page view', }), ); @@ -216,7 +216,7 @@ describe('Trigger System', () => { expect(mockCollector.push).toHaveBeenCalledWith( expect.objectContaining({ - event: 'entity action', + name: 'entity action', entity: 'entity', action: 'action', trigger: Triggers.Click, @@ -449,7 +449,7 @@ describe('Trigger System', () => { // Should have called push with entity load event expect(mockCollector.push).toHaveBeenCalledWith( expect.objectContaining({ - event: 'entity load', + name: 'entity load', trigger: 'load', }), ); diff --git a/packages/web/sources/browser/src/translation.ts b/packages/web/sources/browser/src/translation.ts index bdd17b311..cd65ef760 100644 --- a/packages/web/sources/browser/src/translation.ts +++ b/packages/web/sources/browser/src/translation.ts @@ -40,7 +40,7 @@ export function translateToCoreCollector( // Extract entity name from event string const [entity] = String( - isObject(eventOrCommand) ? eventOrCommand.event : eventOrCommand, + isObject(eventOrCommand) ? eventOrCommand.name : eventOrCommand, ).split(' '); // Get data and context either from elements or parameters @@ -82,7 +82,7 @@ export function translateToCoreCollector( // Build unified event from various elb usage patterns const event: WalkerOS.DeepPartialEvent = { - event: String(eventOrCommand || ''), + name: String(eventOrCommand || ''), data: eventData, context: eventContext, nested, diff --git a/packages/web/sources/browser/src/trigger.ts b/packages/web/sources/browser/src/trigger.ts index 3cbee637e..16bb7e069 100644 --- a/packages/web/sources/browser/src/trigger.ts +++ b/packages/web/sources/browser/src/trigger.ts @@ -157,7 +157,7 @@ export async function handleTrigger( return Promise.all( events.map((event: Walker.Event) => translateToCoreCollector(context, { - event: `${event.entity} ${event.action}`, + name: `${event.entity} ${event.action}`, ...event, trigger, }), diff --git a/packages/web/sources/dataLayer/src/__tests__/consent-simple.test.ts b/packages/web/sources/dataLayer/src/__tests__/consent-simple.test.ts index 360b611c8..b71191fa9 100644 --- a/packages/web/sources/dataLayer/src/__tests__/consent-simple.test.ts +++ b/packages/web/sources/dataLayer/src/__tests__/consent-simple.test.ts @@ -38,9 +38,8 @@ describe('DataLayer Source - Consent Mode (Simple)', () => { expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer consent update', + name: 'dataLayer consent update', data: { - event: 'consent update', ad_storage: 'denied', analytics_storage: 'granted', }, @@ -65,9 +64,8 @@ describe('DataLayer Source - Consent Mode (Simple)', () => { expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer consent default', + name: 'dataLayer consent default', data: { - event: 'consent default', ad_storage: 'denied', analytics_storage: 'denied', }, @@ -93,9 +91,8 @@ describe('DataLayer Source - Consent Mode (Simple)', () => { // Should have processed the existing event expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer consent update', + name: 'dataLayer consent update', data: { - event: 'consent update', ad_storage: 'granted', analytics_storage: 'denied', }, @@ -133,15 +130,12 @@ describe('DataLayer Source - Consent Mode (Simple)', () => { expect(collectedEvents).toHaveLength(3); expect(collectedEvents[0].data).toMatchObject({ - event: 'consent update', ad_storage: 'granted', }); expect(collectedEvents[1].data).toMatchObject({ - event: 'consent update', analytics_storage: 'granted', }); expect(collectedEvents[2].data).toMatchObject({ - event: 'consent update', ad_storage: 'denied', }); }); @@ -155,6 +149,6 @@ describe('DataLayer Source - Consent Mode (Simple)', () => { getDataLayer().push(['consent', 'update', { ad_storage: 'granted' }]); expect(collectedEvents).toHaveLength(1); - expect(collectedEvents[0].event).toBe('gtag consent update'); + expect(collectedEvents[0].name).toBe('gtag consent update'); }); }); diff --git a/packages/web/sources/dataLayer/src/__tests__/enhanced.test.ts b/packages/web/sources/dataLayer/src/__tests__/enhanced.test.ts index d5134c217..296017c0b 100644 --- a/packages/web/sources/dataLayer/src/__tests__/enhanced.test.ts +++ b/packages/web/sources/dataLayer/src/__tests__/enhanced.test.ts @@ -42,9 +42,8 @@ describe('DataLayer Source - Enhanced with gtag support', () => { expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer consent update', + name: 'dataLayer consent update', data: { - event: 'consent update', ad_storage: 'granted', analytics_storage: 'denied', }, @@ -69,9 +68,8 @@ describe('DataLayer Source - Enhanced with gtag support', () => { expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer purchase', + name: 'dataLayer purchase', data: { - event: 'purchase', transaction_id: '123', value: 25.99, }, @@ -95,9 +93,8 @@ describe('DataLayer Source - Enhanced with gtag support', () => { expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer config GA_MEASUREMENT_ID', + name: 'dataLayer config GA_MEASUREMENT_ID', data: { - event: 'config GA_MEASUREMENT_ID', send_page_view: false, }, }); @@ -114,9 +111,8 @@ describe('DataLayer Source - Enhanced with gtag support', () => { expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer set currency', + name: 'dataLayer set currency', data: { - event: 'set currency', value: 'EUR', }, }); @@ -133,9 +129,8 @@ describe('DataLayer Source - Enhanced with gtag support', () => { expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer set custom', + name: 'dataLayer set custom', data: { - event: 'set custom', currency: 'EUR', country: 'DE', }, @@ -153,9 +148,8 @@ describe('DataLayer Source - Enhanced with gtag support', () => { expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer custom_event', + name: 'dataLayer custom_event', data: { - event: 'custom_event', user_id: 'user123', }, }); @@ -205,9 +199,8 @@ describe('DataLayer Source - Enhanced with gtag support', () => { expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer consent update', + name: 'dataLayer consent update', data: { - event: 'consent update', ad_storage: 'granted', }, }); diff --git a/packages/web/sources/dataLayer/src/__tests__/filter.test.ts b/packages/web/sources/dataLayer/src/__tests__/filter.test.ts index d1786f12b..2a51763b6 100644 --- a/packages/web/sources/dataLayer/src/__tests__/filter.test.ts +++ b/packages/web/sources/dataLayer/src/__tests__/filter.test.ts @@ -44,8 +44,8 @@ describe('DataLayer Source - Filtering', () => { expect(mockFilter).toHaveBeenCalledTimes(3); expect(collectedEvents).toHaveLength(2); - expect(collectedEvents[0].event).toBe('dataLayer allowed_event'); - expect(collectedEvents[1].event).toBe('dataLayer consent update'); + expect(collectedEvents[0].name).toBe('dataLayer allowed_event'); + expect(collectedEvents[1].name).toBe('dataLayer consent update'); }); test('filter with consent events only', () => { @@ -72,8 +72,8 @@ describe('DataLayer Source - Filtering', () => { ]); expect(collectedEvents).toHaveLength(2); - expect(collectedEvents[0].event).toBe('dataLayer consent update'); - expect(collectedEvents[1].event).toBe('dataLayer consent default'); + expect(collectedEvents[0].name).toBe('dataLayer consent update'); + expect(collectedEvents[1].name).toBe('dataLayer consent default'); }); test('filter processes existing events', () => { @@ -102,8 +102,8 @@ describe('DataLayer Source - Filtering', () => { // Should have processed 2 events (filtered out 'existing_bad') expect(collectedEvents).toHaveLength(2); - expect(collectedEvents[0].event).toBe('dataLayer existing_good'); - expect(collectedEvents[1].event).toBe('dataLayer consent update'); + expect(collectedEvents[0].name).toBe('dataLayer existing_good'); + expect(collectedEvents[1].name).toBe('dataLayer consent update'); }); test('handles filter errors gracefully', () => { @@ -121,7 +121,7 @@ describe('DataLayer Source - Filtering', () => { getDataLayer().push({ event: 'test_event', data: 'test' }); expect(collectedEvents).toHaveLength(1); - expect(collectedEvents[0].event).toBe('dataLayer test_event'); + expect(collectedEvents[0].name).toBe('dataLayer test_event'); }); test('filter return value determines processing', () => { @@ -145,6 +145,6 @@ describe('DataLayer Source - Filtering', () => { getDataLayer().push({ event: 'event2', data: 'test' }); expect(collectedEvents).toHaveLength(1); - expect(collectedEvents[0].event).toBe('dataLayer event1'); + expect(collectedEvents[0].name).toBe('dataLayer event1'); }); }); diff --git a/packages/web/sources/dataLayer/src/__tests__/integration.test.ts b/packages/web/sources/dataLayer/src/__tests__/integration.test.ts index bc5a74051..0ea0e3d44 100644 --- a/packages/web/sources/dataLayer/src/__tests__/integration.test.ts +++ b/packages/web/sources/dataLayer/src/__tests__/integration.test.ts @@ -71,9 +71,8 @@ describe('DataLayer Source - Integration', () => { // Check all consent events were processed expect(collectedEvents[0]).toMatchObject({ - event: 'gtag consent default', + name: 'gtag consent default', data: { - event: 'consent default', ad_storage: 'denied', analytics_storage: 'denied', ad_user_data: 'denied', @@ -82,17 +81,14 @@ describe('DataLayer Source - Integration', () => { }); expect(collectedEvents[1]).toMatchObject({ - event: 'gtag consent update', + name: 'gtag consent update', data: { - event: 'consent update', analytics_storage: 'granted', }, }); expect(collectedEvents[2]).toMatchObject({ - event: 'gtag consent update', data: { - event: 'consent update', ad_storage: 'granted', ad_user_data: 'granted', }, @@ -123,7 +119,7 @@ describe('DataLayer Source - Integration', () => { expect(collectedEvents).toHaveLength(5); - const eventNames = collectedEvents.map((e) => e.event); + const eventNames = collectedEvents.map((e) => e.name); expect(eventNames).toEqual([ 'dataLayer consent update', 'dataLayer purchase', @@ -150,10 +146,10 @@ describe('DataLayer Source - Integration', () => { expect(collectedEvents).toHaveLength(4); // Check order: existing events first, then new events - expect(collectedEvents[0].event).toBe('dataLayer consent default'); - expect(collectedEvents[1].event).toBe('dataLayer existing_event'); - expect(collectedEvents[2].event).toBe('dataLayer consent update'); - expect(collectedEvents[3].event).toBe('dataLayer new_event'); + expect(collectedEvents[0].name).toBe('dataLayer consent default'); + expect(collectedEvents[1].name).toBe('dataLayer existing_event'); + expect(collectedEvents[2].name).toBe('dataLayer consent update'); + expect(collectedEvents[3].name).toBe('dataLayer new_event'); }); test('error handling and robustness', () => { @@ -191,7 +187,7 @@ describe('DataLayer Source - Integration', () => { // Should have processed 3 good events (bad_filter is invalid gtag format) expect(collectedEvents).toHaveLength(3); - const eventNames = collectedEvents.map((e) => e.event); + const eventNames = collectedEvents.map((e) => e.name); expect(eventNames).toEqual([ 'dataLayer consent update', 'dataLayer after_error', diff --git a/packages/web/sources/dataLayer/src/__tests__/minimal.test.ts b/packages/web/sources/dataLayer/src/__tests__/minimal.test.ts index 093bbb1f7..4574c10ff 100644 --- a/packages/web/sources/dataLayer/src/__tests__/minimal.test.ts +++ b/packages/web/sources/dataLayer/src/__tests__/minimal.test.ts @@ -61,8 +61,8 @@ describe('DataLayer Source - Minimal', () => { // Should have captured the event immediately (synchronous) expect(collectedEvents).toHaveLength(1); expect(collectedEvents[0]).toMatchObject({ - event: 'dataLayer test_event', - data: { event: 'test_event', test: 'data' }, + name: 'dataLayer test_event', + data: { test: 'data' }, source: { type: 'dataLayer' }, }); }); @@ -81,8 +81,8 @@ describe('DataLayer Source - Minimal', () => { // Should have processed both existing events expect(collectedEvents).toHaveLength(2); - expect(collectedEvents[0].event).toBe('dataLayer existing_event_1'); - expect(collectedEvents[1].event).toBe('dataLayer existing_event_2'); + expect(collectedEvents[0].name).toBe('dataLayer existing_event_1'); + expect(collectedEvents[1].name).toBe('dataLayer existing_event_2'); }); test('ignores non-object events', () => { @@ -124,7 +124,7 @@ describe('DataLayer Source - Minimal', () => { getDataLayer().push({ event: 'test_event', data: 'test' }); expect(collectedEvents).toHaveLength(1); - expect(collectedEvents[0].event).toBe('custom test_event'); + expect(collectedEvents[0].name).toBe('custom test_event'); }); test('uses custom dataLayer name', () => { @@ -144,6 +144,6 @@ describe('DataLayer Source - Minimal', () => { }); expect(collectedEvents).toHaveLength(1); - expect(collectedEvents[0].event).toBe('dataLayer test_event'); + expect(collectedEvents[0].name).toBe('dataLayer test_event'); }); }); diff --git a/packages/web/sources/dataLayer/src/interceptor.ts b/packages/web/sources/dataLayer/src/interceptor.ts index 2b27e0df8..e7afcd077 100644 --- a/packages/web/sources/dataLayer/src/interceptor.ts +++ b/packages/web/sources/dataLayer/src/interceptor.ts @@ -110,11 +110,11 @@ function processEvent( } const prefix = settings.prefix || 'dataLayer'; - const eventName = `${prefix} ${transformedEvent.event}`; + const eventName = `${prefix} ${transformedEvent.name}`; // Create WalkerOS event structure const walkerEvent: WalkerOS.Event = { - event: eventName, + name: eventName, data: transformedEvent as WalkerOS.Properties, context: {}, globals: {}, @@ -151,10 +151,11 @@ function processEvent( */ function transformDataLayerEvent( rawEvent: unknown, -): { event: string; [key: string]: unknown } | null { +): { name: string; [key: string]: unknown } | null { // Handle direct object format: { event: 'test', data: 'value' } if (isObject(rawEvent) && isString(rawEvent.event)) { - return rawEvent as { event: string; [key: string]: unknown }; + const { event, ...rest } = rawEvent; + return { name: event, ...rest }; } // Handle gtag argument format: ['consent', 'update', { ad_storage: 'granted' }] @@ -177,7 +178,7 @@ function transformDataLayerEvent( */ function transformGtagArgs( args: unknown[], -): { event: string; [key: string]: unknown } | null { +): { name: string; [key: string]: unknown } | null { const [command, action, params] = args; if (!isString(command)) return null; @@ -234,7 +235,7 @@ function transformGtagArgs( } return { - event: eventName, + name: eventName, ...eventData, }; } diff --git a/website/docs/core/server.mdx b/website/docs/core/server.mdx index b73144bf1..3ac875514 100644 --- a/website/docs/core/server.mdx +++ b/website/docs/core/server.mdx @@ -32,7 +32,7 @@ sends HTTP requests using Node.js built-in modules (`http`/`https`). ```js // Simple POST request const response = await sendServer('https://api.example.com/events', { - event: 'page view', + name: 'page view', data: { url: '/home' }, }); diff --git a/website/docs/destinations/event-mapping.mdx b/website/docs/destinations/event-mapping.mdx index 234d886d2..94ed03861 100644 --- a/website/docs/destinations/event-mapping.mdx +++ b/website/docs/destinations/event-mapping.mdx @@ -54,7 +54,7 @@ Map specific entity-action combinations to custom event names: showMiddle={false} labelInput="Configuration" input={`await getMappingEvent( - { event: 'product view' }, + { name: 'product view' }, { product: { view: { name: 'product_viewed' }, @@ -78,7 +78,7 @@ Use wildcards (`*`) to match multiple entities or actions: showMiddle={false} labelInput="Configuration" input={`await getMappingEvent( - { event: 'product click' }, + { name: 'product click' }, { product: { '*': { name: 'product_interaction' }, @@ -106,7 +106,7 @@ Use conditions to apply different mappings based on event properties: labelInput="Configuration" input={`await getMappingEvent( { - event: 'order complete', + name: 'order complete', data: { value: 100 }, }, { @@ -138,7 +138,7 @@ Skip processing certain events by setting `ignore: true`: showMiddle={false} labelInput="Configuration" input={`await getMappingEvent( - { event: 'test event' }, + { name: 'test event' }, { test: { event: { ignore: true }, @@ -198,7 +198,7 @@ Return static values using the `value` property: showMiddle={false} labelInput="Configuration" input={`await getMappingValue( - { event: 'page view' }, + { name: 'page view' }, { value: 'pageview' } );`} output={`"pageview"`} @@ -333,7 +333,7 @@ const destination = { }), push: (event, { data }) => { // Use mapped data - gtag('event', event.event, data); + gtag('event', event.name, data); }, config: { mapping: { diff --git a/website/docs/getting-started/event-model.mdx b/website/docs/getting-started/event-model.mdx index 2e49620db..ea0115d84 100644 --- a/website/docs/getting-started/event-model.mdx +++ b/website/docs/getting-started/event-model.mdx @@ -46,7 +46,7 @@ static, their content can be defined dynamically with different value types. ```js { - event: 'promotion view', // Name as a combination of entity and action + name: 'promotion view', // Name as a combination of entity and action data: { // Arbitrary properties related to the entity name: 'Setting up tracking easily', diff --git a/website/docs/sources/web/browser/tagging.mdx b/website/docs/sources/web/browser/tagging.mdx index 5d2bf8a8d..9edbccda2 100644 --- a/website/docs/sources/web/browser/tagging.mdx +++ b/website/docs/sources/web/browser/tagging.mdx @@ -63,7 +63,7 @@ Tag a page... ```js { - event: 'promotion view', // Name as a combination of entity and action + name: 'promotion view', // Name as a combination of entity and action data: { // Arbitrary properties related to the entity name: 'Setting up tracking easily', From 116d8a368df655b658bd8ac76aa24cb2ead1ffae Mon Sep 17 00:00:00 2001 From: alexander Date: Sun, 7 Sep 2025 22:15:14 +0200 Subject: [PATCH 13/13] 0.1.0 --- apps/demos/storybook/CHANGELOG.md | 11 +++++++++++ apps/demos/storybook/package.json | 2 +- apps/storybook-addon/CHANGELOG.md | 13 +++++++++++++ apps/storybook-addon/package.json | 2 +- apps/walkerjs/CHANGELOG.md | 15 +++++++++++++++ apps/walkerjs/package.json | 12 ++++++------ packages/collector/CHANGELOG.md | 11 +++++++++++ packages/collector/package.json | 4 ++-- packages/core/CHANGELOG.md | 6 ++++++ packages/core/package.json | 2 +- packages/core/src/types/flow.ts | 13 +++++++++++++ packages/core/src/types/index.ts | 1 + packages/server/core/CHANGELOG.md | 11 +++++++++++ packages/server/core/package.json | 4 ++-- packages/server/destinations/aws/CHANGELOG.md | 11 +++++++++++ packages/server/destinations/aws/package.json | 4 ++-- packages/server/destinations/gcp/CHANGELOG.md | 11 +++++++++++ packages/server/destinations/gcp/package.json | 4 ++-- packages/server/destinations/meta/CHANGELOG.md | 12 ++++++++++++ packages/server/destinations/meta/package.json | 6 +++--- packages/web/core/CHANGELOG.md | 11 +++++++++++ packages/web/core/package.json | 4 ++-- packages/web/destinations/api/CHANGELOG.md | 11 +++++++++++ packages/web/destinations/api/package.json | 4 ++-- packages/web/destinations/gtag/CHANGELOG.md | 11 +++++++++++ packages/web/destinations/gtag/package.json | 4 ++-- packages/web/destinations/meta/CHANGELOG.md | 11 +++++++++++ packages/web/destinations/meta/package.json | 4 ++-- packages/web/destinations/piwikpro/CHANGELOG.md | 12 ++++++++++++ packages/web/destinations/piwikpro/package.json | 6 +++--- packages/web/destinations/plausible/CHANGELOG.md | 11 +++++++++++ packages/web/destinations/plausible/package.json | 4 ++-- packages/web/sources/browser/CHANGELOG.md | 12 ++++++++++++ packages/web/sources/browser/package.json | 6 +++--- packages/web/sources/dataLayer/CHANGELOG.md | 12 ++++++++++++ packages/web/sources/dataLayer/package.json | 6 +++--- website/CHANGELOG.md | 11 +++++++++++ website/package.json | 2 +- 38 files changed, 257 insertions(+), 40 deletions(-) create mode 100644 packages/core/src/types/flow.ts diff --git a/apps/demos/storybook/CHANGELOG.md b/apps/demos/storybook/CHANGELOG.md index e309f893a..f59a918ae 100644 --- a/apps/demos/storybook/CHANGELOG.md +++ b/apps/demos/storybook/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/storybook-demo +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/web-source-browser@0.1.0 + ## 0.0.1 ### Patch Changes diff --git a/apps/demos/storybook/package.json b/apps/demos/storybook/package.json index 286ee46b8..335a7886e 100644 --- a/apps/demos/storybook/package.json +++ b/apps/demos/storybook/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/storybook-demo", "private": true, - "version": "0.0.1", + "version": "0.1.0", "type": "module", "scripts": { "dev": "vite", diff --git a/apps/storybook-addon/CHANGELOG.md b/apps/storybook-addon/CHANGELOG.md index e32dfefa5..a7dadd2e7 100644 --- a/apps/storybook-addon/CHANGELOG.md +++ b/apps/storybook-addon/CHANGELOG.md @@ -1,5 +1,18 @@ # @walkeros/storybook-addon +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/web-source-browser@0.1.0 + - @walkeros/web-core@0.1.0 + - @walkeros/core@0.1.0 + ## 0.0.2 ### Patch Changes diff --git a/apps/storybook-addon/package.json b/apps/storybook-addon/package.json index 9d335d1b2..3216fa944 100644 --- a/apps/storybook-addon/package.json +++ b/apps/storybook-addon/package.json @@ -1,6 +1,6 @@ { "name": "@walkeros/storybook-addon", - "version": "0.0.2", + "version": "0.1.0", "description": "Integrate walkerOS tagging support for data collection", "keywords": [ "tracking", diff --git a/apps/walkerjs/CHANGELOG.md b/apps/walkerjs/CHANGELOG.md index 92102d3e4..5687bcd48 100644 --- a/apps/walkerjs/CHANGELOG.md +++ b/apps/walkerjs/CHANGELOG.md @@ -1,5 +1,20 @@ # @walkeros/walker.js +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/web-source-datalayer@0.1.0 + - @walkeros/web-source-browser@0.1.0 + - @walkeros/collector@0.1.0 + - @walkeros/web-core@0.1.0 + - @walkeros/core@0.1.0 + ## 0.0.10 ### Patch Changes diff --git a/apps/walkerjs/package.json b/apps/walkerjs/package.json index 8297ca9ca..f3a314ff5 100644 --- a/apps/walkerjs/package.json +++ b/apps/walkerjs/package.json @@ -1,6 +1,6 @@ { "name": "@walkeros/walker.js", - "version": "0.0.10", + "version": "0.1.0", "description": "Ready-to-use walkerOS bundle with browser source, collector, and dataLayer support", "license": "MIT", "main": "./dist/index.js", @@ -34,11 +34,11 @@ "preview": "npm run build && npx serve -l 3333 examples" }, "dependencies": { - "@walkeros/core": "0.0.8", - "@walkeros/collector": "0.0.8", - "@walkeros/web-core": "0.0.8", - "@walkeros/web-source-browser": "0.0.10", - "@walkeros/web-source-datalayer": "0.0.8" + "@walkeros/core": "0.1.0", + "@walkeros/collector": "0.1.0", + "@walkeros/web-core": "0.1.0", + "@walkeros/web-source-browser": "0.1.0", + "@walkeros/web-source-datalayer": "0.1.0" }, "devDependencies": { "@swc/jest": "^0.2.36", diff --git a/packages/collector/CHANGELOG.md b/packages/collector/CHANGELOG.md index f65bc58bb..a275e8007 100644 --- a/packages/collector/CHANGELOG.md +++ b/packages/collector/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/collector +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/collector/package.json b/packages/collector/package.json index 15c6158e1..7e43a05fa 100644 --- a/packages/collector/package.json +++ b/packages/collector/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/collector", "description": "Unified platform-agnostic collector for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -18,7 +18,7 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/core": "0.0.8" + "@walkeros/core": "0.1.0" }, "devDependencies": {}, "repository": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 1db854f26..a52710d47 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,11 @@ # @walkeros/core +## 0.1.0 + +### Minor Changes + +- fixes + ## 0.0.8 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index 1d1fe353b..8ef84f3e7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/core", "description": "Core types and platform-agnostic utilities for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", diff --git a/packages/core/src/types/flow.ts b/packages/core/src/types/flow.ts new file mode 100644 index 000000000..b74da3cf4 --- /dev/null +++ b/packages/core/src/types/flow.ts @@ -0,0 +1,13 @@ +import type { Collector } from '.'; + +/** + * Flow configuration interface for dynamic walkerOS setup + * Used by bundlers and other tools to configure walkerOS dynamically + */ +export interface Config { + /** Collector configuration - uses existing Collector.Config from core */ + collector: Collector.Config; + + /** NPM packages required for this configuration */ + packages: Record; +} diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts index 622c63d5c..711118534 100644 --- a/packages/core/src/types/index.ts +++ b/packages/core/src/types/index.ts @@ -2,6 +2,7 @@ export * as Collector from './collector'; export * as Data from './data'; export * as Destination from './destination'; export * as Elb from './elb'; +export * as Flow from './flow'; export * as Handler from './handler'; export * as Hooks from './hooks'; export * as Mapping from './mapping'; diff --git a/packages/server/core/CHANGELOG.md b/packages/server/core/CHANGELOG.md index c39416a15..1d75d0c53 100644 --- a/packages/server/core/CHANGELOG.md +++ b/packages/server/core/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/server-core +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/server/core/package.json b/packages/server/core/package.json index dddf15e72..5a2c34912 100644 --- a/packages/server/core/package.json +++ b/packages/server/core/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/server-core", "description": "Server-specific utilities for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -25,7 +25,7 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/core": "0.0.8" + "@walkeros/core": "0.1.0" }, "repository": { "url": "git+https://github.com/elbwalker/walkerOS.git", diff --git a/packages/server/destinations/aws/CHANGELOG.md b/packages/server/destinations/aws/CHANGELOG.md index 5447b0969..9cbc6b3e0 100644 --- a/packages/server/destinations/aws/CHANGELOG.md +++ b/packages/server/destinations/aws/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/server-destination-aws +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/server-core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/server/destinations/aws/package.json b/packages/server/destinations/aws/package.json index e0c707b4a..b01044dd8 100644 --- a/packages/server/destinations/aws/package.json +++ b/packages/server/destinations/aws/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/server-destination-aws", "description": "AWS server destination for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -31,7 +31,7 @@ }, "dependencies": { "@aws-sdk/client-firehose": "^3.606.0", - "@walkeros/server-core": "0.0.8" + "@walkeros/server-core": "0.1.0" }, "devDependencies": {}, "repository": { diff --git a/packages/server/destinations/gcp/CHANGELOG.md b/packages/server/destinations/gcp/CHANGELOG.md index b73dd59cd..fe994d386 100644 --- a/packages/server/destinations/gcp/CHANGELOG.md +++ b/packages/server/destinations/gcp/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/server-destination-gcp +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/server-core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/server/destinations/gcp/package.json b/packages/server/destinations/gcp/package.json index 66eaef746..b2ad09537 100644 --- a/packages/server/destinations/gcp/package.json +++ b/packages/server/destinations/gcp/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/server-destination-gcp", "description": "Google Cloud Platform server destination for walkerOS (BigQuery)", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -19,7 +19,7 @@ }, "dependencies": { "@google-cloud/bigquery": "^7.8.0", - "@walkeros/server-core": "0.0.8" + "@walkeros/server-core": "0.1.0" }, "devDependencies": {}, "repository": { diff --git a/packages/server/destinations/meta/CHANGELOG.md b/packages/server/destinations/meta/CHANGELOG.md index 65cc713a2..1d2282d0e 100644 --- a/packages/server/destinations/meta/CHANGELOG.md +++ b/packages/server/destinations/meta/CHANGELOG.md @@ -1,5 +1,17 @@ # @walkeros/server-destination-meta +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/server-core@0.1.0 + - @walkeros/core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/server/destinations/meta/package.json b/packages/server/destinations/meta/package.json index d68d87ffb..816d39e21 100644 --- a/packages/server/destinations/meta/package.json +++ b/packages/server/destinations/meta/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/server-destination-meta", "description": "Meta server destination for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -30,8 +30,8 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/core": "0.0.8", - "@walkeros/server-core": "0.0.8" + "@walkeros/core": "0.1.0", + "@walkeros/server-core": "0.1.0" }, "devDependencies": {}, "repository": { diff --git a/packages/web/core/CHANGELOG.md b/packages/web/core/CHANGELOG.md index cd87ca624..cb7b20f54 100644 --- a/packages/web/core/CHANGELOG.md +++ b/packages/web/core/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/web-core +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/web/core/package.json b/packages/web/core/package.json index 5e05f5c75..958994dd1 100644 --- a/packages/web/core/package.json +++ b/packages/web/core/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/web-core", "description": "Web-specific utilities for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -25,7 +25,7 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/core": "0.0.8" + "@walkeros/core": "0.1.0" }, "repository": { "url": "git+https://github.com/elbwalker/walkerOS.git", diff --git a/packages/web/destinations/api/CHANGELOG.md b/packages/web/destinations/api/CHANGELOG.md index 716560787..fbd288fc3 100644 --- a/packages/web/destinations/api/CHANGELOG.md +++ b/packages/web/destinations/api/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/web-destination-api +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/web-core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/web/destinations/api/package.json b/packages/web/destinations/api/package.json index 2cfcd928b..e506d5f31 100644 --- a/packages/web/destinations/api/package.json +++ b/packages/web/destinations/api/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/web-destination-api", "description": "Web API destination for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -30,7 +30,7 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/web-core": "0.0.8" + "@walkeros/web-core": "0.1.0" }, "devDependencies": {}, "repository": { diff --git a/packages/web/destinations/gtag/CHANGELOG.md b/packages/web/destinations/gtag/CHANGELOG.md index 967dbd47f..bbf08ddd6 100644 --- a/packages/web/destinations/gtag/CHANGELOG.md +++ b/packages/web/destinations/gtag/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/web-destination-gtag +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/web-core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/web/destinations/gtag/package.json b/packages/web/destinations/gtag/package.json index add413adc..e2aa2fa42 100644 --- a/packages/web/destinations/gtag/package.json +++ b/packages/web/destinations/gtag/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/web-destination-gtag", "description": "Unified Google destination for walkerOS (GA4, Ads, GTM)", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -25,7 +25,7 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/web-core": "0.0.8" + "@walkeros/web-core": "0.1.0" }, "repository": { "url": "git+https://github.com/elbwalker/walkerOS.git", diff --git a/packages/web/destinations/meta/CHANGELOG.md b/packages/web/destinations/meta/CHANGELOG.md index 4d5eac3e7..45baad711 100644 --- a/packages/web/destinations/meta/CHANGELOG.md +++ b/packages/web/destinations/meta/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/web-destination-meta +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/web-core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/web/destinations/meta/package.json b/packages/web/destinations/meta/package.json index edfd61a9e..12508396c 100644 --- a/packages/web/destinations/meta/package.json +++ b/packages/web/destinations/meta/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/web-destination-meta", "description": "Meta pixel web destination for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -30,7 +30,7 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/web-core": "0.0.8" + "@walkeros/web-core": "0.1.0" }, "devDependencies": { "@types/facebook-pixel": "^0.0.31" diff --git a/packages/web/destinations/piwikpro/CHANGELOG.md b/packages/web/destinations/piwikpro/CHANGELOG.md index 10f76662e..4e7371336 100644 --- a/packages/web/destinations/piwikpro/CHANGELOG.md +++ b/packages/web/destinations/piwikpro/CHANGELOG.md @@ -1,5 +1,17 @@ # @walkeros/web-destination-piwikpro +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/web-core@0.1.0 + - @walkeros/core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/web/destinations/piwikpro/package.json b/packages/web/destinations/piwikpro/package.json index ce819dc82..1681be6e8 100644 --- a/packages/web/destinations/piwikpro/package.json +++ b/packages/web/destinations/piwikpro/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/web-destination-piwikpro", "description": "Piwik PRO destination for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -30,8 +30,8 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/core": "0.0.8", - "@walkeros/web-core": "0.0.8" + "@walkeros/core": "0.1.0", + "@walkeros/web-core": "0.1.0" }, "devDependencies": {}, "repository": { diff --git a/packages/web/destinations/plausible/CHANGELOG.md b/packages/web/destinations/plausible/CHANGELOG.md index c27ca1a3d..256daf1e0 100644 --- a/packages/web/destinations/plausible/CHANGELOG.md +++ b/packages/web/destinations/plausible/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/web-destination-plausible +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/web-core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/web/destinations/plausible/package.json b/packages/web/destinations/plausible/package.json index bf2fd51ef..f894ee246 100644 --- a/packages/web/destinations/plausible/package.json +++ b/packages/web/destinations/plausible/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/web-destination-plausible", "description": "Plausible web destination for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -30,7 +30,7 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/web-core": "0.0.8" + "@walkeros/web-core": "0.1.0" }, "devDependencies": {}, "repository": { diff --git a/packages/web/sources/browser/CHANGELOG.md b/packages/web/sources/browser/CHANGELOG.md index 1d72b3a7b..97115fb5a 100644 --- a/packages/web/sources/browser/CHANGELOG.md +++ b/packages/web/sources/browser/CHANGELOG.md @@ -1,5 +1,17 @@ # @walkeros/web-source-browser +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/collector@0.1.0 + - @walkeros/web-core@0.1.0 + ## 0.0.10 ### Patch Changes diff --git a/packages/web/sources/browser/package.json b/packages/web/sources/browser/package.json index 528033dd5..02a993f51 100644 --- a/packages/web/sources/browser/package.json +++ b/packages/web/sources/browser/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/web-source-browser", "description": "Browser DOM source for walkerOS", - "version": "0.0.10", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -25,8 +25,8 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/collector": "0.0.8", - "@walkeros/web-core": "0.0.8" + "@walkeros/collector": "0.1.0", + "@walkeros/web-core": "0.1.0" }, "repository": { "url": "git+https://github.com/elbwalker/walkerOS.git", diff --git a/packages/web/sources/dataLayer/CHANGELOG.md b/packages/web/sources/dataLayer/CHANGELOG.md index 434d7ccec..df4153983 100644 --- a/packages/web/sources/dataLayer/CHANGELOG.md +++ b/packages/web/sources/dataLayer/CHANGELOG.md @@ -1,5 +1,17 @@ # @walkeros/web-source-datalayer +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/collector@0.1.0 + - @walkeros/core@0.1.0 + ## 0.0.8 ### Patch Changes diff --git a/packages/web/sources/dataLayer/package.json b/packages/web/sources/dataLayer/package.json index 3816cd5e2..5ada0cb5a 100644 --- a/packages/web/sources/dataLayer/package.json +++ b/packages/web/sources/dataLayer/package.json @@ -1,7 +1,7 @@ { "name": "@walkeros/web-source-datalayer", "description": "DataLayer source for walkerOS", - "version": "0.0.8", + "version": "0.1.0", "license": "MIT", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -30,8 +30,8 @@ "update": "npx npm-check-updates -u && npm update" }, "dependencies": { - "@walkeros/core": "0.0.8", - "@walkeros/collector": "0.0.8" + "@walkeros/core": "0.1.0", + "@walkeros/collector": "0.1.0" }, "devDependencies": { "@types/gtag.js": "^0.0.20" diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index a0c20e7d8..5fd001ba2 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -1,5 +1,16 @@ # @walkeros/website +## 0.1.0 + +### Minor Changes + +- fixes + +### Patch Changes + +- Updated dependencies + - @walkeros/core@0.1.0 + ## 0.0.1 ### Patch Changes diff --git a/website/package.json b/website/package.json index 25ae14752..6a18167b6 100644 --- a/website/package.json +++ b/website/package.json @@ -1,6 +1,6 @@ { "name": "@walkeros/website", - "version": "0.0.1", + "version": "0.1.0", "private": true, "scripts": { "docusaurus": "docusaurus",