@walkeros/web-destination-segment 4.7.2 → 4.8.0-next-1791441510508
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +6 -2
- package/dist/dev.d.mts +20 -15
- package/dist/dev.d.ts +20 -15
- package/dist/dev.js +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/dev.mjs +1 -1
- package/dist/dev.mjs.map +1 -1
- package/dist/examples/index.d.mts +20 -15
- package/dist/examples/index.d.ts +20 -15
- package/dist/examples/index.js +15 -6
- package/dist/examples/index.mjs +15 -6
- package/dist/index.d.mts +10 -11
- package/dist/index.d.ts +10 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/walkerOS.json +78 -24
- package/package.json +4 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types/index.ts"],"sourcesContent":["import type { WalkerOS } from '@walkeros/core';\nimport { getMappingValue, isArray, isObject, isString } from '@walkeros/core';\nimport { AnalyticsBrowser } from '@segment/analytics-next';\nimport type {\n Destination,\n Env,\n RuntimeState,\n SegmentAnalytics,\n SegmentEventOptions,\n SegmentSDK,\n Settings,\n} from './types';\n\n// Types export\nexport * as DestinationSegment from './types';\n\n/**\n * Real-SDK adapter - wraps AnalyticsBrowser's static load() in our\n * SegmentSDK shape so `env?.analytics ?? realSegment` works.\n *\n * Task 1 confirmed AnalyticsBrowser.load(settings, options) returns an\n * AnalyticsBrowser instance that exposes track/identify/group/page/...\n * The returned instance buffers method calls pre-load and replays them\n * once the internal load promise resolves.\n */\nconst realSegment: SegmentSDK = {\n load: (settings, options) =>\n AnalyticsBrowser.load(settings, options) as unknown as SegmentAnalytics,\n};\n\nfunction getSegment(env: Env | undefined): SegmentSDK {\n return env?.analytics ?? realSegment;\n}\n\n/**\n * Call sdk.load() with destructured Segment settings. Strips walkerOS-\n * specific keys from the options arg passed to the real SDK.\n */\nfunction loadAnalytics(sdk: SegmentSDK, settings: Settings): SegmentAnalytics {\n const {\n apiKey,\n identify: _identify,\n group: _group,\n consent: _consent,\n _state: _existingState,\n ...initOptions\n } = settings;\n\n // Apply walkerOS defaults that differ from Segment defaults.\n const options = {\n initialPageview: false,\n ...initOptions,\n };\n\n return sdk.load({ writeKey: apiKey }, options);\n}\n\n/**\n * Resolve the live analytics instance, loading on demand if deferred.\n * Returns undefined if load() was deferred and consent hasn't fired yet.\n */\nfunction resolveAnalytics(\n sdk: SegmentSDK,\n settings: Settings,\n state: RuntimeState,\n): SegmentAnalytics | undefined {\n if (state.analytics) return state.analytics;\n if (state.loaded) return undefined;\n state.analytics = loadAnalytics(sdk, settings);\n state.loaded = true;\n return state.analytics;\n}\n\n/**\n * Stable hash of a traits object for state-diffing. Uses a deterministic\n * JSON stringify - key order matters but is consistent within a session\n * because object literal keys come in insertion order.\n */\nfunction hashTraits(traits: Record<string, unknown> | undefined): string {\n if (!traits) return '';\n try {\n return JSON.stringify(traits);\n } catch {\n return '';\n }\n}\n\n/**\n * Build the Segment event options object with consent context from the\n * current walker consent state. Returns undefined unless the user has\n * explicitly opted in by configuring `settings.consent` - this keeps the\n * consent forwarding feature off-by-default so events stay clean.\n *\n * `settings.consent` maps walkerOS consent keys → Segment category names.\n * Only walker keys present in the mapping are forwarded.\n */\nfunction buildEventOptions(\n event: WalkerOS.Event,\n settings: Settings,\n): SegmentEventOptions | undefined {\n const map = settings.consent;\n if (!map || Object.keys(map).length === 0) return undefined;\n\n const consentState = event.consent;\n if (!isObject(consentState)) return undefined;\n\n const categoryPreferences: Record<string, boolean> = {};\n for (const [walkerKey, segmentKey] of Object.entries(map)) {\n if (walkerKey in consentState) {\n categoryPreferences[segmentKey] = consentState[walkerKey] === true;\n }\n }\n\n if (Object.keys(categoryPreferences).length === 0) return undefined;\n\n return {\n context: {\n consent: {\n categoryPreferences,\n },\n },\n };\n}\n\n/**\n * Resolve and apply identity mapping. Calls analytics.identify() only\n * when the resolved value differs from the last-fired identity.\n */\nfunction applyIdentify(\n analytics: SegmentAnalytics,\n resolved: Record<string, unknown>,\n state: RuntimeState,\n options?: SegmentEventOptions,\n): void {\n const last = state.lastIdentity || {};\n const userId = isString(resolved.userId) ? resolved.userId : undefined;\n const anonymousId = isString(resolved.anonymousId)\n ? resolved.anonymousId\n : undefined;\n const traits =\n isObject(resolved.traits) && !isArray(resolved.traits)\n ? (resolved.traits as Record<string, unknown>)\n : undefined;\n\n if (anonymousId && anonymousId !== last.anonymousId) {\n analytics.setAnonymousId(anonymousId);\n }\n\n const traitsHash = hashTraits(traits);\n const userIdChanged = userId !== last.userId;\n const traitsChanged = traitsHash !== (last.traitsHash ?? '');\n const hasChange = userIdChanged || (traits !== undefined && traitsChanged);\n\n if (!hasChange && !anonymousId) return;\n\n if (options) {\n analytics.identify(userId, traits || {}, options);\n } else {\n analytics.identify(userId, traits || {});\n }\n\n state.lastIdentity = {\n userId,\n anonymousId: anonymousId ?? last.anonymousId,\n traitsHash,\n };\n}\n\nfunction applyGroup(\n analytics: SegmentAnalytics,\n resolved: Record<string, unknown>,\n state: RuntimeState,\n options?: SegmentEventOptions,\n): void {\n const groupId = isString(resolved.groupId) ? resolved.groupId : undefined;\n if (!groupId) return;\n\n const traits =\n isObject(resolved.traits) && !isArray(resolved.traits)\n ? (resolved.traits as Record<string, unknown>)\n : undefined;\n\n const last = state.lastGroup || {};\n const traitsHash = hashTraits(traits);\n const groupIdChanged = groupId !== last.groupId;\n const traitsChanged = traitsHash !== (last.traitsHash ?? '');\n if (!groupIdChanged && !traitsChanged) return;\n\n if (options) {\n analytics.group(groupId, traits || {}, options);\n } else {\n analytics.group(groupId, traits || {});\n }\n\n state.lastGroup = { groupId, traitsHash };\n}\n\nasync function applyPage(\n analytics: SegmentAnalytics,\n pageMapping: unknown,\n event: WalkerOS.Event,\n collector: unknown,\n options?: SegmentEventOptions,\n): Promise<void> {\n // Boolean true → call page() with no args (SDK auto-collect).\n if (pageMapping === true) {\n if (options) {\n analytics.page(undefined, undefined, undefined, options);\n } else {\n analytics.page();\n }\n return;\n }\n\n const resolved = await getMappingValue(\n event,\n pageMapping as Parameters<typeof getMappingValue>[1],\n { collector } as Parameters<typeof getMappingValue>[2],\n );\n if (!isObject(resolved)) return;\n\n const r = resolved as {\n category?: unknown;\n name?: unknown;\n properties?: unknown;\n };\n const category = isString(r.category) ? r.category : undefined;\n const name = isString(r.name) ? r.name : undefined;\n const properties =\n isObject(r.properties) && !isArray(r.properties)\n ? (r.properties as Record<string, unknown>)\n : undefined;\n\n if (options) {\n analytics.page(category ?? null, name ?? null, properties ?? {}, options);\n } else if (properties !== undefined) {\n analytics.page(category ?? null, name ?? null, properties);\n } else if (name !== undefined || category !== undefined) {\n analytics.page(category ?? null, name ?? null);\n } else {\n analytics.page();\n }\n}\n\nexport const destinationSegment: Destination = {\n type: 'segment',\n\n config: {},\n\n init({ config, env }) {\n if (!config.settings?.apiKey) return false;\n const settings = config.settings as Settings;\n\n const sdk = getSegment(env as Env | undefined);\n const _state: RuntimeState = {\n lastIdentity: {},\n lastGroup: {},\n loaded: false,\n };\n\n // If config.consent is declared, defer load() until on('consent').\n // Otherwise load immediately.\n const requiresConsent =\n !!config.consent && Object.keys(config.consent).length > 0;\n\n if (!requiresConsent) {\n _state.analytics = loadAnalytics(sdk, settings);\n _state.loaded = true;\n }\n\n return { ...config, settings: { ...settings, _state } };\n },\n\n async push(event, { config, rule, env, data, collector }) {\n const sdk = getSegment(env as Env | undefined);\n const settings = (config.settings || {}) as Settings;\n const state: RuntimeState = settings._state || {};\n const analytics = resolveAnalytics(sdk, settings, state);\n if (!analytics) return;\n\n const mappingSettings = rule?.settings || {};\n const options = buildEventOptions(event, settings);\n\n // 1. Reset fires first so subsequent identity calls start clean.\n if (mappingSettings.reset !== undefined) {\n const resolved =\n typeof mappingSettings.reset === 'boolean'\n ? mappingSettings.reset\n : await getMappingValue(event, mappingSettings.reset, { collector });\n if (resolved) {\n analytics.reset();\n state.lastIdentity = {};\n state.lastGroup = {};\n }\n }\n\n // 2. Identity - rule-level override wins over destination-level.\n const identifyMapping = mappingSettings.identify ?? settings.identify;\n if (identifyMapping !== undefined) {\n const resolved = await getMappingValue(event, identifyMapping, {\n collector,\n });\n if (isObject(resolved)) {\n applyIdentify(\n analytics,\n resolved as Record<string, unknown>,\n state,\n options,\n );\n }\n }\n\n // 3. Group - rule-level override wins over destination-level.\n const groupMapping = mappingSettings.group ?? settings.group;\n if (groupMapping !== undefined) {\n const resolved = await getMappingValue(event, groupMapping, {\n collector,\n });\n if (isObject(resolved)) {\n applyGroup(\n analytics,\n resolved as Record<string, unknown>,\n state,\n options,\n );\n }\n }\n\n // 4. Page (explicit first-class Segment pattern)\n if (mappingSettings.page !== undefined) {\n await applyPage(\n analytics,\n mappingSettings.page,\n event,\n collector,\n options,\n );\n }\n\n // 5. Default track call unless rule opts out via silent.\n if (rule?.silent !== true) {\n const eventName = isString(rule?.name) ? rule.name : event.name;\n const properties = isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n\n if (options) {\n analytics.track(eventName, properties, options);\n } else {\n analytics.track(eventName, properties);\n }\n }\n\n settings._state = state;\n },\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n\n const consent = context.data as WalkerOS.Consent;\n const required = context.config?.consent;\n if (!required || Object.keys(required).length === 0) return;\n\n const settings = (context.config?.settings || {}) as Settings;\n const state: RuntimeState = settings._state || {};\n const sdk = getSegment(context.env as Env | undefined);\n\n const allGranted = Object.keys(required).every(\n (key) => consent[key] === true,\n );\n\n if (allGranted && !state.loaded) {\n state.analytics = loadAnalytics(sdk, settings);\n state.loaded = true;\n settings._state = state;\n }\n // If not all granted, walkerOS's config.consent gate blocks subsequent\n // push() calls - no SDK action needed (Segment has no opt-out method).\n },\n};\n\nexport default destinationSegment;\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationWeb } from '@walkeros/web-core';\nimport type {\n AnalyticsBrowserSettings,\n InitOptions,\n} from '@segment/analytics-next';\n\n/**\n * Destination-level settings.\n *\n * Extends Segment's `InitOptions` (minus the `group` key which clashes with\n * walkerOS's own `group` mapping) so every passthrough option (cookie,\n * storage, integrations, plan, etc.) keeps IntelliSense intact. walkerOS\n * adds:\n * - `apiKey` (required) — maps to `writeKey` in `AnalyticsBrowser.load()`\n * - `identify` — destination-level identity mapping (Segment `identify()`)\n * - `group` — destination-level group mapping (Segment `group()`)\n * - `include` — event sections flattened into track `properties`\n * - `consent` — walkerOS consent key → Segment category name mapping\n * - `_state` — runtime state (not user-facing, mutated by init/push)\n */\nexport interface Settings extends Omit<InitOptions, 'group'> {\n /** Segment write key. Maps to `writeKey` in the load() settings arg. */\n apiKey: string;\n /** walkerOS mapping value resolving to an identity object (userId, traits, anonymousId). */\n identify?: WalkerOSMapping.Value;\n /** walkerOS mapping value resolving to a group object (groupId, traits). */\n group?: WalkerOSMapping.Value;\n /**\n * Mapping from walkerOS consent keys → Segment `categoryPreferences` keys.\n * Example: { marketing: \"Advertising\", analytics: \"Analytics\" }\n * If omitted, walkerOS keys are forwarded 1:1.\n */\n consent?: Record<string, string>;\n /** Runtime state — populated by init() and mutated by push(). Not user-facing. */\n _state?: RuntimeState;\n}\n\nexport interface RuntimeState {\n /** Last-set identity values, used to skip redundant identify() calls. */\n lastIdentity?: {\n userId?: string;\n anonymousId?: string;\n traitsHash?: string;\n };\n /** Last-set group assignment, used to skip redundant group() calls. */\n lastGroup?: {\n groupId?: string;\n traitsHash?: string;\n };\n /** Holds the AnalyticsBrowser instance created in init(). */\n analytics?: SegmentAnalytics;\n /** True once load() has been called (may be deferred pending consent). */\n loaded?: boolean;\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Every feature here is a walkerOS mapping\n * value resolved via getMappingValue(). The resolved object's keys drive\n * which Segment SDK methods to call.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n page?: WalkerOSMapping.Value | boolean;\n reset?: WalkerOSMapping.Value | boolean;\n}\n\n/**\n * Segment SDK surface — the subset of @segment/analytics-next the\n * destination actually uses. Mirrors the AnalyticsBrowser instance shape\n * so tests can mock each method individually via env.analytics.\n */\nexport interface SegmentAnalytics {\n track: (\n event: string,\n properties?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n identify: (\n userId?: string | Record<string, unknown>,\n traits?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n group: (\n groupId: string,\n traits?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n page: (\n category?: string | null,\n name?: string | null,\n properties?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n alias: (\n to: string,\n from?: string,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n reset: () => Promise<unknown> | void;\n setAnonymousId: (id: string) => Promise<unknown> | void;\n}\n\n/**\n * Segment event options — the fourth argument to track/identify/group/page\n * that carries context and integration overrides. The destination uses\n * this to stamp consent context on every event.\n */\nexport interface SegmentEventOptions {\n context?: {\n consent?: {\n categoryPreferences?: Record<string, boolean>;\n };\n [key: string]: unknown;\n };\n integrations?: Record<string, boolean | Record<string, unknown>>;\n}\n\n/**\n * The module namespace used for `env?.analytics ?? realSegment`.\n * AnalyticsBrowser is the class; tests mock with a factory that returns\n * an object matching SegmentAnalytics.\n *\n * Task 1 verified: `AnalyticsBrowser.load(settings, options)` returns an\n * `AnalyticsBrowser` instance **synchronously**. The instance buffers\n * method calls until the internal load promise resolves.\n */\nexport interface SegmentSDK {\n load: (\n settings: AnalyticsBrowserSettings,\n options?: InitOptions,\n ) => SegmentAnalytics;\n}\n\n/**\n * Env — optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @segment/analytics-next module.\n * Tests provide a mock via env.analytics = { ... }.\n */\nexport interface Env extends DestinationWeb.Env {\n analytics?: SegmentSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport type Destination = DestinationWeb.Destination<Types>;\nexport type Config = DestinationWeb.Config<Types>;\n\nexport interface SegmentDestination extends Destination {\n env?: Env;\n}\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types/index.ts"],"sourcesContent":["import type { WalkerOS } from '@walkeros/core';\nimport { getMappingValue, isArray, isObject, isString } from '@walkeros/core';\nimport { AnalyticsBrowser } from '@segment/analytics-next';\nimport type {\n Destination,\n Env,\n RuntimeState,\n SegmentAnalytics,\n SegmentEventOptions,\n SegmentSDK,\n Settings,\n} from './types';\n\n// Types export\nexport * as DestinationSegment from './types';\n\n/**\n * Real-SDK adapter - wraps AnalyticsBrowser's static load() in our\n * SegmentSDK shape so `env?.analytics ?? realSegment` works.\n *\n * Task 1 confirmed AnalyticsBrowser.load(settings, options) returns an\n * AnalyticsBrowser instance that exposes track/identify/group/page/...\n * The returned instance buffers method calls pre-load and replays them\n * once the internal load promise resolves.\n */\nconst realSegment: SegmentSDK = {\n load: (settings, options) =>\n AnalyticsBrowser.load(settings, options) as unknown as SegmentAnalytics,\n};\n\nfunction getSegment(env: Env | undefined): SegmentSDK {\n return env?.analytics ?? realSegment;\n}\n\n/**\n * Call sdk.load() with destructured Segment settings. Strips walkerOS-\n * specific keys from the options arg passed to the real SDK.\n */\nfunction loadAnalytics(sdk: SegmentSDK, settings: Settings): SegmentAnalytics {\n const {\n apiKey,\n identify: _identify,\n group: _group,\n consent: _consent,\n _state: _existingState,\n ...initOptions\n } = settings;\n\n // Apply walkerOS defaults that differ from Segment defaults.\n const options = {\n initialPageview: false,\n ...initOptions,\n };\n\n return sdk.load({ writeKey: apiKey }, options);\n}\n\n/**\n * Resolve the live analytics instance, loading on demand if deferred.\n * Returns undefined if load() was deferred and consent hasn't fired yet.\n */\nfunction resolveAnalytics(\n sdk: SegmentSDK,\n settings: Settings,\n state: RuntimeState,\n): SegmentAnalytics | undefined {\n if (state.analytics) return state.analytics;\n if (state.loaded) return undefined;\n state.analytics = loadAnalytics(sdk, settings);\n state.loaded = true;\n return state.analytics;\n}\n\n/**\n * Stable hash of a traits object for state-diffing. Uses a deterministic\n * JSON stringify - key order matters but is consistent within a session\n * because object literal keys come in insertion order.\n */\nfunction hashTraits(traits: Record<string, unknown> | undefined): string {\n if (!traits) return '';\n try {\n return JSON.stringify(traits);\n } catch {\n return '';\n }\n}\n\n/**\n * Build the Segment event options object with consent context from the\n * current walker consent state. Returns undefined unless the user has\n * explicitly opted in by configuring `settings.consent` - this keeps the\n * consent forwarding feature off-by-default so events stay clean.\n *\n * `settings.consent` maps walkerOS consent keys → Segment category names.\n * Only walker keys present in the mapping are forwarded.\n */\nfunction buildEventOptions(\n event: WalkerOS.Event,\n settings: Settings,\n): SegmentEventOptions | undefined {\n const map = settings.consent;\n if (!map || Object.keys(map).length === 0) return undefined;\n\n const consentState = event.consent;\n if (!isObject(consentState)) return undefined;\n\n const categoryPreferences: Record<string, boolean> = {};\n for (const [walkerKey, segmentKey] of Object.entries(map)) {\n if (walkerKey in consentState) {\n categoryPreferences[segmentKey] = consentState[walkerKey] === true;\n }\n }\n\n if (Object.keys(categoryPreferences).length === 0) return undefined;\n\n return {\n context: {\n consent: {\n categoryPreferences,\n },\n },\n };\n}\n\n/**\n * Resolve and apply identity mapping. Calls analytics.identify() only\n * when the resolved value differs from the last-fired identity.\n */\nfunction applyIdentify(\n analytics: SegmentAnalytics,\n resolved: Record<string, unknown>,\n state: RuntimeState,\n options?: SegmentEventOptions,\n): void {\n const last = state.lastIdentity || {};\n const userId = isString(resolved.userId) ? resolved.userId : undefined;\n const anonymousId = isString(resolved.anonymousId)\n ? resolved.anonymousId\n : undefined;\n const traits =\n isObject(resolved.traits) && !isArray(resolved.traits)\n ? (resolved.traits as Record<string, unknown>)\n : undefined;\n\n if (anonymousId && anonymousId !== last.anonymousId) {\n analytics.setAnonymousId(anonymousId);\n }\n\n const traitsHash = hashTraits(traits);\n const userIdChanged = userId !== last.userId;\n const traitsChanged = traitsHash !== (last.traitsHash ?? '');\n const hasChange = userIdChanged || (traits !== undefined && traitsChanged);\n\n if (!hasChange && !anonymousId) return;\n\n if (options) {\n analytics.identify(userId, traits || {}, options);\n } else {\n analytics.identify(userId, traits || {});\n }\n\n state.lastIdentity = {\n userId,\n anonymousId: anonymousId ?? last.anonymousId,\n traitsHash,\n };\n}\n\nfunction applyGroup(\n analytics: SegmentAnalytics,\n resolved: Record<string, unknown>,\n state: RuntimeState,\n options?: SegmentEventOptions,\n): void {\n const groupId = isString(resolved.groupId) ? resolved.groupId : undefined;\n if (!groupId) return;\n\n const traits =\n isObject(resolved.traits) && !isArray(resolved.traits)\n ? (resolved.traits as Record<string, unknown>)\n : undefined;\n\n const last = state.lastGroup || {};\n const traitsHash = hashTraits(traits);\n const groupIdChanged = groupId !== last.groupId;\n const traitsChanged = traitsHash !== (last.traitsHash ?? '');\n if (!groupIdChanged && !traitsChanged) return;\n\n if (options) {\n analytics.group(groupId, traits || {}, options);\n } else {\n analytics.group(groupId, traits || {});\n }\n\n state.lastGroup = { groupId, traitsHash };\n}\n\nasync function applyPage(\n analytics: SegmentAnalytics,\n pageMapping: unknown,\n event: WalkerOS.Event,\n collector: unknown,\n options?: SegmentEventOptions,\n): Promise<void> {\n // Boolean true → call page() with no args (SDK auto-collect).\n if (pageMapping === true) {\n if (options) {\n analytics.page(undefined, undefined, undefined, options);\n } else {\n analytics.page();\n }\n return;\n }\n\n const resolved = await getMappingValue(\n event,\n pageMapping as Parameters<typeof getMappingValue>[1],\n { collector } as Parameters<typeof getMappingValue>[2],\n );\n if (!isObject(resolved)) return;\n\n const r = resolved as {\n category?: unknown;\n name?: unknown;\n properties?: unknown;\n };\n const category = isString(r.category) ? r.category : undefined;\n const name = isString(r.name) ? r.name : undefined;\n const properties =\n isObject(r.properties) && !isArray(r.properties)\n ? (r.properties as Record<string, unknown>)\n : undefined;\n\n if (options) {\n analytics.page(category ?? null, name ?? null, properties ?? {}, options);\n } else if (properties !== undefined) {\n analytics.page(category ?? null, name ?? null, properties);\n } else if (name !== undefined || category !== undefined) {\n analytics.page(category ?? null, name ?? null);\n } else {\n analytics.page();\n }\n}\n\nexport const destinationSegment: Destination = {\n type: 'segment',\n\n config: {},\n\n init({ config, env }) {\n if (!config.settings?.apiKey) return false;\n const settings = config.settings as Settings;\n\n const sdk = getSegment(env as Env | undefined);\n const _state: RuntimeState = {\n lastIdentity: {},\n lastGroup: {},\n loaded: false,\n };\n\n // If config.consent is declared, defer load() until on('consent').\n // Otherwise load immediately.\n const requiresConsent =\n !!config.consent && Object.keys(config.consent).length > 0;\n\n if (!requiresConsent) {\n _state.analytics = loadAnalytics(sdk, settings);\n _state.loaded = true;\n }\n\n return { ...config, settings: { ...settings, _state } };\n },\n\n async push(event, { config, rule, env, data, collector }) {\n const sdk = getSegment(env as Env | undefined);\n const settings = (config.settings || {}) as Settings;\n const state: RuntimeState = settings._state || {};\n const analytics = resolveAnalytics(sdk, settings, state);\n if (!analytics) return;\n\n const mappingSettings = rule?.settings || {};\n const options = buildEventOptions(event, settings);\n\n // 1. Reset fires first so subsequent identity calls start clean.\n if (mappingSettings.reset !== undefined) {\n const resolved =\n typeof mappingSettings.reset === 'boolean'\n ? mappingSettings.reset\n : await getMappingValue(event, mappingSettings.reset, { collector });\n if (resolved) {\n analytics.reset();\n state.lastIdentity = {};\n state.lastGroup = {};\n }\n }\n\n // 2. Identity - rule-level override wins over destination-level.\n const identifyMapping = mappingSettings.identify ?? settings.identify;\n if (identifyMapping !== undefined) {\n const resolved = await getMappingValue(event, identifyMapping, {\n collector,\n });\n if (isObject(resolved)) {\n applyIdentify(\n analytics,\n resolved as Record<string, unknown>,\n state,\n options,\n );\n }\n }\n\n // 3. Group - rule-level override wins over destination-level.\n const groupMapping = mappingSettings.group ?? settings.group;\n if (groupMapping !== undefined) {\n const resolved = await getMappingValue(event, groupMapping, {\n collector,\n });\n if (isObject(resolved)) {\n applyGroup(\n analytics,\n resolved as Record<string, unknown>,\n state,\n options,\n );\n }\n }\n\n // 4. Page (explicit first-class Segment pattern)\n if (mappingSettings.page !== undefined) {\n await applyPage(\n analytics,\n mappingSettings.page,\n event,\n collector,\n options,\n );\n }\n\n // 5. Default track call unless rule opts out via silent.\n if (rule?.silent !== true) {\n const eventName = isString(rule?.name) ? rule.name : event.name;\n const properties = isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n\n if (options) {\n analytics.track(eventName, properties, options);\n } else {\n analytics.track(eventName, properties);\n }\n }\n\n settings._state = state;\n },\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n\n const consent = context.data as WalkerOS.Consent;\n const required = context.config?.consent;\n if (!required || Object.keys(required).length === 0) return;\n\n const settings = (context.config?.settings || {}) as Settings;\n const state: RuntimeState = settings._state || {};\n const sdk = getSegment(context.env as Env | undefined);\n\n const allGranted = Object.keys(required).every(\n (key) => consent[key] === true,\n );\n\n if (allGranted && !state.loaded) {\n state.analytics = loadAnalytics(sdk, settings);\n state.loaded = true;\n settings._state = state;\n }\n // If not all granted, walkerOS's config.consent gate blocks subsequent\n // push() calls - no SDK action needed (Segment has no opt-out method).\n },\n};\n\nexport default destinationSegment;\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationWeb } from '@walkeros/web-core';\nimport type {\n AnalyticsBrowserSettings,\n InitOptions,\n} from '@segment/analytics-next';\n\n/**\n * Destination-level settings.\n *\n * Extends Segment's `InitOptions` (minus the `group` key which clashes with\n * walkerOS's own `group` mapping) so every passthrough option (cookie,\n * storage, integrations, plan, etc.) keeps IntelliSense intact. walkerOS\n * adds:\n * - `apiKey` (required): maps to `writeKey` in `AnalyticsBrowser.load()`\n * - `identify`: destination-level identity mapping (Segment `identify()`)\n * - `group`: destination-level group mapping (Segment `group()`)\n * - `consent`: walkerOS consent key → Segment category name mapping\n * - `_state`: runtime state (not user-facing, mutated by init/push)\n */\nexport interface Settings extends Omit<InitOptions, 'group'> {\n /** Segment write key. Maps to `writeKey` in the load() settings arg. */\n apiKey: string;\n /** walkerOS mapping value resolving to an identity object (userId, traits, anonymousId). */\n identify?: WalkerOSMapping.Value;\n /** walkerOS mapping value resolving to a group object (groupId, traits). */\n group?: WalkerOSMapping.Value;\n /**\n * Mapping from walkerOS consent keys → Segment `categoryPreferences` keys.\n * Example: { marketing: \"Advertising\", analytics: \"Analytics\" }\n * Only mapped keys are forwarded. If omitted, no consent context is sent.\n */\n consent?: Record<string, string>;\n /** Runtime state: populated by init() and mutated by push(). Not user-facing. */\n _state?: RuntimeState;\n}\n\nexport interface RuntimeState {\n /** Last-set identity values, used to skip redundant identify() calls. */\n lastIdentity?: {\n userId?: string;\n anonymousId?: string;\n traitsHash?: string;\n };\n /** Last-set group assignment, used to skip redundant group() calls. */\n lastGroup?: {\n groupId?: string;\n traitsHash?: string;\n };\n /** Holds the AnalyticsBrowser instance created in init(). */\n analytics?: SegmentAnalytics;\n /** True once load() has been called (may be deferred pending consent). */\n loaded?: boolean;\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Every feature here is a walkerOS mapping\n * value resolved via getMappingValue(). The resolved object's keys drive\n * which Segment SDK methods to call.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n page?: WalkerOSMapping.Value | boolean;\n reset?: WalkerOSMapping.Value | boolean;\n}\n\n/**\n * Segment SDK surface: the subset of @segment/analytics-next the\n * destination actually uses. Mirrors the AnalyticsBrowser instance shape\n * so tests can mock each method individually via env.analytics.\n */\nexport interface SegmentAnalytics {\n track: (\n event: string,\n properties?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n identify: (\n userId?: string | Record<string, unknown>,\n traits?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n group: (\n groupId: string,\n traits?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n page: (\n category?: string | null,\n name?: string | null,\n properties?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n alias: (\n to: string,\n from?: string,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n reset: () => Promise<unknown> | void;\n setAnonymousId: (id: string) => Promise<unknown> | void;\n}\n\n/**\n * Segment event options: the fourth argument to track/identify/group/page\n * that carries context and integration overrides. The destination uses\n * this to stamp consent context on every event.\n */\nexport interface SegmentEventOptions {\n context?: {\n consent?: {\n categoryPreferences?: Record<string, boolean>;\n };\n [key: string]: unknown;\n };\n integrations?: Record<string, boolean | Record<string, unknown>>;\n}\n\n/**\n * The module namespace used for `env?.analytics ?? realSegment`.\n * AnalyticsBrowser is the class; tests mock with a factory that returns\n * an object matching SegmentAnalytics.\n *\n * Task 1 verified: `AnalyticsBrowser.load(settings, options)` returns an\n * `AnalyticsBrowser` instance **synchronously**. The instance buffers\n * method calls until the internal load promise resolves.\n */\nexport interface SegmentSDK {\n load: (\n settings: AnalyticsBrowserSettings,\n options?: InitOptions,\n ) => SegmentAnalytics;\n}\n\n/**\n * Env: optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @segment/analytics-next module.\n * Tests provide a mock via env.analytics = { ... }.\n */\nexport interface Env extends DestinationWeb.Env {\n analytics?: SegmentSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport type Destination = DestinationWeb.Destination<Types>;\nexport type Config = DestinationWeb.Config<Types>;\n\nexport interface SegmentDestination extends Destination {\n env?: Env;\n}\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types/index.ts"],"sourcesContent":["import type { WalkerOS } from '@walkeros/core';\nimport { getMappingValue, isArray, isObject, isString } from '@walkeros/core';\nimport { AnalyticsBrowser } from '@segment/analytics-next';\nimport type {\n Destination,\n Env,\n RuntimeState,\n SegmentAnalytics,\n SegmentEventOptions,\n SegmentSDK,\n Settings,\n} from './types';\n\n// Types export\nexport * as DestinationSegment from './types';\n\n/**\n * Real-SDK adapter - wraps AnalyticsBrowser's static load() in our\n * SegmentSDK shape so `env?.analytics ?? realSegment` works.\n *\n * Task 1 confirmed AnalyticsBrowser.load(settings, options) returns an\n * AnalyticsBrowser instance that exposes track/identify/group/page/...\n * The returned instance buffers method calls pre-load and replays them\n * once the internal load promise resolves.\n */\nconst realSegment: SegmentSDK = {\n load: (settings, options) =>\n AnalyticsBrowser.load(settings, options) as unknown as SegmentAnalytics,\n};\n\nfunction getSegment(env: Env | undefined): SegmentSDK {\n return env?.analytics ?? realSegment;\n}\n\n/**\n * Call sdk.load() with destructured Segment settings. Strips walkerOS-\n * specific keys from the options arg passed to the real SDK.\n */\nfunction loadAnalytics(sdk: SegmentSDK, settings: Settings): SegmentAnalytics {\n const {\n apiKey,\n identify: _identify,\n group: _group,\n consent: _consent,\n _state: _existingState,\n ...initOptions\n } = settings;\n\n // Apply walkerOS defaults that differ from Segment defaults.\n const options = {\n initialPageview: false,\n ...initOptions,\n };\n\n return sdk.load({ writeKey: apiKey }, options);\n}\n\n/**\n * Resolve the live analytics instance, loading on demand if deferred.\n * Returns undefined if load() was deferred and consent hasn't fired yet.\n */\nfunction resolveAnalytics(\n sdk: SegmentSDK,\n settings: Settings,\n state: RuntimeState,\n): SegmentAnalytics | undefined {\n if (state.analytics) return state.analytics;\n if (state.loaded) return undefined;\n state.analytics = loadAnalytics(sdk, settings);\n state.loaded = true;\n return state.analytics;\n}\n\n/**\n * Stable hash of a traits object for state-diffing. Uses a deterministic\n * JSON stringify - key order matters but is consistent within a session\n * because object literal keys come in insertion order.\n */\nfunction hashTraits(traits: Record<string, unknown> | undefined): string {\n if (!traits) return '';\n try {\n return JSON.stringify(traits);\n } catch {\n return '';\n }\n}\n\n/**\n * Build the Segment event options object with consent context from the\n * current walker consent state. Returns undefined unless the user has\n * explicitly opted in by configuring `settings.consent` - this keeps the\n * consent forwarding feature off-by-default so events stay clean.\n *\n * `settings.consent` maps walkerOS consent keys → Segment category names.\n * Only walker keys present in the mapping are forwarded.\n */\nfunction buildEventOptions(\n event: WalkerOS.Event,\n settings: Settings,\n): SegmentEventOptions | undefined {\n const map = settings.consent;\n if (!map || Object.keys(map).length === 0) return undefined;\n\n const consentState = event.consent;\n if (!isObject(consentState)) return undefined;\n\n const categoryPreferences: Record<string, boolean> = {};\n for (const [walkerKey, segmentKey] of Object.entries(map)) {\n if (walkerKey in consentState) {\n categoryPreferences[segmentKey] = consentState[walkerKey] === true;\n }\n }\n\n if (Object.keys(categoryPreferences).length === 0) return undefined;\n\n return {\n context: {\n consent: {\n categoryPreferences,\n },\n },\n };\n}\n\n/**\n * Resolve and apply identity mapping. Calls analytics.identify() only\n * when the resolved value differs from the last-fired identity.\n */\nfunction applyIdentify(\n analytics: SegmentAnalytics,\n resolved: Record<string, unknown>,\n state: RuntimeState,\n options?: SegmentEventOptions,\n): void {\n const last = state.lastIdentity || {};\n const userId = isString(resolved.userId) ? resolved.userId : undefined;\n const anonymousId = isString(resolved.anonymousId)\n ? resolved.anonymousId\n : undefined;\n const traits =\n isObject(resolved.traits) && !isArray(resolved.traits)\n ? (resolved.traits as Record<string, unknown>)\n : undefined;\n\n if (anonymousId && anonymousId !== last.anonymousId) {\n analytics.setAnonymousId(anonymousId);\n }\n\n const traitsHash = hashTraits(traits);\n const userIdChanged = userId !== last.userId;\n const traitsChanged = traitsHash !== (last.traitsHash ?? '');\n const hasChange = userIdChanged || (traits !== undefined && traitsChanged);\n\n if (!hasChange && !anonymousId) return;\n\n if (options) {\n analytics.identify(userId, traits || {}, options);\n } else {\n analytics.identify(userId, traits || {});\n }\n\n state.lastIdentity = {\n userId,\n anonymousId: anonymousId ?? last.anonymousId,\n traitsHash,\n };\n}\n\nfunction applyGroup(\n analytics: SegmentAnalytics,\n resolved: Record<string, unknown>,\n state: RuntimeState,\n options?: SegmentEventOptions,\n): void {\n const groupId = isString(resolved.groupId) ? resolved.groupId : undefined;\n if (!groupId) return;\n\n const traits =\n isObject(resolved.traits) && !isArray(resolved.traits)\n ? (resolved.traits as Record<string, unknown>)\n : undefined;\n\n const last = state.lastGroup || {};\n const traitsHash = hashTraits(traits);\n const groupIdChanged = groupId !== last.groupId;\n const traitsChanged = traitsHash !== (last.traitsHash ?? '');\n if (!groupIdChanged && !traitsChanged) return;\n\n if (options) {\n analytics.group(groupId, traits || {}, options);\n } else {\n analytics.group(groupId, traits || {});\n }\n\n state.lastGroup = { groupId, traitsHash };\n}\n\nasync function applyPage(\n analytics: SegmentAnalytics,\n pageMapping: unknown,\n event: WalkerOS.Event,\n collector: unknown,\n options?: SegmentEventOptions,\n): Promise<void> {\n // Boolean true → call page() with no args (SDK auto-collect).\n if (pageMapping === true) {\n if (options) {\n analytics.page(undefined, undefined, undefined, options);\n } else {\n analytics.page();\n }\n return;\n }\n\n const resolved = await getMappingValue(\n event,\n pageMapping as Parameters<typeof getMappingValue>[1],\n { collector } as Parameters<typeof getMappingValue>[2],\n );\n if (!isObject(resolved)) return;\n\n const r = resolved as {\n category?: unknown;\n name?: unknown;\n properties?: unknown;\n };\n const category = isString(r.category) ? r.category : undefined;\n const name = isString(r.name) ? r.name : undefined;\n const properties =\n isObject(r.properties) && !isArray(r.properties)\n ? (r.properties as Record<string, unknown>)\n : undefined;\n\n if (options) {\n analytics.page(category ?? null, name ?? null, properties ?? {}, options);\n } else if (properties !== undefined) {\n analytics.page(category ?? null, name ?? null, properties);\n } else if (name !== undefined || category !== undefined) {\n analytics.page(category ?? null, name ?? null);\n } else {\n analytics.page();\n }\n}\n\nexport const destinationSegment: Destination = {\n type: 'segment',\n\n config: {},\n\n init({ config, env }) {\n if (!config.settings?.apiKey) return false;\n const settings = config.settings as Settings;\n\n const sdk = getSegment(env as Env | undefined);\n const _state: RuntimeState = {\n lastIdentity: {},\n lastGroup: {},\n loaded: false,\n };\n\n // If config.consent is declared, defer load() until on('consent').\n // Otherwise load immediately.\n const requiresConsent =\n !!config.consent && Object.keys(config.consent).length > 0;\n\n if (!requiresConsent) {\n _state.analytics = loadAnalytics(sdk, settings);\n _state.loaded = true;\n }\n\n return { ...config, settings: { ...settings, _state } };\n },\n\n async push(event, { config, rule, env, data, collector }) {\n const sdk = getSegment(env as Env | undefined);\n const settings = (config.settings || {}) as Settings;\n const state: RuntimeState = settings._state || {};\n const analytics = resolveAnalytics(sdk, settings, state);\n if (!analytics) return;\n\n const mappingSettings = rule?.settings || {};\n const options = buildEventOptions(event, settings);\n\n // 1. Reset fires first so subsequent identity calls start clean.\n if (mappingSettings.reset !== undefined) {\n const resolved =\n typeof mappingSettings.reset === 'boolean'\n ? mappingSettings.reset\n : await getMappingValue(event, mappingSettings.reset, { collector });\n if (resolved) {\n analytics.reset();\n state.lastIdentity = {};\n state.lastGroup = {};\n }\n }\n\n // 2. Identity - rule-level override wins over destination-level.\n const identifyMapping = mappingSettings.identify ?? settings.identify;\n if (identifyMapping !== undefined) {\n const resolved = await getMappingValue(event, identifyMapping, {\n collector,\n });\n if (isObject(resolved)) {\n applyIdentify(\n analytics,\n resolved as Record<string, unknown>,\n state,\n options,\n );\n }\n }\n\n // 3. Group - rule-level override wins over destination-level.\n const groupMapping = mappingSettings.group ?? settings.group;\n if (groupMapping !== undefined) {\n const resolved = await getMappingValue(event, groupMapping, {\n collector,\n });\n if (isObject(resolved)) {\n applyGroup(\n analytics,\n resolved as Record<string, unknown>,\n state,\n options,\n );\n }\n }\n\n // 4. Page (explicit first-class Segment pattern)\n if (mappingSettings.page !== undefined) {\n await applyPage(\n analytics,\n mappingSettings.page,\n event,\n collector,\n options,\n );\n }\n\n // 5. Default track call unless rule opts out via silent.\n if (rule?.silent !== true) {\n const eventName = isString(rule?.name) ? rule.name : event.name;\n const properties = isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n\n if (options) {\n analytics.track(eventName, properties, options);\n } else {\n analytics.track(eventName, properties);\n }\n }\n\n settings._state = state;\n },\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n\n const consent = context.data as WalkerOS.Consent;\n const required = context.config?.consent;\n if (!required || Object.keys(required).length === 0) return;\n\n const settings = (context.config?.settings || {}) as Settings;\n const state: RuntimeState = settings._state || {};\n const sdk = getSegment(context.env as Env | undefined);\n\n const allGranted = Object.keys(required).every(\n (key) => consent[key] === true,\n );\n\n if (allGranted && !state.loaded) {\n state.analytics = loadAnalytics(sdk, settings);\n state.loaded = true;\n settings._state = state;\n }\n // If not all granted, walkerOS's config.consent gate blocks subsequent\n // push() calls - no SDK action needed (Segment has no opt-out method).\n },\n};\n\nexport default destinationSegment;\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationWeb } from '@walkeros/web-core';\nimport type {\n AnalyticsBrowserSettings,\n InitOptions,\n} from '@segment/analytics-next';\n\n/**\n * Destination-level settings.\n *\n * Extends Segment's `InitOptions` (minus the `group` key which clashes with\n * walkerOS's own `group` mapping) so every passthrough option (cookie,\n * storage, integrations, plan, etc.) keeps IntelliSense intact. walkerOS\n * adds:\n * - `apiKey` (required) — maps to `writeKey` in `AnalyticsBrowser.load()`\n * - `identify` — destination-level identity mapping (Segment `identify()`)\n * - `group` — destination-level group mapping (Segment `group()`)\n * - `include` — event sections flattened into track `properties`\n * - `consent` — walkerOS consent key → Segment category name mapping\n * - `_state` — runtime state (not user-facing, mutated by init/push)\n */\nexport interface Settings extends Omit<InitOptions, 'group'> {\n /** Segment write key. Maps to `writeKey` in the load() settings arg. */\n apiKey: string;\n /** walkerOS mapping value resolving to an identity object (userId, traits, anonymousId). */\n identify?: WalkerOSMapping.Value;\n /** walkerOS mapping value resolving to a group object (groupId, traits). */\n group?: WalkerOSMapping.Value;\n /**\n * Mapping from walkerOS consent keys → Segment `categoryPreferences` keys.\n * Example: { marketing: \"Advertising\", analytics: \"Analytics\" }\n * If omitted, walkerOS keys are forwarded 1:1.\n */\n consent?: Record<string, string>;\n /** Runtime state — populated by init() and mutated by push(). Not user-facing. */\n _state?: RuntimeState;\n}\n\nexport interface RuntimeState {\n /** Last-set identity values, used to skip redundant identify() calls. */\n lastIdentity?: {\n userId?: string;\n anonymousId?: string;\n traitsHash?: string;\n };\n /** Last-set group assignment, used to skip redundant group() calls. */\n lastGroup?: {\n groupId?: string;\n traitsHash?: string;\n };\n /** Holds the AnalyticsBrowser instance created in init(). */\n analytics?: SegmentAnalytics;\n /** True once load() has been called (may be deferred pending consent). */\n loaded?: boolean;\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Every feature here is a walkerOS mapping\n * value resolved via getMappingValue(). The resolved object's keys drive\n * which Segment SDK methods to call.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n page?: WalkerOSMapping.Value | boolean;\n reset?: WalkerOSMapping.Value | boolean;\n}\n\n/**\n * Segment SDK surface — the subset of @segment/analytics-next the\n * destination actually uses. Mirrors the AnalyticsBrowser instance shape\n * so tests can mock each method individually via env.analytics.\n */\nexport interface SegmentAnalytics {\n track: (\n event: string,\n properties?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n identify: (\n userId?: string | Record<string, unknown>,\n traits?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n group: (\n groupId: string,\n traits?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n page: (\n category?: string | null,\n name?: string | null,\n properties?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n alias: (\n to: string,\n from?: string,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n reset: () => Promise<unknown> | void;\n setAnonymousId: (id: string) => Promise<unknown> | void;\n}\n\n/**\n * Segment event options — the fourth argument to track/identify/group/page\n * that carries context and integration overrides. The destination uses\n * this to stamp consent context on every event.\n */\nexport interface SegmentEventOptions {\n context?: {\n consent?: {\n categoryPreferences?: Record<string, boolean>;\n };\n [key: string]: unknown;\n };\n integrations?: Record<string, boolean | Record<string, unknown>>;\n}\n\n/**\n * The module namespace used for `env?.analytics ?? realSegment`.\n * AnalyticsBrowser is the class; tests mock with a factory that returns\n * an object matching SegmentAnalytics.\n *\n * Task 1 verified: `AnalyticsBrowser.load(settings, options)` returns an\n * `AnalyticsBrowser` instance **synchronously**. The instance buffers\n * method calls until the internal load promise resolves.\n */\nexport interface SegmentSDK {\n load: (\n settings: AnalyticsBrowserSettings,\n options?: InitOptions,\n ) => SegmentAnalytics;\n}\n\n/**\n * Env — optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @segment/analytics-next module.\n * Tests provide a mock via env.analytics = { ... }.\n */\nexport interface Env extends DestinationWeb.Env {\n analytics?: SegmentSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport type Destination = DestinationWeb.Destination<Types>;\nexport type Config = DestinationWeb.Config<Types>;\n\nexport interface SegmentDestination extends Destination {\n env?: Env;\n}\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types/index.ts"],"sourcesContent":["import type { WalkerOS } from '@walkeros/core';\nimport { getMappingValue, isArray, isObject, isString } from '@walkeros/core';\nimport { AnalyticsBrowser } from '@segment/analytics-next';\nimport type {\n Destination,\n Env,\n RuntimeState,\n SegmentAnalytics,\n SegmentEventOptions,\n SegmentSDK,\n Settings,\n} from './types';\n\n// Types export\nexport * as DestinationSegment from './types';\n\n/**\n * Real-SDK adapter - wraps AnalyticsBrowser's static load() in our\n * SegmentSDK shape so `env?.analytics ?? realSegment` works.\n *\n * Task 1 confirmed AnalyticsBrowser.load(settings, options) returns an\n * AnalyticsBrowser instance that exposes track/identify/group/page/...\n * The returned instance buffers method calls pre-load and replays them\n * once the internal load promise resolves.\n */\nconst realSegment: SegmentSDK = {\n load: (settings, options) =>\n AnalyticsBrowser.load(settings, options) as unknown as SegmentAnalytics,\n};\n\nfunction getSegment(env: Env | undefined): SegmentSDK {\n return env?.analytics ?? realSegment;\n}\n\n/**\n * Call sdk.load() with destructured Segment settings. Strips walkerOS-\n * specific keys from the options arg passed to the real SDK.\n */\nfunction loadAnalytics(sdk: SegmentSDK, settings: Settings): SegmentAnalytics {\n const {\n apiKey,\n identify: _identify,\n group: _group,\n consent: _consent,\n _state: _existingState,\n ...initOptions\n } = settings;\n\n // Apply walkerOS defaults that differ from Segment defaults.\n const options = {\n initialPageview: false,\n ...initOptions,\n };\n\n return sdk.load({ writeKey: apiKey }, options);\n}\n\n/**\n * Resolve the live analytics instance, loading on demand if deferred.\n * Returns undefined if load() was deferred and consent hasn't fired yet.\n */\nfunction resolveAnalytics(\n sdk: SegmentSDK,\n settings: Settings,\n state: RuntimeState,\n): SegmentAnalytics | undefined {\n if (state.analytics) return state.analytics;\n if (state.loaded) return undefined;\n state.analytics = loadAnalytics(sdk, settings);\n state.loaded = true;\n return state.analytics;\n}\n\n/**\n * Stable hash of a traits object for state-diffing. Uses a deterministic\n * JSON stringify - key order matters but is consistent within a session\n * because object literal keys come in insertion order.\n */\nfunction hashTraits(traits: Record<string, unknown> | undefined): string {\n if (!traits) return '';\n try {\n return JSON.stringify(traits);\n } catch {\n return '';\n }\n}\n\n/**\n * Build the Segment event options object with consent context from the\n * current walker consent state. Returns undefined unless the user has\n * explicitly opted in by configuring `settings.consent` - this keeps the\n * consent forwarding feature off-by-default so events stay clean.\n *\n * `settings.consent` maps walkerOS consent keys → Segment category names.\n * Only walker keys present in the mapping are forwarded.\n */\nfunction buildEventOptions(\n event: WalkerOS.Event,\n settings: Settings,\n): SegmentEventOptions | undefined {\n const map = settings.consent;\n if (!map || Object.keys(map).length === 0) return undefined;\n\n const consentState = event.consent;\n if (!isObject(consentState)) return undefined;\n\n const categoryPreferences: Record<string, boolean> = {};\n for (const [walkerKey, segmentKey] of Object.entries(map)) {\n if (walkerKey in consentState) {\n categoryPreferences[segmentKey] = consentState[walkerKey] === true;\n }\n }\n\n if (Object.keys(categoryPreferences).length === 0) return undefined;\n\n return {\n context: {\n consent: {\n categoryPreferences,\n },\n },\n };\n}\n\n/**\n * Resolve and apply identity mapping. Calls analytics.identify() only\n * when the resolved value differs from the last-fired identity.\n */\nfunction applyIdentify(\n analytics: SegmentAnalytics,\n resolved: Record<string, unknown>,\n state: RuntimeState,\n options?: SegmentEventOptions,\n): void {\n const last = state.lastIdentity || {};\n const userId = isString(resolved.userId) ? resolved.userId : undefined;\n const anonymousId = isString(resolved.anonymousId)\n ? resolved.anonymousId\n : undefined;\n const traits =\n isObject(resolved.traits) && !isArray(resolved.traits)\n ? (resolved.traits as Record<string, unknown>)\n : undefined;\n\n if (anonymousId && anonymousId !== last.anonymousId) {\n analytics.setAnonymousId(anonymousId);\n }\n\n const traitsHash = hashTraits(traits);\n const userIdChanged = userId !== last.userId;\n const traitsChanged = traitsHash !== (last.traitsHash ?? '');\n const hasChange = userIdChanged || (traits !== undefined && traitsChanged);\n\n if (!hasChange && !anonymousId) return;\n\n if (options) {\n analytics.identify(userId, traits || {}, options);\n } else {\n analytics.identify(userId, traits || {});\n }\n\n state.lastIdentity = {\n userId,\n anonymousId: anonymousId ?? last.anonymousId,\n traitsHash,\n };\n}\n\nfunction applyGroup(\n analytics: SegmentAnalytics,\n resolved: Record<string, unknown>,\n state: RuntimeState,\n options?: SegmentEventOptions,\n): void {\n const groupId = isString(resolved.groupId) ? resolved.groupId : undefined;\n if (!groupId) return;\n\n const traits =\n isObject(resolved.traits) && !isArray(resolved.traits)\n ? (resolved.traits as Record<string, unknown>)\n : undefined;\n\n const last = state.lastGroup || {};\n const traitsHash = hashTraits(traits);\n const groupIdChanged = groupId !== last.groupId;\n const traitsChanged = traitsHash !== (last.traitsHash ?? '');\n if (!groupIdChanged && !traitsChanged) return;\n\n if (options) {\n analytics.group(groupId, traits || {}, options);\n } else {\n analytics.group(groupId, traits || {});\n }\n\n state.lastGroup = { groupId, traitsHash };\n}\n\nasync function applyPage(\n analytics: SegmentAnalytics,\n pageMapping: unknown,\n event: WalkerOS.Event,\n collector: unknown,\n options?: SegmentEventOptions,\n): Promise<void> {\n // Boolean true → call page() with no args (SDK auto-collect).\n if (pageMapping === true) {\n if (options) {\n analytics.page(undefined, undefined, undefined, options);\n } else {\n analytics.page();\n }\n return;\n }\n\n const resolved = await getMappingValue(\n event,\n pageMapping as Parameters<typeof getMappingValue>[1],\n { collector } as Parameters<typeof getMappingValue>[2],\n );\n if (!isObject(resolved)) return;\n\n const r = resolved as {\n category?: unknown;\n name?: unknown;\n properties?: unknown;\n };\n const category = isString(r.category) ? r.category : undefined;\n const name = isString(r.name) ? r.name : undefined;\n const properties =\n isObject(r.properties) && !isArray(r.properties)\n ? (r.properties as Record<string, unknown>)\n : undefined;\n\n if (options) {\n analytics.page(category ?? null, name ?? null, properties ?? {}, options);\n } else if (properties !== undefined) {\n analytics.page(category ?? null, name ?? null, properties);\n } else if (name !== undefined || category !== undefined) {\n analytics.page(category ?? null, name ?? null);\n } else {\n analytics.page();\n }\n}\n\nexport const destinationSegment: Destination = {\n type: 'segment',\n\n config: {},\n\n init({ config, env }) {\n if (!config.settings?.apiKey) return false;\n const settings = config.settings as Settings;\n\n const sdk = getSegment(env as Env | undefined);\n const _state: RuntimeState = {\n lastIdentity: {},\n lastGroup: {},\n loaded: false,\n };\n\n // If config.consent is declared, defer load() until on('consent').\n // Otherwise load immediately.\n const requiresConsent =\n !!config.consent && Object.keys(config.consent).length > 0;\n\n if (!requiresConsent) {\n _state.analytics = loadAnalytics(sdk, settings);\n _state.loaded = true;\n }\n\n return { ...config, settings: { ...settings, _state } };\n },\n\n async push(event, { config, rule, env, data, collector }) {\n const sdk = getSegment(env as Env | undefined);\n const settings = (config.settings || {}) as Settings;\n const state: RuntimeState = settings._state || {};\n const analytics = resolveAnalytics(sdk, settings, state);\n if (!analytics) return;\n\n const mappingSettings = rule?.settings || {};\n const options = buildEventOptions(event, settings);\n\n // 1. Reset fires first so subsequent identity calls start clean.\n if (mappingSettings.reset !== undefined) {\n const resolved =\n typeof mappingSettings.reset === 'boolean'\n ? mappingSettings.reset\n : await getMappingValue(event, mappingSettings.reset, { collector });\n if (resolved) {\n analytics.reset();\n state.lastIdentity = {};\n state.lastGroup = {};\n }\n }\n\n // 2. Identity - rule-level override wins over destination-level.\n const identifyMapping = mappingSettings.identify ?? settings.identify;\n if (identifyMapping !== undefined) {\n const resolved = await getMappingValue(event, identifyMapping, {\n collector,\n });\n if (isObject(resolved)) {\n applyIdentify(\n analytics,\n resolved as Record<string, unknown>,\n state,\n options,\n );\n }\n }\n\n // 3. Group - rule-level override wins over destination-level.\n const groupMapping = mappingSettings.group ?? settings.group;\n if (groupMapping !== undefined) {\n const resolved = await getMappingValue(event, groupMapping, {\n collector,\n });\n if (isObject(resolved)) {\n applyGroup(\n analytics,\n resolved as Record<string, unknown>,\n state,\n options,\n );\n }\n }\n\n // 4. Page (explicit first-class Segment pattern)\n if (mappingSettings.page !== undefined) {\n await applyPage(\n analytics,\n mappingSettings.page,\n event,\n collector,\n options,\n );\n }\n\n // 5. Default track call unless rule opts out via silent.\n if (rule?.silent !== true) {\n const eventName = isString(rule?.name) ? rule.name : event.name;\n const properties = isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n\n if (options) {\n analytics.track(eventName, properties, options);\n } else {\n analytics.track(eventName, properties);\n }\n }\n\n settings._state = state;\n },\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n\n const consent = context.data as WalkerOS.Consent;\n const required = context.config?.consent;\n if (!required || Object.keys(required).length === 0) return;\n\n const settings = (context.config?.settings || {}) as Settings;\n const state: RuntimeState = settings._state || {};\n const sdk = getSegment(context.env as Env | undefined);\n\n const allGranted = Object.keys(required).every(\n (key) => consent[key] === true,\n );\n\n if (allGranted && !state.loaded) {\n state.analytics = loadAnalytics(sdk, settings);\n state.loaded = true;\n settings._state = state;\n }\n // If not all granted, walkerOS's config.consent gate blocks subsequent\n // push() calls - no SDK action needed (Segment has no opt-out method).\n },\n};\n\nexport default destinationSegment;\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationWeb } from '@walkeros/web-core';\nimport type {\n AnalyticsBrowserSettings,\n InitOptions,\n} from '@segment/analytics-next';\n\n/**\n * Destination-level settings.\n *\n * Extends Segment's `InitOptions` (minus the `group` key which clashes with\n * walkerOS's own `group` mapping) so every passthrough option (cookie,\n * storage, integrations, plan, etc.) keeps IntelliSense intact. walkerOS\n * adds:\n * - `apiKey` (required): maps to `writeKey` in `AnalyticsBrowser.load()`\n * - `identify`: destination-level identity mapping (Segment `identify()`)\n * - `group`: destination-level group mapping (Segment `group()`)\n * - `consent`: walkerOS consent key → Segment category name mapping\n * - `_state`: runtime state (not user-facing, mutated by init/push)\n */\nexport interface Settings extends Omit<InitOptions, 'group'> {\n /** Segment write key. Maps to `writeKey` in the load() settings arg. */\n apiKey: string;\n /** walkerOS mapping value resolving to an identity object (userId, traits, anonymousId). */\n identify?: WalkerOSMapping.Value;\n /** walkerOS mapping value resolving to a group object (groupId, traits). */\n group?: WalkerOSMapping.Value;\n /**\n * Mapping from walkerOS consent keys → Segment `categoryPreferences` keys.\n * Example: { marketing: \"Advertising\", analytics: \"Analytics\" }\n * Only mapped keys are forwarded. If omitted, no consent context is sent.\n */\n consent?: Record<string, string>;\n /** Runtime state: populated by init() and mutated by push(). Not user-facing. */\n _state?: RuntimeState;\n}\n\nexport interface RuntimeState {\n /** Last-set identity values, used to skip redundant identify() calls. */\n lastIdentity?: {\n userId?: string;\n anonymousId?: string;\n traitsHash?: string;\n };\n /** Last-set group assignment, used to skip redundant group() calls. */\n lastGroup?: {\n groupId?: string;\n traitsHash?: string;\n };\n /** Holds the AnalyticsBrowser instance created in init(). */\n analytics?: SegmentAnalytics;\n /** True once load() has been called (may be deferred pending consent). */\n loaded?: boolean;\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Every feature here is a walkerOS mapping\n * value resolved via getMappingValue(). The resolved object's keys drive\n * which Segment SDK methods to call.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n page?: WalkerOSMapping.Value | boolean;\n reset?: WalkerOSMapping.Value | boolean;\n}\n\n/**\n * Segment SDK surface: the subset of @segment/analytics-next the\n * destination actually uses. Mirrors the AnalyticsBrowser instance shape\n * so tests can mock each method individually via env.analytics.\n */\nexport interface SegmentAnalytics {\n track: (\n event: string,\n properties?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n identify: (\n userId?: string | Record<string, unknown>,\n traits?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n group: (\n groupId: string,\n traits?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n page: (\n category?: string | null,\n name?: string | null,\n properties?: Record<string, unknown>,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n alias: (\n to: string,\n from?: string,\n options?: SegmentEventOptions,\n ) => Promise<unknown> | void;\n reset: () => Promise<unknown> | void;\n setAnonymousId: (id: string) => Promise<unknown> | void;\n}\n\n/**\n * Segment event options: the fourth argument to track/identify/group/page\n * that carries context and integration overrides. The destination uses\n * this to stamp consent context on every event.\n */\nexport interface SegmentEventOptions {\n context?: {\n consent?: {\n categoryPreferences?: Record<string, boolean>;\n };\n [key: string]: unknown;\n };\n integrations?: Record<string, boolean | Record<string, unknown>>;\n}\n\n/**\n * The module namespace used for `env?.analytics ?? realSegment`.\n * AnalyticsBrowser is the class; tests mock with a factory that returns\n * an object matching SegmentAnalytics.\n *\n * Task 1 verified: `AnalyticsBrowser.load(settings, options)` returns an\n * `AnalyticsBrowser` instance **synchronously**. The instance buffers\n * method calls until the internal load promise resolves.\n */\nexport interface SegmentSDK {\n load: (\n settings: AnalyticsBrowserSettings,\n options?: InitOptions,\n ) => SegmentAnalytics;\n}\n\n/**\n * Env: optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @segment/analytics-next module.\n * Tests provide a mock via env.analytics = { ... }.\n */\nexport interface Env extends DestinationWeb.Env {\n analytics?: SegmentSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport type Destination = DestinationWeb.Destination<Types>;\nexport type Config = DestinationWeb.Config<Types>;\n\nexport interface SegmentDestination extends Destination {\n env?: Env;\n}\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n"],"mappings":"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","names":[]}
|
package/dist/walkerOS.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$meta": {
|
|
3
3
|
"package": "@walkeros/web-destination-segment",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.8.0-next-1791441510508",
|
|
5
5
|
"type": "destination",
|
|
6
6
|
"platform": [
|
|
7
7
|
"web"
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
},
|
|
42
42
|
"cdnURL": {
|
|
43
43
|
"type": "string",
|
|
44
|
-
"description": "
|
|
44
|
+
"description": "Not applied: the destination passes it in the load() options, where Analytics.js does not read it, so settings are always fetched from https://cdn.segment.com."
|
|
45
45
|
},
|
|
46
46
|
"initialPageview": {
|
|
47
47
|
"type": "boolean",
|
|
48
|
-
"description": "When true, the SDK fires an automatic initial page() call on load. Default: false
|
|
48
|
+
"description": "When true, the SDK fires an automatic initial page() call on load. Default: false (walkerOS sources handle page tracking, so this is disabled to avoid duplicate page views)."
|
|
49
49
|
},
|
|
50
50
|
"disableClientPersistence": {
|
|
51
51
|
"type": "boolean",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"description": "Enable/disable downstream Segment destinations. Example: { \"All\": true, \"Mixpanel\": false }."
|
|
73
73
|
},
|
|
74
74
|
"identify": {
|
|
75
|
-
"description": "Destination-level identity mapping. Resolves to an object with any of: userId, traits, anonymousId.
|
|
75
|
+
"description": "Destination-level identity mapping. Resolves to an object with any of: userId, traits, anonymousId. Resolved on every push: analytics.identify fires on the first push and when userId or traits change, and on every push while anonymousId is set."
|
|
76
76
|
},
|
|
77
77
|
"group": {
|
|
78
78
|
"description": "Destination-level group mapping. Resolves to an object with: groupId, traits. Fires on the first push and re-fires on change."
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"additionalProperties": {
|
|
86
86
|
"type": "string"
|
|
87
87
|
},
|
|
88
|
-
"description": "Mapping from walkerOS consent keys → Segment `categoryPreferences` keys. Example: { \"marketing\": \"Advertising\", \"analytics\": \"Analytics\" }.
|
|
88
|
+
"description": "Mapping from walkerOS consent keys → Segment `categoryPreferences` keys. Example: { \"marketing\": \"Advertising\", \"analytics\": \"Analytics\" }. Only mapped keys are forwarded; when omitted, no consent context is sent."
|
|
89
89
|
}
|
|
90
90
|
},
|
|
91
91
|
"required": [
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
"consent": {
|
|
163
163
|
"functional": true
|
|
164
164
|
},
|
|
165
|
-
"id": "
|
|
165
|
+
"id": "faaf22436bb0122a",
|
|
166
166
|
"trigger": "test",
|
|
167
167
|
"entity": "company",
|
|
168
168
|
"action": "update",
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
},
|
|
209
209
|
"consentContextForwarded": {
|
|
210
210
|
"title": "Consent context",
|
|
211
|
-
"description": "
|
|
211
|
+
"description": "With settings.consent mapping analytics to Analytics and marketing to Advertising, the event consent is stamped on every Segment call via context.consent.categoryPreferences for downstream filtering.",
|
|
212
212
|
"in": {
|
|
213
213
|
"name": "product view",
|
|
214
214
|
"data": {
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
"analytics": true,
|
|
241
241
|
"marketing": true
|
|
242
242
|
},
|
|
243
|
-
"id": "
|
|
243
|
+
"id": "f2d033cf93d5c0bd",
|
|
244
244
|
"trigger": "load",
|
|
245
245
|
"entity": "product",
|
|
246
246
|
"action": "view",
|
|
@@ -277,9 +277,63 @@
|
|
|
277
277
|
]
|
|
278
278
|
]
|
|
279
279
|
},
|
|
280
|
+
"consentContextOmitted": {
|
|
281
|
+
"public": false,
|
|
282
|
+
"in": {
|
|
283
|
+
"name": "product view",
|
|
284
|
+
"data": {
|
|
285
|
+
"id": "ers",
|
|
286
|
+
"name": "Everyday Ruck Snack",
|
|
287
|
+
"color": "black",
|
|
288
|
+
"size": "l",
|
|
289
|
+
"price": 420
|
|
290
|
+
},
|
|
291
|
+
"context": {
|
|
292
|
+
"shopping": [
|
|
293
|
+
"detail",
|
|
294
|
+
0
|
|
295
|
+
]
|
|
296
|
+
},
|
|
297
|
+
"globals": {
|
|
298
|
+
"pagegroup": "shop"
|
|
299
|
+
},
|
|
300
|
+
"custom": {
|
|
301
|
+
"completely": "random"
|
|
302
|
+
},
|
|
303
|
+
"user": {
|
|
304
|
+
"id": "us3r",
|
|
305
|
+
"device": "c00k13",
|
|
306
|
+
"session": "s3ss10n"
|
|
307
|
+
},
|
|
308
|
+
"nested": [],
|
|
309
|
+
"consent": {
|
|
310
|
+
"analytics": true,
|
|
311
|
+
"marketing": false
|
|
312
|
+
},
|
|
313
|
+
"id": "d0faad707fa9fa38",
|
|
314
|
+
"trigger": "load",
|
|
315
|
+
"entity": "product",
|
|
316
|
+
"action": "view",
|
|
317
|
+
"timestamp": 1700000113000,
|
|
318
|
+
"timing": 3.14,
|
|
319
|
+
"source": {
|
|
320
|
+
"count": 1,
|
|
321
|
+
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
|
|
322
|
+
"type": "collector",
|
|
323
|
+
"schema": "4"
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
"out": [
|
|
327
|
+
[
|
|
328
|
+
"analytics.track",
|
|
329
|
+
"product view",
|
|
330
|
+
{}
|
|
331
|
+
]
|
|
332
|
+
]
|
|
333
|
+
},
|
|
280
334
|
"consentGrantDeferredLoad": {
|
|
281
335
|
"title": "Consent deferred load",
|
|
282
|
-
"description": "
|
|
336
|
+
"description": "With consent: { analytics: true } in the destination config, a walker consent grant triggers the deferred Segment analytics.load with the configured writeKey.",
|
|
283
337
|
"command": "consent",
|
|
284
338
|
"in": {
|
|
285
339
|
"analytics": true
|
|
@@ -337,7 +391,7 @@
|
|
|
337
391
|
"consent": {
|
|
338
392
|
"functional": true
|
|
339
393
|
},
|
|
340
|
-
"id": "
|
|
394
|
+
"id": "5d677b7e2f5a6369",
|
|
341
395
|
"trigger": "load",
|
|
342
396
|
"entity": "product",
|
|
343
397
|
"action": "view",
|
|
@@ -360,7 +414,7 @@
|
|
|
360
414
|
},
|
|
361
415
|
"destinationLevelIdentify": {
|
|
362
416
|
"title": "Destination identify",
|
|
363
|
-
"description": "
|
|
417
|
+
"description": "With settings.identify mapping userId to 'user.id', the destination calls analytics.identify before the default track.",
|
|
364
418
|
"in": {
|
|
365
419
|
"name": "page view",
|
|
366
420
|
"data": {
|
|
@@ -399,7 +453,7 @@
|
|
|
399
453
|
"consent": {
|
|
400
454
|
"functional": true
|
|
401
455
|
},
|
|
402
|
-
"id": "
|
|
456
|
+
"id": "539191ad5bece714",
|
|
403
457
|
"trigger": "load",
|
|
404
458
|
"entity": "page",
|
|
405
459
|
"action": "view",
|
|
@@ -434,7 +488,7 @@
|
|
|
434
488
|
},
|
|
435
489
|
"destinationLevelInclude": {
|
|
436
490
|
"title": "Include data",
|
|
437
|
-
"description": "
|
|
491
|
+
"description": "With include: ['data'] in the destination config, the event data section is flattened into prefixed Segment track properties.",
|
|
438
492
|
"in": {
|
|
439
493
|
"name": "product view",
|
|
440
494
|
"data": {
|
|
@@ -465,7 +519,7 @@
|
|
|
465
519
|
"consent": {
|
|
466
520
|
"functional": true
|
|
467
521
|
},
|
|
468
|
-
"id": "
|
|
522
|
+
"id": "9fb483eedd199468",
|
|
469
523
|
"trigger": "load",
|
|
470
524
|
"entity": "product",
|
|
471
525
|
"action": "view",
|
|
@@ -595,7 +649,7 @@
|
|
|
595
649
|
"consent": {
|
|
596
650
|
"functional": true
|
|
597
651
|
},
|
|
598
|
-
"id": "
|
|
652
|
+
"id": "ace963525353e615",
|
|
599
653
|
"trigger": "load",
|
|
600
654
|
"entity": "order",
|
|
601
655
|
"action": "complete",
|
|
@@ -714,7 +768,7 @@
|
|
|
714
768
|
"consent": {
|
|
715
769
|
"functional": true
|
|
716
770
|
},
|
|
717
|
-
"id": "
|
|
771
|
+
"id": "505e6669f12162b4",
|
|
718
772
|
"trigger": "load",
|
|
719
773
|
"entity": "page",
|
|
720
774
|
"action": "view",
|
|
@@ -756,7 +810,7 @@
|
|
|
756
810
|
},
|
|
757
811
|
"pageViewMinimal": {
|
|
758
812
|
"title": "Page view minimal",
|
|
759
|
-
"description": "A mapping with page: true fires an empty analytics.page call relying on
|
|
813
|
+
"description": "A mapping with page: true fires an empty analytics.page call relying on Segment's auto-collection.",
|
|
760
814
|
"in": {
|
|
761
815
|
"name": "page view",
|
|
762
816
|
"data": {
|
|
@@ -795,7 +849,7 @@
|
|
|
795
849
|
"consent": {
|
|
796
850
|
"functional": true
|
|
797
851
|
},
|
|
798
|
-
"id": "
|
|
852
|
+
"id": "9b82f9523dad2aff",
|
|
799
853
|
"trigger": "load",
|
|
800
854
|
"entity": "page",
|
|
801
855
|
"action": "view",
|
|
@@ -858,7 +912,7 @@
|
|
|
858
912
|
"consent": {
|
|
859
913
|
"functional": true
|
|
860
914
|
},
|
|
861
|
-
"id": "
|
|
915
|
+
"id": "3d74b8e6a352d27d",
|
|
862
916
|
"trigger": "test",
|
|
863
917
|
"entity": "profile",
|
|
864
918
|
"action": "update",
|
|
@@ -901,7 +955,7 @@
|
|
|
901
955
|
},
|
|
902
956
|
"ruleIncludeReplaces": {
|
|
903
957
|
"title": "Rule include overrides",
|
|
904
|
-
"description": "
|
|
958
|
+
"description": "With include: ['data'] in the destination config, the rule's include: ['globals'] replaces it, so this event forwards only globals.",
|
|
905
959
|
"in": {
|
|
906
960
|
"name": "order complete",
|
|
907
961
|
"data": {
|
|
@@ -979,7 +1033,7 @@
|
|
|
979
1033
|
"consent": {
|
|
980
1034
|
"functional": true
|
|
981
1035
|
},
|
|
982
|
-
"id": "
|
|
1036
|
+
"id": "6f83f93c2785edef",
|
|
983
1037
|
"trigger": "load",
|
|
984
1038
|
"entity": "order",
|
|
985
1039
|
"action": "complete",
|
|
@@ -1051,7 +1105,7 @@
|
|
|
1051
1105
|
"consent": {
|
|
1052
1106
|
"functional": true
|
|
1053
1107
|
},
|
|
1054
|
-
"id": "
|
|
1108
|
+
"id": "1674200bc045497b",
|
|
1055
1109
|
"trigger": "test",
|
|
1056
1110
|
"entity": "user",
|
|
1057
1111
|
"action": "login",
|
|
@@ -1146,7 +1200,7 @@
|
|
|
1146
1200
|
"consent": {
|
|
1147
1201
|
"functional": true
|
|
1148
1202
|
},
|
|
1149
|
-
"id": "
|
|
1203
|
+
"id": "1e3f1a26bbe402b5",
|
|
1150
1204
|
"trigger": "test",
|
|
1151
1205
|
"entity": "user",
|
|
1152
1206
|
"action": "logout",
|
|
@@ -1213,7 +1267,7 @@
|
|
|
1213
1267
|
"consent": {
|
|
1214
1268
|
"functional": true
|
|
1215
1269
|
},
|
|
1216
|
-
"id": "
|
|
1270
|
+
"id": "31169705eeeeefcb",
|
|
1217
1271
|
"trigger": "test",
|
|
1218
1272
|
"entity": "debug",
|
|
1219
1273
|
"action": "noise",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walkeros/web-destination-segment",
|
|
3
3
|
"description": "Segment CDP web destination for walkerOS (@segment/analytics-next, full Segment Spec)",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.8.0-next-1791441510508",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@segment/analytics-next": "^1.82.0",
|
|
42
|
-
"@walkeros/core": "4.
|
|
43
|
-
"@walkeros/web-core": "4.
|
|
42
|
+
"@walkeros/core": "4.8.0-next-1791441510508",
|
|
43
|
+
"@walkeros/web-core": "4.8.0-next-1791441510508"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@walkeros/collector": "4.
|
|
46
|
+
"@walkeros/collector": "4.8.0-next-1791441510508"
|
|
47
47
|
},
|
|
48
48
|
"repository": {
|
|
49
49
|
"url": "git+https://github.com/elbwalker/walkerOS.git",
|