@analyticscli/sdk 0.1.0-preview.4 → 0.1.0-preview.6

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.cjs CHANGED
@@ -21,8 +21,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  AnalyticsClient: () => AnalyticsClient,
24
- BROWSER_API_KEY_ENV_KEYS: () => BROWSER_API_KEY_ENV_KEYS,
25
- DEFAULT_API_KEY_ENV_KEYS: () => DEFAULT_API_KEY_ENV_KEYS,
26
24
  ONBOARDING_EVENTS: () => ONBOARDING_EVENTS,
27
25
  ONBOARDING_PROGRESS_EVENT_ORDER: () => ONBOARDING_PROGRESS_EVENT_ORDER,
28
26
  ONBOARDING_SCREEN_EVENT_PREFIXES: () => ONBOARDING_SCREEN_EVENT_PREFIXES,
@@ -33,12 +31,11 @@ __export(index_exports, {
33
31
  PAYWALL_SKIP_EVENT_CANDIDATES: () => PAYWALL_SKIP_EVENT_CANDIDATES,
34
32
  PURCHASE_EVENTS: () => PURCHASE_EVENTS,
35
33
  PURCHASE_SUCCESS_EVENT_CANDIDATES: () => PURCHASE_SUCCESS_EVENT_CANDIDATES,
36
- REACT_NATIVE_API_KEY_ENV_KEYS: () => REACT_NATIVE_API_KEY_ENV_KEYS,
34
+ createAnalyticsContext: () => createAnalyticsContext,
37
35
  init: () => init,
38
36
  initAsync: () => initAsync,
39
- initBrowserFromEnv: () => initBrowserFromEnv,
40
- initFromEnv: () => initFromEnv,
41
- initReactNativeFromEnv: () => initReactNativeFromEnv
37
+ initConsentFirst: () => initConsentFirst,
38
+ initConsentFirstAsync: () => initConsentFirstAsync
42
39
  });
43
40
  module.exports = __toCommonJS(index_exports);
44
41
 
