@walkeros/web-destination-optimizely 3.4.0-next-1776749829492

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/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @walkeros/web-destination-optimizely
2
+
3
+ Optimizely Feature Experimentation web destination for
4
+ [walkerOS](https://github.com/elbwalker/walkerOS). Forwards conversion events to
5
+ Optimizely via the official `@optimizely/optimizely-sdk` v6 modular API with
6
+ support for revenue/value event tags, user targeting attributes, and
7
+ consent-based client lifecycle.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @walkeros/web-destination-optimizely
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```json
18
+ {
19
+ "destinations": {
20
+ "optimizely": {
21
+ "package": "@walkeros/web-destination-optimizely",
22
+ "config": {
23
+ "consent": { "analytics": true },
24
+ "settings": {
25
+ "sdkKey": "YOUR_SDK_KEY",
26
+ "userId": "user.id"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ Programmatic:
35
+
36
+ ```ts
37
+ import { startFlow } from '@walkeros/collector';
38
+ import destinationOptimizely from '@walkeros/web-destination-optimizely';
39
+
40
+ const { elb } = await startFlow();
41
+
42
+ elb('walker destination', destinationOptimizely, {
43
+ consent: { analytics: true },
44
+ settings: {
45
+ sdkKey: 'YOUR_SDK_KEY',
46
+ userId: 'user.id',
47
+ },
48
+ });
49
+ ```
50
+
51
+ ## Settings
52
+
53
+ | Key | Type | Default | Description |
54
+ | ---------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------- |
55
+ | `sdkKey` | `string` | — | Required. Optimizely Feature Experimentation SDK key (Settings > Environments). |
56
+ | `userId` | `Mapping` | — | walkerOS mapping value resolving to the userId used for bucketing (e.g. `"user.id"`). Required per event. |
57
+ | `attributes` | `Mapping` | — | Destination-level user attributes for audience targeting. Applied via `createUserContext()`. |
58
+ | `updateInterval` | `number` | `60000` | Datafile polling interval (ms). |
59
+ | `autoUpdate` | `boolean` | `true` | Poll for datafile updates. |
60
+ | `batchSize` | `number` | `10` | Events per batch (batch event processor). |
61
+ | `flushInterval` | `number` | `1000` | Batch flush interval (ms). |
62
+ | `skipOdp` | `boolean` | `true` | Skip Optimizely Data Platform manager init. |
63
+
64
+ ## Mapping
65
+
66
+ Per-rule overrides under `mapping.<entity>.<action>.settings`:
67
+
68
+ | Key | Type | Description |
69
+ | ------------ | --------- | --------------------------------------------------------------------------------------- |
70
+ | `eventKey` | `string` | Override event key sent to Optimizely. If omitted, the walkerOS event name is used. |
71
+ | `revenue` | `Mapping` | Resolves to integer cents. Passed as `eventTags.revenue`. |
72
+ | `value` | `Mapping` | Resolves to a float. Passed as `eventTags.value`. |
73
+ | `eventTags` | `Mapping` | Extra tags. Spread into the `eventTags` object. |
74
+ | `attributes` | `Mapping` | Per-event user attributes. Applied via `setAttribute()` before the `trackEvent()` call. |
75
+
76
+ Use `rule.name` to rename the event key and `rule.skip = true` to fire
77
+ attributes without a `trackEvent()` call.
78
+
79
+ ## Revenue
80
+
81
+ Optimizely expects revenue as an **integer in cents** (e.g. `7281` = `$72.81`).
82
+ The destination passes the resolved value through without conversion — you must
83
+ provide cents.
84
+
85
+ ```json
86
+ {
87
+ "order": {
88
+ "complete": {
89
+ "name": "purchase",
90
+ "settings": {
91
+ "revenue": "data.revenue_cents",
92
+ "value": "data.total"
93
+ }
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ ## Consent
100
+
101
+ Two layers:
102
+
103
+ 1. **`config.consent`** — walkerOS gates delivery. Events are queued until
104
+ required consent keys resolve to `true`.
105
+ 2. **`on('consent')`** — the destination closes the Optimizely client (flushing
106
+ queued events and stopping polling) when any required key flips to `false`.
107
+ On re-grant, the next push re-initializes the client.
108
+
109
+ ```json
110
+ "config": { "consent": { "analytics": true } }
111
+ ```
112
+
113
+ ## Decide / Feature Flags
114
+
115
+ This destination intentionally does **not** expose `decide()` — experiment
116
+ decisions belong in application code where UI branching happens. This package
117
+ covers the outbound conversion-tracking use case.
118
+
119
+ ## License
120
+
121
+ MIT
package/dist/dev.d.mts ADDED
@@ -0,0 +1,193 @@
1
+ import * as _walkeros_core_dev from '@walkeros/core/dev';
2
+ import { z } from '@walkeros/core/dev';
3
+ import { Mapping as Mapping$1, Flow } from '@walkeros/core';
4
+ import { DestinationWeb } from '@walkeros/web-core';
5
+
6
+ declare const SettingsSchema: z.ZodObject<{
7
+ sdkKey: z.ZodString;
8
+ userId: z.ZodOptional<z.ZodUnknown>;
9
+ attributes: z.ZodOptional<z.ZodUnknown>;
10
+ updateInterval: z.ZodOptional<z.ZodNumber>;
11
+ autoUpdate: z.ZodOptional<z.ZodBoolean>;
12
+ batchSize: z.ZodOptional<z.ZodNumber>;
13
+ flushInterval: z.ZodOptional<z.ZodNumber>;
14
+ skipOdp: z.ZodOptional<z.ZodBoolean>;
15
+ }, z.core.$strip>;
16
+ type Settings$1 = z.infer<typeof SettingsSchema>;
17
+
18
+ declare const MappingSchema: z.ZodObject<{
19
+ eventKey: z.ZodOptional<z.ZodString>;
20
+ revenue: z.ZodOptional<z.ZodUnknown>;
21
+ value: z.ZodOptional<z.ZodUnknown>;
22
+ eventTags: z.ZodOptional<z.ZodUnknown>;
23
+ attributes: z.ZodOptional<z.ZodUnknown>;
24
+ }, z.core.$strip>;
25
+ type Mapping = z.infer<typeof MappingSchema>;
26
+
27
+ declare const settings: _walkeros_core_dev.JSONSchema;
28
+ declare const mapping: _walkeros_core_dev.JSONSchema;
29
+
30
+ type index$1_Mapping = Mapping;
31
+ declare const index$1_MappingSchema: typeof MappingSchema;
32
+ declare const index$1_SettingsSchema: typeof SettingsSchema;
33
+ declare const index$1_mapping: typeof mapping;
34
+ declare const index$1_settings: typeof settings;
35
+ declare namespace index$1 {
36
+ export { type index$1_Mapping as Mapping, index$1_MappingSchema as MappingSchema, type Settings$1 as Settings, index$1_SettingsSchema as SettingsSchema, index$1_mapping as mapping, index$1_settings as settings };
37
+ }
38
+
39
+ /**
40
+ * Destination-level settings.
41
+ */
42
+ interface Settings {
43
+ /** Optimizely Feature Experimentation SDK key. Required. */
44
+ sdkKey: string;
45
+ /** walkerOS mapping value to resolve userId for experiment bucketing. */
46
+ userId?: Mapping$1.Value;
47
+ /** User attributes for audience targeting, applied to every event. */
48
+ attributes?: Mapping$1.Value;
49
+ /** Polling interval for datafile updates in ms. Default: 60000. */
50
+ updateInterval?: number;
51
+ /** Auto-update datafile via polling. Default: true. */
52
+ autoUpdate?: boolean;
53
+ /** Batch event processor: events per batch. Default: 10. */
54
+ batchSize?: number;
55
+ /** Batch event processor: flush interval in ms. Default: 1000. */
56
+ flushInterval?: number;
57
+ /** Skip ODP manager initialization. Default: true. */
58
+ skipOdp?: boolean;
59
+ /** Runtime state -- not user-facing. Mutated by init/push. */
60
+ _state?: RuntimeState;
61
+ }
62
+ interface RuntimeState {
63
+ /** The Optimizely client instance (typed as OptimizelyClient). */
64
+ client?: OptimizelyClient;
65
+ /** Cached user context. Recreated when userId changes. */
66
+ userContext?: OptimizelyUserContext;
67
+ /** Last resolved userId to detect identity changes. */
68
+ lastUserId?: string;
69
+ }
70
+ /**
71
+ * OptimizelyClient -- the subset of the Optimizely SDK client the destination
72
+ * actually uses. Tests provide a mock via env.optimizely.
73
+ */
74
+ interface OptimizelyClient {
75
+ onReady: () => Promise<{
76
+ success: boolean;
77
+ }>;
78
+ createUserContext: (userId: string, attributes?: Record<string, unknown>) => OptimizelyUserContext | null;
79
+ close: () => void;
80
+ }
81
+ /**
82
+ * OptimizelyUserContext -- user context methods the destination calls.
83
+ */
84
+ interface OptimizelyUserContext {
85
+ trackEvent: (eventKey: string, eventTags?: Record<string, unknown>) => void;
86
+ setAttribute: (key: string, value: unknown) => void;
87
+ }
88
+ /**
89
+ * OptimizelySDK -- factory functions the destination imports from the SDK.
90
+ * Tests provide this via env.optimizely to avoid importing the real SDK.
91
+ */
92
+ interface OptimizelySDK {
93
+ createInstance: (config: Record<string, unknown>) => OptimizelyClient;
94
+ createPollingProjectConfigManager: (config: Record<string, unknown>) => unknown;
95
+ createBatchEventProcessor: (config: Record<string, unknown>) => unknown;
96
+ }
97
+ /**
98
+ * Env -- optional SDK override. Production leaves env.optimizely undefined
99
+ * and the destination falls back to the real @optimizely/optimizely-sdk
100
+ * import. Tests provide a mock via env.optimizely.
101
+ */
102
+ interface Env extends DestinationWeb.Env {
103
+ optimizely?: OptimizelySDK;
104
+ }
105
+
106
+ /**
107
+ * Pre-init env -- all methods are no-ops until the test runner wires spies.
108
+ */
109
+ declare const init: Env | undefined;
110
+ /**
111
+ * Post-init env -- same shape. The test runner clones this and replaces
112
+ * individual methods with jest.fn() so it can assert on calls.
113
+ */
114
+ declare const push: Env;
115
+ /** Simulation tracking paths for CLI --simulate. */
116
+ declare const simulation: string[];
117
+
118
+ declare const env_init: typeof init;
119
+ declare const env_push: typeof push;
120
+ declare const env_simulation: typeof simulation;
121
+ declare namespace env {
122
+ export { env_init as init, env_push as push, env_simulation as simulation };
123
+ }
124
+
125
+ /**
126
+ * Extended step example that may carry destination-level settings overrides.
127
+ */
128
+ type OptimizelyStepExample = Flow.StepExample & {
129
+ settings?: Partial<Settings>;
130
+ };
131
+ /**
132
+ * Default event forwarding -- every walkerOS event becomes
133
+ * userContext.trackEvent(event.name). No mapping, no eventTags.
134
+ */
135
+ declare const defaultEventForwarding: OptimizelyStepExample;
136
+ /**
137
+ * Mapped event name -- mapping.name renames the event key for Optimizely.
138
+ * The eventKey must match an event created in the Optimizely project.
139
+ */
140
+ declare const mappedEventName: OptimizelyStepExample;
141
+ /**
142
+ * Revenue tracking -- mapping.settings.revenue resolves to an integer
143
+ * (cents). Passed as eventTags.revenue. The value is a pass-through;
144
+ * the user must provide cents (e.g. 55500 = $555.00).
145
+ */
146
+ declare const orderCompleteRevenue: OptimizelyStepExample;
147
+ /**
148
+ * Per-event attributes -- mapping.settings.attributes resolves to
149
+ * key-value pairs that are applied via setAttribute() before trackEvent().
150
+ */
151
+ declare const signupWithAttributes: OptimizelyStepExample;
152
+ /**
153
+ * Wildcard ignore -- walkerOS's standard way to drop events. The rule
154
+ * matches but does nothing. The destination fires zero SDK calls.
155
+ */
156
+ declare const wildcardIgnored: OptimizelyStepExample;
157
+ /**
158
+ * Skip track with attributes only -- fires setAttribute calls but no
159
+ * trackEvent. Useful for enriching user context without a conversion.
160
+ */
161
+ declare const attributesOnlySkipTrack: OptimizelyStepExample;
162
+ /**
163
+ * Consent revoked -- the destination closes the Optimizely client,
164
+ * flushing queued events and stopping datafile polling.
165
+ */
166
+ declare const consentRevoked: OptimizelyStepExample;
167
+ /**
168
+ * Consent granted -- no immediate SDK action needed. The destination
169
+ * re-initializes on the next push (walkerOS queues events until consent
170
+ * is granted, then re-inits). No calls expected.
171
+ */
172
+ declare const consentGranted: OptimizelyStepExample;
173
+
174
+ type step_OptimizelyStepExample = OptimizelyStepExample;
175
+ declare const step_attributesOnlySkipTrack: typeof attributesOnlySkipTrack;
176
+ declare const step_consentGranted: typeof consentGranted;
177
+ declare const step_consentRevoked: typeof consentRevoked;
178
+ declare const step_defaultEventForwarding: typeof defaultEventForwarding;
179
+ declare const step_mappedEventName: typeof mappedEventName;
180
+ declare const step_orderCompleteRevenue: typeof orderCompleteRevenue;
181
+ declare const step_signupWithAttributes: typeof signupWithAttributes;
182
+ declare const step_wildcardIgnored: typeof wildcardIgnored;
183
+ declare namespace step {
184
+ export { type step_OptimizelyStepExample as OptimizelyStepExample, step_attributesOnlySkipTrack as attributesOnlySkipTrack, step_consentGranted as consentGranted, step_consentRevoked as consentRevoked, step_defaultEventForwarding as defaultEventForwarding, step_mappedEventName as mappedEventName, step_orderCompleteRevenue as orderCompleteRevenue, step_signupWithAttributes as signupWithAttributes, step_wildcardIgnored as wildcardIgnored };
185
+ }
186
+
187
+ declare const index_env: typeof env;
188
+ declare const index_step: typeof step;
189
+ declare namespace index {
190
+ export { index_env as env, index_step as step };
191
+ }
192
+
193
+ export { index as examples, index$1 as schemas };
package/dist/dev.d.ts ADDED
@@ -0,0 +1,193 @@
1
+ import * as _walkeros_core_dev from '@walkeros/core/dev';
2
+ import { z } from '@walkeros/core/dev';
3
+ import { Mapping as Mapping$1, Flow } from '@walkeros/core';
4
+ import { DestinationWeb } from '@walkeros/web-core';
5
+
6
+ declare const SettingsSchema: z.ZodObject<{
7
+ sdkKey: z.ZodString;
8
+ userId: z.ZodOptional<z.ZodUnknown>;
9
+ attributes: z.ZodOptional<z.ZodUnknown>;
10
+ updateInterval: z.ZodOptional<z.ZodNumber>;
11
+ autoUpdate: z.ZodOptional<z.ZodBoolean>;
12
+ batchSize: z.ZodOptional<z.ZodNumber>;
13
+ flushInterval: z.ZodOptional<z.ZodNumber>;
14
+ skipOdp: z.ZodOptional<z.ZodBoolean>;
15
+ }, z.core.$strip>;
16
+ type Settings$1 = z.infer<typeof SettingsSchema>;
17
+
18
+ declare const MappingSchema: z.ZodObject<{
19
+ eventKey: z.ZodOptional<z.ZodString>;
20
+ revenue: z.ZodOptional<z.ZodUnknown>;
21
+ value: z.ZodOptional<z.ZodUnknown>;
22
+ eventTags: z.ZodOptional<z.ZodUnknown>;
23
+ attributes: z.ZodOptional<z.ZodUnknown>;
24
+ }, z.core.$strip>;
25
+ type Mapping = z.infer<typeof MappingSchema>;
26
+
27
+ declare const settings: _walkeros_core_dev.JSONSchema;
28
+ declare const mapping: _walkeros_core_dev.JSONSchema;
29
+
30
+ type index$1_Mapping = Mapping;
31
+ declare const index$1_MappingSchema: typeof MappingSchema;
32
+ declare const index$1_SettingsSchema: typeof SettingsSchema;
33
+ declare const index$1_mapping: typeof mapping;
34
+ declare const index$1_settings: typeof settings;
35
+ declare namespace index$1 {
36
+ export { type index$1_Mapping as Mapping, index$1_MappingSchema as MappingSchema, type Settings$1 as Settings, index$1_SettingsSchema as SettingsSchema, index$1_mapping as mapping, index$1_settings as settings };
37
+ }
38
+
39
+ /**
40
+ * Destination-level settings.
41
+ */
42
+ interface Settings {
43
+ /** Optimizely Feature Experimentation SDK key. Required. */
44
+ sdkKey: string;
45
+ /** walkerOS mapping value to resolve userId for experiment bucketing. */
46
+ userId?: Mapping$1.Value;
47
+ /** User attributes for audience targeting, applied to every event. */
48
+ attributes?: Mapping$1.Value;
49
+ /** Polling interval for datafile updates in ms. Default: 60000. */
50
+ updateInterval?: number;
51
+ /** Auto-update datafile via polling. Default: true. */
52
+ autoUpdate?: boolean;
53
+ /** Batch event processor: events per batch. Default: 10. */
54
+ batchSize?: number;
55
+ /** Batch event processor: flush interval in ms. Default: 1000. */
56
+ flushInterval?: number;
57
+ /** Skip ODP manager initialization. Default: true. */
58
+ skipOdp?: boolean;
59
+ /** Runtime state -- not user-facing. Mutated by init/push. */
60
+ _state?: RuntimeState;
61
+ }
62
+ interface RuntimeState {
63
+ /** The Optimizely client instance (typed as OptimizelyClient). */
64
+ client?: OptimizelyClient;
65
+ /** Cached user context. Recreated when userId changes. */
66
+ userContext?: OptimizelyUserContext;
67
+ /** Last resolved userId to detect identity changes. */
68
+ lastUserId?: string;
69
+ }
70
+ /**
71
+ * OptimizelyClient -- the subset of the Optimizely SDK client the destination
72
+ * actually uses. Tests provide a mock via env.optimizely.
73
+ */
74
+ interface OptimizelyClient {
75
+ onReady: () => Promise<{
76
+ success: boolean;
77
+ }>;
78
+ createUserContext: (userId: string, attributes?: Record<string, unknown>) => OptimizelyUserContext | null;
79
+ close: () => void;
80
+ }
81
+ /**
82
+ * OptimizelyUserContext -- user context methods the destination calls.
83
+ */
84
+ interface OptimizelyUserContext {
85
+ trackEvent: (eventKey: string, eventTags?: Record<string, unknown>) => void;
86
+ setAttribute: (key: string, value: unknown) => void;
87
+ }
88
+ /**
89
+ * OptimizelySDK -- factory functions the destination imports from the SDK.
90
+ * Tests provide this via env.optimizely to avoid importing the real SDK.
91
+ */
92
+ interface OptimizelySDK {
93
+ createInstance: (config: Record<string, unknown>) => OptimizelyClient;
94
+ createPollingProjectConfigManager: (config: Record<string, unknown>) => unknown;
95
+ createBatchEventProcessor: (config: Record<string, unknown>) => unknown;
96
+ }
97
+ /**
98
+ * Env -- optional SDK override. Production leaves env.optimizely undefined
99
+ * and the destination falls back to the real @optimizely/optimizely-sdk
100
+ * import. Tests provide a mock via env.optimizely.
101
+ */
102
+ interface Env extends DestinationWeb.Env {
103
+ optimizely?: OptimizelySDK;
104
+ }
105
+
106
+ /**
107
+ * Pre-init env -- all methods are no-ops until the test runner wires spies.
108
+ */
109
+ declare const init: Env | undefined;
110
+ /**
111
+ * Post-init env -- same shape. The test runner clones this and replaces
112
+ * individual methods with jest.fn() so it can assert on calls.
113
+ */
114
+ declare const push: Env;
115
+ /** Simulation tracking paths for CLI --simulate. */
116
+ declare const simulation: string[];
117
+
118
+ declare const env_init: typeof init;
119
+ declare const env_push: typeof push;
120
+ declare const env_simulation: typeof simulation;
121
+ declare namespace env {
122
+ export { env_init as init, env_push as push, env_simulation as simulation };
123
+ }
124
+
125
+ /**
126
+ * Extended step example that may carry destination-level settings overrides.
127
+ */
128
+ type OptimizelyStepExample = Flow.StepExample & {
129
+ settings?: Partial<Settings>;
130
+ };
131
+ /**
132
+ * Default event forwarding -- every walkerOS event becomes
133
+ * userContext.trackEvent(event.name). No mapping, no eventTags.
134
+ */
135
+ declare const defaultEventForwarding: OptimizelyStepExample;
136
+ /**
137
+ * Mapped event name -- mapping.name renames the event key for Optimizely.
138
+ * The eventKey must match an event created in the Optimizely project.
139
+ */
140
+ declare const mappedEventName: OptimizelyStepExample;
141
+ /**
142
+ * Revenue tracking -- mapping.settings.revenue resolves to an integer
143
+ * (cents). Passed as eventTags.revenue. The value is a pass-through;
144
+ * the user must provide cents (e.g. 55500 = $555.00).
145
+ */
146
+ declare const orderCompleteRevenue: OptimizelyStepExample;
147
+ /**
148
+ * Per-event attributes -- mapping.settings.attributes resolves to
149
+ * key-value pairs that are applied via setAttribute() before trackEvent().
150
+ */
151
+ declare const signupWithAttributes: OptimizelyStepExample;
152
+ /**
153
+ * Wildcard ignore -- walkerOS's standard way to drop events. The rule
154
+ * matches but does nothing. The destination fires zero SDK calls.
155
+ */
156
+ declare const wildcardIgnored: OptimizelyStepExample;
157
+ /**
158
+ * Skip track with attributes only -- fires setAttribute calls but no
159
+ * trackEvent. Useful for enriching user context without a conversion.
160
+ */
161
+ declare const attributesOnlySkipTrack: OptimizelyStepExample;
162
+ /**
163
+ * Consent revoked -- the destination closes the Optimizely client,
164
+ * flushing queued events and stopping datafile polling.
165
+ */
166
+ declare const consentRevoked: OptimizelyStepExample;
167
+ /**
168
+ * Consent granted -- no immediate SDK action needed. The destination
169
+ * re-initializes on the next push (walkerOS queues events until consent
170
+ * is granted, then re-inits). No calls expected.
171
+ */
172
+ declare const consentGranted: OptimizelyStepExample;
173
+
174
+ type step_OptimizelyStepExample = OptimizelyStepExample;
175
+ declare const step_attributesOnlySkipTrack: typeof attributesOnlySkipTrack;
176
+ declare const step_consentGranted: typeof consentGranted;
177
+ declare const step_consentRevoked: typeof consentRevoked;
178
+ declare const step_defaultEventForwarding: typeof defaultEventForwarding;
179
+ declare const step_mappedEventName: typeof mappedEventName;
180
+ declare const step_orderCompleteRevenue: typeof orderCompleteRevenue;
181
+ declare const step_signupWithAttributes: typeof signupWithAttributes;
182
+ declare const step_wildcardIgnored: typeof wildcardIgnored;
183
+ declare namespace step {
184
+ export { type step_OptimizelyStepExample as OptimizelyStepExample, step_attributesOnlySkipTrack as attributesOnlySkipTrack, step_consentGranted as consentGranted, step_consentRevoked as consentRevoked, step_defaultEventForwarding as defaultEventForwarding, step_mappedEventName as mappedEventName, step_orderCompleteRevenue as orderCompleteRevenue, step_signupWithAttributes as signupWithAttributes, step_wildcardIgnored as wildcardIgnored };
185
+ }
186
+
187
+ declare const index_env: typeof env;
188
+ declare const index_step: typeof step;
189
+ declare namespace index {
190
+ export { index_env as env, index_step as step };
191
+ }
192
+
193
+ export { index as examples, index$1 as schemas };