flowgrid-sdk 1.1.0 โ 1.2.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 +320 -8
- package/dist/browser.d.ts +6 -7
- package/dist/flowgrid.min.js +1 -1
- package/dist/flowgrid.min.js.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/client/transport.d.ts +175 -0
- package/dist/lib/consent/index.d.ts +37 -0
- package/dist/lib/consent/manager.d.ts +197 -0
- package/dist/lib/consent/types.d.ts +162 -0
- package/dist/lib/features/analytics/attribution.d.ts +3 -3
- package/dist/lib/features/analytics/events.d.ts +3 -3
- package/dist/lib/features/analytics/funnels.d.ts +3 -3
- package/dist/lib/features/analytics/heatmaps.d.ts +9 -3
- package/dist/lib/features/analytics/identify.d.ts +3 -3
- package/dist/lib/features/analytics/page-views.d.ts +3 -3
- package/dist/lib/features/analytics/performance.d.ts +3 -3
- package/dist/lib/features/analytics/retention.d.ts +3 -3
- package/dist/lib/features/analytics/sessions.d.ts +3 -3
- package/dist/lib/features/core/activation.d.ts +3 -3
- package/dist/lib/features/core/experiment.d.ts +86 -70
- package/dist/lib/features/core/index.d.ts +1 -3
- package/dist/lib/features/core/track-feature.d.ts +3 -3
- package/dist/lib/features/core/track-prompt.d.ts +3 -3
- package/dist/lib/features/ecommerce/cart.d.ts +3 -3
- package/dist/lib/features/ecommerce/checkout.d.ts +3 -3
- package/dist/lib/features/ecommerce/inventory.d.ts +3 -3
- package/dist/lib/features/ecommerce/ltv.d.ts +3 -3
- package/dist/lib/features/ecommerce/products.d.ts +3 -3
- package/dist/lib/features/ecommerce/promotions.d.ts +3 -3
- package/dist/lib/features/ecommerce/purchases.d.ts +3 -3
- package/dist/lib/features/ecommerce/refunds.d.ts +3 -3
- package/dist/lib/features/ecommerce/search.d.ts +3 -3
- package/dist/lib/features/ecommerce/subscriptions.d.ts +3 -3
- package/dist/lib/features/ecommerce/wishlist.d.ts +3 -3
- package/dist/lib/features/enterprise/acquisition.d.ts +2 -2
- package/dist/lib/features/enterprise/alerts.d.ts +2 -2
- package/dist/lib/features/enterprise/churn.d.ts +2 -2
- package/dist/lib/features/enterprise/cohorts.d.ts +2 -2
- package/dist/lib/features/enterprise/engagement.d.ts +24 -3
- package/dist/lib/features/enterprise/forecasting.d.ts +2 -2
- package/dist/lib/features/enterprise/monetization.d.ts +2 -2
- package/dist/lib/features/enterprise/multi-path-funnels.d.ts +2 -2
- package/dist/lib/features/enterprise/paths.d.ts +2 -2
- package/dist/lib/features/enterprise/security.d.ts +2 -2
- package/dist/lib/features/enterprise/support.d.ts +2 -2
- package/dist/lib/frameworks/nextjs/client.d.ts +7 -13
- package/dist/lib/frameworks/nextjs/server.d.ts +5 -15
- package/dist/lib/frameworks/node/index.d.ts +2 -6
- package/dist/lib/frameworks/nuxt/index.d.ts +7 -3
- package/dist/lib/frameworks/react/CookieBanner.d.ts +91 -0
- package/dist/lib/frameworks/react/index.d.ts +97 -135
- package/dist/lib/frameworks/vue/CookieBanner.d.ts +82 -0
- package/dist/lib/frameworks/vue/index.d.ts +28 -25
- package/dist/lib/types/analytics.d.ts +62 -0
- package/dist/lib/types/common.d.ts +2 -0
- package/dist/lib/utils/identity.d.ts +93 -0
- package/dist/lib/utils/storage.d.ts +69 -0
- package/package.json +13 -2
- package/dist/lib/client/enterprise-ftrpc.d.ts +0 -51
- package/dist/lib/client/ftrpc.d.ts +0 -132
- package/dist/lib/client/modules.d.ts +0 -104
- package/dist/lib/features/core/feature-flag.d.ts +0 -249
- package/dist/lib/utils/functions.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
# Flowgrid SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Production-grade TypeScript SDK for product analytics, ecommerce tracking, experiments, and **universal feature flags** (client & server).
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
7
|
+
- **๐ฉ Feature Flags** โ Client & server-side evaluation with targeting and rollouts
|
|
8
|
+
- **๐ Product Analytics** โ Page views, sessions, events, funnels, retention, attribution
|
|
9
|
+
- **๐ Ecommerce Tracking** โ Products, cart, checkout, purchases, subscriptions
|
|
10
|
+
- **๐งช A/B Testing** โ Experiments with statistical significance
|
|
11
|
+
- **๐ฏ User Identification** โ Identity resolution and traits tracking
|
|
12
|
+
- **๐ Enterprise Analytics** โ Cohorts, churn prediction, monetization metrics
|
|
13
|
+
- **โ๏ธ Framework Support** โ React, Vue, Next.js, Nuxt, Node.js
|
|
14
|
+
- **๐ CMS Embed** โ Drop-in `<script>` tag for Bubble, WordPress, Wix, Squarespace, Webflow
|
|
15
|
+
- **๐ช Cookie Consent** โ GDPR-compliant consent management with customisable banners
|
|
16
|
+
- **๐ Privacy First** โ DNT/GPC support, bot filtering, consent gating
|
|
17
|
+
- **โก High Performance** โ < 0.1ms flag evaluation, sendBeacon transport, offline buffering
|
|
11
18
|
|
|
12
19
|
## Installation
|
|
13
20
|
|
|
@@ -15,12 +22,317 @@ Flowgrid SDK is a JavaScript/TypeScript library for integrating with the Flowgri
|
|
|
15
22
|
npm install flowgrid-sdk
|
|
16
23
|
```
|
|
17
24
|
|
|
18
|
-
##
|
|
25
|
+
## Quick Start
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
### 1. Script Tag (CMS / No-Build)
|
|
28
|
+
|
|
29
|
+
Drop this into any HTML page โ works on WordPress, Wix, Bubble, Squarespace, Webflow, etc.
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<script src="https://cdn.flowgrid.io/flowgrid.min.js"></script>
|
|
33
|
+
<script>
|
|
34
|
+
FlowGrid.init({
|
|
35
|
+
webId: 'web_abc123',
|
|
36
|
+
endpoint: 'https://api.flowgrid.io',
|
|
37
|
+
apiKey: 'key_xxx',
|
|
38
|
+
});
|
|
39
|
+
</script>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Node / TypeScript
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import {
|
|
46
|
+
Events,
|
|
47
|
+
Sessions,
|
|
48
|
+
PageViews,
|
|
49
|
+
Identify,
|
|
50
|
+
Products,
|
|
51
|
+
CartTracking,
|
|
52
|
+
Purchases,
|
|
53
|
+
Funnels,
|
|
54
|
+
Activation,
|
|
55
|
+
Experiment,
|
|
56
|
+
} from 'flowgrid-sdk';
|
|
57
|
+
|
|
58
|
+
const events = new Events('web_123', 'https://api.flowgrid.io', 'key_xxx');
|
|
59
|
+
await events.track({ eventName: 'signup', properties: { plan: 'pro' } });
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. React
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { FlowGridProvider, useFlowGrid, CookieBanner } from 'flowgrid-sdk/react';
|
|
66
|
+
import { ConsentManager } from 'flowgrid-sdk';
|
|
67
|
+
|
|
68
|
+
const consent = new ConsentManager();
|
|
69
|
+
|
|
70
|
+
function App() {
|
|
71
|
+
return (
|
|
72
|
+
<FlowGridProvider webId="web_123" endpoint="https://api.flowgrid.io" apiKey="key_xxx">
|
|
73
|
+
<MyApp />
|
|
74
|
+
<CookieBanner consent={consent} config={{ title: 'We use cookies' }} />
|
|
75
|
+
</FlowGridProvider>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function MyComponent() {
|
|
80
|
+
const { trackEvent, isReady } = useFlowGrid();
|
|
81
|
+
return <button onClick={() => trackEvent('cta_click')}>Go</button>;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 4. Vue 3
|
|
86
|
+
|
|
87
|
+
```vue
|
|
88
|
+
<script setup>
|
|
89
|
+
import { useFlowGrid, useEcommerce, CookieBanner } from 'flowgrid-sdk/vue';
|
|
90
|
+
import { ConsentManager } from 'flowgrid-sdk';
|
|
91
|
+
|
|
92
|
+
const { trackEvent, isReady } = useFlowGrid();
|
|
93
|
+
const { trackAddToCart } = useEcommerce();
|
|
94
|
+
const consent = new ConsentManager();
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<template>
|
|
98
|
+
<button @click="trackEvent('cta_click')">Go</button>
|
|
99
|
+
<CookieBanner :consent="consent" :config="{ title: 'Cookie Preferences' }" />
|
|
100
|
+
</template>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 5. Next.js (App Router)
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
// app/layout.tsx
|
|
107
|
+
import { FlowGridProvider, PageViewTracker, CookieBanner } from 'flowgrid-sdk/nextjs/client';
|
|
108
|
+
import { ConsentManager } from 'flowgrid-sdk';
|
|
109
|
+
|
|
110
|
+
const consent = new ConsentManager();
|
|
111
|
+
|
|
112
|
+
export default function RootLayout({ children }) {
|
|
113
|
+
return (
|
|
114
|
+
<FlowGridProvider webId="web_123" endpoint="https://api.flowgrid.io" apiKey="key_xxx">
|
|
115
|
+
<PageViewTracker />
|
|
116
|
+
{children}
|
|
117
|
+
<CookieBanner consent={consent} />
|
|
118
|
+
</FlowGridProvider>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Server-side tracking:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// app/api/track/route.ts
|
|
127
|
+
import { createServerFlowGrid } from 'flowgrid-sdk/nextjs/server';
|
|
128
|
+
|
|
129
|
+
export async function POST(req: Request) {
|
|
130
|
+
const fg = createServerFlowGrid({ webId: 'web_123', endpoint: '...', apiKey: '...' });
|
|
131
|
+
await fg.events.track({ eventName: 'server_event', properties: {} });
|
|
132
|
+
return Response.json({ ok: true });
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 6. Nuxt 3
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// plugins/flowgrid.client.ts
|
|
140
|
+
import { FlowGridPlugin } from 'flowgrid-sdk/nuxt';
|
|
141
|
+
|
|
142
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
143
|
+
nuxtApp.vueApp.use(FlowGridPlugin, {
|
|
144
|
+
webId: useRuntimeConfig().public.flowgridWebId,
|
|
145
|
+
endpoint: useRuntimeConfig().public.flowgridEndpoint,
|
|
146
|
+
apiKey: useRuntimeConfig().public.flowgridApiKey,
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```vue
|
|
152
|
+
<script setup>
|
|
153
|
+
import { CookieBanner } from 'flowgrid-sdk/nuxt';
|
|
154
|
+
import { ConsentManager } from 'flowgrid-sdk';
|
|
155
|
+
const consent = new ConsentManager();
|
|
156
|
+
</script>
|
|
157
|
+
<template>
|
|
158
|
+
<CookieBanner :consent="consent" />
|
|
159
|
+
</template>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 7. Node.js (Server-only)
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { createFlowGridNode } from 'flowgrid-sdk/node';
|
|
166
|
+
|
|
167
|
+
const fg = createFlowGridNode({ webId: 'web_123', endpoint: '...', apiKey: '...' });
|
|
168
|
+
await fg.events.track({ eventName: 'order_processed', properties: { orderId: '123' } });
|
|
169
|
+
await fg.flush(); // Flush buffered events before shutdown
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Cookie Consent Management
|
|
175
|
+
|
|
176
|
+
The SDK ships a framework-agnostic `ConsentManager` and pre-built cookie banner components for React, Vue, Next.js, and Nuxt.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { ConsentManager } from 'flowgrid-sdk';
|
|
180
|
+
|
|
181
|
+
const consent = new ConsentManager({
|
|
182
|
+
cookieDomain: '.example.com',
|
|
183
|
+
cookieExpiry: 365,
|
|
184
|
+
respectDNT: true,
|
|
185
|
+
onChange: (prefs) => {
|
|
186
|
+
console.log('Consent updated:', prefs);
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Programmatic control
|
|
191
|
+
consent.acceptAll();
|
|
192
|
+
consent.rejectNonEssential();
|
|
193
|
+
consent.update({ analytics: true, marketing: false, preferences: true });
|
|
194
|
+
consent.hasCategory('analytics'); // true
|
|
195
|
+
consent.reset(); // Clear stored consent
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Banner Configuration
|
|
199
|
+
|
|
200
|
+
All banner components accept the same config:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
{
|
|
204
|
+
title: 'Cookie Preferences',
|
|
205
|
+
description: 'We use cookies to improve your experience.',
|
|
206
|
+
privacyPolicyUrl: '/privacy',
|
|
207
|
+
acceptAllLabel: 'Accept All',
|
|
208
|
+
rejectAllLabel: 'Reject All',
|
|
209
|
+
customizeLabel: 'Customize',
|
|
210
|
+
saveLabel: 'Save Preferences',
|
|
211
|
+
forceShow: false,
|
|
212
|
+
categoryDescriptions: {
|
|
213
|
+
necessary: 'Required for the site to function.',
|
|
214
|
+
analytics: 'Help us understand how you use the site.',
|
|
215
|
+
marketing: 'Used to show relevant ads.',
|
|
216
|
+
preferences: 'Remember your settings.',
|
|
217
|
+
},
|
|
218
|
+
theme: {
|
|
219
|
+
position: 'bottom', // 'top' | 'bottom' | 'bottom-left' | 'bottom-right' | 'center'
|
|
220
|
+
primaryColor: '#4F46E5',
|
|
221
|
+
backgroundColor: '#FFFFFF',
|
|
222
|
+
textColor: '#1F2937',
|
|
223
|
+
fontFamily: 'system-ui, sans-serif',
|
|
224
|
+
borderRadius: '12px',
|
|
225
|
+
zIndex: 9999,
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## CMS / No-Build Integration
|
|
233
|
+
|
|
234
|
+
### WordPress
|
|
235
|
+
|
|
236
|
+
Add to your theme's `functions.php` or use a custom HTML block:
|
|
237
|
+
|
|
238
|
+
```php
|
|
239
|
+
function flowgrid_tracking() {
|
|
240
|
+
echo '<script src="https://cdn.flowgrid.io/flowgrid.min.js"></script>';
|
|
241
|
+
echo '<script>FlowGrid.init({ webId: "web_123", endpoint: "https://api.flowgrid.io", apiKey: "key_xxx" });</script>';
|
|
242
|
+
}
|
|
243
|
+
add_action('wp_footer', 'flowgrid_tracking');
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Wix
|
|
247
|
+
|
|
248
|
+
Site Settings โ Custom Code โ Add `<script>` tag to body end.
|
|
249
|
+
|
|
250
|
+
### Bubble
|
|
251
|
+
|
|
252
|
+
Settings โ SEO/Meta Tags โ Script in body.
|
|
253
|
+
|
|
254
|
+
### Webflow / Squarespace
|
|
255
|
+
|
|
256
|
+
Project Settings โ Custom Code โ Footer Code.
|
|
257
|
+
|
|
258
|
+
### Goal Tracking
|
|
259
|
+
|
|
260
|
+
Add `data-goal="goal_name"` to any element to track clicks as goals:
|
|
261
|
+
|
|
262
|
+
```html
|
|
263
|
+
<button data-goal="signup_cta">Sign Up Free</button>
|
|
264
|
+
<a href="/pricing" data-goal="view_pricing">View Pricing</a>
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Ecommerce Tracking
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { Products, CartTracking, Checkout, Purchases } from 'flowgrid-sdk/ecommerce';
|
|
273
|
+
|
|
274
|
+
const products = new Products('web_123', endpoint, apiKey);
|
|
275
|
+
const cart = new CartTracking('web_123', endpoint, apiKey);
|
|
276
|
+
|
|
277
|
+
// Track product view
|
|
278
|
+
await products.view({ productId: 'sku_1', name: 'Widget', price: 29.99, currency: 'USD' });
|
|
279
|
+
|
|
280
|
+
// Add to cart
|
|
281
|
+
await cart.addItem({
|
|
282
|
+
product: { productId: 'sku_1', name: 'Widget', price: 29.99, currency: 'USD' },
|
|
283
|
+
quantity: 2,
|
|
284
|
+
cartId: 'cart_abc',
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Track purchase
|
|
288
|
+
const purchases = new Purchases('web_123', endpoint, apiKey);
|
|
289
|
+
await purchases.track({
|
|
290
|
+
order: {
|
|
291
|
+
orderId: 'order_123',
|
|
292
|
+
items: [{ productId: 'sku_1', name: 'Widget', price: 29.99, quantity: 2, lineTotal: 59.98, currency: 'USD' }],
|
|
293
|
+
subtotal: 59.98,
|
|
294
|
+
total: 64.98,
|
|
295
|
+
currency: 'USD',
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Feature Flags
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { FeatureFlagsClient } from 'flowgrid-sdk';
|
|
306
|
+
|
|
307
|
+
const flags = new FeatureFlagsClient('web_123', 'api.flowgrid.com', 'sk_live_xxx');
|
|
308
|
+
await flags.initialize({ id: 'user_123', country: 'US', plan: 'pro' });
|
|
309
|
+
|
|
310
|
+
const enabled = flags.isEnabled('new_checkout');
|
|
311
|
+
const variant = flags.getVariant('checkout_design');
|
|
312
|
+
const value = flags.getValue('max_items');
|
|
313
|
+
const reason = flags.getFlagReason('new_checkout');
|
|
22
314
|
```
|
|
23
315
|
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Package Exports
|
|
319
|
+
|
|
320
|
+
| Import Path | Description |
|
|
321
|
+
|---|---|
|
|
322
|
+
| `flowgrid-sdk` | All modules |
|
|
323
|
+
| `flowgrid-sdk/react` | React provider, hooks, CookieBanner |
|
|
324
|
+
| `flowgrid-sdk/vue` | Vue plugin, composables, CookieBanner |
|
|
325
|
+
| `flowgrid-sdk/nextjs` | Next.js integration |
|
|
326
|
+
| `flowgrid-sdk/nextjs/client` | Client components (provider, hooks, CookieBanner) |
|
|
327
|
+
| `flowgrid-sdk/nextjs/server` | Server-side tracking |
|
|
328
|
+
| `flowgrid-sdk/nuxt` | Nuxt plugin, composables, CookieBanner |
|
|
329
|
+
| `flowgrid-sdk/node` | Node.js-only (batch + flush) |
|
|
330
|
+
| `flowgrid-sdk/analytics` | Analytics features only |
|
|
331
|
+
| `flowgrid-sdk/ecommerce` | Ecommerce features only |
|
|
332
|
+
| `flowgrid-sdk/core` | Core features (activation, experiments) |
|
|
333
|
+
| `flowgrid-sdk/consent` | Consent management |
|
|
334
|
+
| `flowgrid-sdk/types` | TypeScript type definitions |
|
|
335
|
+
|
|
24
336
|
## Documentation
|
|
25
337
|
|
|
26
338
|
See the [official documentation](https://flow-grid.xyz/documentation) for detailed usage and API reference.
|
package/dist/browser.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ interface BrowserConfig {
|
|
|
27
27
|
webId: string;
|
|
28
28
|
endpoint: string;
|
|
29
29
|
apiKey: string;
|
|
30
|
+
visitorId?: string;
|
|
30
31
|
autoTrack?: {
|
|
31
32
|
pageViews?: boolean;
|
|
32
33
|
sessions?: boolean;
|
|
@@ -37,7 +38,6 @@ declare class FlowGridBrowser {
|
|
|
37
38
|
private activation;
|
|
38
39
|
private trackFeature;
|
|
39
40
|
private experiment;
|
|
40
|
-
private featureFlag;
|
|
41
41
|
private pageViews;
|
|
42
42
|
private sessions;
|
|
43
43
|
private events;
|
|
@@ -47,6 +47,8 @@ declare class FlowGridBrowser {
|
|
|
47
47
|
private checkout;
|
|
48
48
|
private purchases;
|
|
49
49
|
private sessionId;
|
|
50
|
+
private visitorId;
|
|
51
|
+
private identity;
|
|
50
52
|
/**
|
|
51
53
|
* Initialize FlowGrid SDK for browser use.
|
|
52
54
|
*/
|
|
@@ -103,15 +105,12 @@ declare class FlowGridBrowser {
|
|
|
103
105
|
*/
|
|
104
106
|
private startSession;
|
|
105
107
|
/**
|
|
106
|
-
*
|
|
107
|
-
*/
|
|
108
|
-
private generateSessionId;
|
|
109
|
-
/**
|
|
110
|
-
* Get or create cart ID from localStorage.
|
|
108
|
+
* Get or create cart ID via identity manager storage.
|
|
111
109
|
*/
|
|
112
110
|
private getOrCreateCartId;
|
|
113
111
|
/**
|
|
114
|
-
*
|
|
112
|
+
* Read UTM attribution from __flowgrid_* cookies
|
|
113
|
+
* set by track.js / flowgrid_oss_client_sdk.js.
|
|
115
114
|
*/
|
|
116
115
|
private parseUTM;
|
|
117
116
|
}
|
package/dist/flowgrid.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.flowgrid=e():t.flowgrid=e()}(this,()=>(()=>{"use strict";var t={d:(e,i)=>{for(var a in i)t.o(i,a)&&!t.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:i[a]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{default:()=>w});class i{constructor(t,e,i){if(!t?.trim()||!e?.trim())throw new Error("SDK init failed: webId and endpoint are required.");this.webId=t.trim(),this.endpoint=this.normalizeEndpoint(e),this.apiKey=i}async init(t,e={},i="POST"){this.validateInput(t,e);const a=e.type||"event",r=`${this.endpoint}/sdk/functions?call=${encodeURIComponent(a)}`,n=JSON.stringify({web_id:this.webId,event_name:t,properties:e});return this.executeRequest(r,i,"GET"===i?void 0:n)}async executeRequest(t,e,a){let r=0;for(;;){const n=new AbortController,s=setTimeout(()=>n.abort(),i.TIMEOUT_MS);try{const i=await fetch(t,{method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:a,signal:n.signal});if(clearTimeout(s),!i.ok){const t=await this.safeRead(i);throw new Error(`SDK request failed (${i.status} ${i.statusText}) ${t}`)}return await i.json()}catch(t){if(clearTimeout(s),t instanceof Error&&("AbortError"===t.name||t.message.includes("fetch"))&&r<i.MAX_RETRIES){r++;continue}if(t instanceof Error&&"AbortError"===t.name)throw new Error("SDK request timed out");throw t}}}validateInput(t,e){if(!t||"string"!=typeof t||!t.trim())throw new Error("Event name must be a non-empty string");if(t.length>100)throw new Error("Event name must be less than 100 characters");let a;try{a=JSON.stringify({web_id:this.webId,event_name:t,properties:e})}catch{throw new Error("Properties must be JSON serializable")}if((new TextEncoder).encode(a).length>i.MAX_PAYLOAD_SIZE)throw new Error("Event payload too large (max 10KB)");const r=["password","token","secret","apikey","authorization"],n=a.toLowerCase();for(const t of r)n.includes(t)&&console.warn(`SDK Warning: Properties may contain sensitive data (${t}).`)}normalizeEndpoint(t){let e=t.trim();return e.startsWith("http://")||e.startsWith("https://")||(e=`https://${e}`),e.replace(/\/+$/,"")}async safeRead(t){try{return await t.text()}catch{return""}}}i.MAX_PAYLOAD_SIZE=1e4,i.TIMEOUT_MS=1e4,i.MAX_RETRIES=1;class a extends i{async create(t){return await this.init(t.eventName,{...t.properties,type:"activation",timestamp:(new Date).toISOString()})}async trackOnboardingStep(t){return await this.init("onboarding_step",{type:"activation",subtype:"onboarding_step",userId:t.userId,stepId:t.stepId,stepName:t.stepName,stepOrder:t.stepOrder,totalSteps:t.totalSteps,progress:Math.round(t.stepOrder/t.totalSteps*100),timeSpent:t.timeSpent,skipped:t.skipped||!1,...t.properties,timestamp:(new Date).toISOString()})}async completeOnboarding(t,e,i,a){return await this.init("onboarding_completed",{type:"activation",subtype:"onboarding_complete",userId:t,totalTimeSpent:e,stepsCompleted:i,stepsSkipped:a,completionRate:Math.round(i/(i+a)*100),timestamp:(new Date).toISOString()})}async trackFirstAction(t){return await this.init("first_action",{type:"activation",subtype:"first_action",userId:t.userId,actionName:t.actionName,timeSinceSignup:t.timeSinceSignup,source:t.source,...t.properties,timestamp:(new Date).toISOString()})}async trackSignup(t,e,i,a){return await this.init("signup_completed",{type:"activation",subtype:"signup",userId:t,method:e,source:i,...a,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_activation_analytics",{type:"query",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}class r extends i{async create(t){return await this.init("create_tracking",{type:"feature_usage",subtype:"create_custom",userId:t.userId,category:t.category,action:t.action,...t.properties,timestamp:(new Date).toISOString()})}async trackUsage(t){return await this.init("feature_usage",{type:"feature_usage",subtype:t.action,featureId:t.featureId,featureName:t.featureName,userId:t.userId,action:t.action,duration:t.duration,success:t.success,category:t.category,...t.properties,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_feature_analytics",{type:"query",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}class n extends i{async track(t){return await this.init(t.eventName,{...t.properties,type:"experiment",variant:t.variant,timestamp:(new Date).toISOString()})}async define(t){return await this.init("experiment_defined",{type:"experiment",subtype:"definition",...t,timestamp:(new Date).toISOString()})}async assign(t){return await this.init("experiment_assigned",{type:"experiment",subtype:"assignment",experimentId:t.experimentId,userId:t.userId,variantId:t.variantId,source:t.source||"random",timestamp:(new Date).toISOString()})}async trackConversion(t){return await this.init("experiment_conversion",{type:"experiment",subtype:"conversion",experimentId:t.experimentId,userId:t.userId,variantId:t.variantId,metricName:t.metricName,value:t.value,timestamp:(new Date).toISOString()})}async trackExposure(t,e,i){return await this.init("experiment_exposure",{type:"experiment",subtype:"exposure",experimentId:t,userId:e,variantId:i,timestamp:(new Date).toISOString()})}async getResults(t){return await this.init("get_experiment_results",{type:"query",experimentId:t,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_experiment_analytics",{type:"query",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}async getVariant(t,e){return await this.init("get_experiment_variant",{type:"query",experimentId:t,userId:e,timestamp:(new Date).toISOString()})}}class s extends i{async evaluate(t){return await this.init(t.eventName,{...t.properties,type:"feature_flag_evaluation",timestamp:(new Date).toISOString()})}async trackEvaluation(t){return await this.init("flag_evaluated",{type:"feature_flag_evaluation",flagKey:t.flagKey,userId:t.userId,context:t.context,result:t.result,reason:t.reason||"default",duration:t.duration,timestamp:(new Date).toISOString()})}async define(t){return await this.init("flag_defined",{...t,type:"feature_flag_definition",timestamp:(new Date).toISOString()})}async trackStatusChange(t,e,i){return await this.init("flag_status_changed",{type:"feature_flag_status",flagKey:t,enabled:e,changedBy:i,timestamp:(new Date).toISOString()})}async trackRolloutChange(t,e,i,a){return await this.init("flag_rollout_changed",{type:"feature_flag_rollout",flagKey:t,previousPercentage:e,newPercentage:i,changedBy:a,timestamp:(new Date).toISOString()})}}class o extends i{async track(t){return await this.init("page_view",{type:"page_view",url:t.url,path:t.path,title:t.title||"",referrer:t.referrer||"",userId:t.userId,sessionId:t.sessionId,...t.properties,timestamp:(new Date).toISOString()})}async trackScrollDepth(t){return await this.init("scroll_depth",{type:"scroll_depth",path:t.path,scrollDepth:Math.min(100,Math.max(0,t.scrollDepth)),timeAtDepth:t.timeAtDepth,timestamp:(new Date).toISOString()})}async trackTimeOnPage(t){return await this.init("time_on_page",{type:"time_on_page",path:t.path,duration:t.duration,engaged:t.engaged??!1,timestamp:(new Date).toISOString()})}async trackExit(t,e,i){return await this.init("page_exit",{type:"page_exit",path:t,duration:e,scrollDepth:i,timestamp:(new Date).toISOString()})}async getAnalytics(t,e){return await this.init("get_page_analytics",{type:"query",filter:t||{range:"last30days"},pagination:e||{page:1,limit:20},timestamp:(new Date).toISOString()})}}class c extends i{async start(t={}){return await this.init("session_start",{type:"session_start",user:t.user,device:t.device,location:t.location,source:t.source||t.utm?.utmSource,medium:t.medium||t.utm?.utmMedium,campaign:t.campaign||t.utm?.utmCampaign,utm:t.utm,landingPage:t.landingPage||("undefined"!=typeof window?window.location.href:""),referrer:t.referrer||("undefined"!=typeof document?document.referrer:""),...t.properties,timestamp:(new Date).toISOString()})}async update(t,e){return await this.init("session_update",{type:"session_update",sessionId:t,...e,timestamp:(new Date).toISOString()})}async end(t){return await this.init("session_end",{type:"session_end",sessionId:t,timestamp:(new Date).toISOString()})}async heartbeat(t){return await this.init("session_heartbeat",{type:"session_heartbeat",sessionId:t,timestamp:(new Date).toISOString()})}async getAnalytics(t,e){return await this.init("get_session_analytics",{type:"query",filter:t||{range:"last30days"},pagination:e||{page:1,limit:20},timestamp:(new Date).toISOString()})}async getRealTime(){return await this.init("get_realtime_sessions",{type:"query",realtime:!0,timestamp:(new Date).toISOString()})}}class u extends i{async track(t){return await this.init("custom_event",{type:"custom_event",eventName:t.eventName,category:t.category||"general",action:t.action,label:t.label,value:t.value,nonInteraction:t.nonInteraction||!1,userId:t.userId,sessionId:t.sessionId,...t.properties,timestamp:(new Date).toISOString()})}async trackBatch(t){const e=t.events.map(t=>({...t,timestamp:(new Date).toISOString()}));return await this.init("batch_events",{type:"batch_events",events:e,count:e.length,timestamp:(new Date).toISOString()})}async trackClick(t){return await this.init("click_event",{type:"click_event",...t,timestamp:(new Date).toISOString()})}async trackForm(t){return await this.init("form_event",{type:"form_event",...t,timestamp:(new Date).toISOString()})}async trackSearch(t){return await this.init("search_event",{type:"search_event",query:t.query,category:t.category,resultsCount:t.resultsCount,resultClicked:t.resultClicked,clickedPosition:t.clickedPosition,filters:t.filters,timestamp:(new Date).toISOString()})}async trackError(t){return await this.init("error_event",{type:"error_event",message:t.message,stack:t.stack,errorType:t.type,fatal:t.fatal||!1,context:t.context,userAction:t.userAction,timestamp:(new Date).toISOString()})}async getAnalytics(t,e){return await this.init("get_event_analytics",{type:"query",filter:t||{range:"last30days"},pagination:e||{page:1,limit:50},timestamp:(new Date).toISOString()})}}class p extends i{async user(t){return await this.init("identify_user",{type:"identify",userId:t.userId,anonymousId:t.anonymousId,traits:t.traits||{},timestamp:(new Date).toISOString()})}async updateTraits(t,e){return await this.init("update_traits",{type:"update_traits",userId:t,traits:e,timestamp:(new Date).toISOString()})}async alias(t){return await this.init("alias_user",{type:"alias",previousId:t.previousId,userId:t.userId,timestamp:(new Date).toISOString()})}async group(t){return await this.init("group_user",{type:"group",userId:t.userId,groupId:t.groupId,traits:t.traits||{},timestamp:(new Date).toISOString()})}async reset(t){return await this.init("reset_identity",{type:"reset",userId:t,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_user_analytics",{type:"query",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}class d extends i{async view(t){return await this.init("product_view",{type:"ecommerce",subtype:"product_view",product:{productId:t.productId,name:t.name,brand:t.brand,category:t.category,variant:t.variant,price:t.price,currency:t.currency,url:t.url,imageUrl:t.imageUrl,rating:t.rating,reviewCount:t.reviewCount,stockStatus:t.stockStatus,attributes:t.attributes},source:t.source,searchQuery:t.searchQuery,userId:t.userId,sessionId:t.sessionId,...t.properties,timestamp:(new Date).toISOString()})}async impression(t){return await this.init("product_impression",{type:"ecommerce",subtype:"product_impression",list:t.list,productCount:t.list.products.length,userId:t.userId,sessionId:t.sessionId,timestamp:(new Date).toISOString()})}async click(t){return await this.init("product_click",{type:"ecommerce",subtype:"product_click",product:t.product,listId:t.listId,position:t.position,userId:t.userId,timestamp:(new Date).toISOString()})}async quickView(t,e){return await this.init("product_quick_view",{type:"ecommerce",subtype:"product_quick_view",product:t,listId:e,timestamp:(new Date).toISOString()})}async interaction(t,e,i){return await this.init("product_interaction",{type:"ecommerce",subtype:"product_interaction",productId:t,action:e,...i,timestamp:(new Date).toISOString()})}async getAnalytics(t,e){return await this.init("get_product_analytics",{type:"ecommerce",subtype:"get_product_analytics",filter:t||{range:"last30days"},pagination:e||{page:1,limit:20},timestamp:(new Date).toISOString()})}async getProductPerformance(t,e){return await this.init("get_product_performance",{type:"ecommerce",subtype:"get_product_performance",productId:t,filter:e||{range:"last30days"},timestamp:(new Date).toISOString()})}}class m extends i{async addItem(t){const e=t.product.price*t.quantity-(t.product.discount||0);return await this.init("add_to_cart",{type:"ecommerce",subtype:"add_to_cart",product:t.product,quantity:t.quantity,lineTotal:e,cartId:t.cartId,userId:t.userId,source:t.source,...t.properties,timestamp:(new Date).toISOString()})}async removeItem(t){return await this.init("remove_from_cart",{type:"ecommerce",subtype:"remove_from_cart",product:t.product,quantity:t.quantity,cartId:t.cartId,userId:t.userId,reason:t.reason||"user_action",timestamp:(new Date).toISOString()})}async updateItem(t){return await this.init("update_cart_item",{type:"ecommerce",subtype:"update_cart_item",product:t.product,previousQuantity:t.previousQuantity,newQuantity:t.newQuantity,quantityChange:t.newQuantity-t.previousQuantity,cartId:t.cartId,timestamp:(new Date).toISOString()})}async view(t){return await this.init("view_cart",{type:"ecommerce",subtype:"view_cart",cart:t.cart,cartValue:t.cart.total,itemCount:t.cart.itemCount,userId:t.userId,timestamp:(new Date).toISOString()})}async abandon(t,e){return await this.init("cart_abandoned",{type:"ecommerce",subtype:"cart_abandoned",cart:t,cartValue:t.total,itemCount:t.itemCount,reason:e,timestamp:(new Date).toISOString()})}async applyCoupon(t,e,i,a){return await this.init("apply_coupon",{type:"ecommerce",subtype:"apply_coupon",cartId:t,couponCode:e,success:i,discount:a,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_cart_analytics",{type:"ecommerce",subtype:"get_cart_analytics",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}class y extends i{async begin(t){return await this.init("begin_checkout",{type:"ecommerce",subtype:"begin_checkout",cart:t.cart,cartValue:t.cart.total,itemCount:t.cart.itemCount,userId:t.userId,sessionId:t.sessionId,...t.properties,timestamp:(new Date).toISOString()})}async step(t){return await this.init("checkout_step",{type:"ecommerce",subtype:"checkout_step",step:t.step,stepNumber:t.stepNumber,cart:t.cart,option:t.option,userId:t.userId,timestamp:(new Date).toISOString()})}async addShipping(t){return await this.init("add_shipping_info",{type:"ecommerce",subtype:"shipping_info",cart:t.cart,shipping:t.shipping,shippingMethod:t.shipping.method,shippingCost:t.shipping.cost,userId:t.userId,timestamp:(new Date).toISOString()})}async addPayment(t){return await this.init("add_payment_info",{type:"ecommerce",subtype:"payment_info",cart:t.cart,payment:t.payment,paymentMethod:t.payment.method,userId:t.userId,timestamp:(new Date).toISOString()})}async abandon(t,e,i){return await this.init("checkout_abandoned",{type:"ecommerce",subtype:"checkout_abandoned",cart:t,step:e,cartValue:t.total,reason:i,timestamp:(new Date).toISOString()})}async paymentAttempt(t,e,i,a){return await this.init("payment_attempt",{type:"ecommerce",subtype:"payment_attempt",success:t,cart:e,payment:i,errorCode:a,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_checkout_analytics",{type:"ecommerce",subtype:"checkout_analytics",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}class l extends i{async track(t){const e=t.order;return await this.init("purchase",{type:"ecommerce",subtype:"purchase",order:{orderId:e.orderId,items:e.items,subtotal:e.subtotal,discountTotal:e.discountTotal,shippingTotal:e.shippingTotal,taxTotal:e.taxTotal,total:e.total,currency:e.currency,shipping:e.shipping,payment:e.payment,coupons:e.coupons,affiliation:e.affiliation},revenue:e.total,itemCount:e.items.reduce((t,e)=>t+e.quantity,0),userId:t.userId,sessionId:t.sessionId,attribution:t.attribution,...t.properties,timestamp:(new Date).toISOString()})}async statusChange(t,e,i){return await this.init("order_status_change",{type:"ecommerce",subtype:"order_status_change",orderId:t,newStatus:e,previousStatus:i,timestamp:(new Date).toISOString()})}async shipped(t,e){return await this.init("order_shipped",{type:"ecommerce",subtype:"order_shipped",orderId:t,shipping:e,timestamp:(new Date).toISOString()})}async delivered(t,e){return await this.init("order_delivered",{type:"ecommerce",subtype:"order_delivered",orderId:t,deliveredAt:e||(new Date).toISOString(),timestamp:(new Date).toISOString()})}async getRevenue(t){return await this.init("get_revenue_analytics",{type:"ecommerce",subtype:"get_revenue_analytics",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}async getRecentOrders(t){return await this.init("get_recent_orders",{type:"ecommerce",subtype:"get_recent_orders",pagination:t||{page:1,limit:20},timestamp:(new Date).toISOString()})}async getOrder(t){return await this.init("get_order",{type:"ecommerce",subtype:"get_order",orderId:t,timestamp:(new Date).toISOString()})}}const g=new class{constructor(){this.config=null,this.activation=null,this.trackFeature=null,this.experiment=null,this.featureFlag=null,this.pageViews=null,this.sessions=null,this.events=null,this.identifyModule=null,this.products=null,this.cart=null,this.checkout=null,this.purchases=null,this.sessionId=null}init(t){this.config=t;const{webId:e,endpoint:i,apiKey:g}=t;this.activation=new a(e,i,g),this.trackFeature=new r(e,i,g),this.experiment=new n(e,i,g),this.featureFlag=new s(e,i,g),this.pageViews=new o(e,i,g),this.sessions=new c(e,i,g),this.events=new u(e,i,g),this.identifyModule=new p(e,i,g),this.products=new d(e,i,g),this.cart=new m(e,i,g),this.checkout=new y(e,i,g),this.purchases=new l(e,i,g),!1!==t.autoTrack?.sessions&&this.startSession(),!1!==t.autoTrack?.pageViews&&this.page(),console.log("FlowGrid SDK initialized")}async track(t,e){if(!this.events)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");await this.events.track({eventName:t,category:"custom",properties:{...e,sessionId:this.sessionId}})}async page(t,e){if(!this.pageViews)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");const i=t||window.location.href,a=e||document.title;await this.pageViews.track({url:i,path:window.location.pathname,title:a,referrer:document.referrer,sessionId:this.sessionId||void 0})}async identify(t,e){if(!this.identifyModule)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");await this.identifyModule.user({userId:t,traits:e||{}})}async productView(t){if(!this.products)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");await this.products.view({productId:t.productId,name:t.name,price:t.price,currency:t.currency||"USD",category:t.category,brand:t.brand,sessionId:this.sessionId||void 0})}async addToCart(t,e){if(!this.cart)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");await this.cart.addItem({product:{productId:t.productId,name:t.name,price:t.price,currency:t.currency||"USD"},quantity:t.quantity,cartId:e||this.getOrCreateCartId()})}async purchase(t){if(!this.purchases)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");const e=t.currency||"USD",i=t.items.map(t=>({productId:t.productId,name:t.name,price:t.price,currency:e,quantity:t.quantity,lineTotal:t.price*t.quantity})),a=i.reduce((t,e)=>t+e.lineTotal,0);await this.purchases.track({order:{orderId:t.orderId,items:i,subtotal:a,discountTotal:0,shippingTotal:0,taxTotal:0,total:t.total,currency:e},sessionId:this.sessionId||void 0})}async startSession(){this.sessions&&(this.sessionId=this.generateSessionId(),await this.sessions.start({landingPage:window.location.href,referrer:document.referrer,utm:this.parseUTM()}))}generateSessionId(){return`sess_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}getOrCreateCartId(){let t=localStorage.getItem("flowgrid_cart_id");return t||(t=`cart_${Date.now()}_${Math.random().toString(36).substring(2,11)}`,localStorage.setItem("flowgrid_cart_id",t)),t}parseUTM(){const t=new URLSearchParams(window.location.search);return{utmSource:t.get("utm_source")||void 0,utmMedium:t.get("utm_medium")||void 0,utmCampaign:t.get("utm_campaign")||void 0,utmTerm:t.get("utm_term")||void 0,utmContent:t.get("utm_content")||void 0}}},w=g;return"undefined"!=typeof window&&(window.flowgrid=g),e})());
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.flowgrid=e():t.flowgrid=e()}(this,()=>(()=>{"use strict";var t={d:(e,i)=>{for(var r in i)t.o(i,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:i[r]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{default:()=>v});class i{constructor(t,e,i,r,s,n){if(!t?.trim()||!e?.trim())throw new Error("SDK init failed: webId and endpoint are required.");this.webId=t.trim(),this.endpoint=this.normalizeEndpoint(e),this.apiKey=i,this.identityManager=s,this.visitorId=r??"",this.transportConfig={botDetection:n?.botDetection??"block",respectDNT:n?.respectDNT??!0,sampleRate:n?.sampleRate??1},this.drainEventBuffer()}static setConsent(t){Object.assign(i.consent,t)}static hasConsent(t){return i.consent[t]??!1}static isLikelyBot(){return!("undefined"==typeof navigator||!navigator.webdriver&&!/bot|crawl|spider|headless|phantom|puppeteer/i.test(navigator.userAgent))}isDNTEnabled(){return"undefined"!=typeof navigator&&(!!navigator.globalPrivacyControl||"1"===navigator.doNotTrack)}static getDeviceContext(){return"undefined"==typeof window||"undefined"==typeof navigator?{}:{_screen_width:window.screen?.width,_screen_height:window.screen?.height,_device_type:/Mobi|Android/i.test(navigator.userAgent)?"mobile":"desktop",_language:navigator.language}}sendViaBeacon(t,e={}){if("undefined"==typeof navigator||"function"!=typeof navigator.sendBeacon)return!1;const r=this.identityManager?this.identityManager.getVisitorId():this.visitorId??"",s=e.type||"event",n=`${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(s)}`,a=JSON.stringify({web_id:this.webId,visitor_id:r,event_name:t,properties:{...e,...i.getDeviceContext()}});return navigator.sendBeacon(n,new Blob([a],{type:"application/json"}))}bufferEvent(t,e,r){if("undefined"!=typeof localStorage)try{const s=localStorage.getItem(i.EVENT_BUFFER_KEY),n=s?JSON.parse(s):[];if(n.length>=i.MAX_BUFFER_EVENTS)return;const a=JSON.stringify([...n,{url:t,method:e,body:r}]);if((new TextEncoder).encode(a).length>i.MAX_BUFFER_SIZE)return;n.push({url:t,method:e,body:r}),localStorage.setItem(i.EVENT_BUFFER_KEY,JSON.stringify(n))}catch{}}drainEventBuffer(){if("undefined"!=typeof localStorage)try{const t=localStorage.getItem(i.EVENT_BUFFER_KEY);if(!t)return;const e=JSON.parse(t);localStorage.removeItem(i.EVENT_BUFFER_KEY);for(const t of e)fetch(t.url,{method:t.method,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:t.body}).catch(()=>{})}catch{}}async init(t,e={},r="POST"){if(!i.hasConsent("analytics"))return{};if(this.transportConfig.respectDNT&&this.isDNTEnabled())return{};if("block"===this.transportConfig.botDetection&&i.isLikelyBot())return{};this.validateInput(t,e),"tag"===this.transportConfig.botDetection&&i.isLikelyBot()&&(e={...e,_bot:!0});const s={...i.getDeviceContext(),...e},n=s.type||"event",a=`${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(n)}`,o=this.identityManager?this.identityManager.getVisitorId():this.visitorId??"",c=JSON.stringify({web_id:this.webId,visitor_id:o,event_name:t,properties:s});return this.executeRequest(a,r,"GET"===r?void 0:c)}async initFeature(t,e){if(this.validateInput(t,e),!e.type)throw new Error("Feature events must include a type property");const i=`${this.endpoint}/api/v1/sdk/functions?call=${encodeURIComponent(e.type)}`,r=this.identityManager?this.identityManager.getVisitorId():this.visitorId??"",s=JSON.stringify({web_id:this.webId,visitor_id:r,event_name:t,properties:e});return this.executeRequest(i,"POST",s)}async executeRequest(t,e,r,s){let n=0;for(;;){const a=new AbortController,o=setTimeout(()=>a.abort(),i.TIMEOUT_MS);try{const c=await fetch(t,{method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...s},body:r,signal:a.signal});if(clearTimeout(o),c.status>=500&&n<i.MAX_RETRIES){n++,await this.backoff(n);continue}if(!c.ok){const t=await this.safeRead(c);throw new Error(`SDK request failed (${c.status} ${c.statusText}) ${t}`)}return await c.json()}catch(s){if(clearTimeout(o),s instanceof Error&&("AbortError"===s.name||s.message.includes("fetch"))&&n<i.MAX_RETRIES){n++,await this.backoff(n);continue}if(n>=i.MAX_RETRIES&&(r&&"undefined"!=typeof navigator&&"function"==typeof navigator.sendBeacon&&navigator.sendBeacon(t,new Blob([r],{type:"application/json"}))||this.bufferEvent(t,e,r)),s instanceof Error&&"AbortError"===s.name)throw new Error("SDK request timed out");throw s}}}backoff(t){const e=1e3*Math.pow(2,t-1);return new Promise(t=>setTimeout(t,e))}validateInput(t,e){if(!t||"string"!=typeof t||!t.trim())throw new Error("Event name must be a non-empty string");if(t.length>100)throw new Error("Event name must be less than 100 characters");let r;try{r=JSON.stringify({web_id:this.webId,visitor_id:this.visitorId??"",event_name:t,properties:e})}catch{throw new Error("Properties must be JSON serializable")}if((new TextEncoder).encode(r).length>i.MAX_PAYLOAD_SIZE)throw new Error("Event payload too large (max 10KB)");const s=["password","token","secret","apikey","authorization"],n=r.toLowerCase();for(const t of s)n.includes(t)&&console.warn(`SDK Warning: Properties may contain sensitive data (${t}).`)}normalizeEndpoint(t){let e=t.trim();return e.startsWith("http://")||e.startsWith("https://")||(e=`https://${e}`),e.replace(/\/+$/,"")}async safeRead(t){try{return await t.text()}catch{return""}}}i.MAX_PAYLOAD_SIZE=1e4,i.TIMEOUT_MS=1e4,i.MAX_RETRIES=3,i.MAX_BUFFER_EVENTS=100,i.MAX_BUFFER_SIZE=5e4,i.EVENT_BUFFER_KEY="fg_event_buffer",i.consent={analytics:!0,marketing:!0};class r extends i{async create(t){return await this.init(t.eventName,{...t.properties,type:"activation",timestamp:(new Date).toISOString()})}async trackOnboardingStep(t){return await this.init("onboarding_step",{type:"activation",subtype:"onboarding_step",userId:t.userId,stepId:t.stepId,stepName:t.stepName,stepOrder:t.stepOrder,totalSteps:t.totalSteps,progress:Math.round(t.stepOrder/t.totalSteps*100),timeSpent:t.timeSpent,skipped:t.skipped||!1,...t.properties,timestamp:(new Date).toISOString()})}async completeOnboarding(t,e,i,r){return await this.init("onboarding_completed",{type:"activation",subtype:"onboarding_complete",userId:t,totalTimeSpent:e,stepsCompleted:i,stepsSkipped:r,completionRate:Math.round(i/(i+r)*100),timestamp:(new Date).toISOString()})}async trackFirstAction(t){return await this.init("first_action",{type:"activation",subtype:"first_action",userId:t.userId,actionName:t.actionName,timeSinceSignup:t.timeSinceSignup,source:t.source,...t.properties,timestamp:(new Date).toISOString()})}async trackSignup(t,e,i,r){return await this.init("signup_completed",{type:"activation",subtype:"signup",userId:t,method:e,source:i,...r,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_activation_analytics",{type:"query",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}class s extends i{async create(t){return await this.init("create_tracking",{type:"feature_usage",subtype:"create_custom",userId:t.userId,category:t.category,action:t.action,...t.properties,timestamp:(new Date).toISOString()})}async trackUsage(t){return await this.init("feature_usage",{type:"feature_usage",subtype:t.action,featureId:t.featureId,featureName:t.featureName,userId:t.userId,action:t.action,duration:t.duration,success:t.success,category:t.category,...t.properties,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_feature_analytics",{type:"query",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}function n(t){let e=2166136261;for(let i=0;i<t.length;i++)e^=t.charCodeAt(i),e=16777619*e>>>0;return e}const a="fg_exp_assignments";class o extends i{constructor(){super(...arguments),this.experiments={},this.assignments={}}loadAssignments(){try{if("undefined"!=typeof localStorage){const t=localStorage.getItem(a);return t?JSON.parse(t):{}}}catch{}return{}}saveAssignments(){try{"undefined"!=typeof localStorage&&localStorage.setItem(a,JSON.stringify(this.assignments))}catch{}}injectAssignments(t){this.assignments={...this.assignments,...t}}getAssignments(){return{...this.assignments}}getOrCreateAnonId(){try{if("undefined"!=typeof localStorage){const t=localStorage.getItem(o.ANON_ID_KEY);if(t)return t;const e="undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const e=16*Math.random()|0;return("x"===t?e:3&e|8).toString(16)});return localStorage.setItem(o.ANON_ID_KEY,e),e}}catch{}return"anon_"+n(String(Math.random())).toString(36)}deterministicAssign(t,e,i){const r=n(t+e);let s=0;for(const t of i)s+=t.weight;const a=r%1e4/1e4*s;let o=0;for(const t of i)if(o+=t.weight,a<o)return t.id;return i[i.length-1].id}assignVariant(t,e){this.assignments=this.loadAssignments();const i=this.assignments[t];if(i&&e.some(t=>t.id===i))return;const r=this.identityManager?.getVisitorId?.()||this.visitorId||this.getOrCreateAnonId(),s=this.deterministicAssign(r,t,e);this.assignments[t]=s,this.saveAssignments(),this.init("experiment_assignment",{type:"experiment",subtype:"assignment",experimentId:t,variantId:s,assignmentSource:"sdk_init",timestamp:(new Date).toISOString()}).catch(()=>{})}async verifyExperiments(t){const e={web_id:this.webId,experiments:t.map(t=>({experiment_id:t.id,variants:t.variants,properties:t.properties||{}}))};try{const t=await fetch(`${this.endpoint}/api/v1/experiments/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),i=await t.json();if(i.ok&&Array.isArray(i.data))for(const t of i.data)"error"!==t.action&&(this.experiments[t.experiment_id]={variants:t.variants,properties:t.properties??{},verified:!0},this.assignVariant(t.experiment_id,t.variants))}catch{for(const e of t)this.experiments[e.id]={variants:e.variants,properties:e.properties||{},verified:!1},this.assignVariant(e.id,e.variants)}}async initExperiments(t){this.assignments=this.loadAssignments(),t.length>0&&await this.verifyExperiments(t)}getLocalVariant(t){return this.assignments=this.loadAssignments(),this.assignments[t]??null}async trackLocalConversion(t,e="conversion",i=1){const r=this.getLocalVariant(t);r&&await this.init("experiment_conversion",{type:"experiment",subtype:"conversion",experimentId:t,variantId:r,metricName:e,metricValue:i,timestamp:(new Date).toISOString()})}async track(t){return await this.init(t.eventName,{...t.properties,type:"experiment",subtype:"custom",variant:t.variant,timestamp:(new Date).toISOString()})}async define(t){return await this.init("experiment_definition",{type:"experiment",subtype:"definition",...t,timestamp:(new Date).toISOString()})}async assign(t){return await this.init("experiment_assignment",{type:"experiment",subtype:"assignment",experimentId:t.experimentId,userId:t.userId,variantId:t.variantId,source:t.source||"random",timestamp:(new Date).toISOString()})}async trackConversion(t){return await this.init("experiment_conversion",{type:"experiment",subtype:"conversion",experimentId:t.experimentId,userId:t.userId,variantId:t.variantId,metricName:t.metricName,value:t.value,timestamp:(new Date).toISOString()})}async trackExposure(t,e,i){return await this.init("experiment_exposure",{type:"experiment",subtype:"exposure",experimentId:t,userId:e,variantId:i,timestamp:(new Date).toISOString()})}async getResults(t){return await this.init("get_experiment_results",{type:"query",experimentId:t,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_experiment_analytics",{type:"query",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}async getVariant(t,e){return await this.init("get_experiment_variant",{type:"query",experimentId:t,userId:e,timestamp:(new Date).toISOString()})}}o.ANON_ID_KEY="fg_anon_id";class c extends i{async track(t){return await this.init("page_view",{type:"page_view",url:t.url,path:t.path,title:t.title||"",referrer:t.referrer||"",userId:t.userId,sessionId:t.sessionId,...t.properties,timestamp:(new Date).toISOString()})}async trackScrollDepth(t){return await this.init("scroll_depth",{type:"scroll_depth",path:t.path,scrollDepth:Math.min(100,Math.max(0,t.scrollDepth)),timeAtDepth:t.timeAtDepth,timestamp:(new Date).toISOString()})}async trackTimeOnPage(t){return await this.init("time_on_page",{type:"time_on_page",path:t.path,duration:t.duration,engaged:t.engaged??!1,timestamp:(new Date).toISOString()})}async trackExit(t,e,i){return await this.init("page_exit",{type:"page_exit",path:t,duration:e,scrollDepth:i,timestamp:(new Date).toISOString()})}async getAnalytics(t,e){return await this.init("get_page_analytics",{type:"query",filter:t||{range:"last30days"},pagination:e||{page:1,limit:20},timestamp:(new Date).toISOString()})}}class d extends i{async start(t={}){return await this.init("session_start",{type:"session_start",user:t.user,device:t.device,location:t.location,source:t.source||t.utm?.utmSource,medium:t.medium||t.utm?.utmMedium,campaign:t.campaign||t.utm?.utmCampaign,utm:t.utm,landingPage:t.landingPage||("undefined"!=typeof window?window.location.href:""),referrer:t.referrer||("undefined"!=typeof document?document.referrer:""),...t.properties,timestamp:(new Date).toISOString()})}async update(t,e){return await this.init("session_update",{type:"session_update",sessionId:t,...e,timestamp:(new Date).toISOString()})}async end(t){return await this.init("session_end",{type:"session_end",sessionId:t,timestamp:(new Date).toISOString()})}async heartbeat(t){return await this.init("session_heartbeat",{type:"session_heartbeat",sessionId:t,timestamp:(new Date).toISOString()})}async getAnalytics(t,e){return await this.init("get_session_analytics",{type:"query",filter:t||{range:"last30days"},pagination:e||{page:1,limit:20},timestamp:(new Date).toISOString()})}async getRealTime(){return await this.init("get_realtime_sessions",{type:"query",realtime:!0,timestamp:(new Date).toISOString()})}}class p extends i{async track(t){return await this.init("custom_event",{type:"custom_event",eventName:t.eventName,category:t.category||"general",action:t.action,label:t.label,value:t.value,nonInteraction:t.nonInteraction||!1,userId:t.userId,sessionId:t.sessionId,...t.properties,timestamp:(new Date).toISOString()})}async trackBatch(t){const e=t.events.map(t=>({...t,timestamp:(new Date).toISOString()}));return await this.init("batch_events",{type:"batch_events",events:e,count:e.length,timestamp:(new Date).toISOString()})}async trackClick(t){return await this.init("click_event",{type:"click_event",...t,timestamp:(new Date).toISOString()})}async trackForm(t){return await this.init("form_event",{type:"form_event",...t,timestamp:(new Date).toISOString()})}async trackSearch(t){return await this.init("search_event",{type:"search_event",query:t.query,category:t.category,resultsCount:t.resultsCount,resultClicked:t.resultClicked,clickedPosition:t.clickedPosition,filters:t.filters,timestamp:(new Date).toISOString()})}async trackError(t){return await this.init("error_event",{type:"error_event",message:t.message,stack:t.stack,errorType:t.type,fatal:t.fatal||!1,context:t.context,userAction:t.userAction,timestamp:(new Date).toISOString()})}async getAnalytics(t,e){return await this.init("get_event_analytics",{type:"query",filter:t||{range:"last30days"},pagination:e||{page:1,limit:50},timestamp:(new Date).toISOString()})}}class u extends i{async user(t){return await this.init("identify_user",{type:"identify",userId:t.userId,anonymousId:t.anonymousId,traits:t.traits||{},timestamp:(new Date).toISOString()})}async updateTraits(t,e){return await this.init("update_traits",{type:"update_traits",userId:t,traits:e,timestamp:(new Date).toISOString()})}async alias(t){return await this.init("alias_user",{type:"alias",previousId:t.previousId,userId:t.userId,timestamp:(new Date).toISOString()})}async group(t){return await this.init("group_user",{type:"group",userId:t.userId,groupId:t.groupId,traits:t.traits||{},timestamp:(new Date).toISOString()})}async reset(t){return await this.init("reset_identity",{type:"reset",userId:t,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_user_analytics",{type:"query",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}class m extends i{async view(t){return await this.init("product_view",{type:"ecommerce",subtype:"product_view",product:{productId:t.productId,name:t.name,brand:t.brand,category:t.category,variant:t.variant,price:t.price,currency:t.currency,url:t.url,imageUrl:t.imageUrl,rating:t.rating,reviewCount:t.reviewCount,stockStatus:t.stockStatus,attributes:t.attributes},source:t.source,searchQuery:t.searchQuery,userId:t.userId,sessionId:t.sessionId,...t.properties,timestamp:(new Date).toISOString()})}async impression(t){return await this.init("product_impression",{type:"ecommerce",subtype:"product_impression",list:t.list,productCount:t.list.products.length,userId:t.userId,sessionId:t.sessionId,timestamp:(new Date).toISOString()})}async click(t){return await this.init("product_click",{type:"ecommerce",subtype:"product_click",product:t.product,listId:t.listId,position:t.position,userId:t.userId,timestamp:(new Date).toISOString()})}async quickView(t,e){return await this.init("product_quick_view",{type:"ecommerce",subtype:"product_quick_view",product:t,listId:e,timestamp:(new Date).toISOString()})}async interaction(t,e,i){return await this.init("product_interaction",{type:"ecommerce",subtype:"product_interaction",productId:t,action:e,...i,timestamp:(new Date).toISOString()})}async getAnalytics(t,e){return await this.init("get_product_analytics",{type:"ecommerce",subtype:"get_product_analytics",filter:t||{range:"last30days"},pagination:e||{page:1,limit:20},timestamp:(new Date).toISOString()})}async getProductPerformance(t,e){return await this.init("get_product_performance",{type:"ecommerce",subtype:"get_product_performance",productId:t,filter:e||{range:"last30days"},timestamp:(new Date).toISOString()})}}class h extends i{async addItem(t){const e=t.product.price*t.quantity-(t.product.discount||0);return await this.init("add_to_cart",{type:"ecommerce",subtype:"add_to_cart",product:t.product,quantity:t.quantity,lineTotal:e,cartId:t.cartId,userId:t.userId,source:t.source,...t.properties,timestamp:(new Date).toISOString()})}async removeItem(t){return await this.init("remove_from_cart",{type:"ecommerce",subtype:"remove_from_cart",product:t.product,quantity:t.quantity,cartId:t.cartId,userId:t.userId,reason:t.reason||"user_action",timestamp:(new Date).toISOString()})}async updateItem(t){return await this.init("update_cart_item",{type:"ecommerce",subtype:"update_cart_item",product:t.product,previousQuantity:t.previousQuantity,newQuantity:t.newQuantity,quantityChange:t.newQuantity-t.previousQuantity,cartId:t.cartId,timestamp:(new Date).toISOString()})}async view(t){return await this.init("view_cart",{type:"ecommerce",subtype:"view_cart",cart:t.cart,cartValue:t.cart.total,itemCount:t.cart.itemCount,userId:t.userId,timestamp:(new Date).toISOString()})}async abandon(t,e){return await this.init("cart_abandoned",{type:"ecommerce",subtype:"cart_abandoned",cart:t,cartValue:t.total,itemCount:t.itemCount,reason:e,timestamp:(new Date).toISOString()})}async applyCoupon(t,e,i,r){return await this.init("apply_coupon",{type:"ecommerce",subtype:"apply_coupon",cartId:t,couponCode:e,success:i,discount:r,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_cart_analytics",{type:"ecommerce",subtype:"get_cart_analytics",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}class g extends i{async begin(t){return await this.init("begin_checkout",{type:"ecommerce",subtype:"begin_checkout",cart:t.cart,cartValue:t.cart.total,itemCount:t.cart.itemCount,userId:t.userId,sessionId:t.sessionId,...t.properties,timestamp:(new Date).toISOString()})}async step(t){return await this.init("checkout_step",{type:"ecommerce",subtype:"checkout_step",step:t.step,stepNumber:t.stepNumber,cart:t.cart,option:t.option,userId:t.userId,timestamp:(new Date).toISOString()})}async addShipping(t){return await this.init("add_shipping_info",{type:"ecommerce",subtype:"shipping_info",cart:t.cart,shipping:t.shipping,shippingMethod:t.shipping.method,shippingCost:t.shipping.cost,userId:t.userId,timestamp:(new Date).toISOString()})}async addPayment(t){return await this.init("add_payment_info",{type:"ecommerce",subtype:"payment_info",cart:t.cart,payment:t.payment,paymentMethod:t.payment.method,userId:t.userId,timestamp:(new Date).toISOString()})}async abandon(t,e,i){return await this.init("checkout_abandoned",{type:"ecommerce",subtype:"checkout_abandoned",cart:t,step:e,cartValue:t.total,reason:i,timestamp:(new Date).toISOString()})}async paymentAttempt(t,e,i,r){return await this.init("payment_attempt",{type:"ecommerce",subtype:"payment_attempt",success:t,cart:e,payment:i,errorCode:r,timestamp:(new Date).toISOString()})}async getAnalytics(t){return await this.init("get_checkout_analytics",{type:"ecommerce",subtype:"checkout_analytics",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}}class y extends i{async track(t){const e=t.order;return await this.init("purchase",{type:"ecommerce",subtype:"purchase",order:{orderId:e.orderId,items:e.items,subtotal:e.subtotal,discountTotal:e.discountTotal,shippingTotal:e.shippingTotal,taxTotal:e.taxTotal,total:e.total,currency:e.currency,shipping:e.shipping,payment:e.payment,coupons:e.coupons,affiliation:e.affiliation},revenue:e.total,itemCount:e.items.reduce((t,e)=>t+e.quantity,0),userId:t.userId,sessionId:t.sessionId,attribution:t.attribution,...t.properties,timestamp:(new Date).toISOString()})}async statusChange(t,e,i){return await this.init("order_status_change",{type:"ecommerce",subtype:"order_status_change",orderId:t,newStatus:e,previousStatus:i,timestamp:(new Date).toISOString()})}async shipped(t,e){return await this.init("order_shipped",{type:"ecommerce",subtype:"order_shipped",orderId:t,shipping:e,timestamp:(new Date).toISOString()})}async delivered(t,e){return await this.init("order_delivered",{type:"ecommerce",subtype:"order_delivered",orderId:t,deliveredAt:e||(new Date).toISOString(),timestamp:(new Date).toISOString()})}async getRevenue(t){return await this.init("get_revenue_analytics",{type:"ecommerce",subtype:"get_revenue_analytics",filter:t||{range:"last30days"},timestamp:(new Date).toISOString()})}async getRecentOrders(t){return await this.init("get_recent_orders",{type:"ecommerce",subtype:"get_recent_orders",pagination:t||{page:1,limit:20},timestamp:(new Date).toISOString()})}async getOrder(t){return await this.init("get_order",{type:"ecommerce",subtype:"get_order",orderId:t,timestamp:(new Date).toISOString()})}}function l(t){if("undefined"==typeof document)return null;const e=document.cookie.match(new RegExp("(^| )"+t+"=([^;]+)"));return e?e[2]:null}const I="fg_visitor_id",w="fg_session_id",f="fg_session_touch";class S{constructor(t){this.visitorId="",this.sessionId="",this.memoryStore={},this.config={visitorId:t?.visitorId??"",cookieDomain:t?.cookieDomain??"",visitorMaxAgeDays:t?.visitorMaxAgeDays??365,sessionTimeoutMinutes:t?.sessionTimeoutMinutes??30,storage:t?.storage??(this.detectBrowser()?"cookie":"memory"),serverCookies:t?.serverCookies,compat:t?.compat??!0},this.visitorId=this.resolveVisitorId(),this.sessionId=""}getVisitorId(){return this.visitorId||(this.visitorId=this.resolveVisitorId()),this.visitorId}getSessionId(){if("memory"===this.config.storage)return this.getMemorySessionId();if(!this.detectBrowser())return this.getMemorySessionId();try{const t=sessionStorage.getItem(w),e=sessionStorage.getItem(f);if(t&&e){if(Date.now()-parseInt(e,10)<60*this.config.sessionTimeoutMinutes*1e3)return sessionStorage.setItem(f,String(Date.now())),this.sessionId=t,t}const i=this.generateSessionId();return sessionStorage.setItem(w,i),sessionStorage.setItem(f,String(Date.now())),this.sessionId=i,i}catch{return this.getMemorySessionId()}}setVisitorId(t){this.visitorId=t,this.persistVisitorId(t)}clearSession(){if(this.sessionId="",delete this.memoryStore[w],delete this.memoryStore[f],this.detectBrowser())try{sessionStorage.removeItem(w),sessionStorage.removeItem(f)}catch{}}clearAll(){if(this.visitorId="",this.sessionId="",this.memoryStore={},this.detectBrowser())try{this.deleteCookie(this.visitorCookieName),this.deleteCookie(I),localStorage.removeItem(this.visitorCookieName),localStorage.removeItem(I),localStorage.removeItem("fg_exp_assignments"),sessionStorage.removeItem(w),sessionStorage.removeItem(f)}catch{}}isBrowser(){return this.detectBrowser()}get visitorCookieName(){return this.config.compat?"visitor_id":I}resolveVisitorId(){if(this.config.visitorId)return this.config.visitorId;if(this.detectBrowser()){const t=function(){try{return l("visitor_id")||localStorage.getItem("visitor_id")}catch{return l("visitor_id")}}();if(t){const e=this.readCookie(I)||this.readLocalStorage(I);return e&&e!==t&&this.removeLegacyCookie(),this.persistVisitorId(t),t}}if(this.config.compat){const t=this.readCookie(I)||this.readLocalStorage(I);if(t)return this.persistVisitorId(t),this.removeLegacyCookie(),t}const t=this.readCookie(this.visitorCookieName);if(t)return t;if(this.detectBrowser())try{const t=localStorage.getItem(this.visitorCookieName);if(t)return t}catch{}if(this.config.serverCookies){const t=this.readServerCookie(this.visitorCookieName);if(t)return t}if(this.memoryStore[this.visitorCookieName])return this.memoryStore[this.visitorCookieName];const e=this.generateVisitorId();return this.persistVisitorId(e),e}readLocalStorage(t){if(!this.detectBrowser())return null;try{return localStorage.getItem(t)}catch{return null}}removeLegacyCookie(){if(this.deleteCookie(I),this.detectBrowser())try{localStorage.removeItem(I)}catch{}}persistVisitorId(t){const e=this.visitorCookieName;if("memory"!==this.config.storage)if(this.detectBrowser()){this.writeCookie(e,t,this.config.visitorMaxAgeDays);try{localStorage.setItem(e,t)}catch{}}else this.memoryStore[e]=t;else this.memoryStore[e]=t}getMemorySessionId(){const t=this.memoryStore[w],e=this.memoryStore[f];if(t&&e&&Date.now()-parseInt(e,10)<60*this.config.sessionTimeoutMinutes*1e3)return this.memoryStore[f]=String(Date.now()),this.sessionId=t,t;const i=this.generateSessionId();return this.memoryStore[w]=i,this.memoryStore[f]=String(Date.now()),this.sessionId=i,i}readCookie(t){return this.config.serverCookies?this.readServerCookie(t):this.detectBrowser()?l(t):null}readServerCookie(t){if(!this.config.serverCookies)return null;const e=this.config.serverCookies.get(t);return e?"object"==typeof e&&"value"in e?e.value||null:"string"==typeof e&&e||null:null}writeCookie(t,e,i){if(!this.detectBrowser())return;const r=24*i*60*60,s=this.config.cookieDomain?`; Domain=${this.config.cookieDomain}`:"",n="https:"===location.protocol?"; Secure":"";document.cookie=`${t}=${encodeURIComponent(e)}; Path=/; Max-Age=${r}; SameSite=Lax${n}${s}`}deleteCookie(t){if(!this.detectBrowser())return;const e=this.config.cookieDomain?`; Domain=${this.config.cookieDomain}`:"";document.cookie=`${t}=; Path=/; Max-Age=0${e}`}generateVisitorId(){return`fg_${this.uuidV4()}`}generateSessionId(){return`fgs_${Date.now()}_${this.randomHex(8)}`}uuidV4(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const e=16*Math.random()|0;return("x"===t?e:3&e|8).toString(16)})}randomHex(t){const e=new Uint8Array(t);if("undefined"!=typeof crypto&&crypto.getRandomValues)crypto.getRandomValues(e);else for(let i=0;i<t;i++)e[i]=Math.floor(256*Math.random());return Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}detectBrowser(){return"undefined"!=typeof window&&"undefined"!=typeof document}}const _=new class{constructor(){this.config=null,this.activation=null,this.trackFeature=null,this.experiment=null,this.pageViews=null,this.sessions=null,this.events=null,this.identifyModule=null,this.products=null,this.cart=null,this.checkout=null,this.purchases=null,this.sessionId=null,this.visitorId="",this.identity=null}init(t){this.config=t;const{webId:e,endpoint:i,apiKey:n}=t;this.identity=new S({visitorId:t.visitorId}),this.visitorId=this.identity.getVisitorId(),this.activation=new r(e,i,n,this.visitorId,this.identity),this.trackFeature=new s(e,i,n,this.visitorId,this.identity),this.experiment=new o(e,i,n,this.visitorId,this.identity),this.pageViews=new c(e,i,n,this.visitorId,this.identity),this.sessions=new d(e,i,n,this.visitorId,this.identity),this.events=new p(e,i,n,this.visitorId,this.identity),this.identifyModule=new u(e,i,n,this.visitorId,this.identity),this.products=new m(e,i,n,this.visitorId,this.identity),this.cart=new h(e,i,n,this.visitorId,this.identity),this.checkout=new g(e,i,n,this.visitorId,this.identity),this.purchases=new y(e,i,n,this.visitorId,this.identity),!1!==t.autoTrack?.sessions&&this.startSession(),!1!==t.autoTrack?.pageViews&&this.page(),console.log("FlowGrid SDK initialized")}async track(t,e){if(!this.events)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");await this.events.track({eventName:t,category:"custom",properties:{...e,sessionId:this.sessionId}})}async page(t,e){if(!this.pageViews)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");const i=t||window.location.href,r=e||document.title;await this.pageViews.track({url:i,path:window.location.pathname,title:r,referrer:document.referrer,sessionId:this.sessionId||void 0})}async identify(t,e){if(!this.identifyModule)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");await this.identifyModule.user({userId:t,traits:e||{}})}async productView(t){if(!this.products)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");await this.products.view({productId:t.productId,name:t.name,price:t.price,currency:t.currency||"USD",category:t.category,brand:t.brand,sessionId:this.sessionId||void 0})}async addToCart(t,e){if(!this.cart)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");await this.cart.addItem({product:{productId:t.productId,name:t.name,price:t.price,currency:t.currency||"USD"},quantity:t.quantity,cartId:e||this.getOrCreateCartId()})}async purchase(t){if(!this.purchases)throw new Error("FlowGrid not initialized. Call flowgrid.init() first.");const e=t.currency||"USD",i=t.items.map(t=>({productId:t.productId,name:t.name,price:t.price,currency:e,quantity:t.quantity,lineTotal:t.price*t.quantity})),r=i.reduce((t,e)=>t+e.lineTotal,0);await this.purchases.track({order:{orderId:t.orderId,items:i,subtotal:r,discountTotal:0,shippingTotal:0,taxTotal:0,total:t.total,currency:e},sessionId:this.sessionId||void 0})}async startSession(){this.sessions&&this.identity&&(this.sessionId=this.identity.getSessionId(),await this.sessions.start({landingPage:window.location.href,referrer:document.referrer,utm:this.parseUTM()}))}getOrCreateCartId(){let t=null;try{t=localStorage.getItem("flowgrid_cart_id")}catch{}if(!t){t=`cart_${Date.now()}_${Math.random().toString(36).substring(2,11)}`;try{localStorage.setItem("flowgrid_cart_id",t)}catch{}}return t}parseUTM(){return{utmSource:l("__flowgrid_utm_source")||void 0,utmMedium:l("__flowgrid_utm_medium")||void 0,utmCampaign:l("__flowgrid_utm_campaign")||void 0,utmTerm:l("__flowgrid_utm_term")||void 0,utmContent:l("__flowgrid_utm_content")||void 0,ref:l("__flowgrid_ref")||void 0,source:l("__flowgrid_source")||void 0,via:l("__flowgrid_via")||void 0}}},v=_;return"undefined"!=typeof window&&(window.flowgrid=_),e})());
|
|
2
2
|
//# sourceMappingURL=flowgrid.min.js.map
|