@@ -137,6 +134,9 @@ var validateEvent = (event, index) => {
137
134
  if (!isOptionalStringMax(event.platform, 64)) {
138
135
  return { success: false, reason: `events[${index}].platform is invalid` };
139
136
  }
137
+ if (!isOptionalStringMax(event.projectSurface, 64)) {
138
+ return { success: false, reason: `events[${index}].projectSurface is invalid` };
139
+ }
140
140
  if (!isOptionalStringMax(event.appVersion, 64)) {
141
141
  return { success: false, reason: `events[${index}].appVersion is invalid` };
142
142
  }
@@ -565,6 +565,7 @@ var sanitizeSurveyResponseInput = (input) => {
565
565
  };
566
566
 
567
567
  // src/analytics-client.ts
568
+ var DEFAULT_CONSENT_STORAGE_KEY = "analyticscli:consent:v1";
568
569
  var AnalyticsClient = class {
569
570
  apiKey;
570
571
  hasIngestConfig;
@@ -574,10 +575,17 @@ var AnalyticsClient = class {
574
575
  maxRetries;
575
576
  debug;
576
577
  platform;
578
+ projectSurface;
577
579
  appVersion;
580
+ identityTrackingMode;
578
581
  context;
582
+ configuredStorage;
579
583
  storage;
580
584
  storageReadsAreAsync;
585
+ persistConsentState;
586
+ consentStorageKey;
587
+ hasExplicitInitialConsent;
588
+ hasExplicitInitialFullTrackingConsent;
581
589
  sessionTimeoutMs;
582
590
  dedupeOnboardingStepViewsPerSession;
583
591
  runtimeEnv;
@@ -588,6 +596,7 @@ var AnalyticsClient = class {
588
596
  flushTimer = null;
589
597
  isFlushing = false;
590
598
  consentGranted = true;
599
+ fullTrackingConsentGranted = false;
591
600
  userId = null;
592
601
  anonId;
593
602
  sessionId;
@@ -610,34 +619,46 @@ var AnalyticsClient = class {
610
619
  this.maxRetries = normalizedOptions.maxRetries ?? 4;
611
620
  this.debug = normalizedOptions.debug ?? false;
612
621
  this.platform = this.normalizePlatformOption(normalizedOptions.platform) ?? detectDefaultPlatform();
622
+ this.projectSurface = this.normalizeProjectSurfaceOption(normalizedOptions.projectSurface);
613
623
  this.appVersion = this.readRequiredStringOption(normalizedOptions.appVersion) || detectDefaultAppVersion();
624
+ this.identityTrackingMode = this.resolveIdentityTrackingModeOption(normalizedOptions);
614
625
  this.context = { ...normalizedOptions.context ?? {} };
615
626
  this.runtimeEnv = detectRuntimeEnv();
616
- const useCookieStorage = normalizedOptions.useCookieStorage ?? Boolean(normalizedOptions.cookieDomain);
617
- const cookieStorage = resolveCookieStorageAdapter(
618
- useCookieStorage,
619
- normalizedOptions.cookieDomain,
620
- normalizedOptions.cookieMaxAgeSeconds ?? DEFAULT_COOKIE_MAX_AGE_SECONDS
621
- );
622
- const browserStorage = resolveBrowserStorageAdapter();
623
- this.storage = normalizedOptions.storage ?? (cookieStorage && browserStorage ? combineStorageAdapters(cookieStorage, browserStorage) : cookieStorage ?? browserStorage);
624
- this.storageReadsAreAsync = this.detectAsyncStorageReads();
627
+ this.persistConsentState = normalizedOptions.persistConsentState ?? false;
628
+ this.consentStorageKey = this.readRequiredStringOption(normalizedOptions.consentStorageKey) || DEFAULT_CONSENT_STORAGE_KEY;
629
+ this.hasExplicitInitialConsent = typeof normalizedOptions.initialConsentGranted === "boolean";
630
+ this.hasExplicitInitialFullTrackingConsent = typeof normalizedOptions.initialFullTrackingConsentGranted === "boolean";
625
631
  this.sessionTimeoutMs = normalizedOptions.sessionTimeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;
626
- this.dedupeOnboardingStepViewsPerSession = normalizedOptions.dedupeOnboardingStepViewsPerSession ?? false;
627
- const providedAnonId = normalizedOptions.anonId?.trim();
628
- const providedSessionId = normalizedOptions.sessionId?.trim();
632
+ this.dedupeOnboardingStepViewsPerSession = normalizedOptions.dedupeOnboardingStepViewsPerSession ?? true;
633
+ this.configuredStorage = this.resolveConfiguredStorage(normalizedOptions);
634
+ const persistedFullTrackingConsent = this.readPersistedConsentSync(this.configuredStorage);
635
+ const configuredFullTrackingConsent = normalizedOptions.initialFullTrackingConsentGranted;
636
+ const initialFullTrackingConsentGranted = typeof configuredFullTrackingConsent === "boolean" ? configuredFullTrackingConsent : persistedFullTrackingConsent ?? false;
637
+ this.fullTrackingConsentGranted = this.identityTrackingMode === "always_on" || initialFullTrackingConsentGranted;
638
+ this.storage = this.isFullTrackingActive() ? this.configuredStorage : null;
639
+ this.storageReadsAreAsync = this.detectAsyncStorageReads();
640
+ const providedAnonId = this.isFullTrackingActive() ? this.readRequiredStringOption(normalizedOptions.anonId) : "";
641
+ const providedSessionId = this.isFullTrackingActive() ? this.readRequiredStringOption(normalizedOptions.sessionId) : "";
629
642
  this.hasExplicitAnonId = Boolean(providedAnonId);
630
643
  this.hasExplicitSessionId = Boolean(providedSessionId);
631
644
  this.anonId = providedAnonId || this.ensureDeviceId();
632
645
  this.sessionId = providedSessionId || this.ensureSessionId();
633
646
  this.sessionEventSeq = this.readSessionEventSeq(this.sessionId);
634
- this.consentGranted = this.hasIngestConfig;
647
+ const persistedConsent = this.readPersistedConsentSync(this.storage);
648
+ const configuredConsent = normalizedOptions.initialConsentGranted;
649
+ const initialConsentGranted = typeof configuredConsent === "boolean" ? configuredConsent : persistedConsent ?? this.hasIngestConfig;
650
+ this.consentGranted = this.hasIngestConfig && initialConsentGranted;
651
+ if (this.hasExplicitInitialConsent && this.persistConsentState) {
652
+ this.writePersistedConsent(this.storage, this.consentGranted);
653
+ }
654
+ if (this.hasExplicitInitialFullTrackingConsent && this.persistConsentState) {
655
+ this.writePersistedConsent(this.configuredStorage, this.fullTrackingConsentGranted);
656
+ }
635
657
  this.hydrationPromise = this.hydrateIdentityFromStorage();
636
658
  this.startAutoFlush();
637
659
  }
638
660
  /**
639
- * Resolves once any async storage adapter hydration completes.
640
- * Useful in React Native when using async persistence (for example AsyncStorage).
661
+ * Resolves once client initialization work completes.
641
662
  */
642
663
  async ready() {
643
664
  await this.hydrationPromise;
@@ -646,22 +667,41 @@ var AnalyticsClient = class {
646
667
  * Enables or disables event collection.
647
668
  * When disabled, queued events are dropped immediately.
648
669
  */
649
- setConsent(granted) {
670
+ setConsent(granted, options = {}) {
650
671
  if (granted && !this.hasIngestConfig) {
651
672
  this.log("Ignoring consent opt-in because `apiKey` is missing");
652
673
  return;
653
674
  }
654
675
  this.consentGranted = granted;
676
+ if ((options.persist ?? true) && this.persistConsentState) {
677
+ this.writePersistedConsent(this.storage, granted);
678
+ }
679
+ if (this.identityTrackingMode === "consent_gated") {
680
+ this.setFullTrackingConsent(granted, options);
681
+ }
655
682
  if (!granted) {
656
683
  this.queue = [];
657
684
  this.deferredEventsBeforeHydration = [];
658
685
  }
659
686
  }
660
- optIn() {
661
- this.setConsent(true);
687
+ optIn(options) {
688
+ this.setConsent(true, options);
689
+ }
690
+ optOut(options) {
691
+ this.setConsent(false, options);
662
692
  }
663
- optOut() {
664
- this.setConsent(false);
693
+ getConsent() {
694
+ return this.consentGranted;
695
+ }
696
+ getConsentState() {
697
+ const persisted = this.readPersistedConsentSync(this.storage);
698
+ if (persisted === true) {
699
+ return "granted";
700
+ }
701
+ if (persisted === false) {
702
+ return "denied";
703
+ }
704
+ return this.consentGranted ? "granted" : "unknown";
665
705
  }
666
706
  /**
667
707
  * Sets or updates shared event context fields (useful for mobile device/app metadata).
@@ -677,22 +717,25 @@ var AnalyticsClient = class {
677
717
  * Anonymous history remains linked by anonId/sessionId.
678
718
  */
679
719
  identify(userId, traits) {
680
- if (!this.consentGranted) {
681
- return;
682
- }
683
720
  const normalizedUserId = this.readRequiredStringOption(userId);
684
721
  if (!normalizedUserId) {
685
- this.log("Dropping identify call without required `userId`");
686
722
  return;
687
723
  }
724
+ if (!this.isFullTrackingActive()) {
725
+ this.log("Ignoring identify() because identity persistence is not enabled");
726
+ return;
727
+ }
728
+ this.userId = normalizedUserId;
729
+ if (!this.consentGranted) {
730
+ return;
731
+ }
732
+ const normalizedTraits = this.cloneProperties(traits);
688
733
  if (this.shouldDeferEventsUntilHydrated()) {
689
- const deferredTraits = this.cloneProperties(traits);
690
734
  this.deferEventUntilHydrated(() => {
691
- this.identify(normalizedUserId, deferredTraits);
735
+ this.identify(normalizedUserId, normalizedTraits);
692
736
  });
693
737
  return;
694
738
  }
695
- this.userId = normalizedUserId;
696
739
  const sessionId = this.getSessionId();
697
740
  this.enqueue({
698
741
  eventId: randomId(),
@@ -701,8 +744,9 @@ var AnalyticsClient = class {
701
744
  sessionId,
702
745
  anonId: this.anonId,
703
746
  userId: normalizedUserId,
704
- properties: this.withRuntimeMetadata(traits, sessionId),
747
+ properties: this.withRuntimeMetadata(normalizedTraits, sessionId),
705
748
  platform: this.platform,
749
+ projectSurface: this.projectSurface,
706
750
  appVersion: this.appVersion,
707
751
  ...this.withEventContext(),
708
752
  type: "identify"
@@ -714,13 +758,39 @@ var AnalyticsClient = class {
714
758
  * - pass null/undefined/empty string to clear user linkage
715
759
  */
716
760
  setUser(userId, traits) {
717
- const normalizedUserId = typeof userId === "string" ? this.readRequiredStringOption(userId) : "";
761
+ const normalizedUserId = this.readRequiredStringOption(userId);
718
762
  if (!normalizedUserId) {
719
763
  this.clearUser();
720
764
  return;
721
765
  }
722
766
  this.identify(normalizedUserId, traits);
723
767
  }
768
+ /**
769
+ * Sets consent specifically for persistent identity tracking.
770
+ * In `consent_gated` mode this toggles strict-vs-full identity behavior while generic event tracking can stay enabled.
771
+ */
772
+ setFullTrackingConsent(granted, options = {}) {
773
+ if (this.identityTrackingMode === "strict") {
774
+ return;
775
+ }
776
+ if (this.identityTrackingMode === "always_on") {
777
+ return;
778
+ }
779
+ this.fullTrackingConsentGranted = granted;
780
+ if ((options.persist ?? true) && this.persistConsentState) {
781
+ this.writePersistedConsent(this.configuredStorage, granted);
782
+ }
783
+ this.applyIdentityTrackingState();
784
+ }
785
+ optInFullTracking(options) {
786
+ this.setFullTrackingConsent(true, options);
787
+ }
788
+ optOutFullTracking(options) {
789
+ this.setFullTrackingConsent(false, options);
790
+ }
791
+ isFullTrackingEnabled() {
792
+ return this.isFullTrackingActive();
793
+ }
724
794
  /**
725
795
  * Clears the current identified user from in-memory SDK state.
726
796
  */
@@ -751,9 +821,10 @@ var AnalyticsClient = class {
751
821
  ts: nowIso(),
752
822
  sessionId,
753
823
  anonId: this.anonId,
754
- userId: this.userId,
824
+ userId: this.getEventUserId(),
755
825
  properties: this.withRuntimeMetadata(properties, sessionId),
756
826
  platform: this.platform,
827
+ projectSurface: this.projectSurface,
757
828
  appVersion: this.appVersion,
758
829
  ...this.withEventContext(),
759
830
  type: "track"
@@ -850,6 +921,8 @@ var AnalyticsClient = class {
850
921
  /**
851
922
  * Creates a scoped paywall tracker that applies shared paywall defaults to every journey event.
852
923
  * Useful when a flow has a stable `source`, `paywallId`, `offering`, or experiment metadata.
924
+ * Reuse the returned tracker for that flow context; creating a new tracker per event resets
925
+ * paywall entry correlation.
853
926
  */
854
927
  createPaywallTracker(defaults) {
855
928
  const { source: rawDefaultSource, ...defaultProperties } = defaults;
@@ -953,9 +1026,10 @@ var AnalyticsClient = class {
953
1026
  ts: nowIso(),
954
1027
  sessionId,
955
1028
  anonId: this.anonId,
956
- userId: this.userId,
1029
+ userId: this.getEventUserId(),
957
1030
  properties: this.withRuntimeMetadata(properties, sessionId),
958
1031
  platform: this.platform,
1032
+ projectSurface: this.projectSurface,
959
1033
  appVersion: this.appVersion,
960
1034
  ...this.withEventContext(),
961
1035
  type: "screen"
@@ -988,9 +1062,10 @@ var AnalyticsClient = class {
988
1062
  ts: nowIso(),
989
1063
  sessionId,
990
1064
  anonId: this.anonId,
991
- userId: this.userId,
1065
+ userId: this.getEventUserId(),
992
1066
  properties: this.withRuntimeMetadata({ message, rating, ...properties }, sessionId),
993
1067
  platform: this.platform,
1068
+ projectSurface: this.projectSurface,
994
1069
  appVersion: this.appVersion,
995
1070
  ...this.withEventContext(),
996
1071
  type: "feedback"
@@ -1078,6 +1153,36 @@ var AnalyticsClient = class {
1078
1153
  }
1079
1154
  }
1080
1155
  }
1156
+ parsePersistedConsent(raw) {
1157
+ if (raw === "granted") {
1158
+ return true;
1159
+ }
1160
+ if (raw === "denied") {
1161
+ return false;
1162
+ }
1163
+ return null;
1164
+ }
1165
+ readPersistedConsentSync(storage) {
1166
+ if (!this.persistConsentState) {
1167
+ return null;
1168
+ }
1169
+ if (storage === this.storage && this.storageReadsAreAsync) {
1170
+ return null;
1171
+ }
1172
+ return this.parsePersistedConsent(readStorageSync(storage, this.consentStorageKey));
1173
+ }
1174
+ async readPersistedConsentAsync(storage) {
1175
+ if (!this.persistConsentState) {
1176
+ return null;
1177
+ }
1178
+ return this.parsePersistedConsent(await readStorageAsync(storage, this.consentStorageKey));
1179
+ }
1180
+ writePersistedConsent(storage, granted) {
1181
+ if (!this.persistConsentState) {
1182
+ return;
1183
+ }
1184
+ writeStorageSync(storage, this.consentStorageKey, granted ? "granted" : "denied");
1185
+ }
1081
1186
  startAutoFlush() {
1082
1187
  if (!this.hasIngestConfig) {
1083
1188
  return;
@@ -1169,10 +1274,11 @@ var AnalyticsClient = class {
1169
1274
  return;
1170
1275
  }
1171
1276
  try {
1172
- const [storedAnonId, storedSessionId, storedLastSeen] = await Promise.all([
1277
+ const [storedAnonId, storedSessionId, storedLastSeen, storedConsent] = await Promise.all([
1173
1278
  readStorageAsync(this.storage, DEVICE_ID_KEY),
1174
1279
  readStorageAsync(this.storage, SESSION_ID_KEY),
1175
- readStorageAsync(this.storage, LAST_SEEN_KEY)
1280
+ readStorageAsync(this.storage, LAST_SEEN_KEY),
1281
+ this.readPersistedConsentAsync(this.storage)
1176
1282
  ]);
1177
1283
  if (!this.hasExplicitAnonId && storedAnonId) {
1178
1284
  this.anonId = storedAnonId;
@@ -1184,6 +1290,13 @@ var AnalyticsClient = class {
1184
1290
  this.inMemoryLastSeenMs = Date.now();
1185
1291
  }
1186
1292
  }
1293
+ if (!this.hasExplicitInitialConsent && typeof storedConsent === "boolean") {
1294
+ this.consentGranted = this.hasIngestConfig && storedConsent;
1295
+ if (!this.consentGranted) {
1296
+ this.queue = [];
1297
+ this.deferredEventsBeforeHydration = [];
1298
+ }
1299
+ }
1187
1300
  this.sessionEventSeq = await this.readSessionEventSeqAsync(this.sessionId);
1188
1301
  await this.hydrateOnboardingStepViewState(this.sessionId);
1189
1302
  writeStorageSync(this.storage, DEVICE_ID_KEY, this.anonId);
@@ -1352,6 +1465,81 @@ var AnalyticsClient = class {
1352
1465
  return null;
1353
1466
  }
1354
1467
  }
1468
+ resolveIdentityTrackingModeOption(options) {
1469
+ const explicitMode = this.readRequiredStringOption(options.identityTrackingMode).toLowerCase();
1470
+ if (explicitMode === "strict") {
1471
+ return "strict";
1472
+ }
1473
+ if (explicitMode === "consent_gated") {
1474
+ return "consent_gated";
1475
+ }
1476
+ if (explicitMode === "always_on") {
1477
+ return "always_on";
1478
+ }
1479
+ if (options.enableFullTrackingWithoutConsent === true) {
1480
+ return "always_on";
1481
+ }
1482
+ return "consent_gated";
1483
+ }
1484
+ resolveConfiguredStorage(options) {
1485
+ if (this.identityTrackingMode === "strict") {
1486
+ if (options.storage || options.useCookieStorage || options.cookieDomain) {
1487
+ this.log("Ignoring storage/cookie configuration because identityTrackingMode=strict");
1488
+ }
1489
+ return null;
1490
+ }
1491
+ const customStorage = options.storage ?? null;
1492
+ const browserStorage = resolveBrowserStorageAdapter();
1493
+ const primaryStorage = customStorage ?? browserStorage;
1494
+ const cookieStorage = resolveCookieStorageAdapter(
1495
+ options.useCookieStorage === true,
1496
+ this.readRequiredStringOption(options.cookieDomain) || void 0,
1497
+ this.normalizeCookieMaxAgeSeconds(options.cookieMaxAgeSeconds)
1498
+ );
1499
+ if (primaryStorage && cookieStorage) {
1500
+ return combineStorageAdapters(primaryStorage, cookieStorage);
1501
+ }
1502
+ return primaryStorage ?? cookieStorage;
1503
+ }
1504
+ normalizeCookieMaxAgeSeconds(value) {
1505
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
1506
+ return DEFAULT_COOKIE_MAX_AGE_SECONDS;
1507
+ }
1508
+ return Math.floor(value);
1509
+ }
1510
+ isFullTrackingActive() {
1511
+ if (!this.hasIngestConfig) {
1512
+ return false;
1513
+ }
1514
+ if (this.identityTrackingMode === "always_on") {
1515
+ return true;
1516
+ }
1517
+ if (this.identityTrackingMode === "strict") {
1518
+ return false;
1519
+ }
1520
+ return this.fullTrackingConsentGranted;
1521
+ }
1522
+ applyIdentityTrackingState() {
1523
+ if (!this.isFullTrackingActive()) {
1524
+ this.storage = null;
1525
+ this.storageReadsAreAsync = false;
1526
+ this.userId = null;
1527
+ return;
1528
+ }
1529
+ this.storage = this.configuredStorage;
1530
+ this.storageReadsAreAsync = this.detectAsyncStorageReads();
1531
+ this.sessionId = this.ensureSessionId();
1532
+ this.sessionEventSeq = this.readSessionEventSeq(this.sessionId);
1533
+ writeStorageSync(this.storage, DEVICE_ID_KEY, this.anonId);
1534
+ writeStorageSync(this.storage, SESSION_ID_KEY, this.sessionId);
1535
+ writeStorageSync(this.storage, LAST_SEEN_KEY, String(this.inMemoryLastSeenMs));
1536
+ }
1537
+ getEventUserId() {
1538
+ if (!this.isFullTrackingActive()) {
1539
+ return null;
1540
+ }
1541
+ return this.userId;
1542
+ }
1355
1543
  withEventContext() {
1356
1544
  return {
1357
1545
  appBuild: this.context.appBuild,
@@ -1387,6 +1575,16 @@ var AnalyticsClient = class {
1387
1575
  }
1388
1576
  return void 0;
1389
1577
  }
1578
+ normalizeProjectSurfaceOption(value) {
1579
+ const normalized = this.readRequiredStringOption(value).toLowerCase();
1580
+ if (!normalized) {
1581
+ return void 0;
1582
+ }
1583
+ if (normalized.length > 64) {
1584
+ return normalized.slice(0, 64);
1585
+ }
1586
+ return normalized;
1587
+ }
1390
1588
  log(message, data) {
1391
1589
  if (!this.debug) {
1392
1590
  return;
@@ -1395,123 +1593,114 @@ var AnalyticsClient = class {
1395
1593
  }
1396
1594
  reportMissingApiKey() {
1397
1595
  console.error(
1398
- "[analyticscli-sdk] Missing required `apiKey`. Tracking is disabled (safe no-op). Pass your long write key."
1596
+ "[analyticscli-sdk] Missing required `apiKey`. Tracking is disabled (safe no-op). Pass your publishable API key."
1399
1597
  );
1400
1598
  }
1401
1599
  };
1402
1600
 
1403
- // src/bootstrap.ts
1404
- var DEFAULT_API_KEY_ENV_KEYS = [
1405
- "ANALYTICSCLI_WRITE_KEY",
1406
- "NEXT_PUBLIC_ANALYTICSCLI_WRITE_KEY",
1407
- "EXPO_PUBLIC_ANALYTICSCLI_WRITE_KEY",
1408
- "VITE_ANALYTICSCLI_WRITE_KEY"
1409
- ];
1410
- var readTrimmedString2 = (value) => {
1411
- if (typeof value === "string") {
1412
- return value.trim();
1413
- }
1414
- if (typeof value === "number" || typeof value === "boolean") {
1415
- return String(value).trim();
1416
- }
1417
- return "";
1418
- };
1419
- var resolveDefaultEnv = () => {
1420
- const withProcess = globalThis;
1421
- if (typeof withProcess.process?.env === "object" && withProcess.process.env !== null) {
1422
- return withProcess.process.env;
1601
+ // src/context.ts
1602
+ var normalizeInitInput = (input) => {
1603
+ if (typeof input === "string") {
1604
+ return { apiKey: input };
1423
1605
  }
1424
- return {};
1425
- };
1426
- var resolveValueFromEnv = (env, keys) => {
1427
- for (const key of keys) {
1428
- const value = readTrimmedString2(env[key]);
1429
- if (value.length > 0) {
1430
- return value;
1431
- }
1606
+ if (input === null || input === void 0) {
1607
+ return {};
1432
1608
  }
1433
- return "";
1609
+ return input;
1434
1610
  };
1435
- var toMissingMessage = (details) => {
1436
- const parts = [];
1437
- if (details.missingApiKey) {
1438
- parts.push(`apiKey (searched: ${details.searchedApiKeyEnvKeys.join(", ") || "none"})`);
1611
+ var resolveClient = (input) => {
1612
+ if (input instanceof AnalyticsClient) {
1613
+ return input;
1439
1614
  }
1440
- return `[analyticscli-sdk] Missing required configuration: ${parts.join("; ")}.`;
1615
+ return new AnalyticsClient(normalizeInitInput(input ?? {}));
1441
1616
  };
1442
- var initFromEnv = (options = {}) => {
1443
- const {
1444
- env,
1445
- apiKey,
1446
- apiKeyEnvKeys,
1447
- missingConfigMode = "noop",
1448
- onMissingConfig,
1449
- ...clientOptions
1450
- } = options;
1451
- const resolvedApiKeyEnvKeys = [...apiKeyEnvKeys ?? DEFAULT_API_KEY_ENV_KEYS];
1452
- const envSource = env ?? resolveDefaultEnv();
1453
- const resolvedApiKey = readTrimmedString2(apiKey) || resolveValueFromEnv(envSource, resolvedApiKeyEnvKeys);
1454
- const missingConfig = {
1455
- missingApiKey: resolvedApiKey.length === 0,
1456
- searchedApiKeyEnvKeys: resolvedApiKeyEnvKeys
1617
+ var createAnalyticsContext = (options = {}) => {
1618
+ const client = resolveClient(options.client);
1619
+ let onboardingTracker = client.createOnboardingTracker(options.onboarding ?? {});
1620
+ let paywallTracker = options.paywall ? client.createPaywallTracker(options.paywall) : null;
1621
+ const consent = {
1622
+ get: () => client.getConsent(),
1623
+ getState: () => client.getConsentState(),
1624
+ set: (granted, setOptions) => client.setConsent(granted, setOptions),
1625
+ optIn: (setOptions) => client.optIn(setOptions),
1626
+ optOut: (setOptions) => client.optOut(setOptions),
1627
+ setFullTracking: (granted, setOptions) => client.setFullTrackingConsent(granted, setOptions),
1628
+ optInFullTracking: (setOptions) => client.optInFullTracking(setOptions),
1629
+ optOutFullTracking: (setOptions) => client.optOutFullTracking(setOptions),
1630
+ isFullTrackingEnabled: () => client.isFullTrackingEnabled()
1631
+ };
1632
+ const user = {
1633
+ identify: (userId, traits) => client.identify(userId, traits),
1634
+ set: (userId, traits) => client.setUser(userId, traits),
1635
+ clear: () => client.clearUser()
1636
+ };
1637
+ return {
1638
+ client,
1639
+ get onboarding() {
1640
+ return onboardingTracker;
1641
+ },
1642
+ get paywall() {
1643
+ return paywallTracker;
1644
+ },
1645
+ consent,
1646
+ user,
1647
+ track: (eventName, properties) => client.track(eventName, properties),
1648
+ trackOnboardingEvent: (eventName, properties) => client.trackOnboardingEvent(eventName, properties),
1649
+ trackOnboardingSurveyResponse: (input, eventName) => client.trackOnboardingSurveyResponse(input, eventName),
1650
+ trackPaywallEvent: (eventName, properties) => client.trackPaywallEvent(eventName, properties),
1651
+ screen: (name, properties) => client.screen(name, properties),
1652
+ page: (name, properties) => client.page(name, properties),
1653
+ feedback: (message, rating, properties) => client.feedback(message, rating, properties),
1654
+ setContext: (context) => client.setContext(context),
1655
+ createOnboarding: (defaults) => client.createOnboardingTracker(defaults),
1656
+ createPaywall: (defaults) => client.createPaywallTracker(defaults),
1657
+ configureOnboarding: (defaults) => {
1658
+ onboardingTracker = client.createOnboardingTracker(defaults);
1659
+ return onboardingTracker;
1660
+ },
1661
+ configurePaywall: (defaults) => {
1662
+ paywallTracker = client.createPaywallTracker(defaults);
1663
+ return paywallTracker;
1664
+ },
1665
+ ready: () => client.ready(),
1666
+ flush: () => client.flush(),
1667
+ shutdown: () => client.shutdown()
1457
1668
  };
1458
- if (missingConfig.missingApiKey) {
1459
- onMissingConfig?.(missingConfig);
1460
- if (missingConfigMode === "throw") {
1461
- throw new Error(toMissingMessage(missingConfig));
1462
- }
1463
- }
1464
- return new AnalyticsClient({
1465
- ...clientOptions,
1466
- apiKey: resolvedApiKey
1467
- });
1468
1669
  };
1469
1670
 
1470
1671
  // src/index.ts
1471
- var normalizeInitInput = (input) => {
1672
+ var normalizeInitInput2 = (input) => {
1472
1673
  if (typeof input === "string") {
1473
1674
  return { apiKey: input };
1474
1675
  }
1676
+ if (input === null || input === void 0) {
1677
+ return {};
1678
+ }
1475
1679
  return input;
1476
1680
  };
1477
1681
  var init = (input = {}) => {
1478
- return new AnalyticsClient(normalizeInitInput(input));
1682
+ return new AnalyticsClient(normalizeInitInput2(input));
1683
+ };
1684
+ var initConsentFirst = (input = {}) => {
1685
+ const normalized = normalizeInitInput2(input);
1686
+ return new AnalyticsClient({
1687
+ ...normalized,
1688
+ initialConsentGranted: false
1689
+ });
1479
1690
  };
1480
1691
  var initAsync = async (input = {}) => {
1481
- const client = new AnalyticsClient(normalizeInitInput(input));
1692
+ const client = new AnalyticsClient(normalizeInitInput2(input));
1482
1693
  await client.ready();
1483
1694
  return client;
1484
1695
  };
1485
- var BROWSER_API_KEY_ENV_KEYS = [
1486
- "ANALYTICSCLI_WRITE_KEY",
1487
- "NEXT_PUBLIC_ANALYTICSCLI_WRITE_KEY",
1488
- "PUBLIC_ANALYTICSCLI_WRITE_KEY",
1489
- "VITE_ANALYTICSCLI_WRITE_KEY",
1490
- "EXPO_PUBLIC_ANALYTICSCLI_WRITE_KEY"
1491
- ];
1492
- var REACT_NATIVE_API_KEY_ENV_KEYS = [
1493
- "ANALYTICSCLI_WRITE_KEY",
1494
- "EXPO_PUBLIC_ANALYTICSCLI_WRITE_KEY"
1495
- ];
1496
- var initBrowserFromEnv = (options = {}) => {
1497
- const { apiKeyEnvKeys, ...rest } = options;
1498
- return initFromEnv({
1499
- ...rest,
1500
- apiKeyEnvKeys: [...apiKeyEnvKeys ?? BROWSER_API_KEY_ENV_KEYS]
1501
- });
1502
- };
1503
- var initReactNativeFromEnv = (options = {}) => {
1504
- const { apiKeyEnvKeys, ...rest } = options;
1505
- return initFromEnv({
1506
- ...rest,
1507
- apiKeyEnvKeys: [...apiKeyEnvKeys ?? REACT_NATIVE_API_KEY_ENV_KEYS]
1508
- });
1696
+ var initConsentFirstAsync = async (input = {}) => {
1697
+ const client = initConsentFirst(input);
1698
+ await client.ready();
1699
+ return client;
1509
1700
  };
1510
1701
  // Annotate the CommonJS export names for ESM import in node:
1511
1702
  0 && (module.exports = {
1512
1703
  AnalyticsClient,
1513
- BROWSER_API_KEY_ENV_KEYS,
1514
- DEFAULT_API_KEY_ENV_KEYS,
1515
1704
  ONBOARDING_EVENTS,
1516
1705
  ONBOARDING_PROGRESS_EVENT_ORDER,
1517
1706
  ONBOARDING_SCREEN_EVENT_PREFIXES,
@@ -1522,10 +1711,9 @@ var initReactNativeFromEnv = (options = {}) => {
1522
1711
  PAYWALL_SKIP_EVENT_CANDIDATES,
1523
1712
  PURCHASE_EVENTS,
1524
1713
  PURCHASE_SUCCESS_EVENT_CANDIDATES,
1525
- REACT_NATIVE_API_KEY_ENV_KEYS,
1714
+ createAnalyticsContext,
1526
1715
  init,
1527
1716
  initAsync,
1528
- initBrowserFromEnv,
1529
- initFromEnv,
1530
- initReactNativeFromEnv
1717
+ initConsentFirst,
1718
+ initConsentFirstAsync
1531
1719
  });