@pendo/openfeature-web-provider 0.1.0

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,175 @@
1
+ # @pendo/openfeature-web-provider
2
+
3
+ OpenFeature provider for [Pendo](https://www.pendo.io/) feature flags in web browsers.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @pendo/openfeature-web-provider @openfeature/web-sdk
9
+ ```
10
+
11
+ ## Prerequisites
12
+
13
+ The Pendo agent must be installed on your page. The provider reads flags from `window.pendo.segmentFlags` which is populated by the agent.
14
+
15
+ ```html
16
+ <script>
17
+ (function(apiKey){
18
+ // Pendo agent snippet
19
+ // ...
20
+ })('YOUR_PENDO_API_KEY');
21
+ </script>
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Basic Setup
27
+
28
+ ```typescript
29
+ import { OpenFeature } from '@openfeature/web-sdk';
30
+ import { PendoProvider } from '@pendo/openfeature-web-provider';
31
+
32
+ // Initialize the provider (waits for Pendo agent to be ready)
33
+ await OpenFeature.setProviderAndWait(new PendoProvider());
34
+
35
+ // Get a client
36
+ const client = OpenFeature.getClient();
37
+ ```
38
+
39
+ ### Evaluating Flags
40
+
41
+ ```typescript
42
+ // Boolean flag
43
+ const showNewFeature = await client.getBooleanValue('new-checkout-flow', false);
44
+
45
+ // String flag (returns "on" or "off")
46
+ const variant = await client.getStringValue('checkout-variant', 'control');
47
+
48
+ // Number flag (returns 1 or 0)
49
+ const flagValue = await client.getNumberValue('feature-score', 0);
50
+
51
+ // Object flag (returns { enabled: true/false })
52
+ const config = await client.getObjectValue('feature-config', { enabled: false });
53
+ ```
54
+
55
+ ### Event Tracking
56
+
57
+ Track custom events to Pendo:
58
+
59
+ ```typescript
60
+ const provider = new PendoProvider();
61
+ await OpenFeature.setProviderAndWait(provider);
62
+
63
+ // Track an event
64
+ provider.track('checkout_started', undefined, { cartValue: '99.99' });
65
+
66
+ // The context parameter is ignored in the web provider
67
+ // since the Pendo agent already knows the current visitor
68
+ provider.track('feature_used');
69
+ ```
70
+
71
+ ### Telemetry Hook
72
+
73
+ Automatically track all flag evaluations to Pendo using the telemetry hook:
74
+
75
+ ```typescript
76
+ import { OpenFeature } from '@openfeature/web-sdk';
77
+ import { PendoProvider, PendoTelemetryHook } from '@pendo/openfeature-web-provider';
78
+
79
+ const provider = new PendoProvider();
80
+ const telemetryHook = new PendoTelemetryHook();
81
+
82
+ await OpenFeature.setProviderAndWait(provider);
83
+ OpenFeature.addHooks(telemetryHook);
84
+ ```
85
+
86
+ #### Telemetry Hook Options
87
+
88
+ ```typescript
89
+ const telemetryHook = new PendoTelemetryHook({
90
+ // Optional: Custom event name (default: "flag_evaluated")
91
+ eventName: 'feature_flag_evaluated',
92
+
93
+ // Optional: Filter which flags to track
94
+ flagFilter: (flagKey) => flagKey.startsWith('feature_'),
95
+ });
96
+ ```
97
+
98
+ #### Event Payload
99
+
100
+ Each flag evaluation sends a track event with these properties:
101
+
102
+ | Property | Description |
103
+ |----------|-------------|
104
+ | `flag_key` | The flag that was evaluated |
105
+ | `flag_variant` | "on", "off", or variant name |
106
+ | `flag_reason` | "TARGETING_MATCH", "DEFAULT", or "ERROR" |
107
+ | `flag_value` | Stringified value |
108
+ | `provider_name` | "pendo-provider" |
109
+
110
+ ### Configuration Options
111
+
112
+ ```typescript
113
+ const provider = new PendoProvider({
114
+ // API key for Pendo initialization (optional if already initialized)
115
+ apiKey: 'YOUR_API_KEY',
116
+
117
+ // Timeout waiting for Pendo agent to be ready (default: 5000ms)
118
+ readyTimeout: 10000,
119
+ });
120
+ ```
121
+
122
+ ## How It Works
123
+
124
+ 1. The provider waits for the Pendo agent to initialize
125
+ 2. Flag evaluation checks if the flag key exists in `window.pendo.segmentFlags`
126
+ 3. If the flag key is present, the flag is enabled; otherwise, it returns the default value
127
+
128
+ ## Context
129
+
130
+ The web provider does not require explicit context for flag evaluation since the Pendo agent maintains visitor/account identity automatically. However, you can pass context for consistency with the OpenFeature API:
131
+
132
+ ```typescript
133
+ const enabled = await client.getBooleanValue('my-flag', false, {
134
+ targetingKey: 'user-123', // Ignored by web provider
135
+ accountId: 'account-456', // Ignored by web provider
136
+ });
137
+ ```
138
+
139
+ ## Resolution Details
140
+
141
+ | Scenario | Reason | Variant |
142
+ |----------|--------|---------|
143
+ | Flag key in segmentFlags | `TARGETING_MATCH` | `on` |
144
+ | Flag key not in segmentFlags | `DEFAULT` | `off` |
145
+ | Pendo not ready | `DEFAULT` | `default` |
146
+
147
+ ## Troubleshooting
148
+
149
+ ### Flags always return default values
150
+
151
+ 1. Check that the Pendo agent is properly installed and initialized
152
+ 2. Verify the visitor is in a segment that has the flag enabled
153
+ 3. Check browser console for `[PendoProvider]` warnings
154
+
155
+ ### Provider times out
156
+
157
+ Increase the `readyTimeout` option:
158
+
159
+ ```typescript
160
+ new PendoProvider({ readyTimeout: 15000 });
161
+ ```
162
+
163
+ ### Track events not appearing
164
+
165
+ Ensure the Pendo agent's `track` function is available:
166
+
167
+ ```typescript
168
+ if (window.pendo?.track) {
169
+ provider.track('my-event');
170
+ }
171
+ ```
172
+
173
+ ## License
174
+
175
+ MIT
@@ -0,0 +1,102 @@
1
+ import type { Provider, ProviderMetadata, ResolutionDetails, EvaluationContext, JsonValue, Hook, TrackingEventDetails } from "@openfeature/web-sdk";
2
+ import { ClientProviderStatus } from "@openfeature/web-sdk";
3
+ declare global {
4
+ interface Window {
5
+ pendo?: {
6
+ segmentFlags?: string[];
7
+ isReady?: () => boolean;
8
+ initialize?: (options: unknown) => void;
9
+ track?: (event: string, properties?: Record<string, string>) => void;
10
+ };
11
+ }
12
+ }
13
+ export interface PendoProviderOptions {
14
+ /**
15
+ * Optional: Pendo API key for initialization.
16
+ * If not provided, assumes Pendo is already initialized on the page.
17
+ */
18
+ apiKey?: string;
19
+ /**
20
+ * Optional: Timeout in milliseconds to wait for Pendo to be ready.
21
+ * Default: 5000ms
22
+ */
23
+ readyTimeout?: number;
24
+ }
25
+ /**
26
+ * OpenFeature provider for Pendo feature flags.
27
+ *
28
+ * This provider evaluates feature flags by checking the `pendo.segmentFlags` array
29
+ * which is populated by the Pendo agent based on segment membership.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * import { OpenFeature } from '@openfeature/web-sdk';
34
+ * import { PendoProvider } from '@pendo/openfeature-web-provider';
35
+ *
36
+ * await OpenFeature.setProviderAndWait(new PendoProvider());
37
+ *
38
+ * const client = OpenFeature.getClient();
39
+ * const enabled = await client.getBooleanValue('myFeature', false);
40
+ * ```
41
+ */
42
+ export declare class PendoProvider implements Provider {
43
+ readonly metadata: ProviderMetadata;
44
+ readonly rulesChanged?: () => void;
45
+ status: ClientProviderStatus;
46
+ hooks?: Hook[];
47
+ private options;
48
+ private readyPromise;
49
+ constructor(options?: PendoProviderOptions);
50
+ /**
51
+ * Initialize the provider.
52
+ * Waits for Pendo to be ready before resolving.
53
+ */
54
+ initialize(): Promise<void>;
55
+ /**
56
+ * Wait for Pendo to be ready with timeout.
57
+ */
58
+ private waitForPendo;
59
+ /**
60
+ * Shutdown the provider.
61
+ */
62
+ onClose(): Promise<void>;
63
+ /**
64
+ * Resolve a boolean flag value.
65
+ *
66
+ * Checks if the flagKey exists in pendo.segmentFlags array.
67
+ */
68
+ resolveBooleanEvaluation(flagKey: string, defaultValue: boolean, _context: EvaluationContext): ResolutionDetails<boolean>;
69
+ /**
70
+ * Resolve a string flag value.
71
+ *
72
+ * For Pendo flags, this returns "on" if the flag is enabled, "off" otherwise.
73
+ */
74
+ resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext): ResolutionDetails<string>;
75
+ /**
76
+ * Resolve a number flag value.
77
+ *
78
+ * For Pendo flags, this returns 1 if enabled, 0 if disabled.
79
+ */
80
+ resolveNumberEvaluation(flagKey: string, defaultValue: number, context: EvaluationContext): ResolutionDetails<number>;
81
+ /**
82
+ * Resolve an object flag value.
83
+ *
84
+ * For Pendo flags, this returns { enabled: true/false }.
85
+ */
86
+ resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext): ResolutionDetails<T>;
87
+ /**
88
+ * Get the segment flags array from Pendo.
89
+ */
90
+ private getSegmentFlags;
91
+ /**
92
+ * Track a custom event in Pendo.
93
+ *
94
+ * This method delegates to the Pendo agent's track function.
95
+ * The Pendo agent must be initialized on the page for this to work.
96
+ *
97
+ * @param trackingEventName - The name of the event to track
98
+ * @param _context - Evaluation context (unused in web provider, Pendo agent handles visitor context)
99
+ * @param trackingEventDetails - Optional additional event properties
100
+ */
101
+ track(trackingEventName: string, _context: EvaluationContext, trackingEventDetails: TrackingEventDetails): void;
102
+ }
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PendoProvider = void 0;
4
+ const web_sdk_1 = require("@openfeature/web-sdk");
5
+ /**
6
+ * OpenFeature provider for Pendo feature flags.
7
+ *
8
+ * This provider evaluates feature flags by checking the `pendo.segmentFlags` array
9
+ * which is populated by the Pendo agent based on segment membership.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { OpenFeature } from '@openfeature/web-sdk';
14
+ * import { PendoProvider } from '@pendo/openfeature-web-provider';
15
+ *
16
+ * await OpenFeature.setProviderAndWait(new PendoProvider());
17
+ *
18
+ * const client = OpenFeature.getClient();
19
+ * const enabled = await client.getBooleanValue('myFeature', false);
20
+ * ```
21
+ */
22
+ class PendoProvider {
23
+ constructor(options = {}) {
24
+ this.metadata = {
25
+ name: "pendo-provider",
26
+ };
27
+ this.status = web_sdk_1.ClientProviderStatus.NOT_READY;
28
+ this.readyPromise = null;
29
+ this.options = {
30
+ readyTimeout: 5000,
31
+ ...options,
32
+ };
33
+ }
34
+ /**
35
+ * Initialize the provider.
36
+ * Waits for Pendo to be ready before resolving.
37
+ */
38
+ async initialize() {
39
+ if (this.readyPromise) {
40
+ return this.readyPromise;
41
+ }
42
+ this.readyPromise = this.waitForPendo();
43
+ await this.readyPromise;
44
+ this.status = web_sdk_1.ClientProviderStatus.READY;
45
+ }
46
+ /**
47
+ * Wait for Pendo to be ready with timeout.
48
+ */
49
+ async waitForPendo() {
50
+ const timeout = this.options.readyTimeout ?? 5000;
51
+ const startTime = Date.now();
52
+ return new Promise((resolve, reject) => {
53
+ const check = () => {
54
+ // Check if pendo is available and ready
55
+ if (typeof window !== "undefined" && window.pendo) {
56
+ if (window.pendo.isReady?.() || window.pendo.segmentFlags !== undefined) {
57
+ resolve();
58
+ return;
59
+ }
60
+ }
61
+ // Check timeout
62
+ if (Date.now() - startTime > timeout) {
63
+ // Don't reject - just resolve and work without Pendo
64
+ // Flags will default to false
65
+ console.warn("[PendoProvider] Pendo not ready within timeout. Flags will use default values.");
66
+ resolve();
67
+ return;
68
+ }
69
+ // Try again
70
+ setTimeout(check, 100);
71
+ };
72
+ // Also listen for pendo:ready event
73
+ if (typeof document !== "undefined") {
74
+ document.addEventListener("pendo:ready", () => {
75
+ resolve();
76
+ }, { once: true });
77
+ }
78
+ check();
79
+ });
80
+ }
81
+ /**
82
+ * Shutdown the provider.
83
+ */
84
+ async onClose() {
85
+ this.status = web_sdk_1.ClientProviderStatus.NOT_READY;
86
+ this.readyPromise = null;
87
+ }
88
+ /**
89
+ * Resolve a boolean flag value.
90
+ *
91
+ * Checks if the flagKey exists in pendo.segmentFlags array.
92
+ */
93
+ resolveBooleanEvaluation(flagKey, defaultValue, _context) {
94
+ const flags = this.getSegmentFlags();
95
+ if (!flags) {
96
+ return {
97
+ value: defaultValue,
98
+ reason: "DEFAULT",
99
+ variant: "default",
100
+ };
101
+ }
102
+ const enabled = flags.includes(flagKey);
103
+ return {
104
+ value: enabled,
105
+ reason: enabled ? "TARGETING_MATCH" : "DEFAULT",
106
+ variant: enabled ? "on" : "off",
107
+ };
108
+ }
109
+ /**
110
+ * Resolve a string flag value.
111
+ *
112
+ * For Pendo flags, this returns "on" if the flag is enabled, "off" otherwise.
113
+ */
114
+ resolveStringEvaluation(flagKey, defaultValue, context) {
115
+ const boolResult = this.resolveBooleanEvaluation(flagKey, false, context);
116
+ if (boolResult.reason === "DEFAULT" && !boolResult.value) {
117
+ return {
118
+ value: defaultValue,
119
+ reason: "DEFAULT",
120
+ variant: "default",
121
+ };
122
+ }
123
+ return {
124
+ value: boolResult.value ? "on" : "off",
125
+ reason: boolResult.reason,
126
+ variant: boolResult.variant,
127
+ };
128
+ }
129
+ /**
130
+ * Resolve a number flag value.
131
+ *
132
+ * For Pendo flags, this returns 1 if enabled, 0 if disabled.
133
+ */
134
+ resolveNumberEvaluation(flagKey, defaultValue, context) {
135
+ const boolResult = this.resolveBooleanEvaluation(flagKey, false, context);
136
+ if (boolResult.reason === "DEFAULT" && !boolResult.value) {
137
+ return {
138
+ value: defaultValue,
139
+ reason: "DEFAULT",
140
+ variant: "default",
141
+ };
142
+ }
143
+ return {
144
+ value: boolResult.value ? 1 : 0,
145
+ reason: boolResult.reason,
146
+ variant: boolResult.variant,
147
+ };
148
+ }
149
+ /**
150
+ * Resolve an object flag value.
151
+ *
152
+ * For Pendo flags, this returns { enabled: true/false }.
153
+ */
154
+ resolveObjectEvaluation(flagKey, defaultValue, context) {
155
+ const boolResult = this.resolveBooleanEvaluation(flagKey, false, context);
156
+ if (boolResult.reason === "DEFAULT" && !boolResult.value) {
157
+ return {
158
+ value: defaultValue,
159
+ reason: "DEFAULT",
160
+ variant: "default",
161
+ };
162
+ }
163
+ return {
164
+ value: { enabled: boolResult.value },
165
+ reason: boolResult.reason,
166
+ variant: boolResult.variant,
167
+ };
168
+ }
169
+ /**
170
+ * Get the segment flags array from Pendo.
171
+ */
172
+ getSegmentFlags() {
173
+ if (typeof window === "undefined") {
174
+ return undefined;
175
+ }
176
+ return window.pendo?.segmentFlags;
177
+ }
178
+ /**
179
+ * Track a custom event in Pendo.
180
+ *
181
+ * This method delegates to the Pendo agent's track function.
182
+ * The Pendo agent must be initialized on the page for this to work.
183
+ *
184
+ * @param trackingEventName - The name of the event to track
185
+ * @param _context - Evaluation context (unused in web provider, Pendo agent handles visitor context)
186
+ * @param trackingEventDetails - Optional additional event properties
187
+ */
188
+ track(trackingEventName, _context, trackingEventDetails) {
189
+ if (typeof window === "undefined" || !window.pendo?.track) {
190
+ console.warn("[PendoProvider] Pendo agent not available for tracking");
191
+ return;
192
+ }
193
+ // Convert TrackingEventDetails to Record<string, string> for Pendo agent
194
+ const properties = {};
195
+ for (const [key, value] of Object.entries(trackingEventDetails)) {
196
+ if (value !== undefined && value !== null) {
197
+ properties[key] = String(value);
198
+ }
199
+ }
200
+ // Fire-and-forget: delegate to Pendo agent
201
+ window.pendo.track(trackingEventName, properties);
202
+ }
203
+ }
204
+ exports.PendoProvider = PendoProvider;
205
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGVuZG9Qcm92aWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9QZW5kb1Byb3ZpZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQVNBLGtEQUE0RDtBQTJCNUQ7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFhLGFBQWE7SUFZeEIsWUFBWSxVQUFnQyxFQUFFO1FBWHJDLGFBQVEsR0FBcUI7WUFDcEMsSUFBSSxFQUFFLGdCQUFnQjtTQUN2QixDQUFDO1FBR0YsV0FBTSxHQUF5Qiw4QkFBb0IsQ0FBQyxTQUFTLENBQUM7UUFJdEQsaUJBQVksR0FBeUIsSUFBSSxDQUFDO1FBR2hELElBQUksQ0FBQyxPQUFPLEdBQUc7WUFDYixZQUFZLEVBQUUsSUFBSTtZQUNsQixHQUFHLE9BQU87U0FDWCxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxVQUFVO1FBQ2QsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDdEIsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDO1FBQzNCLENBQUM7UUFFRCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUN4QyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUM7UUFDeEIsSUFBSSxDQUFDLE1BQU0sR0FBRyw4QkFBb0IsQ0FBQyxLQUFLLENBQUM7SUFDM0MsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLFlBQVk7UUFDeEIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLElBQUksSUFBSSxDQUFDO1FBQ2xELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUU3QixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQ3JDLE1BQU0sS0FBSyxHQUFHLEdBQUcsRUFBRTtnQkFDakIsd0NBQXdDO2dCQUN4QyxJQUFJLE9BQU8s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QU0sS0FBSyxXQUFXLEVBQUUsQ0FBQztZQUNsQyxPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO1FBRUQsT0FBTyxNQUFNLENBQUMsS0FBSyxFQUFFLFlBQVksQ0FBQztJQUNwQyxDQUFDO0lBRUQ7Ozs7Ozs7OztPQVNHO0lBQ0gsS0FBSyxDQUNILGlCQUF5QixFQUN6QixRQUEyQixFQUMzQixvQkFBMEM7UUFFMUMsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDO1lBQzFELE9BQU8sQ0FBQyxJQUFJLENBQUMsd0RBQXdELENBQUMsQ0FBQztZQUN2RSxPQUFPO1FBQ1QsQ0FBQztRQUVELHlFQUF5RTtRQUN6RSxNQUFNLFVBQVUsR0FBMkIsRUFBRSxDQUFDO1FBQzlDLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLG9CQUFvQixDQUFDLEVBQUUsQ0FBQztZQUNoRSxJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxLQUFLLElBQUksRUFBRSxDQUFDO2dCQUMxQyxVQUFVLENBQUMsR0FBRyxDQUFDLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ2xDLENBQUM7UUFDSCxDQUFDO1FBRUQsMkNBQTJDO1FBQzNDLE1BQU0sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLGlCQUFpQixFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQ3BELENBQUM7Q0FDRjtBQWhQRCxzQ0FnUEMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7XG4gIFByb3ZpZGVyLFxuICBQcm92aWRlck1ldGFkYXRhLFxuICBSZXNvbHV0aW9uRGV0YWlscyxcbiAgRXZhbHVhdGlvbkNvbnRleHQsXG4gIEpzb25WYWx1ZSxcbiAgSG9vayxcbiAgVHJhY2tpbmdFdmVudERldGFpbHMsXG59IGZyb20gXCJAb3BlbmZlYXR1cmUvd2ViLXNka1wiO1xuaW1wb3J0IHsgQ2xpZW50UHJvdmlkZXJTdGF0dXMgfSBmcm9tIFwiQG9wZW5mZWF0dXJlL3dlYi1zZGtcIjtcblxuZGVjbGFyZSBnbG9iYWwge1xuICBpbnRlcmZhY2UgV2luZG93IHtcbiAgICBwZW5kbz86IHtcbiAgICAgIHNlZ21lbnRGbGFncz86IHN0cmluZ1tdO1xuICAgICAgaXNSZWFkeT86ICgpID0+IGJvb2xlYW47XG4gICAgICBpbml0aWFsaXplPzogKG9wdGlvbnM6IHVua25vd24pID0+IHZvaWQ7XG4gICAgICB0cmFjaz86IChldmVudDogc3RyaW5nLCBwcm9wZXJ0aWVzPzogUmVjb3JkPHN0cmluZywgc3RyaW5nPikgPT4gdm9pZDtcbiAgICB9O1xuICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGVuZG9Qcm92aWRlck9wdGlvbnMge1xuICAvKipcbiAgICogT3B0aW9uYWw6IFBlbmRvIEFQSSBrZXkgZm9yIGluaXRpYWxpemF0aW9uLlxuICAgKiBJZiBub3QgcHJvdmlkZWQsIGFzc3VtZXMgUGVuZG8gaXMgYWxyZWFkeSBpbml0aWFsaXplZCBvbiB0aGUgcGFnZS5cbiAgICovXG4gIGFwaUtleT86IHN0cmluZztcblxuICAvKipcbiAgICogT3B0aW9uYWw6IFRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIFBlbmRvIHRvIGJlIHJlYWR5LlxuICAgKiBEZWZhdWx0OiA1MDAwbXNcbiAgICovXG4gIHJlYWR5VGltZW91dD86IG51bWJlcjtcbn1cblxuLyoqXG4gKiBPcGVuRmVhdHVyZSBwcm92aWRlciBmb3IgUGVuZG8gZmVhdHVyZSBmbGFncy5cbiAqXG4gKiBUaGlzIHByb3ZpZGVyIGV2YWx1YXRlcyBmZWF0dXJlIGZsYWdzIGJ5IGNoZWNraW5nIHRoZSBgcGVuZG8uc2VnbWVudEZsYWdzYCBhcnJheVxuICogd2hpY2ggaXMgcG9wdWxhdGVkIGJ5IHRoZSBQZW5kbyBhZ2VudCBiYXNlZCBvbiBzZWdtZW50IG1lbWJlcnNoaXAuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGltcG9ydCB7IE9wZW5GZWF0dXJlIH0gZnJvbSAnQG9wZW5mZWF0dXJlL3dlYi1zZGsnO1xuICogaW1wb3J0IHsgUGVuZG9Qcm92aWRlciB9IGZyb20gJ0BwZW5kby9vcGVuZmVhdHVyZS13ZWItcHJvdmlkZXInO1xuICpcbiAqIGF3YWl0IE9wZW5GZWF0dXJlLnNldFByb3ZpZGVyQW5kV2FpdChuZXcgUGVuZG9Qcm92aWRlcigpKTtcbiAqXG4gKiBjb25zdCBjbGllbnQgPSBPcGVuRmVhdHVyZS5nZXRDbGllbnQoKTtcbiAqIGNvbnN0IGVuYWJsZWQgPSBhd2FpdCBjbGllbnQuZ2V0Qm9vbGVhblZhbHVlKCdteUZlYXR1cmUnLCBmYWxzZSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIFBlbmRvUHJvdmlkZXIgaW1wbGVtZW50cyBQcm92aWRlciB7XG4gIHJlYWRvbmx5IG1ldGFkYXRhOiBQcm92aWRlck1ldGFkYXRhID0ge1xuICAgIG5hbWU6IFwicGVuZG8tcHJvdmlkZXJcIixcbiAgfTtcblxuICByZWFkb25seSBydWxlc0NoYW5nZWQ/OiAoKSA9PiB2b2lkO1xuICBzdGF0dXM6IENsaWVudFByb3ZpZGVyU3RhdHVzID0gQ2xpZW50UHJvdmlkZXJTdGF0dXMuTk9UX1JFQURZO1xuICBob29rcz86IEhvb2tbXTtcblxuICBwcml2YXRlIG9wdGlvbnM6IFBlbmRvUHJvdmlkZXJPcHRpb25zO1xuICBwcml2YXRlIHJlYWR5UHJvbWlzZTogUHJvbWlzZTx2b2lkPiB8IG51bGwgPSBudWxsO1xuXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnM6IFBlbmRvUHJvdmlkZXJPcHRpb25zID0ge30pIHtcbiAgICB0aGlzLm9wdGlvbnMgPSB7XG4gICAgICByZWFkeVRpbWVvdXQ6IDUwMDAsXG4gICAgICAuLi5vcHRpb25zLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogSW5pdGlhbGl6ZSB0aGUgcHJvdmlkZXIuXG4gICAqIFdhaXRzIGZvciBQZW5kbyB0byBiZSByZWFkeSBiZWZvcmUgcmVzb2x2aW5nLlxuICAgKi9cbiAgYXN5bmMgaW5pdGlhbGl6ZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5yZWFkeVByb21pc2UpIHtcbiAgICAgIHJldHVybiB0aGlzLnJlYWR5UHJvbWlzZTtcbiAgICB9XG5cbiAgICB0aGlzLnJlYWR5UHJvbWlzZSA9IHRoaXMud2FpdEZvclBlbmRvKCk7XG4gICAgYXdhaXQgdGhpcy5yZWFkeVByb21pc2U7XG4gICAgdGhpcy5zdGF0dXMgPSBDbGllbnRQcm92aWRlclN0YXR1cy5SRUFEWTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXYWl0IGZvciBQZW5kbyB0byBiZSByZWFkeSB3aXRoIHRpbWVvdXQuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHdhaXRGb3JQZW5kbygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB0aW1lb3V0ID0gdGhpcy5vcHRpb25zLnJlYWR5VGltZW91dCA/PyA1MDAwO1xuICAgIGNvbnN0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG5cbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgY2hlY2sgPSAoKSA9PiB7XG4gICAgICAgIC8vIENoZWNrIGlmIHBlbmRvIGlzIGF2YWlsYWJsZSBhbmQgcmVhZHlcbiAgICAgICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LnBlbmRvKSB7XG4gICAgICAgICAgaWYgKHdpbmRvdy5wZW5kby5pc1JlYWR5Py4oKSB8fCB3aW5kb3cucGVuZG8uc2VnbWVudEZsYWdzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDaGVjayB0aW1lb3V0XG4gICAgICAgIGlmIChEYXRlLm5vdygpIC0gc3RhcnRUaW1lID4gdGltZW91dCkge1xuICAgICAgICAgIC8vIERvbid0IHJlamVjdCAtIGp1c3QgcmVzb2x2ZSBhbmQgd29yayB3aXRob3V0IFBlbmRvXG4gICAgICAgICAgLy8gRmxhZ3Mgd2lsbCBkZWZhdWx0IHRvIGZhbHNlXG4gICAgICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgICAgXCJbUGVuZG9Qcm92aWRlcl0gUGVuZG8gbm90IHJlYWR5IHdpdGhpbiB0aW1lb3V0LiBGbGFncyB3aWxsIHVzZSBkZWZhdWx0IHZhbHVlcy5cIlxuICAgICAgICAgICk7XG4gICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFRyeSBhZ2FpblxuICAgICAgICBzZXRUaW1lb3V0KGNoZWNrLCAxMDApO1xuICAgICAgfTtcblxuICAgICAgLy8gQWxzbyBsaXN0ZW4gZm9yIHBlbmRvOnJlYWR5IGV2ZW50XG4gICAgICBpZiAodHlwZW9mIGRvY3VtZW50ICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgXCJwZW5kbzpyZWFkeVwiLFxuICAgICAgICAgICgpID0+IHtcbiAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICB9LFxuICAgICAgICAgIHsgb25jZTogdHJ1ZSB9XG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGNoZWNrKCk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogU2h1dGRvd24gdGhlIHByb3ZpZGVyLlxuICAgKi9cbiAgYXN5bmMgb25DbG9zZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLnN0YXR1cyA9IENsaWVudFByb3ZpZGVyU3RhdHVzLk5PVF9SRUFEWTtcbiAgICB0aGlzLnJlYWR5UHJvbWlzZSA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogUmVzb2x2ZSBhIGJvb2xlYW4gZmxhZyB2YWx1ZS5cbiAgICpcbiAgICogQ2hlY2tzIGlmIHRoZSBmbGFnS2V5IGV4aXN0cyBpbiBwZW5kby5zZWdtZW50RmxhZ3MgYXJyYXkuXG4gICAqL1xuICByZXNvbHZlQm9vbGVhbkV2YWx1YXRpb24oXG4gICAgZmxhZ0tleTogc3RyaW5nLFxuICAgIGRlZmF1bHRWYWx1ZTogYm9vbGVhbixcbiAgICBfY29udGV4dDogRXZhbHVhdGlvbkNvbnRleHRcbiAgKTogUmVzb2x1dGlvbkRldGFpbHM8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IGZsYWdzID0gdGhpcy5nZXRTZWdtZW50RmxhZ3MoKTtcblxuICAgIGlmICghZmxhZ3MpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHZhbHVlOiBkZWZhdWx0VmFsdWUsXG4gICAgICAgIHJlYXNvbjogXCJERUZBVUxUXCIsXG4gICAgICAgIHZhcmlhbnQ6IFwiZGVmYXVsdFwiLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICBjb25zdCBlbmFibGVkID0gZmxhZ3MuaW5jbHVkZXMoZmxhZ0tleSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgdmFsdWU6IGVuYWJsZWQsXG4gICAgICByZWFzb246IGVuYWJsZWQgPyBcIlRBUkdFVElOR19NQVRDSFwiIDogXCJERUZBVUxUXCIsXG4gICAgICB2YXJpYW50OiBlbmFibGVkID8gXCJvblwiIDogXCJvZmZcIixcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmUgYSBzdHJpbmcgZmxhZyB2YWx1ZS5cbiAgICpcbiAgICogRm9yIFBlbmRvIGZsYWdzLCB0aGlzIHJldHVybnMgXCJvblwiIGlmIHRoZSBmbGFnIGlzIGVuYWJsZWQsIFwib2ZmXCIgb3RoZXJ3aXNlLlxuICAgKi9cbiAgcmVzb2x2ZVN0cmluZ0V2YWx1YXRpb24oXG4gICAgZmxhZ0tleTogc3RyaW5nLFxuICAgIGRlZmF1bHRWYWx1ZTogc3RyaW5nLFxuICAgIGNvbnRleHQ6IEV2YWx1YXRpb25Db250ZXh0XG4gICk6IFJlc29sdXRpb25EZXRhaWxzPHN0cmluZz4ge1xuICAgIGNvbnN0IGJvb2xSZXN1bHQgPSB0aGlzLnJlc29sdmVCb29sZWFuRXZhbHVhdGlvbihmbGFnS2V5LCBmYWxzZSwgY29udGV4dCk7XG5cbiAgICBpZiAoYm9vbFJlc3VsdC5yZWFzb24gPT09IFwiREVGQVVMVFwiICYmICFib29sUmVzdWx0LnZhbHVlKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB2YWx1ZTogZGVmYXVsdFZhbHVlLFxuICAgICAgICByZWFzb246IFwiREVGQVVMVFwiLFxuICAgICAgICB2YXJpYW50OiBcImRlZmF1bHRcIixcbiAgICAgIH07XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIHZhbHVlOiBib29sUmVzdWx0LnZhbHVlID8gXCJvblwiIDogXCJvZmZcIixcbiAgICAgIHJlYXNvbjogYm9vbFJlc3VsdC5yZWFzb24sXG4gICAgICB2YXJpYW50OiBib29sUmVzdWx0LnZhcmlhbnQsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNvbHZlIGEgbnVtYmVyIGZsYWcgdmFsdWUuXG4gICAqXG4gICAqIEZvciBQZW5kbyBmbGFncywgdGhpcyByZXR1cm5zIDEgaWYgZW5hYmxlZCwgMCBpZiBkaXNhYmxlZC5cbiAgICovXG4gIHJlc29sdmVOdW1iZXJFdmFsdWF0aW9uKFxuICAgIGZsYWdLZXk6IHN0cmluZyxcbiAgICBkZWZhdWx0VmFsdWU6IG51bWJlcixcbiAgICBjb250ZXh0OiBFdmFsdWF0aW9uQ29udGV4dFxuICApOiBSZXNvbHV0aW9uRGV0YWlsczxudW1iZXI+IHtcbiAgICBjb25zdCBib29sUmVzdWx0ID0gdGhpcy5yZXNvbHZlQm9vbGVhbkV2YWx1YXRpb24oZmxhZ0tleSwgZmFsc2UsIGNvbnRleHQpO1xuXG4gICAgaWYgKGJvb2xSZXN1bHQucmVhc29uID09PSBcIkRFRkFVTFRcIiAmJiAhYm9vbFJlc3VsdC52YWx1ZSkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdmFsdWU6IGRlZmF1bHRWYWx1ZSxcbiAgICAgICAgcmVhc29uOiBcIkRFRkFVTFRcIixcbiAgICAgICAgdmFyaWFudDogXCJkZWZhdWx0XCIsXG4gICAgICB9O1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICB2YWx1ZTogYm9vbFJlc3VsdC52YWx1ZSA/IDEgOiAwLFxuICAgICAgcmVhc29uOiBib29sUmVzdWx0LnJlYXNvbixcbiAgICAgIHZhcmlhbnQ6IGJvb2xSZXN1bHQudmFyaWFudCxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmUgYW4gb2JqZWN0IGZsYWcgdmFsdWUuXG4gICAqXG4gICAqIEZvciBQZW5kbyBmbGFncywgdGhpcyByZXR1cm5zIHsgZW5hYmxlZDogdHJ1ZS9mYWxzZSB9LlxuICAgKi9cbiAgcmVzb2x2ZU9iamVjdEV2YWx1YXRpb248VCBleHRlbmRzIEpzb25WYWx1ZT4oXG4gICAgZmxhZ0tleTogc3RyaW5nLFxuICAgIGRlZmF1bHRWYWx1ZTogVCxcbiAgICBjb250ZXh0OiBFdmFsdWF0aW9uQ29udGV4dFxuICApOiBSZXNvbHV0aW9uRGV0YWlsczxUPiB7XG4gICAgY29uc3QgYm9vbFJlc3VsdCA9IHRoaXMucmVzb2x2ZUJvb2xlYW5FdmFsdWF0aW9uKGZsYWdLZXksIGZhbHNlLCBjb250ZXh0KTtcblxuICAgIGlmIChib29sUmVzdWx0LnJlYXNvbiA9PT0gXCJERUZBVUxUXCIgJiYgIWJvb2xSZXN1bHQudmFsdWUpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHZhbHVlOiBkZWZhdWx0VmFsdWUsXG4gICAgICAgIHJlYXNvbjogXCJERUZBVUxUXCIsXG4gICAgICAgIHZhcmlhbnQ6IFwiZGVmYXVsdFwiLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgdmFsdWU6IHsgZW5hYmxlZDogYm9vbFJlc3VsdC52YWx1ZSB9IGFzIHVua25vd24gYXMgVCxcbiAgICAgIHJlYXNvbjogYm9vbFJlc3VsdC5yZWFzb24sXG4gICAgICB2YXJpYW50OiBib29sUmVzdWx0LnZhcmlhbnQsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIHNlZ21lbnQgZmxhZ3MgYXJyYXkgZnJvbSBQZW5kby5cbiAgICovXG4gIHByaXZhdGUgZ2V0U2VnbWVudEZsYWdzKCk6IHN0cmluZ1tdIHwgdW5kZWZpbmVkIHtcbiAgICBpZiAodHlwZW9mIHdpbmRvdyA9PT0gXCJ1bmRlZmluZWRcIikge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICByZXR1cm4gd2luZG93LnBlbmRvPy5zZWdtZW50RmxhZ3M7XG4gIH1cblxuICAvKipcbiAgICogVHJhY2sgYSBjdXN0b20gZXZlbnQgaW4gUGVuZG8uXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGRlbGVnYXRlcyB0byB0aGUgUGVuZG8gYWdlbnQncyB0cmFjayBmdW5jdGlvbi5cbiAgICogVGhlIFBlbmRvIGFnZW50IG11c3QgYmUgaW5pdGlhbGl6ZWQgb24gdGhlIHBhZ2UgZm9yIHRoaXMgdG8gd29yay5cbiAgICpcbiAgICogQHBhcmFtIHRyYWNraW5nRXZlbnROYW1lIC0gVGhlIG5hbWUgb2YgdGhlIGV2ZW50IHRvIHRyYWNrXG4gICAqIEBwYXJhbSBfY29udGV4dCAtIEV2YWx1YXRpb24gY29udGV4dCAodW51c2VkIGluIHdlYiBwcm92aWRlciwgUGVuZG8gYWdlbnQgaGFuZGxlcyB2aXNpdG9yIGNvbnRleHQpXG4gICAqIEBwYXJhbSB0cmFja2luZ0V2ZW50RGV0YWlscyAtIE9wdGlvbmFsIGFkZGl0aW9uYWwgZXZlbnQgcHJvcGVydGllc1xuICAgKi9cbiAgdHJhY2soXG4gICAgdHJhY2tpbmdFdmVudE5hbWU6IHN0cmluZyxcbiAgICBfY29udGV4dDogRXZhbHVhdGlvbkNvbnRleHQsXG4gICAgdHJhY2tpbmdFdmVudERldGFpbHM6IFRyYWNraW5nRXZlbnREZXRhaWxzXG4gICk6IHZvaWQge1xuICAgIGlmICh0eXBlb2Ygd2luZG93ID09PSBcInVuZGVmaW5lZFwiIHx8ICF3aW5kb3cucGVuZG8/LnRyYWNrKSB7XG4gICAgICBjb25zb2xlLndhcm4oXCJbUGVuZG9Qcm92aWRlcl0gUGVuZG8gYWdlbnQgbm90IGF2YWlsYWJsZSBmb3IgdHJhY2tpbmdcIik7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gQ29udmVydCBUcmFja2luZ0V2ZW50RGV0YWlscyB0byBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IGZvciBQZW5kbyBhZ2VudFxuICAgIGNvbnN0IHByb3BlcnRpZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyh0cmFja2luZ0V2ZW50RGV0YWlscykpIHtcbiAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkICYmIHZhbHVlICE9PSBudWxsKSB7XG4gICAgICAgIHByb3BlcnRpZXNba2V5XSA9IFN0cmluZyh2YWx1ZSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRmlyZS1hbmQtZm9yZ2V0OiBkZWxlZ2F0ZSB0byBQZW5kbyBhZ2VudFxuICAgIHdpbmRvdy5wZW5kby50cmFjayh0cmFja2luZ0V2ZW50TmFtZSwgcHJvcGVydGllcyk7XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,47 @@
1
+ import type { Hook, HookContext, EvaluationDetails, FlagValue } from "@openfeature/web-sdk";
2
+ export interface PendoTelemetryHookOptions {
3
+ /**
4
+ * Event name for flag evaluation tracking.
5
+ * Default: "flag_evaluated"
6
+ */
7
+ eventName?: string;
8
+ /**
9
+ * Optional filter function to selectively track flags.
10
+ * Return true to track the flag, false to skip.
11
+ */
12
+ flagFilter?: (flagKey: string) => boolean;
13
+ }
14
+ /**
15
+ * OpenFeature hook that automatically tracks flag evaluations to Pendo.
16
+ *
17
+ * This hook sends an event to Pendo after each flag evaluation, allowing you
18
+ * to analyze feature flag usage alongside your other Pendo analytics.
19
+ *
20
+ * The hook uses the Pendo agent's track function, so the Pendo agent must be
21
+ * initialized on the page for tracking to work.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { OpenFeature } from '@openfeature/web-sdk';
26
+ * import { PendoProvider, PendoTelemetryHook } from '@pendo/openfeature-web-provider';
27
+ *
28
+ * const provider = new PendoProvider();
29
+ * const telemetryHook = new PendoTelemetryHook();
30
+ *
31
+ * await OpenFeature.setProviderAndWait(provider);
32
+ * OpenFeature.addHooks(telemetryHook);
33
+ * ```
34
+ */
35
+ export declare class PendoTelemetryHook implements Hook {
36
+ private options;
37
+ constructor(options?: PendoTelemetryHookOptions);
38
+ /**
39
+ * Called after a flag is successfully evaluated.
40
+ * Sends the evaluation result to Pendo as a track event.
41
+ */
42
+ after(hookContext: Readonly<HookContext<FlagValue>>, evaluationDetails: EvaluationDetails<FlagValue>): void;
43
+ /**
44
+ * Convert a flag value to a string for tracking.
45
+ */
46
+ private stringifyValue;
47
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PendoTelemetryHook = void 0;
4
+ /**
5
+ * OpenFeature hook that automatically tracks flag evaluations to Pendo.
6
+ *
7
+ * This hook sends an event to Pendo after each flag evaluation, allowing you
8
+ * to analyze feature flag usage alongside your other Pendo analytics.
9
+ *
10
+ * The hook uses the Pendo agent's track function, so the Pendo agent must be
11
+ * initialized on the page for tracking to work.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { OpenFeature } from '@openfeature/web-sdk';
16
+ * import { PendoProvider, PendoTelemetryHook } from '@pendo/openfeature-web-provider';
17
+ *
18
+ * const provider = new PendoProvider();
19
+ * const telemetryHook = new PendoTelemetryHook();
20
+ *
21
+ * await OpenFeature.setProviderAndWait(provider);
22
+ * OpenFeature.addHooks(telemetryHook);
23
+ * ```
24
+ */
25
+ class PendoTelemetryHook {
26
+ constructor(options = {}) {
27
+ this.options = {
28
+ eventName: "flag_evaluated",
29
+ ...options,
30
+ };
31
+ }
32
+ /**
33
+ * Called after a flag is successfully evaluated.
34
+ * Sends the evaluation result to Pendo as a track event.
35
+ */
36
+ after(hookContext, evaluationDetails) {
37
+ const { flagKey } = hookContext;
38
+ // Check if this flag should be tracked
39
+ if (this.options.flagFilter && !this.options.flagFilter(flagKey)) {
40
+ return;
41
+ }
42
+ // Check if Pendo agent is available
43
+ if (typeof window === "undefined" || !window.pendo?.track) {
44
+ return;
45
+ }
46
+ const properties = {
47
+ flag_key: flagKey,
48
+ flag_variant: evaluationDetails.variant || "unknown",
49
+ flag_reason: evaluationDetails.reason || "UNKNOWN",
50
+ flag_value: this.stringifyValue(evaluationDetails.value),
51
+ provider_name: hookContext.providerMetadata.name,
52
+ };
53
+ // Fire-and-forget: delegate to Pendo agent
54
+ window.pendo.track(this.options.eventName, properties);
55
+ }
56
+ /**
57
+ * Convert a flag value to a string for tracking.
58
+ */
59
+ stringifyValue(value) {
60
+ if (value === null) {
61
+ return "null";
62
+ }
63
+ if (typeof value === "object") {
64
+ return JSON.stringify(value);
65
+ }
66
+ return String(value);
67
+ }
68
+ }
69
+ exports.PendoTelemetryHook = PendoTelemetryHook;
70
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGVuZG9UZWxlbWV0cnlIb29rLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1BlbmRvVGVsZW1ldHJ5SG9vay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFxQkE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBQ0gsTUFBYSxrQkFBa0I7SUFLN0IsWUFBWSxVQUFxQyxFQUFFO1FBQ2pELElBQUksQ0FBQyxPQUFPLEdBQUc7WUFDYixTQUFTLEVBQUUsZ0JBQWdCO1lBQzNCLEdBQUcsT0FBTztTQUNYLENBQUM7SUFDSixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsS0FBSyxDQUNILFdBQTZDLEVBQzdDLGlCQUErQztRQUUvQyxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsV0FBVyxDQUFDO1FBRWhDLHVDQUF1QztRQUN2QyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUNqRSxPQUFPO1FBQ1QsQ0FBQztRQUVELG9DQUFvQztRQUNwQyxJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLENBQUM7WUFDMUQsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLFVBQVUsR0FBMkI7WUFDekMsUUFBUSxFQUFFLE9BQU87WUFDakIsWUFBWSxFQUFFLGlCQUFpQixDQUFDLE9BQU8sSUFBSSxTQUFTO1lBQ3BELFdBQVcsRUFBRSxpQkFBaUIsQ0FBQyxNQUFNLElBQUksU0FBUztZQUNsRCxVQUFVLEVBQUUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUM7WUFDeEQsYUFBYSxFQUFFLFdBQVcsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJO1NBQ2pELENBQUM7UUFFRiwyQ0FBMkM7UUFDM0MsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDekQsQ0FBQztJQUVEOztPQUVHO0lBQ0ssY0FBYyxDQUFDLEtBQWdCO1FBQ3JDLElBQUksS0FBSyxLQUFLLElBQUksRUFBRSxDQUFDO1lBQ25CLE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUM7UUFDRCxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzlCLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUMvQixDQUFDO1FBQ0QsT0FBTyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDdkIsQ0FBQztDQUNGO0FBeERELGdEQXdEQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHtcbiAgSG9vayxcbiAgSG9va0NvbnRleHQsXG4gIEV2YWx1YXRpb25EZXRhaWxzLFxuICBGbGFnVmFsdWUsXG59IGZyb20gXCJAb3BlbmZlYXR1cmUvd2ViLXNka1wiO1xuXG5leHBvcnQgaW50ZXJmYWNlIFBlbmRvVGVsZW1ldHJ5SG9va09wdGlvbnMge1xuICAvKipcbiAgICogRXZlbnQgbmFtZSBmb3IgZmxhZyBldmFsdWF0aW9uIHRyYWNraW5nLlxuICAgKiBEZWZhdWx0OiBcImZsYWdfZXZhbHVhdGVkXCJcbiAgICovXG4gIGV2ZW50TmFtZT86IHN0cmluZztcblxuICAvKipcbiAgICogT3B0aW9uYWwgZmlsdGVyIGZ1bmN0aW9uIHRvIHNlbGVjdGl2ZWx5IHRyYWNrIGZsYWdzLlxuICAgKiBSZXR1cm4gdHJ1ZSB0byB0cmFjayB0aGUgZmxhZywgZmFsc2UgdG8gc2tpcC5cbiAgICovXG4gIGZsYWdGaWx0ZXI/OiAoZmxhZ0tleTogc3RyaW5nKSA9PiBib29sZWFuO1xufVxuXG4vKipcbiAqIE9wZW5GZWF0dXJlIGhvb2sgdGhhdCBhdXRvbWF0aWNhbGx5IHRyYWNrcyBmbGFnIGV2YWx1YXRpb25zIHRvIFBlbmRvLlxuICpcbiAqIFRoaXMgaG9vayBzZW5kcyBhbiBldmVudCB0byBQZW5kbyBhZnRlciBlYWNoIGZsYWcgZXZhbHVhdGlvbiwgYWxsb3dpbmcgeW91XG4gKiB0byBhbmFseXplIGZlYXR1cmUgZmxhZyB1c2FnZSBhbG9uZ3NpZGUgeW91ciBvdGhlciBQZW5kbyBhbmFseXRpY3MuXG4gKlxuICogVGhlIGhvb2sgdXNlcyB0aGUgUGVuZG8gYWdlbnQncyB0cmFjayBmdW5jdGlvbiwgc28gdGhlIFBlbmRvIGFnZW50IG11c3QgYmVcbiAqIGluaXRpYWxpemVkIG9uIHRoZSBwYWdlIGZvciB0cmFja2luZyB0byB3b3JrLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBPcGVuRmVhdHVyZSB9IGZyb20gJ0BvcGVuZmVhdHVyZS93ZWItc2RrJztcbiAqIGltcG9ydCB7IFBlbmRvUHJvdmlkZXIsIFBlbmRvVGVsZW1ldHJ5SG9vayB9IGZyb20gJ0BwZW5kby9vcGVuZmVhdHVyZS13ZWItcHJvdmlkZXInO1xuICpcbiAqIGNvbnN0IHByb3ZpZGVyID0gbmV3IFBlbmRvUHJvdmlkZXIoKTtcbiAqIGNvbnN0IHRlbGVtZXRyeUhvb2sgPSBuZXcgUGVuZG9UZWxlbWV0cnlIb29rKCk7XG4gKlxuICogYXdhaXQgT3BlbkZlYXR1cmUuc2V0UHJvdmlkZXJBbmRXYWl0KHByb3ZpZGVyKTtcbiAqIE9wZW5GZWF0dXJlLmFkZEhvb2tzKHRlbGVtZXRyeUhvb2spO1xuICogYGBgXG4gKi9cbmV4cG9ydCBjbGFzcyBQZW5kb1RlbGVtZXRyeUhvb2sgaW1wbGVtZW50cyBIb29rIHtcbiAgcHJpdmF0ZSBvcHRpb25zOiBSZXF1aXJlZDxPbWl0PFBlbmRvVGVsZW1ldHJ5SG9va09wdGlvbnMsIFwiZmxhZ0ZpbHRlclwiPj4gJiB7XG4gICAgZmxhZ0ZpbHRlcj86IChmbGFnS2V5OiBzdHJpbmcpID0+IGJvb2xlYW47XG4gIH07XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogUGVuZG9UZWxlbWV0cnlIb29rT3B0aW9ucyA9IHt9KSB7XG4gICAgdGhpcy5vcHRpb25zID0ge1xuICAgICAgZXZlbnROYW1lOiBcImZsYWdfZXZhbHVhdGVkXCIsXG4gICAgICAuLi5vcHRpb25zLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ2FsbGVkIGFmdGVyIGEgZmxhZyBpcyBzdWNjZXNzZnVsbHkgZXZhbHVhdGVkLlxuICAgKiBTZW5kcyB0aGUgZXZhbHVhdGlvbiByZXN1bHQgdG8gUGVuZG8gYXMgYSB0cmFjayBldmVudC5cbiAgICovXG4gIGFmdGVyKFxuICAgIGhvb2tDb250ZXh0OiBSZWFkb25seTxIb29rQ29udGV4dDxGbGFnVmFsdWU+PixcbiAgICBldmFsdWF0aW9uRGV0YWlsczogRXZhbHVhdGlvbkRldGFpbHM8RmxhZ1ZhbHVlPlxuICApOiB2b2lkIHtcbiAgICBjb25zdCB7IGZsYWdLZXkgfSA9IGhvb2tDb250ZXh0O1xuXG4gICAgLy8gQ2hlY2sgaWYgdGhpcyBmbGFnIHNob3VsZCBiZSB0cmFja2VkXG4gICAgaWYgKHRoaXMub3B0aW9ucy5mbGFnRmlsdGVyICYmICF0aGlzLm9wdGlvbnMuZmxhZ0ZpbHRlcihmbGFnS2V5KSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIENoZWNrIGlmIFBlbmRvIGFnZW50IGlzIGF2YWlsYWJsZVxuICAgIGlmICh0eXBlb2Ygd2luZG93ID09PSBcInVuZGVmaW5lZFwiIHx8ICF3aW5kb3cucGVuZG8/LnRyYWNrKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgcHJvcGVydGllczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICAgIGZsYWdfa2V5OiBmbGFnS2V5LFxuICAgICAgZmxhZ192YXJpYW50OiBldmFsdWF0aW9uRGV0YWlscy52YXJpYW50IHx8IFwidW5rbm93blwiLFxuICAgICAgZmxhZ19yZWFzb246IGV2YWx1YXRpb25EZXRhaWxzLnJlYXNvbiB8fCBcIlVOS05PV05cIixcbiAgICAgIGZsYWdfdmFsdWU6IHRoaXMuc3RyaW5naWZ5VmFsdWUoZXZhbHVhdGlvbkRldGFpbHMudmFsdWUpLFxuICAgICAgcHJvdmlkZXJfbmFtZTogaG9va0NvbnRleHQucHJvdmlkZXJNZXRhZGF0YS5uYW1lLFxuICAgIH07XG5cbiAgICAvLyBGaXJlLWFuZC1mb3JnZXQ6IGRlbGVnYXRlIHRvIFBlbmRvIGFnZW50XG4gICAgd2luZG93LnBlbmRvLnRyYWNrKHRoaXMub3B0aW9ucy5ldmVudE5hbWUsIHByb3BlcnRpZXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnZlcnQgYSBmbGFnIHZhbHVlIHRvIGEgc3RyaW5nIGZvciB0cmFja2luZy5cbiAgICovXG4gIHByaXZhdGUgc3RyaW5naWZ5VmFsdWUodmFsdWU6IEZsYWdWYWx1ZSk6IHN0cmluZyB7XG4gICAgaWYgKHZhbHVlID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gXCJudWxsXCI7XG4gICAgfVxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeSh2YWx1ZSk7XG4gICAgfVxuICAgIHJldHVybiBTdHJpbmcodmFsdWUpO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,4 @@
1
+ export { PendoProvider } from "./PendoProvider";
2
+ export type { PendoProviderOptions } from "./PendoProvider";
3
+ export { PendoTelemetryHook } from "./PendoTelemetryHook";
4
+ export type { PendoTelemetryHookOptions } from "./PendoTelemetryHook";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PendoTelemetryHook = exports.PendoProvider = void 0;
4
+ var PendoProvider_1 = require("./PendoProvider");
5
+ Object.defineProperty(exports, "PendoProvider", { enumerable: true, get: function () { return PendoProvider_1.PendoProvider; } });
6
+ var PendoTelemetryHook_1 = require("./PendoTelemetryHook");
7
+ Object.defineProperty(exports, "PendoTelemetryHook", { enumerable: true, get: function () { return PendoTelemetryHook_1.PendoTelemetryHook; } });
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaURBQWdEO0FBQXZDLDhHQUFBLGFBQWEsT0FBQTtBQUV0QiwyREFBMEQ7QUFBakQsd0hBQUEsa0JBQWtCLE9BQUEiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgeyBQZW5kb1Byb3ZpZGVyIH0gZnJvbSBcIi4vUGVuZG9Qcm92aWRlclwiO1xuZXhwb3J0IHR5cGUgeyBQZW5kb1Byb3ZpZGVyT3B0aW9ucyB9IGZyb20gXCIuL1BlbmRvUHJvdmlkZXJcIjtcbmV4cG9ydCB7IFBlbmRvVGVsZW1ldHJ5SG9vayB9IGZyb20gXCIuL1BlbmRvVGVsZW1ldHJ5SG9va1wiO1xuZXhwb3J0IHR5cGUgeyBQZW5kb1RlbGVtZXRyeUhvb2tPcHRpb25zIH0gZnJvbSBcIi4vUGVuZG9UZWxlbWV0cnlIb29rXCI7XG4iXX0=
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@pendo/openfeature-web-provider",
3
+ "version": "0.1.0",
4
+ "description": "OpenFeature provider for Pendo feature flags (web/browser)",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "test": "jest",
14
+ "lint": "eslint src"
15
+ },
16
+ "peerDependencies": {
17
+ "@openfeature/web-sdk": "^1.0.0"
18
+ },
19
+ "devDependencies": {
20
+ "@openfeature/web-sdk": "^1.0.0",
21
+ "@types/node": "^20.0.0",
22
+ "typescript": "^5.3.0"
23
+ },
24
+ "keywords": [
25
+ "openfeature",
26
+ "feature-flags",
27
+ "pendo",
28
+ "provider"
29
+ ],
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/pendo-io/pendo-flags.git",
34
+ "directory": "packages/web-provider"
35
+ }
36
+ }