@fire-spark/analytics 0.0.3 → 0.1.1

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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/types.d.ts
2
- declare const FUNNEL_EVENTS: readonly ["menu_viewed", "product_viewed", "cart_item_added", "order_placed"];
2
+ declare const FUNNEL_EVENTS: readonly ["menu_viewed", "product_viewed", "add_to_cart", "purchase"];
3
3
  declare const INTERACTION_EVENTS: readonly ["product_visited", "category_visited"];
4
4
  type FunnelEventName = (typeof FUNNEL_EVENTS)[number];
5
5
  type InteractionEventName = (typeof INTERACTION_EVENTS)[number];
@@ -26,6 +26,35 @@ type AnalyticsScope = {
26
26
  brand_id?: string;
27
27
  fulfillment_id?: string;
28
28
  };
29
+ type DestinationProviderId = "ga4" | "meta" | "masivo" | "tiktok";
30
+ type AnalyticsRuntime = "web" | "react-native";
31
+ type AnalyticsConsent = {
32
+ analytics: boolean;
33
+ ads: boolean;
34
+ };
35
+ type AnalyticsSampleOptions = {
36
+ firstPerEvent?: boolean;
37
+ rate?: number;
38
+ };
39
+ type NormalizedEvent = {
40
+ name: string;
41
+ params: Record<string, unknown>;
42
+ items?: Array<Record<string, unknown>>;
43
+ userProperties?: Record<string, unknown>;
44
+ context: Record<string, unknown>;
45
+ runtime: AnalyticsRuntime;
46
+ consent: AnalyticsConsent;
47
+ };
48
+ type AnalyticsAdapter = {
49
+ provider: DestinationProviderId;
50
+ send: (event: NormalizedEvent) => Promise<void> | void;
51
+ };
52
+ type TrackOptions = {
53
+ only?: DestinationProviderId[];
54
+ exclude?: DestinationProviderId[];
55
+ extra?: AnalyticsAdapter[];
56
+ variant?: string;
57
+ };
29
58
  type MenuViewedEvent = {
30
59
  name: "menu_viewed";
31
60
  properties?: Record<string, unknown>;
@@ -36,15 +65,15 @@ type ProductViewedEvent = {
36
65
  product_id: string;
37
66
  };
38
67
  };
