@virex-tech/paywallo-sdk 2.2.4 → 2.2.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.js CHANGED
@@ -830,9 +830,9 @@ var init_CampaignGateService = __esm({
830
830
  }
831
831
  }
832
832
  getPreloadedCampaign(placement) {
833
- const cached = this.preloadedCampaigns.get(placement);
834
- if (!cached) return null;
835
- const age = Date.now() - cached.preloadedAt;
833
+ const cached2 = this.preloadedCampaigns.get(placement);
834
+ if (!cached2) return null;
835
+ const age = Date.now() - cached2.preloadedAt;
836
836
  if (age >= PRELOAD_CACHE_TTL_MS) {
837
837
  this.preloadedCampaigns.delete(placement);
838
838
  return null;
@@ -841,7 +841,7 @@ var init_CampaignGateService = __esm({
841
841
  void this.preloadCampaign(this.apiClientRef, placement).catch(() => {
842
842
  });
843
843
  }
844
- return cached.campaign;
844
+ return cached2.campaign;
845
845
  }
846
846
  async preloadAllActive(apiClient, debug) {
847
847
  this.apiClientRef = apiClient;
@@ -2024,13 +2024,21 @@ var init_NativePushBridge = __esm({
2024
2024
  if (!raw || typeof raw !== "object") return null;
2025
2025
  const r = raw;
2026
2026
  const notification = r["notification"];
2027
+ const asString = (v) => typeof v === "string" ? v : void 0;
2027
2028
  return {
2028
- messageId: typeof r["messageId"] === "string" ? r["messageId"] : void 0,
2029
+ messageId: asString(r["messageId"]),
2029
2030
  data: typeof r["data"] === "object" && r["data"] !== null ? r["data"] : void 0,
2030
2031
  notification: notification && typeof notification === "object" ? {
2031
- title: typeof notification["title"] === "string" ? notification["title"] : void 0,
2032
- body: typeof notification["body"] === "string" ? notification["body"] : void 0
2032
+ title: asString(notification["title"]),
2033
+ body: asString(notification["body"]),
2034
+ imageUrl: asString(notification["imageUrl"])
2033
2035
  } : void 0,
2036
+ // Pass through top-level title/body/imageUrl emitted by the native modules
2037
+ // (both iOS and Android currently emit these at the top level rather than
2038
+ // nested under `notification`).
2039
+ title: asString(r["title"]),
2040
+ body: asString(r["body"]),
2041
+ imageUrl: asString(r["imageUrl"]),
2034
2042
  sentTime: typeof r["sentTime"] === "number" ? r["sentTime"] : void 0
2035
2043
  };
2036
2044
  } catch {
@@ -2165,12 +2173,13 @@ function extractPayload(data) {
2165
2173
  }
2166
2174
  function setupForegroundHandler(deps, onReceive) {
2167
2175
  const { bridge, tracker, debug } = deps;
2168
- const unsubscribe = bridge.onMessage(async (message) => {
2176
+ const unsubscribe2 = bridge.onMessage(async (message) => {
2169
2177
  const data = message.data ?? {};
2170
2178
  const payload = extractPayload({
2171
2179
  ...data,
2172
- title: data["title"] ?? message.notification?.title ?? "",
2173
- body: data["body"] ?? message.notification?.body ?? ""
2180
+ title: data["title"] ?? message.notification?.title ?? message.title ?? "",
2181
+ body: data["body"] ?? message.notification?.body ?? message.body ?? "",
2182
+ imageUrl: data["imageUrl"] ?? message.notification?.imageUrl ?? message.imageUrl
2174
2183
  });
2175
2184
  if (debug) console.log("[Paywallo PUSH] foreground message received:", payload.notificationId);
2176
2185
  onReceive(payload);
@@ -2182,7 +2191,7 @@ function setupForegroundHandler(deps, onReceive) {
2182
2191
  messageId
2183
2192
  });
2184
2193
  });
2185
- return unsubscribe;
2194
+ return unsubscribe2;
2186
2195
  }
2187
2196
  var init_ForegroundHandler = __esm({
2188
2197
  "src/domains/notifications/handlers/ForegroundHandler.ts"() {
@@ -2361,8 +2370,8 @@ async function getInitialNotificationPayload(bridge) {
2361
2370
  notificationId: String(data["notificationId"] ?? data["notification_id"] ?? ""),
2362
2371
  campaignId: String(data["campaignId"] ?? data["campaign_id"] ?? ""),
2363
2372
  variantKey: data["variantKey"] != null ? String(data["variantKey"]) : void 0,
2364
- title: String(data["title"] ?? message.notification?.title ?? ""),
2365
- body: String(data["body"] ?? message.notification?.body ?? ""),
2373
+ title: String(data["title"] ?? message.notification?.title ?? message.title ?? ""),
2374
+ body: String(data["body"] ?? message.notification?.body ?? message.body ?? ""),
2366
2375
  data
2367
2376
  };
2368
2377
  }
@@ -2377,8 +2386,8 @@ var init_NotificationHandlerSetup = __esm({
2377
2386
  // src/core/version.ts
2378
2387
  function resolveVersion() {
2379
2388
  try {
2380
- if ("2.2.4") {
2381
- return "2.2.4";
2389
+ if ("2.2.6") {
2390
+ return "2.2.6";
2382
2391
  }
2383
2392
  } catch {
2384
2393
  }
@@ -2450,9 +2459,6 @@ function mapPermissionStatus(status) {
2450
2459
  function detectPlatform2() {
2451
2460
  return import_react_native11.Platform.OS === "ios" ? "ios" : "android";
2452
2461
  }
2453
- function detectEnvironment() {
2454
- return typeof __DEV__ !== "undefined" && __DEV__ ? "sandbox" : "production";
2455
- }
2456
2462
  async function getAppVersion() {
2457
2463
  try {
2458
2464
  const { collectDeviceInfo: collectDeviceInfo2 } = await Promise.resolve().then(() => (init_deviceInfo(), deviceInfo_exports));
@@ -2493,7 +2499,7 @@ async function clearPersistedToken(secureStorage2) {
2493
2499
  } catch {
2494
2500
  }
2495
2501
  }
2496
- async function buildRegistration(token, getDeviceId, getDistinctId) {
2502
+ async function buildRegistration(token, getDeviceId, getDistinctId, environment) {
2497
2503
  const appVersion = await getAppVersion();
2498
2504
  return {
2499
2505
  token,
@@ -2504,7 +2510,7 @@ async function buildRegistration(token, getDeviceId, getDistinctId) {
2504
2510
  deviceId: getDeviceId?.() ?? void 0,
2505
2511
  locale: await getLocale(),
2506
2512
  timezone: getTimezone2(),
2507
- environment: detectEnvironment()
2513
+ environment
2508
2514
  };
2509
2515
  }
2510
2516
  async function registerToken(deps, token) {
@@ -2513,7 +2519,8 @@ async function registerToken(deps, token) {
2513
2519
  if (debug) console.log("[Paywallo PUSH] httpClient not ready, skipping token registration");
2514
2520
  return;
2515
2521
  }
2516
- const registration = await buildRegistration(token, getDeviceId, getDistinctId);
2522
+ const { environment } = deps;
2523
+ const registration = await buildRegistration(token, getDeviceId, getDistinctId, environment);
2517
2524
  try {
2518
2525
  await httpClient.post(
2519
2526
  TOKEN_ENDPOINT,
@@ -2563,7 +2570,8 @@ async function registerAndPersistToken(deps, token) {
2563
2570
  appKey: deps.appKey,
2564
2571
  getDeviceId: deps.getDeviceId,
2565
2572
  getDistinctId: deps.getDistinctId,
2566
- debug: deps.debug
2573
+ debug: deps.debug,
2574
+ environment: deps.environment
2567
2575
  },
2568
2576
  token
2569
2577
  );
@@ -2580,7 +2588,8 @@ function subscribeTokenRefresh(bridge, deps, onToken) {
2580
2588
  appKey: deps.appKey,
2581
2589
  getDeviceId: deps.getDeviceId,
2582
2590
  getDistinctId: deps.getDistinctId,
2583
- debug: deps.debug
2591
+ debug: deps.debug,
2592
+ environment: deps.environment
2584
2593
  },
2585
2594
  newToken
2586
2595
  ).catch(() => void 0);
@@ -2788,6 +2797,7 @@ var init_NotificationsManager = __esm({
2788
2797
  this.getDistinctId = null;
2789
2798
  this.appKey = null;
2790
2799
  this.apiBaseUrl = null;
2800
+ this.environment = "production";
2791
2801
  this.tokenRefreshCleanup = null;
2792
2802
  this.foregroundCleanup = null;
2793
2803
  this.pendingHandlersConfig = null;
@@ -2807,6 +2817,7 @@ var init_NotificationsManager = __esm({
2807
2817
  this.secureStorage = deps.secureStorage;
2808
2818
  this.getDeviceId = deps.getDeviceId;
2809
2819
  if (deps.getDistinctId) this.getDistinctId = deps.getDistinctId;
2820
+ if (deps.environment) this.environment = deps.environment;
2810
2821
  }
2811
2822
  async initialize(config) {
2812
2823
  this.applyConfig(config);
@@ -2849,7 +2860,8 @@ var init_NotificationsManager = __esm({
2849
2860
  appKey: this.appKey,
2850
2861
  getDeviceId: this.getDeviceId,
2851
2862
  getDistinctId: this.getDistinctId,
2852
- debug: this.debug
2863
+ debug: this.debug,
2864
+ environment: this.environment
2853
2865
  };
2854
2866
  }
2855
2867
  async doRegisterToken(token) {
@@ -3910,6 +3922,32 @@ var init_IAPTransactionEmitter = __esm({
3910
3922
  if (this.getDebug()) console.log("[Paywallo PURCHASE] transaction event emission failed", err);
3911
3923
  }
3912
3924
  }
3925
+ /**
3926
+ * Emits `checkout_started` quando o usuário inicia a compra (toca em comprar),
3927
+ * antes do StoreKit/Billing abrir — conta mesmo que a sheet nativa seja cancelada.
3928
+ * Vira InitiateCheckout no Meta/TikTok. Fire-and-forget — uma falha de tracking
3929
+ * nunca pode travar a compra.
3930
+ */
3931
+ async emitCheckoutStarted(productId) {
3932
+ try {
3933
+ const apiClient = this.getApiClientFn();
3934
+ if (!apiClient) return;
3935
+ const distinctId = PaywalloClient.getDistinctId();
3936
+ if (!distinctId) return;
3937
+ const product = this.getProductFromCache(productId);
3938
+ const properties = {
3939
+ product_id: productId,
3940
+ amount: product?.priceValue ?? 0,
3941
+ currency: product?.currency ?? "USD"
3942
+ };
3943
+ await apiClient.trackEvent("checkout_started", distinctId, properties, void 0, {
3944
+ priority: "critical"
3945
+ });
3946
+ if (this.getDebug()) console.log("[Paywallo PURCHASE] emitted checkout_started", productId);
3947
+ } catch (err) {
3948
+ if (this.getDebug()) console.log("[Paywallo PURCHASE] checkout_started emission failed", err);
3949
+ }
3950
+ }
3913
3951
  /**
3914
3952
  * Forwards a native transaction update to the EventBatcher.
3915
3953
  * Public so tests can exercise the wiring without a fake NativeEventEmitter.
@@ -3932,9 +3970,9 @@ var init_IAPTransactionEmitter = __esm({
3932
3970
  product_id: update.productId
3933
3971
  };
3934
3972
  if (update.type !== "canceled") {
3935
- const cached = this.getProductFromCache(update.productId);
3936
- const amount = update.amount ?? cached?.priceValue;
3937
- const currency = update.currency ?? cached?.currency;
3973
+ const cached2 = this.getProductFromCache(update.productId);
3974
+ const amount = update.amount ?? cached2?.priceValue;
3975
+ const currency = update.currency ?? cached2?.currency;
3938
3976
  if (typeof amount === "number" && amount > 0) properties.amount = amount;
3939
3977
  if (currency) properties.currency = currency;
3940
3978
  }
@@ -4113,10 +4151,10 @@ var init_OfflineQueueStorage = __esm({
4113
4151
  init_NativeStorage();
4114
4152
  init_QueueJournal();
4115
4153
  OfflineQueueStorage = class {
4116
- constructor(config, emit, log) {
4154
+ constructor(config, emit, log2) {
4117
4155
  this.config = config;
4118
4156
  this.emit = emit;
4119
- this.log = log;
4157
+ this.log = log2;
4120
4158
  }
4121
4159
  async loadFromStorage(queue) {
4122
4160
  const { merged, recovered, hadJournal } = await mergeJournalIntoMain(
@@ -4723,12 +4761,25 @@ var init_QueueProcessor = __esm({
4723
4761
  const freshHeaders = this.config.getFreshHeaders?.() ?? {};
4724
4762
  const headers = { ...persistedHeaders, ...freshHeaders };
4725
4763
  this.log("Sending event batch", { size: batch.length, batchIndex: Math.floor(i / BATCH_SIZE) });
4726
- const response = await this.httpClient.request("/sdk/ingest/batch", {
4727
- method: "POST",
4728
- body: { events: bodies },
4729
- headers,
4730
- skipRetry: false
4731
- });
4764
+ let response;
4765
+ try {
4766
+ response = await this.httpClient.request("/sdk/ingest/batch", {
4767
+ method: "POST",
4768
+ body: { events: bodies },
4769
+ headers,
4770
+ skipRetry: false
4771
+ });
4772
+ } catch (error) {
4773
+ for (const item of batch) {
4774
+ retryIds.add(item.id);
4775
+ }
4776
+ console.error("[Paywallo:QUEUE] event batch network error \u2014 items will retry", { size: batch.length, error: error instanceof Error ? error.message : String(error) });
4777
+ this.log("Event batch threw network error - will retry", {
4778
+ size: batch.length,
4779
+ error: error instanceof Error ? error.message : String(error)
4780
+ });
4781
+ continue;
4782
+ }
4732
4783
  if (response.ok) {
4733
4784
  for (const item of batch) {
4734
4785
  await offlineQueue.removeItem(item.id);
@@ -4740,14 +4791,25 @@ var init_QueueProcessor = __esm({
4740
4791
  await offlineQueue.removeItem(item.id);
4741
4792
  failed++;
4742
4793
  }
4794
+ const responseError = typeof response.data === "object" && response.data !== null && "error" in response.data ? response.data.error : void 0;
4795
+ const isDuplicate = typeof responseError === "string" && responseError.toUpperCase().includes("DUPLICATE");
4796
+ if (isDuplicate) {
4797
+ if (this.config.debug) {
4798
+ console.log("[Paywallo:QUEUE] event batch deduplicated by server \u2014 items dropped (benign)", { status: response.status, size: batch.length, error: responseError });
4799
+ }
4800
+ } else {
4801
+ console.error("[Paywallo:QUEUE] event batch failed PERMANENTLY (4xx) \u2014 items dropped", { status: response.status, size: batch.length, responseBody: typeof response.data === "string" ? response.data.slice(0, 1e3) : JSON.stringify(response.data).slice(0, 1e3), firstItemBody: typeof batch[0].body === "string" ? batch[0].body.slice(0, 1e3) : JSON.stringify(batch[0].body).slice(0, 1e3) });
4802
+ }
4743
4803
  this.log("Event batch failed permanently (4xx) - removing from queue", {
4744
4804
  status: response.status,
4745
- size: batch.length
4805
+ size: batch.length,
4806
+ isDuplicate
4746
4807
  });
4747
4808
  } else {
4748
4809
  for (const item of batch) {
4749
4810
  retryIds.add(item.id);
4750
4811
  }
4812
+ console.error("[Paywallo:QUEUE] event batch server error (5xx) \u2014 will retry", { status: response.status, size: batch.length });
4751
4813
  this.log("Event batch failed with server error - will retry", {
4752
4814
  status: response.status,
4753
4815
  size: batch.length
@@ -4768,12 +4830,22 @@ var init_QueueProcessor = __esm({
4768
4830
  });
4769
4831
  const persistedHeaders = item.headers ?? {};
4770
4832
  const freshHeaders = this.config.getFreshHeaders?.() ?? {};
4771
- const response = await this.httpClient.request(item.url, {
4772
- method: item.method,
4773
- body: item.body,
4774
- headers: { ...persistedHeaders, ...freshHeaders },
4775
- skipRetry: false
4776
- });
4833
+ let response;
4834
+ try {
4835
+ response = await this.httpClient.request(item.url, {
4836
+ method: item.method,
4837
+ body: item.body,
4838
+ headers: { ...persistedHeaders, ...freshHeaders },
4839
+ skipRetry: false
4840
+ });
4841
+ } catch (error) {
4842
+ console.error("[Paywallo:QUEUE] individual item network error \u2014 will retry", { id: item.id, url: item.url, error: error instanceof Error ? error.message : String(error) });
4843
+ this.log("Request threw network error - will retry", {
4844
+ id: item.id,
4845
+ error: error instanceof Error ? error.message : String(error)
4846
+ });
4847
+ return { success: false, permanent: false };
4848
+ }
4777
4849
  if (response.ok) {
4778
4850
  this.log("Queue item processed successfully", { id: item.id });
4779
4851
  return { success: true, permanent: false };
@@ -5064,6 +5136,7 @@ var init_IAPService = __esm({
5064
5136
  this.purchaseInFlight = productId;
5065
5137
  if (this.debug) console.log(`[Paywallo PURCHASE] starting ${productId}`);
5066
5138
  try {
5139
+ void this.emitter.emitCheckoutStarted(productId);
5067
5140
  const purchase = await nativeStoreKit.purchase(productId);
5068
5141
  if (!purchase) {
5069
5142
  if (this.debug) console.log(`[Paywallo PURCHASE] ${productId} cancelled`);
@@ -5853,10 +5926,10 @@ var init_PlanService = __esm({
5853
5926
  }
5854
5927
  async getCurrentPlan(forceRefresh = false) {
5855
5928
  if (!forceRefresh) {
5856
- const cached = this.cache.get();
5857
- if (cached && !cached.isStale) {
5858
- const current = cached.data.plans.find(
5859
- (p) => p.identifier === cached.data.currentIdentifier
5929
+ const cached2 = this.cache.get();
5930
+ if (cached2 && !cached2.isStale) {
5931
+ const current = cached2.data.plans.find(
5932
+ (p) => p.identifier === cached2.data.currentIdentifier
5860
5933
  );
5861
5934
  return current ?? null;
5862
5935
  }
@@ -5883,8 +5956,8 @@ var init_PlanService = __esm({
5883
5956
  }
5884
5957
  async getAllPlans(forceRefresh = false) {
5885
5958
  if (!forceRefresh) {
5886
- const cached = this.cache.get();
5887
- if (cached && !cached.isStale) return cached.data;
5959
+ const cached2 = this.cache.get();
5960
+ if (cached2 && !cached2.isStale) return cached2.data;
5888
5961
  }
5889
5962
  if (!this.cache.get()) {
5890
5963
  try {
@@ -6002,6 +6075,163 @@ var init_analytics = __esm({
6002
6075
  }
6003
6076
  });
6004
6077
 
6078
+ // src/core/events/schemas.ts
6079
+ var import_zod, lifecycleEventSchema, identifyEventSchema, paywallEventSchema, transactionEventSchema, onboardingEventSchema, notificationEventSchema, customEventSchema, EVENT_SCHEMAS;
6080
+ var init_schemas = __esm({
6081
+ "src/core/events/schemas.ts"() {
6082
+ "use strict";
6083
+ import_zod = require("zod");
6084
+ lifecycleEventSchema = import_zod.z.object({
6085
+ type: import_zod.z.enum([
6086
+ "install",
6087
+ "cold_start",
6088
+ "foreground",
6089
+ "background",
6090
+ "session_start",
6091
+ "session_end"
6092
+ ]),
6093
+ session_id: import_zod.z.string().optional(),
6094
+ duration_s: import_zod.z.number().nonnegative().optional(),
6095
+ first_ever: import_zod.z.boolean().optional(),
6096
+ // Contexto leve transportado em lifecycle (útil pra cold_start/install)
6097
+ app_version: import_zod.z.string().optional(),
6098
+ device_type: import_zod.z.enum(["ios", "android"]).optional(),
6099
+ os_version: import_zod.z.string().optional(),
6100
+ // Timestamps opcionais — o EventBatcher injeta `timestamp` no envelope
6101
+ started_at: import_zod.z.string().optional(),
6102
+ ended_at: import_zod.z.string().optional()
6103
+ }).strict();
6104
+ identifyEventSchema = import_zod.z.object({
6105
+ distinct_id: import_zod.z.string().min(1),
6106
+ email: import_zod.z.email().optional(),
6107
+ traits: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
6108
+ attribution: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
6109
+ device: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
6110
+ }).strict();
6111
+ paywallEventSchema = import_zod.z.object({
6112
+ type: import_zod.z.enum(["viewed", "closed", "purchased"]),
6113
+ paywall_id: import_zod.z.string().min(1),
6114
+ placement: import_zod.z.string().optional(),
6115
+ variant_id: import_zod.z.string().optional(),
6116
+ variant_key: import_zod.z.string().optional(),
6117
+ campaign_id: import_zod.z.string().optional(),
6118
+ // `closed`-specific
6119
+ closed_at: import_zod.z.string().optional(),
6120
+ duration_s: import_zod.z.number().nonnegative().optional(),
6121
+ close_reason: import_zod.z.enum(["dismiss", "cta", "purchase", "error", "timeout"]).optional(),
6122
+ scroll_depth: import_zod.z.number().min(0).max(1).optional(),
6123
+ // `purchased`-specific (mínimo — transaction canônica leva detalhes)
6124
+ product_id: import_zod.z.string().optional()
6125
+ }).strict();
6126
+ transactionEventSchema = import_zod.z.object({
6127
+ type: import_zod.z.enum([
6128
+ "completed",
6129
+ "failed",
6130
+ "refunded",
6131
+ "renewed",
6132
+ "canceled",
6133
+ "expired"
6134
+ ]),
6135
+ tx_id: import_zod.z.string().min(1),
6136
+ amount: import_zod.z.number().optional(),
6137
+ currency: import_zod.z.string().length(3).optional(),
6138
+ product_id: import_zod.z.string().optional(),
6139
+ subscription_id: import_zod.z.string().optional(),
6140
+ paywall_id: import_zod.z.string().optional(),
6141
+ variant_id: import_zod.z.string().optional(),
6142
+ reason: import_zod.z.string().optional()
6143
+ }).strict();
6144
+ onboardingEventSchema = import_zod.z.object({
6145
+ type: import_zod.z.enum(["step", "complete", "drop"]),
6146
+ step_index: import_zod.z.number().int().nonnegative().optional(),
6147
+ step_name: import_zod.z.string().optional(),
6148
+ variant_key: import_zod.z.string().optional(),
6149
+ time_on_prev_s: import_zod.z.number().nonnegative().optional()
6150
+ }).strict();
6151
+ notificationEventSchema = import_zod.z.object({
6152
+ type: import_zod.z.enum(["delivered", "displayed", "clicked", "dismissed"]),
6153
+ notification_id: import_zod.z.string().optional(),
6154
+ campaign_id: import_zod.z.string().optional(),
6155
+ variant_key: import_zod.z.string().nullable().optional(),
6156
+ message_id: import_zod.z.string().optional(),
6157
+ device_id: import_zod.z.string().optional(),
6158
+ push_platform: import_zod.z.enum(["ios", "android"]).optional(),
6159
+ timezone: import_zod.z.string().optional(),
6160
+ failure_reason: import_zod.z.string().optional(),
6161
+ app_user_id: import_zod.z.string().optional()
6162
+ }).strict();
6163
+ customEventSchema = import_zod.z.object({
6164
+ name: import_zod.z.string().min(1).regex(
6165
+ /^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/,
6166
+ "custom event name must be snake_case or $reserved"
6167
+ ),
6168
+ props: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
6169
+ }).strict();
6170
+ EVENT_SCHEMAS = {
6171
+ lifecycle: lifecycleEventSchema,
6172
+ identify: identifyEventSchema,
6173
+ paywall: paywallEventSchema,
6174
+ transaction: transactionEventSchema,
6175
+ onboarding: onboardingEventSchema,
6176
+ notification: notificationEventSchema,
6177
+ custom: customEventSchema
6178
+ };
6179
+ }
6180
+ });
6181
+
6182
+ // src/core/events/eventFamilies.ts
6183
+ function isDeprecatedEvent(eventName) {
6184
+ return DEPRECATED_EVENT_NAMES.includes(eventName);
6185
+ }
6186
+ function isCanonicalFamily(name) {
6187
+ return CANONICAL_FAMILIES.includes(name);
6188
+ }
6189
+ function detectFamily(eventName) {
6190
+ if (isCanonicalFamily(eventName) && eventName !== "custom") {
6191
+ return eventName;
6192
+ }
6193
+ return "custom";
6194
+ }
6195
+ function validateEvent(eventName, properties) {
6196
+ const family = detectFamily(eventName);
6197
+ const schema = EVENT_SCHEMAS[family];
6198
+ const input = family === "custom" ? { name: eventName, ...properties ? { props: properties } : {} } : properties ?? {};
6199
+ const parsed = schema.safeParse(input);
6200
+ if (parsed.success) {
6201
+ return { ok: true, family, data: parsed.data };
6202
+ }
6203
+ return { ok: false, family, error: parsed.error };
6204
+ }
6205
+ function formatValidationError(error) {
6206
+ return error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
6207
+ }
6208
+ var CANONICAL_FAMILIES, DEPRECATED_EVENT_NAMES;
6209
+ var init_eventFamilies = __esm({
6210
+ "src/core/events/eventFamilies.ts"() {
6211
+ "use strict";
6212
+ init_schemas();
6213
+ CANONICAL_FAMILIES = [
6214
+ "lifecycle",
6215
+ "identify",
6216
+ "paywall",
6217
+ "transaction",
6218
+ "onboarding",
6219
+ "notification",
6220
+ "custom"
6221
+ ];
6222
+ DEPRECATED_EVENT_NAMES = [
6223
+ "$paywall_purchased",
6224
+ "$paywall_product_selected",
6225
+ "$core_action",
6226
+ "$campaign_impression",
6227
+ "$app_open",
6228
+ "$app_background",
6229
+ "$app_foreground",
6230
+ "session.end"
6231
+ ];
6232
+ }
6233
+ });
6234
+
6005
6235
  // src/core/PaywalloAnalytics.ts
6006
6236
  function isValidEventName(name) {
6007
6237
  if (name.startsWith("$")) return /^\$[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(name);
@@ -6010,7 +6240,7 @@ function isValidEventName(name) {
6010
6240
  async function trackEvent(apiClient, state, eventName, options) {
6011
6241
  const distinctId = identityManager.getDistinctId();
6012
6242
  if (!distinctId) {
6013
- if (state.config?.debug) console.log(`[Paywallo TRACK] ${eventName} skipped (no distinctId)`);
6243
+ console.error("[Paywallo:DROP] event dropped \u2014 no distinctId available yet", { eventName });
6014
6244
  return;
6015
6245
  }
6016
6246
  if (!isValidEventName(eventName)) {
@@ -6019,10 +6249,14 @@ async function trackEvent(apiClient, state, eventName, options) {
6019
6249
  }
6020
6250
  if (state.config?.debug && eventName.startsWith("$purchase")) console.log(`[Paywallo TRACK] ${eventName}`, options?.properties ?? {});
6021
6251
  const sessionId = sessionManager.getSessionId();
6252
+ const family = detectFamily(eventName);
6253
+ const isCustomFamily = family === "custom";
6254
+ const identityProps = isCustomFamily ? identityManager.getProperties() : {};
6255
+ const sessionProp = isCustomFamily && sessionId ? { sessionId } : {};
6022
6256
  await apiClient.trackEvent(
6023
6257
  eventName,
6024
6258
  distinctId,
6025
- { ...identityManager.getProperties(), ...options?.properties, ...sessionId && { sessionId } },
6259
+ { ...identityProps, ...options?.properties, ...sessionProp },
6026
6260
  options?.timestamp,
6027
6261
  options?.priority ? { priority: options.priority } : void 0
6028
6262
  );
@@ -6052,6 +6286,7 @@ var init_PaywalloAnalytics = __esm({
6052
6286
  init_identity();
6053
6287
  init_session();
6054
6288
  init_ClientError();
6289
+ init_eventFamilies();
6055
6290
  }
6056
6291
  });
6057
6292
 
@@ -6316,49 +6551,49 @@ var init_SubscriptionCache = __esm({
6316
6551
  await this.storage.remove(keyFor(distinctId));
6317
6552
  return null;
6318
6553
  }
6319
- const cached = parsed;
6320
- if (cached.data.subscription?.expiresAt) {
6321
- cached.data.subscription.expiresAt = new Date(cached.data.subscription.expiresAt);
6554
+ const cached2 = parsed;
6555
+ if (cached2.data.subscription?.expiresAt) {
6556
+ cached2.data.subscription.expiresAt = new Date(cached2.data.subscription.expiresAt);
6322
6557
  }
6323
- if (cached.data.subscription?.originalPurchaseDate) {
6324
- cached.data.subscription.originalPurchaseDate = new Date(
6325
- cached.data.subscription.originalPurchaseDate
6558
+ if (cached2.data.subscription?.originalPurchaseDate) {
6559
+ cached2.data.subscription.originalPurchaseDate = new Date(
6560
+ cached2.data.subscription.originalPurchaseDate
6326
6561
  );
6327
6562
  }
6328
- if (cached.data.subscription?.latestPurchaseDate) {
6329
- cached.data.subscription.latestPurchaseDate = new Date(
6330
- cached.data.subscription.latestPurchaseDate
6563
+ if (cached2.data.subscription?.latestPurchaseDate) {
6564
+ cached2.data.subscription.latestPurchaseDate = new Date(
6565
+ cached2.data.subscription.latestPurchaseDate
6331
6566
  );
6332
6567
  }
6333
- if (cached.data.subscription?.cancellationDate) {
6334
- cached.data.subscription.cancellationDate = new Date(
6335
- cached.data.subscription.cancellationDate
6568
+ if (cached2.data.subscription?.cancellationDate) {
6569
+ cached2.data.subscription.cancellationDate = new Date(
6570
+ cached2.data.subscription.cancellationDate
6336
6571
  );
6337
6572
  }
6338
- if (cached.data.subscription?.gracePeriodExpiresAt) {
6339
- cached.data.subscription.gracePeriodExpiresAt = new Date(
6340
- cached.data.subscription.gracePeriodExpiresAt
6573
+ if (cached2.data.subscription?.gracePeriodExpiresAt) {
6574
+ cached2.data.subscription.gracePeriodExpiresAt = new Date(
6575
+ cached2.data.subscription.gracePeriodExpiresAt
6341
6576
  );
6342
6577
  }
6343
- cached.isStale = this.isExpired(cached);
6344
- if (!cached.isStale) {
6345
- this.memoryCache.set(distinctId, cached);
6578
+ cached2.isStale = this.isExpired(cached2);
6579
+ if (!cached2.isStale) {
6580
+ this.memoryCache.set(distinctId, cached2);
6346
6581
  }
6347
- return cached;
6582
+ return cached2;
6348
6583
  } catch {
6349
6584
  return null;
6350
6585
  }
6351
6586
  }
6352
6587
  async set(distinctId, data) {
6353
6588
  if (!distinctId) return;
6354
- const cached = {
6589
+ const cached2 = {
6355
6590
  data,
6356
6591
  cachedAt: Date.now(),
6357
6592
  isStale: false
6358
6593
  };
6359
- this.memoryCache.set(distinctId, cached);
6594
+ this.memoryCache.set(distinctId, cached2);
6360
6595
  try {
6361
- await this.storage.set(keyFor(distinctId), JSON.stringify(cached));
6596
+ await this.storage.set(keyFor(distinctId), JSON.stringify(cached2));
6362
6597
  await this.addToIndex(distinctId);
6363
6598
  } catch {
6364
6599
  }
@@ -6402,8 +6637,8 @@ var init_SubscriptionCache = __esm({
6402
6637
  ...allKeys.map((k) => this.storage.remove(keyFor(k)))
6403
6638
  ]);
6404
6639
  }
6405
- isExpired(cached) {
6406
- return Date.now() - cached.cachedAt > this.ttl;
6640
+ isExpired(cached2) {
6641
+ return Date.now() - cached2.cachedAt > this.ttl;
6407
6642
  }
6408
6643
  setTTL(ttl) {
6409
6644
  this.ttl = ttl;
@@ -6459,9 +6694,9 @@ var init_SubscriptionManager = __esm({
6459
6694
  return this.getEmptyStatus();
6460
6695
  }
6461
6696
  if (!forceRefresh) {
6462
- const cached = await this.cache.get(this.cacheKey());
6463
- if (cached && !cached.isStale) {
6464
- return cached.data;
6697
+ const cached2 = await this.cache.get(this.cacheKey());
6698
+ if (cached2 && !cached2.isStale) {
6699
+ return cached2.data;
6465
6700
  }
6466
6701
  }
6467
6702
  try {
@@ -6471,9 +6706,9 @@ var init_SubscriptionManager = __esm({
6471
6706
  return status;
6472
6707
  } catch (error) {
6473
6708
  this.log("Failed to fetch subscription status", error);
6474
- const cached = await this.cache.get(this.cacheKey());
6475
- if (cached) {
6476
- return cached.data;
6709
+ const cached2 = await this.cache.get(this.cacheKey());
6710
+ if (cached2) {
6711
+ return cached2.data;
6477
6712
  }
6478
6713
  return this.getEmptyStatus();
6479
6714
  }
@@ -6888,163 +7123,6 @@ var init_ApiClientQueue = __esm({
6888
7123
  }
6889
7124
  });
6890
7125
 
6891
- // src/core/events/schemas.ts
6892
- var import_zod, lifecycleEventSchema, identifyEventSchema, paywallEventSchema, transactionEventSchema, onboardingEventSchema, notificationEventSchema, customEventSchema, EVENT_SCHEMAS;
6893
- var init_schemas = __esm({
6894
- "src/core/events/schemas.ts"() {
6895
- "use strict";
6896
- import_zod = require("zod");
6897
- lifecycleEventSchema = import_zod.z.object({
6898
- type: import_zod.z.enum([
6899
- "install",
6900
- "cold_start",
6901
- "foreground",
6902
- "background",
6903
- "session_start",
6904
- "session_end"
6905
- ]),
6906
- session_id: import_zod.z.string().optional(),
6907
- duration_s: import_zod.z.number().nonnegative().optional(),
6908
- first_ever: import_zod.z.boolean().optional(),
6909
- // Contexto leve transportado em lifecycle (útil pra cold_start/install)
6910
- app_version: import_zod.z.string().optional(),
6911
- device_type: import_zod.z.enum(["ios", "android"]).optional(),
6912
- os_version: import_zod.z.string().optional(),
6913
- // Timestamps opcionais — o EventBatcher injeta `timestamp` no envelope
6914
- started_at: import_zod.z.string().optional(),
6915
- ended_at: import_zod.z.string().optional()
6916
- }).strict();
6917
- identifyEventSchema = import_zod.z.object({
6918
- distinct_id: import_zod.z.string().min(1),
6919
- email: import_zod.z.email().optional(),
6920
- traits: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
6921
- attribution: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
6922
- device: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
6923
- }).strict();
6924
- paywallEventSchema = import_zod.z.object({
6925
- type: import_zod.z.enum(["viewed", "closed", "purchased"]),
6926
- paywall_id: import_zod.z.string().min(1),
6927
- placement: import_zod.z.string().optional(),
6928
- variant_id: import_zod.z.string().optional(),
6929
- variant_key: import_zod.z.string().optional(),
6930
- campaign_id: import_zod.z.string().optional(),
6931
- // `closed`-specific
6932
- closed_at: import_zod.z.string().optional(),
6933
- duration_s: import_zod.z.number().nonnegative().optional(),
6934
- close_reason: import_zod.z.enum(["dismiss", "cta", "purchase", "error", "timeout"]).optional(),
6935
- scroll_depth: import_zod.z.number().min(0).max(1).optional(),
6936
- // `purchased`-specific (mínimo — transaction canônica leva detalhes)
6937
- product_id: import_zod.z.string().optional()
6938
- }).strict();
6939
- transactionEventSchema = import_zod.z.object({
6940
- type: import_zod.z.enum([
6941
- "completed",
6942
- "failed",
6943
- "refunded",
6944
- "renewed",
6945
- "canceled",
6946
- "expired"
6947
- ]),
6948
- tx_id: import_zod.z.string().min(1),
6949
- amount: import_zod.z.number().optional(),
6950
- currency: import_zod.z.string().length(3).optional(),
6951
- product_id: import_zod.z.string().optional(),
6952
- subscription_id: import_zod.z.string().optional(),
6953
- paywall_id: import_zod.z.string().optional(),
6954
- variant_id: import_zod.z.string().optional(),
6955
- reason: import_zod.z.string().optional()
6956
- }).strict();
6957
- onboardingEventSchema = import_zod.z.object({
6958
- type: import_zod.z.enum(["step", "complete", "drop"]),
6959
- step_index: import_zod.z.number().int().nonnegative().optional(),
6960
- step_name: import_zod.z.string().optional(),
6961
- variant_key: import_zod.z.string().optional(),
6962
- time_on_prev_s: import_zod.z.number().nonnegative().optional()
6963
- }).strict();
6964
- notificationEventSchema = import_zod.z.object({
6965
- type: import_zod.z.enum(["delivered", "displayed", "clicked", "dismissed"]),
6966
- notification_id: import_zod.z.string().optional(),
6967
- campaign_id: import_zod.z.string().optional(),
6968
- variant_key: import_zod.z.string().nullable().optional(),
6969
- message_id: import_zod.z.string().optional(),
6970
- device_id: import_zod.z.string().optional(),
6971
- push_platform: import_zod.z.enum(["ios", "android"]).optional(),
6972
- timezone: import_zod.z.string().optional(),
6973
- failure_reason: import_zod.z.string().optional(),
6974
- app_user_id: import_zod.z.string().optional()
6975
- }).strict();
6976
- customEventSchema = import_zod.z.object({
6977
- name: import_zod.z.string().min(1).regex(
6978
- /^(\$[a-zA-Z0-9_]+|[a-z][a-z0-9_]*)$/,
6979
- "custom event name must be snake_case or $reserved"
6980
- ),
6981
- props: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
6982
- }).strict();
6983
- EVENT_SCHEMAS = {
6984
- lifecycle: lifecycleEventSchema,
6985
- identify: identifyEventSchema,
6986
- paywall: paywallEventSchema,
6987
- transaction: transactionEventSchema,
6988
- onboarding: onboardingEventSchema,
6989
- notification: notificationEventSchema,
6990
- custom: customEventSchema
6991
- };
6992
- }
6993
- });
6994
-
6995
- // src/core/events/eventFamilies.ts
6996
- function isDeprecatedEvent(eventName) {
6997
- return DEPRECATED_EVENT_NAMES.includes(eventName);
6998
- }
6999
- function isCanonicalFamily(name) {
7000
- return CANONICAL_FAMILIES.includes(name);
7001
- }
7002
- function detectFamily(eventName) {
7003
- if (isCanonicalFamily(eventName) && eventName !== "custom") {
7004
- return eventName;
7005
- }
7006
- return "custom";
7007
- }
7008
- function validateEvent(eventName, properties) {
7009
- const family = detectFamily(eventName);
7010
- const schema = EVENT_SCHEMAS[family];
7011
- const input = family === "custom" ? { name: eventName, ...properties ? { props: properties } : {} } : properties ?? {};
7012
- const parsed = schema.safeParse(input);
7013
- if (parsed.success) {
7014
- return { ok: true, family, data: parsed.data };
7015
- }
7016
- return { ok: false, family, error: parsed.error };
7017
- }
7018
- function formatValidationError(error) {
7019
- return error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
7020
- }
7021
- var CANONICAL_FAMILIES, DEPRECATED_EVENT_NAMES;
7022
- var init_eventFamilies = __esm({
7023
- "src/core/events/eventFamilies.ts"() {
7024
- "use strict";
7025
- init_schemas();
7026
- CANONICAL_FAMILIES = [
7027
- "lifecycle",
7028
- "identify",
7029
- "paywall",
7030
- "transaction",
7031
- "onboarding",
7032
- "notification",
7033
- "custom"
7034
- ];
7035
- DEPRECATED_EVENT_NAMES = [
7036
- "$paywall_purchased",
7037
- "$paywall_product_selected",
7038
- "$core_action",
7039
- "$campaign_impression",
7040
- "$app_open",
7041
- "$app_background",
7042
- "$app_foreground",
7043
- "session.end"
7044
- ];
7045
- }
7046
- });
7047
-
7048
7126
  // src/core/eventEnvelopeV2.ts
7049
7127
  function buildV2Envelope(events, providerContext) {
7050
7128
  const context = { ...providerContext };
@@ -7072,6 +7150,9 @@ function buildV2Envelope(events, providerContext) {
7072
7150
  if (context.distinct_id === void 0 && events.length > 0) {
7073
7151
  context.distinct_id = events[0].distinctId;
7074
7152
  }
7153
+ if (!context.distinct_id || context.distinct_id.length === 0) {
7154
+ console.error("[Paywallo:ENVELOPE] distinct_id is empty after resolution \u2014 envelope will fail schema", { providerContext, eventCount: events.length, firstEventName: events[0]?.eventName });
7155
+ }
7075
7156
  return { context, events: v2Events };
7076
7157
  }
7077
7158
  function resolveProviderContext(provider) {
@@ -7125,12 +7206,18 @@ var init_eventEnvelopeV2 = __esm({
7125
7206
  session_id: import_zod2.z.string().optional(),
7126
7207
  device_id: import_zod2.z.string().optional(),
7127
7208
  app_version: import_zod2.z.string().optional(),
7209
+ app_build: import_zod2.z.string().optional(),
7210
+ bundle_id: import_zod2.z.string().optional(),
7128
7211
  sdk_version: import_zod2.z.string().optional(),
7129
7212
  platform: import_zod2.z.string().optional(),
7130
7213
  os_version: import_zod2.z.string().optional(),
7131
7214
  device_model: import_zod2.z.string().optional(),
7132
7215
  timezone: import_zod2.z.string().optional(),
7133
7216
  locale: import_zod2.z.string().optional(),
7217
+ carrier: import_zod2.z.string().optional(),
7218
+ screen_width: import_zod2.z.number().optional(),
7219
+ screen_height: import_zod2.z.number().optional(),
7220
+ screen_density: import_zod2.z.number().optional(),
7134
7221
  attribution: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional(),
7135
7222
  ids: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional()
7136
7223
  });
@@ -7143,12 +7230,21 @@ var init_eventEnvelopeV2 = __esm({
7143
7230
  session_id: ["session_id", "sessionId"],
7144
7231
  device_id: ["device_id", "deviceId"],
7145
7232
  app_version: ["app_version", "appVersion"],
7233
+ // F2.2 — alias só snake_case canônico: campos vêm do context provider, não
7234
+ // de properties de evento. Evita promover (e deletar) chaves do payload do
7235
+ // $app_installed que o server lê (screenWidth etc.).
7236
+ app_build: ["app_build"],
7237
+ bundle_id: ["bundle_id"],
7146
7238
  sdk_version: ["sdk_version", "sdkVersion"],
7147
7239
  platform: ["platform"],
7148
7240
  os_version: ["os_version", "osVersion", "systemVersion"],
7149
7241
  device_model: ["device_model", "deviceModel", "model"],
7150
7242
  timezone: ["timezone", "timeZone"],
7151
7243
  locale: ["locale"],
7244
+ carrier: ["carrier_ctx"],
7245
+ screen_width: ["screen_width"],
7246
+ screen_height: ["screen_height"],
7247
+ screen_density: ["screen_density"],
7152
7248
  attribution: ["attribution"],
7153
7249
  ids: ["ids"]
7154
7250
  };
@@ -7275,8 +7371,8 @@ var init_EventBatcher = __esm({
7275
7371
  for (const event of drained) {
7276
7372
  try {
7277
7373
  await this.postBatch([event], `event_critical:${event.eventName}`, "critical");
7278
- } catch {
7279
- if (this.debug) console.log(`[Paywallo] critical event ${event.eventName} post failed`);
7374
+ } catch (err) {
7375
+ console.error("[Paywallo:CRITICAL] critical event post failed", { eventName: event.eventName, error: err instanceof Error ? err.message : String(err) });
7280
7376
  }
7281
7377
  }
7282
7378
  }
@@ -7289,8 +7385,8 @@ var init_EventBatcher = __esm({
7289
7385
  }
7290
7386
  try {
7291
7387
  await this.postBatch(batch, `event_batch:${batch.length}`);
7292
- } catch {
7293
- if (this.debug) console.log(`[Paywallo] normal batch post failed \u2014 OfflineQueue will handle durability`);
7388
+ } catch (err) {
7389
+ console.error("[Paywallo:NORMAL] batch post failed \u2014 relying on OfflineQueue", { batchSize: batch.length, error: err instanceof Error ? err.message : String(err) });
7294
7390
  }
7295
7391
  }
7296
7392
  async postBatch(events, label, priority = "normal") {
@@ -7305,6 +7401,7 @@ var init_EventBatcher = __esm({
7305
7401
  const parsed = v2EnvelopeSchema.safeParse(envelope);
7306
7402
  if (!parsed.success) {
7307
7403
  if (this.debug) console.log("[Paywallo EVENTS] V2 envelope schema validation failed:", parsed.error.issues);
7404
+ console.error("[Paywallo:CRITICAL] V2 envelope schema validation failed", { issues: parsed.error.issues, eventCount: events.length, firstEventName: events[0]?.eventName, context: providerContext });
7308
7405
  throw parsed.error;
7309
7406
  }
7310
7407
  await this.post(V2_BATCH_ENDPOINT, parsed.data, label, priority);
@@ -7887,7 +7984,8 @@ function setupNotifications(apiClient, config, environment, eventPipeline) {
7887
7984
  httpClient: apiClient.getHttpClient(),
7888
7985
  secureStorage: new SecureStorage(config.debug),
7889
7986
  getDeviceId: () => identityManager.getDeviceId(),
7890
- getDistinctId: () => identityManager.getDistinctId()
7987
+ getDistinctId: () => identityManager.getDistinctId(),
7988
+ environment: environment === "Sandbox" ? "sandbox" : "production"
7891
7989
  });
7892
7990
  notificationsManager.setupHandlers({ eventBatcher: eventPipeline });
7893
7991
  notificationsManager.initialize({
@@ -7971,6 +8069,36 @@ var init_PaywalloState = __esm({
7971
8069
  }
7972
8070
  });
7973
8071
 
8072
+ // src/utils/detectEnvironment.ts
8073
+ async function detectEnvironment() {
8074
+ if (cached !== null) return cached;
8075
+ if (typeof globalThis.__DEV__ !== "undefined" && globalThis.__DEV__) {
8076
+ cached = "sandbox";
8077
+ return cached;
8078
+ }
8079
+ if (!nativeModule) {
8080
+ cached = "production";
8081
+ return cached;
8082
+ }
8083
+ try {
8084
+ const result = await nativeModule.detect();
8085
+ cached = result === "sandbox" ? "sandbox" : "production";
8086
+ return cached;
8087
+ } catch {
8088
+ cached = "production";
8089
+ return cached;
8090
+ }
8091
+ }
8092
+ var import_react_native24, nativeModule, cached;
8093
+ var init_detectEnvironment = __esm({
8094
+ "src/utils/detectEnvironment.ts"() {
8095
+ "use strict";
8096
+ import_react_native24 = require("react-native");
8097
+ nativeModule = import_react_native24.NativeModules.PaywalloEnv ?? null;
8098
+ cached = null;
8099
+ }
8100
+ });
8101
+
7974
8102
  // src/core/PaywalloInitializer.ts
7975
8103
  async function initWithRetry(state, config) {
7976
8104
  while (state.initAttempts <= MAX_INIT_RETRIES) {
@@ -8050,7 +8178,13 @@ async function resolveSessionFlags(state, keys, apiClient, distinctId) {
8050
8178
  }
8051
8179
  }
8052
8180
  async function doInit(state, config) {
8053
- const environment = config.environment ?? (typeof __DEV__ !== "undefined" && __DEV__ ? "Sandbox" : "Production");
8181
+ let environment;
8182
+ if (config.environment) {
8183
+ environment = config.environment;
8184
+ } else {
8185
+ const detected = await detectEnvironment();
8186
+ environment = detected === "sandbox" ? "Sandbox" : "Production";
8187
+ }
8054
8188
  const offlineQueueEnabled = config.offlineQueueEnabled !== false;
8055
8189
  await Promise.all([
8056
8190
  networkMonitor.initialize({ debug: config.debug }),
@@ -8138,15 +8272,31 @@ async function doInit(state, config) {
8138
8272
  timezone = opts.timeZone || void 0;
8139
8273
  } catch {
8140
8274
  }
8275
+ let screenWidth;
8276
+ let screenHeight;
8277
+ let screenDensity;
8278
+ try {
8279
+ const screen = import_react_native25.Dimensions.get("screen");
8280
+ screenWidth = Math.round(screen.width) || void 0;
8281
+ screenHeight = Math.round(screen.height) || void 0;
8282
+ screenDensity = import_react_native25.PixelRatio.get() || void 0;
8283
+ } catch {
8284
+ }
8141
8285
  return {
8142
8286
  distinct_id: identityManager.getDistinctId() || void 0,
8143
8287
  device_id: identityManager.getDeviceId() ?? void 0,
8144
8288
  session_id: sessionManager.getSessionId() ?? void 0,
8145
8289
  app_version: device?.appVersion && device.appVersion !== "0.0.0" ? device.appVersion : void 0,
8290
+ app_build: device?.buildNumber && device.buildNumber !== "0" ? device.buildNumber : void 0,
8291
+ bundle_id: device?.bundleId || void 0,
8146
8292
  os_version: device?.systemVersion && device.systemVersion !== "0.0.0" ? device.systemVersion : void 0,
8147
8293
  device_model: device?.modelId || device?.model || void 0,
8148
8294
  timezone,
8149
8295
  locale,
8296
+ carrier: device?.carrier || void 0,
8297
+ screen_width: screenWidth,
8298
+ screen_height: screenHeight,
8299
+ screen_density: screenDensity,
8150
8300
  ...attribution && Object.keys(attribution).length > 0 && { attribution },
8151
8301
  ...ids && Object.keys(ids).length > 0 && { ids }
8152
8302
  };
@@ -8196,9 +8346,11 @@ async function doInit(state, config) {
8196
8346
  setupAutoPreload(state, apiClient, config, distinctId);
8197
8347
  startBatchPreload(apiClient, config.debug);
8198
8348
  }
8349
+ var import_react_native25;
8199
8350
  var init_PaywalloInitializer = __esm({
8200
8351
  "src/core/PaywalloInitializer.ts"() {
8201
8352
  "use strict";
8353
+ import_react_native25 = require("react-native");
8202
8354
  init_identity();
8203
8355
  init_AdvertisingIdManager();
8204
8356
  init_DeepLinkAttributionCapture();
@@ -8218,6 +8370,7 @@ var init_PaywalloInitializer = __esm({
8218
8370
  init_PaywalloState();
8219
8371
  init_queue();
8220
8372
  init_NativeStorage();
8373
+ init_detectEnvironment();
8221
8374
  }
8222
8375
  });
8223
8376
 
@@ -8622,7 +8775,7 @@ __export(OfferingService_exports, {
8622
8775
  offeringService: () => offeringService
8623
8776
  });
8624
8777
  function resolveProductId(raw) {
8625
- const isIos = import_react_native24.Platform.OS === "ios";
8778
+ const isIos = import_react_native26.Platform.OS === "ios";
8626
8779
  const productId = isIos ? raw.apple_product_id : raw.google_product_id;
8627
8780
  return productId ?? "";
8628
8781
  }
@@ -8641,7 +8794,7 @@ function mapProduct2(raw) {
8641
8794
  };
8642
8795
  }
8643
8796
  function pickPlatformProduct(products) {
8644
- const isIos = import_react_native24.Platform.OS === "ios";
8797
+ const isIos = import_react_native26.Platform.OS === "ios";
8645
8798
  const match = products.find((p) => isIos ? p.apple_product_id : p.google_product_id);
8646
8799
  return match ?? products[0] ?? null;
8647
8800
  }
@@ -8657,11 +8810,11 @@ function mapOffering(raw) {
8657
8810
  product: mapProduct2(rawProduct)
8658
8811
  };
8659
8812
  }
8660
- var import_react_native24, OFFERINGS_CACHE_KEY, DEFAULT_TTL3, OfferingService, offeringService;
8813
+ var import_react_native26, OFFERINGS_CACHE_KEY, DEFAULT_TTL3, OfferingService, offeringService;
8661
8814
  var init_OfferingService = __esm({
8662
8815
  "src/domains/offering/OfferingService.ts"() {
8663
8816
  "use strict";
8664
- import_react_native24 = require("react-native");
8817
+ import_react_native26 = require("react-native");
8665
8818
  init_SecureStorage();
8666
8819
  OFFERINGS_CACHE_KEY = "@paywallo:offerings_cache";
8667
8820
  DEFAULT_TTL3 = 5 * 60 * 1e3;
@@ -8675,8 +8828,8 @@ var init_OfferingService = __esm({
8675
8828
  this.config = config;
8676
8829
  }
8677
8830
  async getOfferings(ids) {
8678
- const cached = this.getMemCache();
8679
- if (cached && !cached.isStale) return this.filterByIds(cached.data, ids);
8831
+ const cached2 = this.getMemCache();
8832
+ if (cached2 && !cached2.isStale) return this.filterByIds(cached2.data, ids);
8680
8833
  if (!this.cache) {
8681
8834
  try {
8682
8835
  const stored = await secureStorage.get(OFFERINGS_CACHE_KEY);
@@ -9292,9 +9445,10 @@ function useSubscription() {
9292
9445
 
9293
9446
  // src/PaywalloProvider.tsx
9294
9447
  var import_react25 = require("react");
9295
- var import_react_native25 = require("react-native");
9448
+ var import_react_native27 = require("react-native");
9296
9449
  init_PaywalloClient();
9297
9450
  init_SecureStorage();
9451
+ init_NativeStorage();
9298
9452
  init_campaign();
9299
9453
  init_paywall();
9300
9454
  init_paywall();
@@ -9326,12 +9480,13 @@ async function detectOsVersion() {
9326
9480
  }
9327
9481
 
9328
9482
  // src/hooks/useAppAutoEvents.ts
9329
- var FIRST_SEEN_KEY = "@paywallo:firstSeen";
9483
+ var FIRST_SEEN_KEY = "@paywallo:firstSeen_v226";
9330
9484
  var DEBOUNCE_MS = 1e3;
9331
9485
  function useAppAutoEvents(config) {
9332
9486
  const { isInitialized, getAppVersion: getAppVersion2, getSessionId, emitLifecycle, storageGet, storageSet, debug } = config;
9333
9487
  const didRunRef = (0, import_react20.useRef)(false);
9334
9488
  (0, import_react20.useEffect)(() => {
9489
+ if (debug) console.log("[Paywallo EVENTS] useEffect fired", { isInitialized, didRunRefCurrent: didRunRef.current });
9335
9490
  if (!isInitialized || didRunRef.current) return;
9336
9491
  const timer = setTimeout(() => {
9337
9492
  if (didRunRef.current) return;
@@ -9341,17 +9496,23 @@ function useAppAutoEvents(config) {
9341
9496
  const appVersion = getAppVersion2();
9342
9497
  const deviceType = await detectPlatform3();
9343
9498
  const osVersion = await detectOsVersion();
9499
+ if (debug) console.log("[Paywallo EVENTS] computed device context", { deviceType, osVersion, appVersion, sessionId, hasSessionId: !!sessionId });
9344
9500
  try {
9501
+ if (debug) console.log("[Paywallo EVENTS] checking install \u2014 about to read FIRST_SEEN_KEY");
9345
9502
  const firstSeen = await storageGet(FIRST_SEEN_KEY);
9346
9503
  if (!firstSeen) {
9347
9504
  await emitLifecycle({ type: "install", device_type: deviceType, os_version: osVersion, app_version: appVersion, ...sessionId ? { session_id: sessionId } : {} }, "critical");
9348
9505
  await storageSet(FIRST_SEEN_KEY, (/* @__PURE__ */ new Date()).toISOString());
9506
+ if (debug) console.log("[Paywallo EVENTS] install event emitted (first time)");
9349
9507
  if (debug) console.log("[Paywallo EVENTS] lifecycle install emitted");
9508
+ } else {
9509
+ if (debug) console.log("[Paywallo EVENTS] install skipped \u2014 firstSeen already exists", { firstSeen });
9350
9510
  }
9351
9511
  } catch (err) {
9352
9512
  if (debug) console.log("[Paywallo EVENTS] lifecycle install check failed", err);
9353
9513
  }
9354
9514
  try {
9515
+ if (debug) console.log("[Paywallo EVENTS] emitting cold_start");
9355
9516
  await emitLifecycle({ type: "cold_start", app_version: appVersion, device_type: deviceType, os_version: osVersion, ...sessionId ? { session_id: sessionId } : {} }, "normal");
9356
9517
  if (debug) console.log("[Paywallo EVENTS] lifecycle cold_start emitted");
9357
9518
  } catch (err) {
@@ -9666,9 +9827,9 @@ function subscriptionInfoToSub(s) {
9666
9827
  }
9667
9828
  async function resolveOfflineFromCache(distinctId) {
9668
9829
  try {
9669
- const cached = await subscriptionCache.get(distinctId);
9670
- if (!cached) return false;
9671
- const sub = cached.data.subscription;
9830
+ const cached2 = await subscriptionCache.get(distinctId);
9831
+ if (!cached2) return false;
9832
+ const sub = cached2.data.subscription;
9672
9833
  return isSubscriptionStillActive(
9673
9834
  sub ? { status: sub.status, expiresAt: sub.expiresAt ?? null } : null
9674
9835
  );
@@ -9738,8 +9899,8 @@ function useSubscriptionSync() {
9738
9899
  if (!apiClient || !distinctId) return false;
9739
9900
  const storeKitResult = await checkStoreKitActive(PaywalloClient.getConfig()?.debug);
9740
9901
  if (storeKitResult !== null) return storeKitResult;
9741
- const cached = cacheRef.current;
9742
- if (cached && cached.expiresAt > Date.now()) return isActiveCacheEntry(cached);
9902
+ const cached2 = cacheRef.current;
9903
+ if (cached2 && cached2.expiresAt > Date.now()) return isActiveCacheEntry(cached2);
9743
9904
  const persistedResult = await checkPersistedCache(distinctId, (sub) => setCacheAndState(sub));
9744
9905
  if (persistedResult !== null) return persistedResult;
9745
9906
  try {
@@ -9756,8 +9917,8 @@ function useSubscriptionSync() {
9756
9917
  const apiClient = PaywalloClient.getApiClient();
9757
9918
  const distinctId = PaywalloClient.getDistinctId();
9758
9919
  if (!apiClient || !distinctId) return null;
9759
- const cached = cacheRef.current;
9760
- if (cached && cached.expiresAt > Date.now()) return cached.data;
9920
+ const cached2 = cacheRef.current;
9921
+ if (cached2 && cached2.expiresAt > Date.now()) return cached2.data;
9761
9922
  try {
9762
9923
  return await fetchAndCache();
9763
9924
  } catch {
@@ -9773,6 +9934,196 @@ function useSubscriptionSync() {
9773
9934
  return { subscription, hasActiveSubscription, getSubscription, invalidateCache };
9774
9935
  }
9775
9936
 
9937
+ // src/domains/paywall/SuperwallAutoBridge.ts
9938
+ init_PaywalloClient();
9939
+
9940
+ // src/core/utils/eventId.ts
9941
+ function hashString(input) {
9942
+ let h1 = 3735928559;
9943
+ let h2 = 1103547991;
9944
+ for (let i = 0; i < input.length; i++) {
9945
+ const ch = input.charCodeAt(i);
9946
+ h1 = Math.imul(h1 ^ ch, 2654435769);
9947
+ h2 = Math.imul(h2 ^ ch, 1598716949);
9948
+ }
9949
+ h1 = Math.imul(h1 ^ h1 >>> 16, 73244475);
9950
+ h1 ^= Math.imul(h2 ^ h2 >>> 13, 351452291);
9951
+ h2 = Math.imul(h2 ^ h2 >>> 16, 73244475);
9952
+ h2 ^= Math.imul(h1 ^ h1 >>> 13, 351452291);
9953
+ const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
9954
+ return toHex(h1) + toHex(h2);
9955
+ }
9956
+ function buildDeterministicEventId(parts) {
9957
+ const raw = hashString(parts.join("|"));
9958
+ const p1 = raw.slice(0, 8);
9959
+ const p2 = raw.slice(8, 12);
9960
+ const p3 = "4" + raw.slice(13, 16);
9961
+ const p4 = (parseInt(raw[16], 16) & 3 | 8).toString(16) + raw.slice(17, 20);
9962
+ const p5 = raw.padEnd(32, "0").slice(20, 32);
9963
+ return `${p1}-${p2}-${p3}-${p4}-${p5}`;
9964
+ }
9965
+
9966
+ // src/domains/paywall/SuperwallAutoBridge.ts
9967
+ var bridgeStarted = false;
9968
+ var unsubscribe = null;
9969
+ var debugEnabled = false;
9970
+ function log(msg, data) {
9971
+ if (!debugEnabled) return;
9972
+ if (data !== void 0) {
9973
+ console.log(`[Paywallo:SuperwallBridge] ${msg}`, data);
9974
+ } else {
9975
+ console.log(`[Paywallo:SuperwallBridge] ${msg}`);
9976
+ }
9977
+ }
9978
+ function minuteBucket(ts) {
9979
+ return String(Math.floor(ts / 6e4));
9980
+ }
9981
+ function buildEventId(type, identifier, ts) {
9982
+ return buildDeterministicEventId(["sw", type, identifier, minuteBucket(ts)]);
9983
+ }
9984
+ async function trackPaywallEvent(type, identifier, ts, extra) {
9985
+ const _eventId = buildEventId(type, identifier, ts);
9986
+ try {
9987
+ const cleanExtra = {};
9988
+ if (extra) {
9989
+ for (const [k, v] of Object.entries(extra)) if (v !== void 0) cleanExtra[k] = v;
9990
+ }
9991
+ await PaywalloClient.track("paywall", {
9992
+ priority: "critical",
9993
+ properties: {
9994
+ type,
9995
+ paywall_id: identifier,
9996
+ ...cleanExtra
9997
+ }
9998
+ });
9999
+ } catch (err) {
10000
+ log("track error", { type, identifier, eventId: _eventId, err: String(err) });
10001
+ }
10002
+ }
10003
+ function mapDismissToCloseReason(resultType) {
10004
+ switch (resultType) {
10005
+ case "purchased":
10006
+ return "purchased";
10007
+ case "restored":
10008
+ return "purchased";
10009
+ // restore counts as a purchase outcome for funnel
10010
+ case "declined":
10011
+ return "dismissed";
10012
+ default:
10013
+ return void 0;
10014
+ }
10015
+ }
10016
+ function buildCallbacks() {
10017
+ return {
10018
+ onPaywallPresent: (info) => {
10019
+ const ts = Date.now();
10020
+ log("onPaywallPresent", { identifier: info.identifier });
10021
+ void trackPaywallEvent("viewed", info.identifier, ts, {
10022
+ placement: info.presentedByEventWithName,
10023
+ variant_id: info.experiment?.variant?.id,
10024
+ campaign_id: info.experiment?.id
10025
+ });
10026
+ },
10027
+ onPaywallDismiss: (info, result) => {
10028
+ const ts = Date.now();
10029
+ log("onPaywallDismiss", { identifier: info.identifier, result: result.type });
10030
+ void trackPaywallEvent("closed", info.identifier, ts, {
10031
+ placement: info.presentedByEventWithName,
10032
+ variant_id: info.experiment?.variant?.id,
10033
+ campaign_id: info.experiment?.id,
10034
+ close_reason: mapDismissToCloseReason(result.type),
10035
+ closed_at: new Date(ts).toISOString()
10036
+ });
10037
+ },
10038
+ onPurchase: (params) => {
10039
+ const ts = Date.now();
10040
+ const safe = params !== null && typeof params === "object" ? params : {};
10041
+ const productId = "productId" in safe && typeof safe.productId === "string" ? safe.productId : "unknown";
10042
+ const identifier = "paywallIdentifier" in safe && typeof safe.paywallIdentifier === "string" ? safe.paywallIdentifier : productId;
10043
+ log("onPurchase", { productId, identifier });
10044
+ void trackPaywallEvent("purchased", identifier, ts, { product_id: productId });
10045
+ },
10046
+ // Global Superwall event listener — used specifically for `transactionComplete`
10047
+ // which is the only event that exposes the full product (currency/price) +
10048
+ // transaction (storeTransactionId) shape needed to create a server-side
10049
+ // `transactions` row. Other events are skipped (paywallOpen/Dismiss already
10050
+ // handled above with their typed callbacks).
10051
+ onSuperwallEvent: (info) => {
10052
+ const ev = info?.event;
10053
+ if (!ev || ev.event !== "transactionComplete") return;
10054
+ const ts = Date.now();
10055
+ const product = ev.product;
10056
+ const transaction = ev.transaction;
10057
+ if (!product) {
10058
+ log("transactionComplete missing product \u2014 skipping transaction track");
10059
+ return;
10060
+ }
10061
+ const productId = product.productIdentifier ?? product.id ?? "unknown";
10062
+ const currency = product.currencyCode ?? "USD";
10063
+ const price = product.price ?? 0;
10064
+ const transactionId = transaction?.storeTransactionId ?? transaction?.originalTransactionIdentifier ?? `${productId}_${String(ts)}`;
10065
+ const isTrial = product.hasFreeTrial === true || (product.trialPeriodPrice ?? 0) > 0;
10066
+ const paywallIdentifier = ev.paywallInfo?.identifier;
10067
+ log("onSuperwallEvent: transactionComplete", { productId, currency, price, transactionId, isTrial });
10068
+ void (async () => {
10069
+ try {
10070
+ await PaywalloClient.track("transaction", {
10071
+ priority: "critical",
10072
+ properties: {
10073
+ type: isTrial ? "trial_started" : "completed",
10074
+ // Server's IngestTranslator looks for `transaction_id`/`product_id`
10075
+ // /`amount`/`currency`/`paywall_id` keys (not `tx_id`). PurchaseHandler
10076
+ // then resolves type=trial/new/renewal based on amount being 0 vs >0.
10077
+ transaction_id: transactionId,
10078
+ product_id: productId,
10079
+ amount: price,
10080
+ currency,
10081
+ ...paywallIdentifier ? { paywall_id: paywallIdentifier } : {}
10082
+ }
10083
+ });
10084
+ } catch (err) {
10085
+ log("transactionComplete track error", { err: String(err) });
10086
+ }
10087
+ })();
10088
+ }
10089
+ };
10090
+ }
10091
+ function startSuperwallAutoBridge(debug = false) {
10092
+ debugEnabled = debug;
10093
+ if (bridgeStarted) {
10094
+ log("already started, skipping");
10095
+ return;
10096
+ }
10097
+ void detectAndStart();
10098
+ }
10099
+ async function detectAndStart() {
10100
+ let subscribeFn;
10101
+ try {
10102
+ const mod = await import("expo-superwall");
10103
+ if (!mod || typeof mod.subscribeToSuperwallEvents !== "function") {
10104
+ const internal = await import("expo-superwall/build/src/internal/superwallEventBridge");
10105
+ if (typeof internal.subscribeToSuperwallEvents !== "function") {
10106
+ log("expo-superwall subscribeToSuperwallEvents not found \u2014 bridge inactive");
10107
+ return;
10108
+ }
10109
+ subscribeFn = internal.subscribeToSuperwallEvents;
10110
+ } else {
10111
+ subscribeFn = mod.subscribeToSuperwallEvents;
10112
+ }
10113
+ } catch {
10114
+ log("expo-superwall not installed \u2014 bridge inactive");
10115
+ return;
10116
+ }
10117
+ try {
10118
+ const callbacks = buildCallbacks();
10119
+ unsubscribe = subscribeFn(void 0, () => callbacks);
10120
+ bridgeStarted = true;
10121
+ log("bridge started");
10122
+ } catch (err) {
10123
+ log("subscribe failed", { err: String(err) });
10124
+ }
10125
+ }
10126
+
9776
10127
  // src/PaywalloProvider.tsx
9777
10128
  init_localization();
9778
10129
  var import_jsx_runtime3 = require("react/jsx-runtime");
@@ -9802,8 +10153,18 @@ function PaywalloProvider({ children, config }) {
9802
10153
  for (const [k, v] of Object.entries(props)) if (v !== void 0) flat[k] = v;
9803
10154
  return PaywalloClient.track("lifecycle", { properties: flat, priority });
9804
10155
  },
9805
- storageGet: (key) => secureStorageRef.current.get(key),
9806
- storageSet: (key, value) => secureStorageRef.current.set(key, value),
10156
+ storageGet: async (key) => {
10157
+ if (key === "@paywallo:firstSeen_v226") {
10158
+ return nativeStorage.get(key);
10159
+ }
10160
+ return secureStorageRef.current.get(key);
10161
+ },
10162
+ storageSet: async (key, value) => {
10163
+ if (key === "@paywallo:firstSeen_v226") {
10164
+ return nativeStorage.set(key, value);
10165
+ }
10166
+ return secureStorageRef.current.set(key, value);
10167
+ },
9807
10168
  debug: config.debug
9808
10169
  });
9809
10170
  const triggerRepreload = (0, import_react25.useCallback)((placement) => {
@@ -9811,8 +10172,8 @@ function PaywalloProvider({ children, config }) {
9811
10172
  });
9812
10173
  }, []);
9813
10174
  const { showingPreloadedWebView, presentPreloadedWebView, resolvePreloadedPaywall, handlePreloadedWebViewReady: baseHandlePreloadedWebViewReady, handlePreloadedClose: baseHandlePreloadedClose, setShowingPreloadedWebView, presentedAtRef } = usePaywallPresenter(preloadedWebView, setPreloadedWebView);
9814
- const SCREEN_HEIGHT = (0, import_react25.useMemo)(() => import_react_native25.Dimensions.get("window").height, []);
9815
- const slideAnim = (0, import_react25.useRef)(new import_react_native25.Animated.Value(SCREEN_HEIGHT)).current;
10175
+ const SCREEN_HEIGHT = (0, import_react25.useMemo)(() => import_react_native27.Dimensions.get("window").height, []);
10176
+ const slideAnim = (0, import_react25.useRef)(new import_react_native27.Animated.Value(SCREEN_HEIGHT)).current;
9816
10177
  const handlePreloadedWebViewReady = (0, import_react25.useCallback)(() => {
9817
10178
  if (PaywalloClient.getConfig()?.debug) {
9818
10179
  console.log(`[Paywallo WEBVIEW] loaded \u2014 placement: ${preloadedWebView?.placement}`);
@@ -9821,7 +10182,7 @@ function PaywalloProvider({ children, config }) {
9821
10182
  }, [preloadedWebView?.placement, baseHandlePreloadedWebViewReady]);
9822
10183
  const handlePreloadedClose = (0, import_react25.useCallback)(() => {
9823
10184
  const placement = preloadedWebView?.placement;
9824
- import_react_native25.Animated.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: import_react_native25.Easing.in(import_react_native25.Easing.cubic), useNativeDriver: true }).start(() => {
10185
+ import_react_native27.Animated.timing(slideAnim, { toValue: SCREEN_HEIGHT, duration: 200, easing: import_react_native27.Easing.in(import_react_native27.Easing.cubic), useNativeDriver: true }).start(() => {
9825
10186
  baseHandlePreloadedClose();
9826
10187
  if (placement) triggerRepreload(placement);
9827
10188
  });
@@ -9850,6 +10211,7 @@ function PaywalloProvider({ children, config }) {
9850
10211
  PaywalloClient.init(configRef.current).then(async () => {
9851
10212
  if (!mounted) return;
9852
10213
  setDistinctId(PaywalloClient.getDistinctId());
10214
+ startSuperwallAutoBridge(configRef.current.debug);
9853
10215
  setIsInitialized(true);
9854
10216
  if (!autoPreloadTriggeredRef.current) {
9855
10217
  autoPreloadTriggeredRef.current = true;
@@ -10070,7 +10432,7 @@ function PaywalloProvider({ children, config }) {
10070
10432
  (0, import_react25.useLayoutEffect)(() => {
10071
10433
  if (showingPreloadedWebView) {
10072
10434
  slideAnim.setValue(SCREEN_HEIGHT);
10073
- import_react_native25.Animated.timing(slideAnim, { toValue: 0, duration: 200, easing: import_react_native25.Easing.out(import_react_native25.Easing.cubic), useNativeDriver: true }).start();
10435
+ import_react_native27.Animated.timing(slideAnim, { toValue: 0, duration: 200, easing: import_react_native27.Easing.out(import_react_native27.Easing.cubic), useNativeDriver: true }).start();
10074
10436
  }
10075
10437
  }, [showingPreloadedWebView, slideAnim, SCREEN_HEIGHT]);
10076
10438
  const preloadStyle = [
@@ -10080,7 +10442,7 @@ function PaywalloProvider({ children, config }) {
10080
10442
  ];
10081
10443
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(PaywalloContext.Provider, { value: contextValue, children: [
10082
10444
  children,
10083
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native25.Animated.View, { style: preloadStyle, pointerEvents: showingPreloadedWebView ? "auto" : "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
10445
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PaywallErrorBoundary, { children: !!preloadedWebView?.craftData && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native27.Animated.View, { style: preloadStyle, pointerEvents: showingPreloadedWebView ? "auto" : "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
10084
10446
  PaywallWebView,
10085
10447
  {
10086
10448
  paywallId: preloadedWebView.paywallId,
@@ -10111,7 +10473,7 @@ function PaywalloProvider({ children, config }) {
10111
10473
  ) })
10112
10474
  ] });
10113
10475
  }
10114
- var styles3 = import_react_native25.StyleSheet.create({
10476
+ var styles3 = import_react_native27.StyleSheet.create({
10115
10477
  preloadOverlay: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#ffffff" },
10116
10478
  hidden: { zIndex: -1, opacity: 0, width: 1, height: 1, overflow: "hidden" },
10117
10479
  visible: { zIndex: 9999, opacity: 1 }