39
- type CartItemAddedEvent = {
40
- name: "cart_item_added";
68
+ type AddToCartEvent = {
69
+ name: "add_to_cart";
41
70
  properties: {
42
71
  product_id: string;
43
72
  quantity: number;
44
73
  };
45
74
  };
46
- type OrderPlacedEvent = {
47
- name: "order_placed";
75
+ type PurchaseEvent = {
76
+ name: "purchase";
48
77
  properties: {
49
78
  order_id: string;
50
79
  total?: number;
@@ -65,7 +94,7 @@ type CategoryVisitedEvent = {
65
94
  category_id: string;
66
95
  };
67
96
  };
68
- type FunnelTrackEvent = MenuViewedEvent | ProductViewedEvent | CartItemAddedEvent | OrderPlacedEvent;
97
+ type FunnelTrackEvent = MenuViewedEvent | ProductViewedEvent | AddToCartEvent | PurchaseEvent;
69
98
  type InteractionTrackEvent = ProductVisitedEvent | CategoryVisitedEvent;
70
99
  type FreeTrackEvent = {
71
100
  name: string;
@@ -82,6 +111,7 @@ type AnalyticsLimits = {
82
111
  maxAgeMs?: number;
83
112
  maxBytes?: number;
84
113
  };
114
+ type AnalyticsMerchantContext = () => Record<string, unknown>;
85
115
  type AnalyticsConfigureOptions = {
86
116
  baseUrl?: string;
87
117
  getAccessToken?: () => Promise<string>;
@@ -89,11 +119,20 @@ type AnalyticsConfigureOptions = {
89
119
  getCustomer: () => AnalyticsCustomer | null;
90
120
  getScope?: () => AnalyticsScope;
91
121
  platform: AnalyticsPlatform;
92
- context?: AnalyticsContext;
122
+ channel_id: string;
123
+ context?: AnalyticsContext | AnalyticsMerchantContext;
124
+ merchantContext?: AnalyticsMerchantContext;
93
125
  isOnline?: () => boolean | Promise<boolean>;
94
126
  limits?: AnalyticsLimits;
95
127
  fetchImpl?: typeof fetch;
96
128
  sessionMs?: number;
129
+ currency?: string;
130
+ adapters?: AnalyticsAdapter[];
131
+ env?: "production" | "staging";
132
+ planVersionId?: string;
133
+ consent?: () => AnalyticsConsent;
134
+ sample?: AnalyticsSampleOptions;
135
+ runtime?: AnalyticsRuntime;
97
136
  };
98
137
  type AnalyticsConfig = {
99
138
  session: {
@@ -110,15 +149,27 @@ declare const flush: () => Promise<void>;
110
149
  //#endregion
111
150
  //#region src/index.d.ts
112
151
  declare const configure: (options: AnalyticsConfigureOptions) => Promise<void>;
113
- declare const track: (event: TrackEvent) => Promise<void>;
152
+ type TrackName = (name: string, params?: Record<string, unknown>, opts?: TrackOptions) => Promise<void>;
153
+ type TrackAlias = (event: TrackEvent) => Promise<void>;
154
+ type TrackEither = (nameOrEvent: string | TrackEvent, params?: Record<string, unknown>, opts?: TrackOptions) => Promise<void>;
155
+ type TrackFn = TrackName & TrackAlias & TrackEither;
156
+ declare const track: TrackFn;
114
157
  declare const endSession: () => Promise<void>;
115
158
  declare const reset: () => Promise<void>;
159
+ declare class Analytics {
160
+ constructor(options?: AnalyticsConfigureOptions);
161
+ configure: typeof configure;
162
+ track: TrackFn;
163
+ flush: typeof flush;
164
+ endSession: typeof endSession;
165
+ reset: typeof reset;
166
+ }
116
167
  declare const analytics: {
117
168
  configure: typeof configure;
118
- track: typeof track;
169
+ track: TrackFn;
119
170
  flush: typeof flush;
120
171
  endSession: typeof endSession;
121
172
  reset: typeof reset;
122
173
  };
123
174
  //#endregion
124
- export { type AnalyticsConfig, type AnalyticsConfigureOptions, type AnalyticsContext, type AnalyticsCustomer, type AnalyticsPlatform, type AnalyticsScope, type AnalyticsStorage, FUNNEL_EVENTS, type FunnelEventName, INTERACTION_EVENTS, type InteractionEventName, type TrackEvent, analytics, configure, endSession, isFunnelEventName, isInteractionEventName, reset, track };
175
+ export { Analytics, type AnalyticsAdapter, type AnalyticsConfig, type AnalyticsConfigureOptions, type AnalyticsConsent, type AnalyticsContext, type AnalyticsCustomer, type AnalyticsPlatform, type AnalyticsRuntime, type AnalyticsSampleOptions, type AnalyticsScope, type AnalyticsStorage, type DestinationProviderId, FUNNEL_EVENTS, type FunnelEventName, INTERACTION_EVENTS, type InteractionEventName, type NormalizedEvent, type TrackOptions as TrackCallOptions, type TrackOptions, type TrackEvent, analytics, configure, endSession, flush, isFunnelEventName, isInteractionEventName, reset, track };
package/dist/index.js CHANGED
@@ -32,36 +32,54 @@ const buildEventContext = (context) => {
32
32
  };
33
33
  };
34
34
  //#endregion
35
- //#region src/retry.ts
36
- const defaultSleep = (ms) => {
37
- return new Promise((resolve) => {
38
- setTimeout(resolve, ms);
39
- });
35
+ //#region src/pii.ts
36
+ const PII_KEYS = /* @__PURE__ */ new Set([
37
+ "phone",
38
+ "device_ip",
39
+ "email",
40
+ "ttclid"
41
+ ]);
42
+ const toHex = (bytes) => {
43
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
40
44
  };
41
- /** 1 attempt + `extraAttempts` retries with exponential backoff. */
42
- const withExponentialRetry = async (run, options = {}) => {
43
- const extraAttempts = options.extraAttempts ?? 3;
44
- const baseDelayMs = options.baseDelayMs ?? 100;
45
- const sleep = options.sleep ?? defaultSleep;
46
- const shouldRetry = options.shouldRetry ?? (() => true);
47
- let lastError;
48
- for (let attempt = 0; attempt <= extraAttempts; attempt += 1) try {
49
- return await run();
50
- } catch (error) {
51
- lastError = error;
52
- if (attempt === extraAttempts) break;
53
- if (!shouldRetry(error)) break;
54
- await sleep(baseDelayMs * 2 ** attempt);
45
+ const isPiiParam = (name) => {
46
+ return PII_KEYS.has(name);
47
+ };
48
+ const hashPiiValue = async (value) => {
49
+ const subtle = globalThis.crypto?.subtle;
50
+ if (!subtle) throw new Error("SHA-256 unavailable");
51
+ const normalized = value.trim().toLowerCase();
52
+ const data = new TextEncoder().encode(normalized);
53
+ const digest = await subtle.digest("SHA-256", data);
54
+ return toHex(new Uint8Array(digest));
55
+ };
56
+ const hashDeep = async (value) => {
57
+ if (typeof value === "string") return hashPiiValue(value);
58
+ if (typeof value === "number" || typeof value === "boolean") return hashPiiValue(String(value));
59
+ if (Array.isArray(value)) return Promise.all(value.map((item) => hashDeep(item)));
60
+ if (!value || typeof value !== "object") return value;
61
+ return hashPiiParams(value);
62
+ };
63
+ const hashPiiParams = async (params) => {
64
+ const next = {};
65
+ for (const [key, value] of Object.entries(params)) {
66
+ if (!isPiiParam(key)) {
67
+ next[key] = value;
68
+ continue;
69
+ }
70
+ try {
71
+ next[key] = await hashDeep(value);
72
+ } catch {}
55
73
  }
56
- throw lastError;
74
+ return next;
57
75
  };
58
76
  //#endregion
59
77
  //#region src/types.ts
60
78
  const FUNNEL_EVENTS = [
61
79
  "menu_viewed",
62
80
  "product_viewed",
63
- "cart_item_added",
64
- "order_placed"
81
+ "add_to_cart",
82
+ "purchase"
65
83
  ];
66
84
  const INTERACTION_EVENTS = ["product_visited", "category_visited"];
67
85
  const isFunnelEventName = (name) => {
@@ -139,17 +157,31 @@ const enqueueEvent = (events, event, now, limits) => {
139
157
  const CONFIG_KEY = "firespark:analytics:config";
140
158
  const DEFAULT_SESSION_MS = 1800 * 1e3;
141
159
  const RETRY_MS = 6e4;
142
- const runtime = {
160
+ const r1 = {
143
161
  options: null,
144
- context: null,
162
+ context: null
163
+ };
164
+ const r2 = {
145
165
  sessionMs: DEFAULT_SESSION_MS,
146
- session: null,
166
+ session: null
167
+ };
168
+ const r3 = {
147
169
  interactions: null,
148
- queue: [],
170
+ queue: []
171
+ };
172
+ const r4 = {
149
173
  hydrated: false,
150
174
  sessionTimer: null,
151
175
  retryTimer: null
152
176
  };
177
+ const r5 = { sampleSeen: null };
178
+ const runtime = {
179
+ ...r1,
180
+ ...r2,
181
+ ...r3,
182
+ ...r4,
183
+ ...r5
184
+ };
153
185
  const getLimits = () => {
154
186
  const limits = runtime.options?.limits;
155
187
  return {
@@ -212,6 +244,698 @@ const resetRuntime = () => {
212
244
  runtime.interactions = null;
213
245
  runtime.queue = [];
214
246
  runtime.hydrated = false;
247
+ runtime.sampleSeen = null;
248
+ };
249
+ //#endregion
250
+ //#region ../../packages/constants/src/tracking-plan-catalog/currency.ts
251
+ const ISO_4217_CODE = /^[A-Z]{3}$/;
252
+ const USD_AMOUNT_PARAM_SUFFIX = "_in_usd";
253
+ const isUsdAmountParam = (name) => {
254
+ return name.endsWith(USD_AMOUNT_PARAM_SUFFIX);
255
+ };
256
+ const dropUsdDeep = (value) => {
257
+ if (Array.isArray(value)) return value.map(dropUsdDeep);
258
+ if (!value || typeof value !== "object") return value;
259
+ const next = {};
260
+ for (const [key, nested] of Object.entries(value)) {
261
+ if (isUsdAmountParam(key)) continue;
262
+ next[key] = dropUsdDeep(nested);
263
+ }
264
+ return next;
265
+ };
266
+ const dropUsdAmountParams = (params) => {
267
+ return dropUsdDeep(params);
268
+ };
269
+ /** ISO 4217 from `merchants.currency_id` → `currencies.code`. */
270
+ const merchantCurrencyCode = (code) => {
271
+ const normalized = code.trim().toUpperCase();
272
+ if (!ISO_4217_CODE.test(normalized)) throw new Error("Merchant currency must be an ISO 4217 code");
273
+ return normalized;
274
+ };
275
+ //#endregion
276
+ //#region ../../packages/constants/src/tracking-plan-catalog/params.ts
277
+ const valueParam = (required = true) => {
278
+ return {
279
+ name: "value",
280
+ type: "currency",
281
+ required,
282
+ scope: "ecommerce"
283
+ };
284
+ };
285
+ const priceParam = (required = false) => {
286
+ return {
287
+ name: "price",
288
+ type: "currency",
289
+ required,
290
+ scope: "item"
291
+ };
292
+ };
293
+ const quantityParam = (required = false) => {
294
+ return {
295
+ name: "quantity",
296
+ type: "integer",
297
+ required,
298
+ scope: "item"
299
+ };
300
+ };
301
+ const itemsParam = () => {
302
+ return {
303
+ name: "items",
304
+ type: "array<object>",
305
+ required: false,
306
+ scope: "ecommerce"
307
+ };
308
+ };
309
+ const transactionIdParam = () => {
310
+ return {
311
+ name: "transaction_id",
312
+ type: "string",
313
+ required: false,
314
+ scope: "event"
315
+ };
316
+ };
317
+ //#endregion
318
+ //#region ../../packages/constants/src/tracking-plan-catalog/events.ts
319
+ const both = (name) => {
320
+ return {
321
+ web: name,
322
+ reactNative: name
323
+ };
324
+ };
325
+ const none = () => {
326
+ return {
327
+ web: null,
328
+ reactNative: null
329
+ };
330
+ };
331
+ const meta = (web, reactNative) => {
332
+ return {
333
+ web,
334
+ reactNative
335
+ };
336
+ };
337
+ const ecommerceParams = (valueRequired = true) => {
338
+ return [
339
+ valueParam(valueRequired),
340
+ itemsParam(),
341
+ transactionIdParam()
342
+ ];
343
+ };
344
+ /**
345
+ * Native events only. Custom events are merchant-authored and never shipped
346
+ * as catalog suggestions or plan defaults.
347
+ */
348
+ const CATALOG_EVENTS = [
349
+ {
350
+ id: "add_to_cart",
351
+ name: "add_to_cart",
352
+ kind: "native",
353
+ category: "E-commerce",
354
+ runtimes: ["web", "react-native"],
355
+ recommendedParams: [
356
+ {
357
+ name: "product_id",
358
+ type: "string",
359
+ required: true,
360
+ scope: "event"
361
+ },
362
+ quantityParam(true),
363
+ ...ecommerceParams(false)
364
+ ],
365
+ destinations: {
366
+ "fire-spark": both("add_to_cart"),
367
+ ga4: both("add_to_cart"),
368
+ meta: meta("AddToCart", "fb_mobile_add_to_cart"),
369
+ tiktok: both("AddToCart"),
370
+ masivo: both("ADD_TO_CART")
371
+ }
372
+ },
373
+ {
374
+ id: "begin_checkout",
375
+ name: "begin_checkout",
376
+ kind: "native",
377
+ category: "E-commerce",
378
+ runtimes: ["web", "react-native"],
379
+ recommendedParams: ecommerceParams(),
380
+ destinations: {
381
+ "fire-spark": both("begin_checkout"),
382
+ ga4: both("begin_checkout"),
383
+ meta: meta("InitiateCheckout", "fb_mobile_initiated_checkout"),
384
+ tiktok: both("InitiateCheckout"),
385
+ masivo: both("begin_checkout")
386
+ }
387
+ },
388
+ {
389
+ id: "purchase",
390
+ name: "purchase",
391
+ kind: "native",
392
+ category: "E-commerce",
393
+ runtimes: ["web", "react-native"],
394
+ recommendedParams: [{
395
+ name: "order_id",
396
+ type: "string",
397
+ required: true,
398
+ scope: "event"
399
+ }, ...ecommerceParams(false)],
400
+ destinations: {
401
+ "fire-spark": both("purchase"),
402
+ ga4: both("purchase"),
403
+ meta: meta("Purchase", "fb_mobile_purchase"),
404
+ tiktok: both("Purchase"),
405
+ masivo: both("PURCHASE")
406
+ }
407
+ },
408
+ {
409
+ id: "view_item",
410
+ name: "view_item",
411
+ kind: "native",
412
+ category: "E-commerce",
413
+ runtimes: ["web", "react-native"],
414
+ recommendedParams: [
415
+ valueParam(false),
416
+ itemsParam(),
417
+ priceParam(),
418
+ quantityParam()
419
+ ],
420
+ destinations: {
421
+ "fire-spark": both("product_viewed"),
422
+ ga4: both("view_item"),
423
+ meta: meta("ViewContent", "fb_mobile_content_view"),
424
+ tiktok: both("ViewContent"),
425
+ masivo: both("view_item")
426
+ }
427
+ },
428
+ {
429
+ id: "sign_up",
430
+ name: "sign_up",
431
+ kind: "native",
432
+ category: "Account",
433
+ runtimes: ["web", "react-native"],
434
+ recommendedParams: [{
435
+ name: "method",
436
+ type: "string",
437
+ required: false,
438
+ scope: "event"
439
+ }],
440
+ destinations: {
441
+ "fire-spark": both("sign_up"),
442
+ ga4: both("sign_up"),
443
+ meta: meta("CompleteRegistration", "fb_mobile_complete_registration"),
444
+ tiktok: both("Registration"),
445
+ masivo: both("sign_up")
446
+ }
447
+ },
448
+ {
449
+ id: "login",
450
+ name: "login",
451
+ kind: "native",
452
+ category: "Account",
453
+ runtimes: ["web", "react-native"],
454
+ recommendedParams: [{
455
+ name: "method",
456
+ type: "string",
457
+ required: false,
458
+ scope: "event"
459
+ }],
460
+ destinations: {
461
+ "fire-spark": both("login"),
462
+ ga4: both("login"),
463
+ meta: both("login"),
464
+ tiktok: both("Login"),
465
+ masivo: both("login")
466
+ }
467
+ },
468
+ {
469
+ id: "page_view",
470
+ name: "page_view",
471
+ kind: "native",
472
+ category: "Navegación",
473
+ runtimes: ["web"],
474
+ recommendedParams: [],
475
+ destinations: {
476
+ "fire-spark": both("page_view"),
477
+ ga4: both("page_view"),
478
+ meta: none(),
479
+ tiktok: none(),
480
+ masivo: none()
481
+ }
482
+ },
483
+ {
484
+ id: "screen_view",
485
+ name: "screen_view",
486
+ kind: "native",
487
+ category: "Navegación",
488
+ runtimes: ["react-native"],
489
+ recommendedParams: [],
490
+ destinations: {
491
+ "fire-spark": both("screen_view"),
492
+ ga4: both("screen_view"),
493
+ meta: none(),
494
+ tiktok: none(),
495
+ masivo: none()
496
+ }
497
+ },
498
+ {
499
+ id: "menu_viewed",
500
+ name: "menu_viewed",
501
+ kind: "native",
502
+ category: "Navegación",
503
+ runtimes: ["web", "react-native"],
504
+ recommendedParams: [],
505
+ destinations: {
506
+ "fire-spark": both("menu_viewed"),
507
+ ga4: both("menu_viewed"),
508
+ meta: both("menu_viewed"),
509
+ tiktok: both("menu_viewed"),
510
+ masivo: both("menu_viewed")
511
+ }
512
+ },
513
+ {
514
+ id: "product_viewed",
515
+ name: "product_viewed",
516
+ kind: "native",
517
+ category: "E-commerce",
518
+ runtimes: ["web", "react-native"],
519
+ recommendedParams: [{
520
+ name: "product_id",
521
+ type: "string",
522
+ required: true,
523
+ scope: "event"
524
+ }],
525
+ destinations: {
526
+ "fire-spark": both("product_viewed"),
527
+ ga4: both("view_item"),
528
+ meta: meta("ViewContent", "fb_mobile_content_view"),
529
+ tiktok: both("ViewContent"),
530
+ masivo: both("view_item")
531
+ }
532
+ },
533
+ {
534
+ id: "product_visited",
535
+ name: "product_visited",
536
+ kind: "native",
537
+ category: "Navegación",
538
+ runtimes: ["web", "react-native"],
539
+ recommendedParams: [
540
+ {
541
+ name: "product_id",
542
+ type: "string",
543
+ required: true,
544
+ scope: "event"
545
+ },
546
+ {
547
+ name: "category_id",
548
+ type: "string",
549
+ required: false,
550
+ scope: "event"
551
+ },
552
+ {
553
+ name: "attribution_source",
554
+ type: "string",
555
+ required: false,
556
+ scope: "event"
557
+ },
558
+ {
559
+ name: "attribution_id",
560
+ type: "string",
561
+ required: false,
562
+ scope: "event"
563
+ }
564
+ ],
565
+ destinations: {
566
+ "fire-spark": both("product_visited"),
567
+ ga4: both("product_visited"),
568
+ meta: both("product_visited"),
569
+ tiktok: both("product_visited"),
570
+ masivo: both("product_visited")
571
+ }
572
+ },
573
+ {
574
+ id: "category_visited",
575
+ name: "category_visited",
576
+ kind: "native",
577
+ category: "Navegación",
578
+ runtimes: ["web", "react-native"],
579
+ recommendedParams: [{
580
+ name: "category_id",
581
+ type: "string",
582
+ required: true,
583
+ scope: "event"
584
+ }],
585
+ destinations: {
586
+ "fire-spark": both("category_visited"),
587
+ ga4: both("category_visited"),
588
+ meta: both("category_visited"),
589
+ tiktok: both("category_visited"),
590
+ masivo: both("category_visited")
591
+ }
592
+ }
593
+ ];
594
+ const eventsByName = new Map(CATALOG_EVENTS.map((event) => [event.name, event]));
595
+ const catalogEventByName = (name) => {
596
+ return eventsByName.get(name);
597
+ };
598
+ const destinationEventName = (developerName, provider, runtime) => {
599
+ const event = catalogEventByName(developerName);
600
+ if (!event) return developerName;
601
+ const binding = event.destinations[provider];
602
+ if (!binding) return developerName;
603
+ if (runtime === "react-native") return binding.reactNative;
604
+ return binding.web;
605
+ };
606
+ //#endregion
607
+ //#region ../../packages/constants/src/tracking-plan-catalog/transforms.ts
608
+ const stringVal = (value) => {
609
+ return typeof value === "string" && value.length > 0 ? value : void 0;
610
+ };
611
+ const numberVal = (value) => {
612
+ if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
613
+ return value;
614
+ };
615
+ const itemId = (item) => {
616
+ return stringVal(item.item_id) ?? stringVal(item.content_id) ?? "";
617
+ };
618
+ const itemQty = (item) => {
619
+ return numberVal(item.quantity) ?? 1;
620
+ };
621
+ const itemPrice = (item) => {
622
+ return numberVal(item.price) ?? numberVal(item.item_price) ?? 0;
623
+ };
624
+ const itemsOf$1 = (input) => {
625
+ if (input.items && input.items.length > 0) return input.items;
626
+ const fromParams = input.params.items;
627
+ if (!Array.isArray(fromParams)) return [];
628
+ return fromParams.filter((row) => {
629
+ return !!row && typeof row === "object" && !Array.isArray(row);
630
+ });
631
+ };
632
+ const valueOf = (input, items) => {
633
+ const explicit = numberVal(input.params.value);
634
+ if (explicit !== void 0) return explicit;
635
+ return items.reduce((sum, item) => sum + itemPrice(item) * itemQty(item), 0);
636
+ };
637
+ /**
638
+ * Delivery-only TikTok names for storefronts already emitting these events.
639
+ * Never used to build or prefill tracking plans.
640
+ */
641
+ const LEGACY_TIKTOK_ALIASES = {
642
+ medios_view_item_delivery: "UnlockAchievement",
643
+ medios_view_item_express: "StartTrial",
644
+ medios_purchase_delivery: "Subscribe",
645
+ medios_purchase_express: "GenerateLead"
646
+ };
647
+ const resolvedName = (input, provider) => {
648
+ const alias = LEGACY_TIKTOK_ALIASES[input.name];
649
+ if (provider === "tiktok" && alias) return alias;
650
+ return destinationEventName(input.name, provider, input.runtime) ?? input.name;
651
+ };
652
+ const ga4Payload = (input, items) => {
653
+ const currency = merchantCurrencyCode(input.currency);
654
+ const value = valueOf(input, items);
655
+ const b1 = {
656
+ items: items.map((item) => {
657
+ return {
658
+ item_id: itemId(item),
659
+ item_name: item.item_name,
660
+ price: itemPrice(item),
661
+ quantity: itemQty(item)
662
+ };
663
+ }),
664
+ value,
665
+ currency
666
+ };
667
+ const transaction_id = stringVal(input.params.transaction_id);
668
+ const order_id = stringVal(input.params.order_id);
669
+ return {
670
+ ...b1,
671
+ transaction_id: transaction_id ?? order_id
672
+ };
673
+ };
674
+ const metaContents = (items) => {
675
+ return items.map((item) => {
676
+ return {
677
+ id: itemId(item),
678
+ quantity: itemQty(item),
679
+ item_price: itemPrice(item)
680
+ };
681
+ });
682
+ };
683
+ const metaPayload = (input, items) => {
684
+ const currency = merchantCurrencyCode(input.currency);
685
+ return {
686
+ value: valueOf(input, items),
687
+ currency,
688
+ contents: metaContents(items)
689
+ };
690
+ };
691
+ const tiktokContents = (items) => {
692
+ return items.map((item) => {
693
+ return {
694
+ content_id: itemId(item),
695
+ quantity: itemQty(item),
696
+ price: itemPrice(item)
697
+ };
698
+ });
699
+ };
700
+ const tiktokPayload = (input, items) => {
701
+ const currency = merchantCurrencyCode(input.currency);
702
+ const params = input.params;
703
+ const ctx = input.context ?? {};
704
+ const b1 = {
705
+ value: valueOf(input, items),
706
+ currency
707
+ };
708
+ const b2 = { content_type: params.content_type };
709
+ const b3 = { content_id: params.content_id };
710
+ const b4 = {
711
+ description: params.description,
712
+ order_id: params.order_id
713
+ };
714
+ const b5 = { contents: tiktokContents(items) };
715
+ const properties = {
716
+ ...b1,
717
+ ...b2,
718
+ ...b3,
719
+ ...b4,
720
+ ...b5
721
+ };
722
+ const user = {
723
+ external_id: ctx.external_id,
724
+ ttclid: ctx.ttclid
725
+ };
726
+ const r1 = { event: resolvedName(input, "tiktok") };
727
+ const r2 = {
728
+ event_time: input.eventTime,
729
+ event_id: input.eventId
730
+ };
731
+ return {
732
+ ...r1,
733
+ ...r2,
734
+ properties,
735
+ context: user
736
+ };
737
+ };
738
+ const masivoPayload = (input, items) => {
739
+ const currency = merchantCurrencyCode(input.currency);
740
+ const ctx = input.context ?? {};
741
+ const products = items.map((item) => {
742
+ const metadata = { prices: {
743
+ net_price: itemPrice(item),
744
+ currency
745
+ } };
746
+ return {
747
+ id: itemId(item),
748
+ quantity: itemQty(item),
749
+ metadata
750
+ };
751
+ });
752
+ const order = {
753
+ id: input.params.order_id ?? input.params.transaction_id,
754
+ currency,
755
+ products
756
+ };
757
+ const metadata = {
758
+ store_id: ctx.store_id,
759
+ vendor_id: ctx.vendor_id
760
+ };
761
+ return {
762
+ type: resolvedName(input, "masivo"),
763
+ order,
764
+ metadata
765
+ };
766
+ };
767
+ const transformForProvider = (provider, input) => {
768
+ const params = dropUsdAmountParams(input.params);
769
+ const itemsIn = input.items?.map((item) => dropUsdAmountParams(item));
770
+ const next = {
771
+ ...input,
772
+ params,
773
+ items: itemsIn
774
+ };
775
+ const items = itemsOf$1(next);
776
+ const name = resolvedName(next, provider);
777
+ if (provider === "ga4") return {
778
+ provider,
779
+ name,
780
+ payload: ga4Payload(next, items)
781
+ };
782
+ if (provider === "meta") return {
783
+ provider,
784
+ name,
785
+ payload: metaPayload(next, items)
786
+ };
787
+ if (provider === "tiktok") return {
788
+ provider,
789
+ name,
790
+ payload: tiktokPayload(next, items)
791
+ };
792
+ if (provider === "masivo") return {
793
+ provider,
794
+ name,
795
+ payload: masivoPayload(next, items)
796
+ };
797
+ return {
798
+ provider,
799
+ name,
800
+ payload: next.params
801
+ };
802
+ };
803
+ //#endregion
804
+ //#region src/uuid.ts
805
+ /** UUID v4 — works when `crypto.randomUUID` is missing (React Native / Hermes). */
806
+ const randomUUID = () => {
807
+ const c = globalThis.crypto;
808
+ if (typeof c?.randomUUID === "function") return c.randomUUID();
809
+ if (typeof c?.getRandomValues !== "function") throw new Error("crypto.getRandomValues is required");
810
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
811
+ c.getRandomValues(bytes);
812
+ bytes[6] = bytes[6] & 15 | 64;
813
+ bytes[8] = bytes[8] & 63 | 128;
814
+ const toHex = (b) => b.toString(16).padStart(2, "0");
815
+ const hex = Array.from(bytes, toHex).join("");
816
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
817
+ };
818
+ //#endregion
819
+ //#region src/fanout.ts
820
+ const ADS_PROVIDERS = /* @__PURE__ */ new Set(["meta", "tiktok"]);
821
+ const DEFAULT_CONSENT = {
822
+ analytics: true,
823
+ ads: true
824
+ };
825
+ const resolveRuntime = (options) => {
826
+ if (options.runtime) return options.runtime;
827
+ return options.platform === "web" ? "web" : "react-native";
828
+ };
829
+ const resolveMerchantContext = (options) => {
830
+ if (options.merchantContext) return options.merchantContext();
831
+ if (typeof options.context === "function") return options.context();
832
+ return { ...options.getScope?.() ?? {} };
833
+ };
834
+ const resolveAdapters = (opts) => {
835
+ const constructed = runtime.options?.adapters ?? [];
836
+ const extra = opts?.extra ?? [];
837
+ const available = [...constructed, ...extra];
838
+ if (opts?.only) {
839
+ for (const id of opts.only) if (!available.some((adapter) => adapter.provider === id)) throw new Error(`Adapter ${id} was never constructed`);
840
+ return available.filter((adapter) => opts.only?.includes(adapter.provider));
841
+ }
842
+ if (!opts?.exclude) return available;
843
+ return available.filter((adapter) => !opts.exclude?.includes(adapter.provider));
844
+ };
845
+ const itemsOf = (params) => {
846
+ if (!Array.isArray(params.items)) return void 0;
847
+ return params.items.filter((row) => {
848
+ return !!row && typeof row === "object" && !Array.isArray(row);
849
+ });
850
+ };
851
+ const sendOne = async (adapter, event, consent, runtimeId, currency, context) => {
852
+ const ads = ADS_PROVIDERS.has(adapter.provider);
853
+ if (ads && !consent.ads) return;
854
+ if (!ads && !consent.analytics) return;
855
+ const raw = event.properties ?? {};
856
+ const params = ads ? await hashPiiParams(raw) : raw;
857
+ const rows = itemsOf(params);
858
+ const items = ads && rows ? await Promise.all(rows.map(hashPiiParams)) : rows;
859
+ const b1 = {
860
+ name: event.name,
861
+ params,
862
+ items,
863
+ currency
864
+ };
865
+ const b2 = {
866
+ runtime: runtimeId,
867
+ eventId: randomUUID()
868
+ };
869
+ const b3 = {
870
+ eventTime: Math.floor(Date.now() / 1e3),
871
+ context
872
+ };
873
+ const input = {
874
+ ...b1,
875
+ ...b2,
876
+ ...b3
877
+ };
878
+ const transformed = transformForProvider(adapter.provider, input);
879
+ const n1 = {
880
+ name: transformed.name,
881
+ params: transformed.payload
882
+ };
883
+ const n2 = {
884
+ items,
885
+ context,
886
+ runtime: runtimeId,
887
+ consent
888
+ };
889
+ const normalized = {
890
+ ...n1,
891
+ ...n2
892
+ };
893
+ await adapter.send(normalized);
894
+ };
895
+ const fanoutAdapters = async (event, opts) => {
896
+ const adapters = resolveAdapters(opts);
897
+ if (adapters.length === 0) return;
898
+ const options = runtime.options;
899
+ if (!options) return;
900
+ const consent = options.consent?.() ?? DEFAULT_CONSENT;
901
+ const runtimeId = resolveRuntime(options);
902
+ const currency = options.currency ?? "USD";
903
+ const context = resolveMerchantContext(options);
904
+ const send = async (adapter) => {
905
+ try {
906
+ await sendOne(...[
907
+ adapter,
908
+ event,
909
+ consent,
910
+ runtimeId
911
+ ], currency, context);
912
+ } catch {}
913
+ };
914
+ await Promise.all(adapters.map(send));
915
+ };
916
+ //#endregion
917
+ //#region src/retry.ts
918
+ const defaultSleep = (ms) => {
919
+ return new Promise((resolve) => {
920
+ setTimeout(resolve, ms);
921
+ });
922
+ };
923
+ /** 1 attempt + `extraAttempts` retries with exponential backoff. */
924
+ const withExponentialRetry = async (run, options = {}) => {
925
+ const extraAttempts = options.extraAttempts ?? 3;
926
+ const baseDelayMs = options.baseDelayMs ?? 100;
927
+ const sleep = options.sleep ?? defaultSleep;
928
+ const shouldRetry = options.shouldRetry ?? (() => true);
929
+ let lastError;
930
+ for (let attempt = 0; attempt <= extraAttempts; attempt += 1) try {
931
+ return await run();
932
+ } catch (error) {
933
+ lastError = error;
934
+ if (attempt === extraAttempts) break;
935
+ if (!shouldRetry(error)) break;
936
+ await sleep(baseDelayMs * 2 ** attempt);
937
+ }
938
+ throw lastError;
215
939
  };
216
940
  //#endregion
217
941
  //#region src/flush.ts
@@ -239,17 +963,20 @@ const isOnline = async () => {
239
963
  if (!check) return true;
240
964
  return check();
241
965
  };
242
- const postChunk = async (events) => {
243
- const http = await httpOptions();
966
+ const storefrontCall = async (path, init) => {
244
967
  return storefrontJson(...[
245
- "/api/storefront/v1/analytics/events",
246
- {
247
- method: "POST",
248
- body: JSON.stringify({ events })
249
- },
250
- http
968
+ path,
969
+ init,
970
+ await httpOptions()
251
971
  ]);
252
972
  };
973
+ const postChunk = async (events) => {
974
+ const init = {
975
+ method: "POST",
976
+ body: JSON.stringify({ events })
977
+ };
978
+ return storefrontCall("/api/storefront/v1/analytics/events", init);
979
+ };
253
980
  const scheduleRetry = () => {
254
981
  if (runtime.retryTimer || runtime.queue.length === 0) return;
255
982
  runtime.retryTimer = setTimeout(() => {
@@ -262,19 +989,21 @@ const preferencesPath = () => {
262
989
  const params = new URLSearchParams();
263
990
  if (scope.fulfillment_id) params.set("fulfillment_id", scope.fulfillment_id);
264
991
  if (scope.store_id) params.set("store_id", scope.store_id);
265
- if (scope.channel_id) params.set("channel_id", scope.channel_id);
992
+ const channel_id = scope.channel_id ?? runtime.options?.channel_id;
993
+ if (channel_id) params.set("channel_id", channel_id);
266
994
  if (scope.brand_id) params.set("brand_id", scope.brand_id);
267
995
  const query = params.toString();
268
996
  const path = "/api/storefront/v1/preferences";
269
997
  return query ? `${path}?${query}` : path;
270
998
  };
271
999
  const fetchRemoteConfig = async () => {
272
- const http = await httpOptions();
273
- return (await storefrontJson(...[
274
- preferencesPath(),
275
- { method: "GET" },
276
- http
277
- ])).analytics;
1000
+ const path = preferencesPath();
1001
+ const prefs = await storefrontCall(path, { method: "GET" });
1002
+ const currency_code = prefs.currency?.code;
1003
+ return {
1004
+ ...prefs.analytics,
1005
+ currency_code
1006
+ };
278
1007
  };
279
1008
  let flushWait = null;
280
1009
  const flushOnce = async () => {
@@ -309,19 +1038,130 @@ const flush = async () => {
309
1038
  return flushWait;
310
1039
  };
311
1040
  //#endregion
312
- //#region src/uuid.ts
313
- /** UUID v4 — works when `crypto.randomUUID` is missing (React Native / Hermes). */
314
- const randomUUID = () => {
315
- const c = globalThis.crypto;
316
- if (typeof c?.randomUUID === "function") return c.randomUUID();
317
- if (typeof c?.getRandomValues !== "function") throw new Error("crypto.getRandomValues is required");
318
- const bytes = /* @__PURE__ */ new Uint8Array(16);
319
- c.getRandomValues(bytes);
320
- bytes[6] = bytes[6] & 15 | 64;
321
- bytes[8] = bytes[8] & 63 | 128;
322
- const toHex = (b) => b.toString(16).padStart(2, "0");
323
- const hex = Array.from(bytes, toHex).join("");
324
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
1041
+ //#region src/sample.ts
1042
+ const SAMPLE_KEY = "firespark:analytics:samples";
1043
+ const PLAN_SAMPLES_PATH = "/api/storefront/v1/analytics/plan-samples";
1044
+ const DEFAULT_RATE = .01;
1045
+ const inferType = (value) => {
1046
+ if (value === null) return "null";
1047
+ if (Array.isArray(value)) {
1048
+ const first = value[0];
1049
+ if (first && typeof first === "object") return "array<object>";
1050
+ return "array";
1051
+ }
1052
+ if (typeof value === "number") return Number.isInteger(value) ? "integer" : "number";
1053
+ return typeof value;
1054
+ };
1055
+ const parseSeen = (raw) => {
1056
+ if (!raw) return /* @__PURE__ */ new Set();
1057
+ try {
1058
+ const parsed = JSON.parse(raw);
1059
+ return new Set(parsed.keys ?? []);
1060
+ } catch {
1061
+ return /* @__PURE__ */ new Set();
1062
+ }
1063
+ };
1064
+ const loadSeen = async () => {
1065
+ if (runtime.sampleSeen) return runtime.sampleSeen;
1066
+ runtime.sampleSeen = parseSeen(await readStorage(SAMPLE_KEY));
1067
+ return runtime.sampleSeen;
1068
+ };
1069
+ const persistSeen = async (seen) => {
1070
+ runtime.sampleSeen = seen;
1071
+ const keys = [...seen];
1072
+ await writeStorage(SAMPLE_KEY, JSON.stringify({ keys }));
1073
+ };
1074
+ const leafName = (path) => {
1075
+ const parts = path.split(".");
1076
+ return parts[parts.length - 1] ?? path;
1077
+ };
1078
+ const shouldOmitParam = (path) => {
1079
+ return isPiiParam(path) || isPiiParam(leafName(path));
1080
+ };
1081
+ const pushParam = (path, value, param_names, inferred_types, presence) => {
1082
+ if (shouldOmitParam(path)) return;
1083
+ param_names.push(path);
1084
+ inferred_types.push(inferType(value));
1085
+ presence.push(value !== void 0 && value !== null);
1086
+ };
1087
+ const describeSample = (event, opts) => {
1088
+ const params = event.properties ?? {};
1089
+ const param_names = [];
1090
+ const inferred_types = [];
1091
+ const presence = [];
1092
+ const p1 = [
1093
+ param_names,
1094
+ inferred_types,
1095
+ presence
1096
+ ];
1097
+ for (const [key, value] of Object.entries(params)) {
1098
+ if (shouldOmitParam(key)) continue;
1099
+ pushParam(key, value, ...p1);
1100
+ if (Array.isArray(value)) {
1101
+ const first = value.find((item) => item && typeof item === "object");
1102
+ if (!first) continue;
1103
+ for (const [ik, iv] of Object.entries(first)) pushParam(`${key}.*.${ik}`, iv, ...p1);
1104
+ continue;
1105
+ }
1106
+ if (!value || typeof value !== "object") continue;
1107
+ for (const [ck, cv] of Object.entries(value)) pushParam(`${key}.${ck}`, cv, ...p1);
1108
+ }
1109
+ const variant = opts?.variant;
1110
+ return {
1111
+ event_key: event.name,
1112
+ param_names,
1113
+ inferred_types,
1114
+ presence,
1115
+ variant
1116
+ };
1117
+ };
1118
+ const shouldSend = (seen, firstPerEvent, rate) => {
1119
+ if (!seen && firstPerEvent) return true;
1120
+ if (rate >= 1) return true;
1121
+ if (rate <= 0) return false;
1122
+ return Math.random() < rate;
1123
+ };
1124
+ const samplePlanEvent = async (event, opts) => {
1125
+ const options = runtime.options;
1126
+ if (!options?.planVersionId) return;
1127
+ const sample = options.sample ?? {};
1128
+ const firstPerEvent = sample.firstPerEvent !== false;
1129
+ const rate = sample.rate ?? DEFAULT_RATE;
1130
+ const variant = opts?.variant;
1131
+ const key = variant ? `${event.name}:${variant}` : event.name;
1132
+ const seen = await loadSeen();
1133
+ const already = seen.has(key);
1134
+ if (!shouldSend(already, firstPerEvent, rate)) return;
1135
+ const described = describeSample(event, opts);
1136
+ const env = options.env ?? "production";
1137
+ const body1 = { plan_version_id: options.planVersionId };
1138
+ const body2 = { event_key: described.event_key };
1139
+ const body3 = {
1140
+ variant_id: described.variant ?? null,
1141
+ env
1142
+ };
1143
+ const body4 = { channel_id: options.channel_id };
1144
+ const body5 = { param_names: described.param_names };
1145
+ const body6 = { inferred_types: described.inferred_types };
1146
+ const body7 = { presence: described.presence };
1147
+ const full = {
1148
+ ...body1,
1149
+ ...body2,
1150
+ ...body3,
1151
+ ...body4,
1152
+ ...body5,
1153
+ ...body6,
1154
+ ...body7
1155
+ };
1156
+ const init = {
1157
+ method: "POST",
1158
+ body: JSON.stringify(full)
1159
+ };
1160
+ await storefrontCall(PLAN_SAMPLES_PATH, init);
1161
+ if (!already) {
1162
+ seen.add(key);
1163
+ await persistSeen(seen);
1164
+ }
325
1165
  };
326
1166
  //#endregion
327
1167
  //#region src/session.ts
@@ -372,6 +1212,14 @@ const shouldCarryMenuViewed = (prev, next) => {
372
1212
  };
373
1213
  //#endregion
374
1214
  //#region src/index.ts
1215
+ const toTrackEvent = (nameOrEvent, params) => {
1216
+ if (typeof nameOrEvent !== "string") return nameOrEvent;
1217
+ if (!params) return { name: nameOrEvent };
1218
+ return {
1219
+ name: nameOrEvent,
1220
+ properties: params
1221
+ };
1222
+ };
375
1223
  const entityIdFromEvent = (event) => {
376
1224
  const props = event.properties;
377
1225
  if (!props) return void 0;
@@ -410,23 +1258,45 @@ const refreshConfig = async () => {
410
1258
  return;
411
1259
  }
412
1260
  try {
413
- runtime.sessionMs = (await fetchRemoteConfig()).session_ms || 18e5;
1261
+ const config = await fetchRemoteConfig();
1262
+ runtime.sessionMs = config.session_ms || 18e5;
414
1263
  await persistConfig(runtime.sessionMs);
1264
+ const code = config.currency_code;
1265
+ if (code && runtime.options && !runtime.options.currency) runtime.options = {
1266
+ ...runtime.options,
1267
+ currency: code
1268
+ };
415
1269
  } catch {
416
1270
  if (!runtime.sessionMs) runtime.sessionMs = DEFAULT_SESSION_MS;
417
1271
  }
418
1272
  };
1273
+ let trackChain = Promise.resolve();
1274
+ const enqueue = (run) => {
1275
+ const next = trackChain.then(run, run);
1276
+ const ok = () => void 0;
1277
+ trackChain = next.then(ok, ok);
1278
+ return next;
1279
+ };
419
1280
  const configure = async (options) => {
420
- runtime.options = options;
421
- runtime.context = buildEventContext(options.context);
422
- await loadPersisted();
423
- await refreshConfig();
424
- armSessionTimer();
425
- if (runtime.queue.length > 0) flush();
1281
+ const channel_id = options.channel_id?.trim() ?? "";
1282
+ if (!channel_id) throw new Error("channel_id is required");
1283
+ return enqueue(async () => {
1284
+ runtime.options = {
1285
+ ...options,
1286
+ channel_id
1287
+ };
1288
+ const ctx = options.context;
1289
+ runtime.context = buildEventContext(typeof ctx === "function" ? void 0 : ctx);
1290
+ await loadPersisted();
1291
+ await refreshConfig();
1292
+ armSessionTimer();
1293
+ if (runtime.queue.length > 0) flush();
1294
+ });
426
1295
  };
427
1296
  const toQueued = (event, now, sessionId, customer) => {
428
1297
  const scope = runtime.options?.getScope?.() ?? {};
429
1298
  const platform = runtime.options?.platform ?? "web";
1299
+ const channel_id = runtime.options?.channel_id ?? scope.channel_id;
430
1300
  const b1 = {
431
1301
  id: randomUUID(),
432
1302
  name: event.name,
@@ -436,7 +1306,7 @@ const toQueued = (event, now, sessionId, customer) => {
436
1306
  const b3 = { timestamp: new Date(now).toISOString() };
437
1307
  const b4 = {
438
1308
  store_id: scope.store_id,
439
- channel_id: scope.channel_id
1309
+ channel_id
440
1310
  };
441
1311
  const b5 = {
442
1312
  brand_id: scope.brand_id,
@@ -517,19 +1387,28 @@ const trackEvent = async (event) => {
517
1387
  runtime.queue = enqueueEvent(runtime.queue, queued, now, getLimits());
518
1388
  await persistSession();
519
1389
  await persistQueue();
520
- if (event.name === "order_placed") {
1390
+ if (event.name === "purchase") {
521
1391
  await endSession();
522
1392
  return;
523
1393
  }
524
1394
  armSessionTimer();
525
1395
  };
526
- let trackChain = Promise.resolve();
527
- const track = (event) => {
528
- const run = () => trackEvent(event);
529
- const next = trackChain.then(run, run);
530
- const ok = () => void 0;
531
- trackChain = next.then(ok, ok);
532
- return next;
1396
+ const swallow = (task) => {
1397
+ return task.catch(() => void 0);
1398
+ };
1399
+ const dispatch = async (event, objectForm, opts) => {
1400
+ const sampleP = swallow(samplePlanEvent(event, opts));
1401
+ resolveAdapters(opts);
1402
+ const fanoutP = swallow(fanoutAdapters(event, opts));
1403
+ const funnel = isFunnelEventName(event.name);
1404
+ const interaction = isInteractionEventName(event.name);
1405
+ if (objectForm || funnel || interaction) await trackEvent(event);
1406
+ await Promise.all([sampleP, fanoutP]);
1407
+ };
1408
+ const track = (nameOrEvent, params, opts) => {
1409
+ const objectForm = typeof nameOrEvent !== "string";
1410
+ const event = toTrackEvent(nameOrEvent, params);
1411
+ return enqueue(() => dispatch(event, objectForm, opts));
533
1412
  };
534
1413
  const endSession = async () => {
535
1414
  clearTimer("sessionTimer");
@@ -545,6 +1424,19 @@ const reset = async () => {
545
1424
  await storage.removeItem(QUEUE_KEY);
546
1425
  await storage.removeItem(CONFIG_KEY);
547
1426
  await storage.removeItem(INTERACTIONS_KEY);
1427
+ await storage.removeItem(SAMPLE_KEY);
1428
+ };
1429
+ var Analytics = class {
1430
+ constructor(options) {
1431
+ if (!options) return;
1432
+ if (!(options.channel_id?.trim() ?? "")) throw new Error("channel_id is required");
1433
+ configure(options);
1434
+ }
1435
+ configure = configure;
1436
+ track = track;
1437
+ flush = flush;
1438
+ endSession = endSession;
1439
+ reset = reset;
548
1440
  };
549
1441
  const analytics = {
550
1442
  configure,
@@ -554,4 +1446,4 @@ const analytics = {
554
1446
  reset
555
1447
  };
556
1448
  //#endregion
557
- export { FUNNEL_EVENTS, INTERACTION_EVENTS, analytics, configure, endSession, isFunnelEventName, isInteractionEventName, reset, track };
1449
+ export { Analytics, FUNNEL_EVENTS, INTERACTION_EVENTS, analytics, configure, endSession, flush, isFunnelEventName, isInteractionEventName, reset, track };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fire-spark/analytics",
3
- "version": "0.0.3",
3
+ "version": "0.1.1",
4
4
  "description": "Fire Spark storefront analytics SDK",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -17,7 +17,7 @@
17
17
  }
18
18
  },
19
19
  "dependencies": {
20
- "@fire-spark/core": "0.0.3"
20
+ "@fire-spark/core": "^0.1.1"
21
21
  },
22
22
  "devDependencies": {
23
23
  "tsdown": "^0.22.14",
@@ -46,7 +46,7 @@
46
46
  }
47
47
  },
48
48
  "scripts": {
49
- "build": "tsdown",
49
+ "build": "tsdown && find ../../packages/constants/src -name '*.d.ts' -delete",
50
50
  "dev": "tsdown --watch",
51
51
  "check-types": "tsc --noEmit",
52
52
  "test:utils": "vitest run",