import { t as ATTStatus } from "./att-BDPyE-Gs.mjs"; import { n as SKANManager } from "./skan-BljOFaeU.mjs"; import { AddToCartEventParams, BeforeSendEvent, BeforeSendHook, BeforeSendHook as BeforeSendHook$1, BeginCheckoutEventParams, ConsentState, ConsentState as ConsentState$1, DeviceContext, DeviceContext as DeviceContext$1, EcommerceItem, Environment, Environment as Environment$1, EventProperties, EventProperties as EventProperties$1, FeatureFlagBootstrap, FeatureFlagBootstrap as FeatureFlagBootstrap$1, FeatureFlagBootstrapData, FeatureFlagCondition, FeatureFlagDefinition, FeatureFlagValue, FeatureFlagValue as FeatureFlagValue$1, FeatureFlagVariant, FeatureFlagsListener, FeatureFlagsListener as FeatureFlagsListener$1, GroupsState, GroupsState as GroupsState$1, LayersCore, LayersError, LayersEvent, LayersEventName, PurchaseEventParams, RefundEventParams, StartTrialEventParams, SubscribeEventParams, SurveyAnswer, SurveyDefinition, SurveyDefinition as SurveyDefinition$1, SurveyDisplay, SurveyPosition, SurveyQuestion, SurveyResponse, SurveyResponse as SurveyResponse$1, SurveyTargeting, SurveyTargetingContext, SurveyType, TypedEventPayload, UserProperties, UserProperties as UserProperties$1, ViewItemEventParams, addToCartEvent as buildAddToCartEvent, beginCheckoutEvent as buildBeginCheckoutEvent, onboardingCompleteEvent as buildOnboardingCompleteEvent, onboardingStartEvent as buildOnboardingStartEvent, paywallShowEvent as buildPaywallShowEvent, purchaseEvent as buildPurchaseEvent, refundEvent as buildRefundEvent, startTrialEvent as buildStartTrialEvent, subscribeEvent as buildSubscribeEvent, viewItemEvent as buildViewItemEvent } from "@layers/core-wasm"; //#region src/standard-events.d.ts /** * Predefined standard event name constants. * * Usage: * ```ts * import { StandardEvents } from '@layers/react-native'; * layers.track(StandardEvents.PURCHASE, { amount: 9.99, currency: 'USD' }); * ``` */ declare const StandardEvents: { readonly APP_INSTALL: "app_install"; readonly APP_OPEN: "app_open"; readonly LOGIN: "login"; readonly SIGN_UP: "sign_up"; readonly REGISTER: "register"; readonly PURCHASE: "purchase_success"; readonly ADD_TO_CART: "add_to_cart"; readonly ADD_TO_WISHLIST: "add_to_wishlist"; readonly INITIATE_CHECKOUT: "initiate_checkout"; readonly BEGIN_CHECKOUT: "begin_checkout"; readonly START_TRIAL: "start_trial"; readonly SUBSCRIBE: "subscribe"; readonly LEVEL_START: "level_start"; readonly LEVEL_COMPLETE: "level_complete"; readonly TUTORIAL_COMPLETE: "tutorial_complete"; readonly SEARCH: "search"; readonly VIEW_ITEM: "view_item"; readonly VIEW_CONTENT: "view_content"; readonly SHARE: "share"; readonly DEEP_LINK: "deep_link_opened"; readonly SCREEN_VIEW: "screen_view"; readonly ONBOARDING_START: "onboarding_start"; readonly ONBOARDING_COMPLETE: "onboarding_complete"; readonly PAYWALL_SHOW: "paywall_show"; }; /** Union type of all standard event name strings. */ type StandardEventName = (typeof StandardEvents)[keyof typeof StandardEvents]; interface StandardEventPayload { event: StandardEventName; properties: EventProperties; } /** Build a login event. */ declare function loginEvent(method?: string): StandardEventPayload; /** Build a sign-up event. */ declare function signUpEvent(method?: string): StandardEventPayload; /** Build a register event. */ declare function registerEvent(method?: string): StandardEventPayload; /** Build a purchase event. */ declare function purchaseEvent(amount: number, currency?: string, itemId?: string): StandardEventPayload; /** Build an add-to-cart event. */ declare function addToCartEvent(itemId: string, price: number, quantity?: number): StandardEventPayload; /** Build an add-to-wishlist event. */ declare function addToWishlistEvent(itemId: string, name?: string, price?: number): StandardEventPayload; /** Build an initiate-checkout event. */ declare function initiateCheckoutEvent(value: number, currency?: string, itemCount?: number): StandardEventPayload; /** Build a start-trial event. */ declare function startTrialEvent(plan?: string, durationDays?: number): StandardEventPayload; /** Build a subscribe event. */ declare function subscribeEvent(plan: string, amount: number, currency?: string): StandardEventPayload; /** Build a level-start event. */ declare function levelStartEvent(level: string): StandardEventPayload; /** Build a level-complete event. */ declare function levelCompleteEvent(level: string, score?: number): StandardEventPayload; /** Build a tutorial-complete event. */ declare function tutorialCompleteEvent(name?: string): StandardEventPayload; /** Build a search event. */ declare function searchEvent(query: string, resultCount?: number): StandardEventPayload; /** Build a view-item event. */ declare function viewItemEvent(itemId: string, name?: string, category?: string): StandardEventPayload; /** Build a view-content event. */ declare function viewContentEvent(contentId: string, contentType?: string, name?: string): StandardEventPayload; /** Build a share event. */ declare function shareEvent(contentType: string, method?: string, contentId?: string): StandardEventPayload; /** Build a screen-view event. */ declare function screenViewEvent(name: string, screenClass?: string): StandardEventPayload; /** Build an onboarding-start event. `screenName` is the first onboarding screen shown. */ declare function onboardingStartEvent(screenName?: string): StandardEventPayload; /** Build an onboarding-complete event. `screenName` is the screen the user finished on. */ declare function onboardingCompleteEvent(screenName?: string): StandardEventPayload; /** * Build a paywall-show event. `placement` names where the paywall appeared * (e.g. `onboarding`, `settings`); `productIds` lists the products offered. */ declare function paywallShowEvent(placement: string, productIds?: string[]): StandardEventPayload; //#endregion //#region src/superwall.d.ts /** Minimal subset of Superwall's PaywallInfo used by this integration. */ interface SuperwallPaywallInfo { identifier: string; name?: string; url?: string; products?: { id: string; }[]; experiment?: { id: string; variantId: string; }; } /** Minimal subset of Superwall's event info. */ interface SuperwallEventInfo { event: { rawName?: string; type?: string; }; params?: Record; } /** Product info from a Superwall paywall purchase. */ interface SuperwallProduct { productIdentifier?: string; id?: string; price?: number; currencyCode?: string; currency?: string; } /** * Forward any Superwall event to Layers, prefixed with `superwall_`. * * Wire this up in your Superwall delegate / event handler: * ```ts * import Superwall from '@superwall/react-native-superwall'; * import { superwallOnEvent } from '@layers/react-native'; * * Superwall.instance.setDelegate({ * handleSuperwallEvent(eventInfo) { * superwallOnEvent(sdk, eventInfo); * } * }); * ``` */ declare function superwallOnEvent(sdk: LayersReactNative, eventInfo: SuperwallEventInfo): void; /** * Track that a paywall was presented to the user. * * ```ts * superwallTrackPresentation(sdk, paywallInfo); * ``` */ declare function superwallTrackPresentation(sdk: LayersReactNative, paywallInfo: SuperwallPaywallInfo): void; /** * Track that a paywall was dismissed. * * ```ts * superwallTrackDismiss(sdk, paywallInfo); * ``` */ declare function superwallTrackDismiss(sdk: LayersReactNative, paywallInfo: SuperwallPaywallInfo): void; /** * Track a purchase initiated from a Superwall paywall. * * ```ts * superwallTrackPurchase(sdk, paywallInfo, product); * ``` */ declare function superwallTrackPurchase(sdk: LayersReactNative, paywallInfo: SuperwallPaywallInfo, product?: SuperwallProduct): void; /** * Track that a paywall was skipped (e.g. holdout, no rule match). * * ```ts * superwallTrackSkip(sdk, paywallInfo, 'holdout'); * ``` */ declare function superwallTrackSkip(sdk: LayersReactNative, paywallInfo: SuperwallPaywallInfo | null, reason: string): void; /** * Get Layers attribution data formatted as Superwall user attributes. * * Pass the returned object to `Superwall.instance.setUserAttributes()`: * ```ts * const attrs = superwallUserAttributes(sdk); * Superwall.instance.setUserAttributes(attrs); * ``` */ declare function superwallUserAttributes(sdk: LayersReactNative): Record; //#endregion //#region src/commerce.d.ts /** Minimal Layers SDK interface required by the commerce module. */ interface CommerceTracker { track(event: string, properties?: EventProperties): void; } /** Purchase details for trackPurchase. */ interface PurchaseParams { productId: string; /** Unit price of the item. Revenue is computed as `price * quantity`. */ price: number; currency: string; transactionId?: string; quantity?: number; isRestored?: boolean; store?: string; properties?: EventProperties; } /** Subscription details for trackSubscription. */ interface SubscriptionParams { productId: string; /** Unit price of the subscription. */ price: number; currency: string; period?: string; transactionId?: string; isRenewal?: boolean; isTrial?: boolean; subscriptionGroupId?: string; originalTransactionId?: string; properties?: EventProperties; } /** Cart item for order and checkout tracking. */ interface CartItem { productId: string; name: string; price: number; quantity?: number; category?: string; } /** Order details for trackOrder. */ interface OrderParams { orderId: string; items: CartItem[]; subtotal: number; currency?: string; tax?: number; shipping?: number; discount?: number; couponCode?: string; properties?: EventProperties; } /** Refund details for trackRefund. */ interface RefundParams { transactionId: string; amount: number; currency: string; reason?: string; properties?: EventProperties; } /** Purchase failure details for trackPurchaseFailed. */ interface PurchaseFailedParams { productId: string; currency: string; errorCode: string | number; errorMessage?: string; properties?: EventProperties; } /** * Track a successful purchase. * * ```ts * import { trackPurchase } from '@layers/react-native'; * * trackPurchase(sdk, { * productId: 'premium_monthly', * price: 9.99, * currency: 'USD', * transactionId: 'txn_abc123', * }); * ``` */ declare function trackPurchase(sdk: CommerceTracker, params: PurchaseParams): void; /** * Track a failed purchase attempt. */ declare function trackPurchaseFailed(sdk: CommerceTracker, params: PurchaseFailedParams): void; /** * Track a subscription purchase or renewal. * * ```ts * import { trackSubscription } from '@layers/react-native'; * * trackSubscription(sdk, { * productId: 'pro_annual', * price: 49.99, * currency: 'USD', * period: 'P1Y', * }); * ``` */ declare function trackSubscription(sdk: CommerceTracker, params: SubscriptionParams): void; /** * Track a completed order with multiple line items. */ declare function trackOrder(sdk: CommerceTracker, params: OrderParams): void; /** * Track an item being added to the cart. */ declare function trackAddToCart(sdk: CommerceTracker, item: CartItem, properties?: EventProperties): void; /** * Track an item being removed from the cart. */ declare function trackRemoveFromCart(sdk: CommerceTracker, item: CartItem, properties?: EventProperties): void; /** * Track beginning the checkout flow. */ declare function trackBeginCheckout(sdk: CommerceTracker, items: CartItem[], currency?: string, properties?: EventProperties): void; /** * Track viewing a product detail page. */ declare function trackViewProduct(sdk: CommerceTracker, productId: string, name: string, price: number, currency?: string, category?: string, properties?: EventProperties): void; /** * Track a refund. */ declare function trackRefund(sdk: CommerceTracker, params: RefundParams): void; //#endregion //#region src/revenuecat.d.ts /** * Minimal interface for RevenueCat's CustomerInfo object. * Avoids a hard dependency on `react-native-purchases`. */ interface RevenueCatCustomerInfo { readonly activeSubscriptions: string[]; readonly originalAppUserId: string; } /** * Minimal interface for RevenueCat's PurchasesPackage object. */ interface RevenueCatPackage { readonly product: { readonly identifier: string; readonly price: number; readonly currencyCode: string; }; } /** * Options for configuring the RevenueCat integration. */ interface RevenueCatConfig { /** The Layers SDK instance to bridge events to. */ sdk: LayersReactNative; /** * Callback invoked when a new subscription is detected. * By default, new subscriptions are tracked as `subscription_start` events. * Set to `null` to disable automatic subscription tracking. */ onSubscriptionStart?: ((productId: string) => void) | null; } /** * Connect Layers to your existing RevenueCat Purchases instance. * * Listens for customer info updates to detect new subscriptions and syncs * subscriber status to Layers user properties. * * @example * ```ts * import Purchases from 'react-native-purchases'; * import { LayersReactNative, connectRevenueCat } from '@layers/react-native'; * * const sdk = new LayersReactNative(); * await layers.init({ appId: 'your-app-id', environment: 'production' }); * * connectRevenueCat({ * sdk, * purchases: Purchases, * }); * ``` * * @param config - Configuration including the SDK instance. * @param purchases - The RevenueCat Purchases instance (duck-typed to avoid hard dependency). */ declare function connectRevenueCat(config: RevenueCatConfig, purchases: { addCustomerInfoUpdateListener: (listener: (info: RevenueCatCustomerInfo) => void) => { remove: () => void; } | void; getCustomerInfo: () => Promise; }): void; /** * Track a RevenueCat package purchase manually. * * @example * ```ts * const offerings = await Purchases.getOfferings(); * const pkg = offerings.current?.availablePackages[0]; * if (pkg) trackRevenueCatPurchase(sdk, pkg); * ``` */ declare function trackRevenueCatPurchase(sdk: LayersReactNative, rcPackage: RevenueCatPackage, store?: string): void; /** * Manually sync RevenueCat subscriber attributes to Layers user properties. * * This is called automatically when using `connectRevenueCat()`, but can also * be called manually if needed. */ declare function syncRevenueCatAttributes(sdk: LayersReactNative, customerInfo: RevenueCatCustomerInfo): void; /** * Reset RevenueCat integration state. For testing only. */ declare function resetRevenueCatForTesting(): void; /** * Whether the RevenueCat integration is currently connected. */ declare function isRevenueCatConnected(): boolean; //#endregion //#region src/background-flush.d.ts /** * Task identifier for Layers background flush. * * Use this constant when registering the background task with * `react-native-background-fetch`, `expo-task-manager`, or any * other background task scheduler. */ declare const BACKGROUND_FLUSH_TASK_NAME = "com.layers.sdk.background-flush"; /** * Minimum recommended interval (in minutes) between background flushes. * Both iOS and Android enforce a minimum of ~15 minutes for background * fetch tasks. */ declare const BACKGROUND_FLUSH_MIN_INTERVAL_MINUTES = 15; /** * Result type returned by the background flush handler. * Compatible with both `react-native-background-fetch` and * `expo-task-manager` result conventions. */ interface BackgroundFlushResult { /** Whether the flush completed successfully. */ success: boolean; /** Number of events flushed, if available. */ eventsFlushed?: number; } /** * Creates a background flush handler function. * * The returned async function, when called by a background task scheduler, * will flush any queued events via the Layers SDK. It is safe to call even * if the SDK is not initialized (it will silently no-op). * * @param getSDK - A getter that returns the current LayersReactNative * instance, or `null` if the SDK is not yet initialized. * @returns An async handler suitable for use with background task libraries. * * @example * ```typescript * const handler = createBackgroundFlushHandler(() => mySDKInstance); * ``` */ declare function createBackgroundFlushHandler(getSDK: () => LayersReactNative | null): () => Promise; /** * Registers a background flush task with `expo-task-manager`. * * This must be called at module scope (outside of any component) as * `expo-task-manager` requires tasks to be defined before the app mounts. * * @param taskManager - The `expo-task-manager` module (import * as TaskManager from 'expo-task-manager'). * @param getSDK - A getter that returns the current LayersReactNative * instance, or `null` if the SDK is not yet initialized. * * @example * ```typescript * import * as TaskManager from 'expo-task-manager'; * import { registerExpoBackgroundFlush } from '@layers/expo'; * * let sdk: LayersReactNative | null = null; * registerExpoBackgroundFlush(TaskManager, () => sdk); * * // Later, in your app initialization: * sdk = new LayersReactNative({ appId: '...', environment: 'production' }); * await layers.init(); * ``` */ declare function registerExpoBackgroundFlush(taskManager: { defineTask: (taskName: string, handler: () => Promise) => void; }, getSDK: () => LayersReactNative | null): void; //#endregion //#region src/navigation-tracking.d.ts /** Minimal SDK surface — supplied by LayersReactNative. */ interface NavigationTrackingHooks { screen(screenName: string, properties?: EventProperties): void; enableDebug?: boolean; } /** * Internal route shape — matches React Navigation's * `NavigationState.routes[i]` structure but only the fields we read. */ interface RouteSnapshot { name: string; params?: Record; } /** State shape we pull from `NavigationState`. */ interface NavigationStateSnapshot { index?: number; routes?: Array; } /** * Build a stateful listener that emits `$screen_view` whenever the active * route changes. The returned function should be passed to React * Navigation's `onStateChange` prop or `navigationRef.addListener('state', * cb)`. It returns nothing — the SDK records the event side-effecting. * * The listener: * - resolves the deepest active route via `getActiveRouteName()` * semantics so nested stack/tab navigators correctly report the * leaf screen * - de-dupes consecutive identical names (some RN versions emit the * same state twice on re-renders) * - attaches route params + previous screen on the screen event */ declare function createNavigationListener(sdk: NavigationTrackingHooks): (state: NavigationStateSnapshot | undefined) => void; /** * React hook that subscribes to a React Navigation NavigationContainerRef * and emits a `$screen_view` for each route change. Pass the SDK instance * and the same `navigationRef` you give to ``. * * ```tsx * import { createNavigationContainerRef } from '@react-navigation/native'; * const navigationRef = createNavigationContainerRef(); * * function Root() { * useLayersNavigationTracking(layers, navigationRef); * return ...; * } * ``` * * The hook is implemented via a lazy `require('react')` so it only fails * if React itself is unavailable — consumers without React Navigation can * still call it and it will silently no-op. */ declare function useLayersNavigationTracking(sdk: NavigationTrackingHooks, navigationRef: { addListener?: (event: string, cb: (...args: unknown[]) => void) => () => void; }): void; /** * Walk a NavigationState tree and return the active leaf route. Mirrors * React Navigation's own `getActiveRouteName()` helper without taking a * peer-dep on `@react-navigation/native`. * * Exported for testing. * @internal */ declare function resolveActiveRoute(state: NavigationStateSnapshot | undefined): RouteSnapshot | null; //#endregion //#region src/exceptions.d.ts /** * Properties emitted alongside a `$exception` event. Names mirror the * `$exception_*` reservation in the wire protocol and the web client. */ interface ExceptionProperties { $exception_type: string; $exception_message: string; $exception_stack?: string; /** Always `false` from this module; see the header. */ $exception_promise_rejection: boolean; /** React Native's `isFatal` flag: `true` when the error takes the app down. */ $exception_fatal?: boolean; [key: string]: unknown; } /** * Sink that the handler delivers `$exception` events to. The SDK passes its * own `track()` here; tests pass a spy. */ type ExceptionSink = (eventName: '$exception', properties: ExceptionProperties) => void; interface ExceptionInstallOptions { /** * Hook for surfacing diagnostic logs from inside the handler. The SDK wires * this to its `enableDebug`-gated console.warn. */ onInternalError?: (message: string, error: unknown) => void; /** * Runs after the sink for a fatal error. The previous handler, which ends * the process in a release build, runs once the returned promise settles or * `fatalTimeoutMs` passes, whichever comes first. The SDK uses it to get the * event onto disk before the app dies. Errors that are not fatal never wait. */ onFatal?: () => Promise | void; /** Upper bound on how long a fatal error waits for `onFatal`. @default 1000 */ fatalTimeoutMs?: number; } /** * Describe whatever React Native handed the global handler. Errors carry a * name, message and stack; anything else thrown (strings, plain objects) is * serialized best-effort. Every field is coerced to a string, since Error * subclasses in the wild override `message` with numbers and getters. */ declare function buildExceptionProperties(error: unknown, isFatal?: boolean): ExceptionProperties; /** * Install the React Native global error handler. * * Returns an idempotent uninstall function that restores the previous * handler when ours is still the current one. Returns a no-op uninstall when * `ErrorUtils` is absent (Node, Jest without the RN preset, web). */ declare function installExceptionAutoCapture(sink: ExceptionSink, options?: ExceptionInstallOptions): () => void; //#endregion //#region src/surveys.d.ts interface SurveysModuleOptions { enabled?: boolean; /** Caller-supplied person properties for targeting. */ personProperties?: Record; /** Caller-supplied feature flag resolver. */ resolveFeatureFlag?: (flagKey: string) => boolean | string | null | undefined; /** * Optional `AsyncStorage`-style persistence for the show-history map. * The RN SDK passes its existing `AsyncStorage` adapter automatically. */ persistence?: { read(key: string): Promise; write(key: string, value: string): Promise; }; } /** * Public surveys API exposed via `LayersReactNative.surveys`. * * Render the actual chrome by mounting `` from * `@layers/react-native/surveys-component` somewhere in your app tree * and binding it to the eligible survey via `surveys.getActive()`. */ declare class SurveysModule { private readonly core; private readonly options; private hydrated; private listeners; constructor(core: LayersCore, options?: SurveysModuleOptions); get enabled(): boolean; knownIds(): string[]; /** * Return surveys currently eligible to show. Builds the targeting context * from caller-supplied person properties + feature flag values. URL * targeting is web-only so it's omitted. */ getActive(): SurveyDefinition[]; /** Mark a survey as shown — emits `survey shown` auto-event. */ markShown(surveyId: string): void; /** Mark a survey as dismissed — emits `survey dismissed` auto-event. */ markDismissed(surveyId: string): void; /** Submit a survey response — emits `survey sent` auto-event. */ submit(surveyId: string, response: SurveyResponse): void; /** * Subscribe to changes in the eligible-surveys list. The callback fires * after every show/dismiss/submit and after the show-history is hydrated * from persistence on init. */ subscribe(listener: (active: SurveyDefinition[]) => void): () => void; configure(patch: Partial): void; private buildContext; private allDefinitions; private hydrate; private persistShowHistory; private notify; } //#endregion //#region src/index.d.ts /** * Partial device-context update accepted by {@link LayersReactNative.setDeviceInfo}. * Omitted fields are preserved; an explicitly supplied `undefined` clears the * field from the stored context. */ type DeviceInfoUpdate = { [Key in keyof DeviceContext]?: Exclude | undefined }; interface LayersRNConfig { appId: string; environment: Environment; appUserId?: string; /** Verbose console logging. @default false */ enableDebug?: boolean; /** * Enable per-device DebugView. The SDK generates and persists a stable * `debug_token` (UUID via AsyncStorage) and sends `X-Debug-Token: ` * on every event upload. Use {@link LayersReactNative.getDebugToken} * to retrieve the token for displaying in dev UIs. * @default false */ debug?: boolean; baseUrl?: string; flushIntervalMs?: number; flushThreshold?: number; maxQueueSize?: number; /** * Whether to automatically fire an `app_open` event during init(). * Set to `false` if you want to fire the event manually. * @default true */ autoTrackAppOpen?: boolean; /** * Whether to automatically track `deep_link_opened` events when a deep link * is received. The tracked event includes the parsed URL components and all * query parameters (UTM params, click IDs like fbclid/gclid/ttclid, etc.) * as flat top-level properties. * * This listener runs in addition to any consumer-registered listener via * `setupDeepLinkListener()` -- it does not replace it. * * @default true */ autoTrackDeepLinks?: boolean; /** * Whether to install a React Native global error handler * (`ErrorUtils.setGlobalHandler`) that turns uncaught JS errors into * `$exception` events. The handler that was installed before still runs * afterwards, so RedBox in development and the native crash in release are * unchanged. Disable if another crash reporter owns the global handler and * you do not want both reporting. * * @default true */ autoTrackExceptions?: boolean; /** * Whether to automatically emit `$app_background` / `$app_foreground` * lifecycle events on AppState transitions. The flush trigger on * background remains active regardless of this flag — only the EVENT * emission is gated. * * @default true */ autoTrackAppLifecycle?: boolean; /** * Whether to emit `$first_open` on the very first launch for a given * install (gated by AsyncStorage). Subsequent launches do not emit it. * * Note: the Rust core emits `$first_open` on first init via the * super-properties / install-id machinery. Setting this to `false` * suppresses any *additional* emission from the wrapper layer; it * does not unwind events emitted by the core. * * @default true */ autoTrackFirstOpen?: boolean; /** * Whether to emit `$app_update` on launch when the persisted app * version differs from the current `device_context.app_version`. * * @default true */ autoTrackAppUpdate?: boolean; /** * Tier 4: optional bootstrap data for feature flags. Pre-seed flag values * so the first render after launch doesn't flicker from default-off to * actual-value while the first /config fetch is in flight. */ bootstrap?: FeatureFlagBootstrap; /** * Your app's `AppState`, passed in explicitly. * * Only needed when your bundler hands the SDK a **second copy** of * `react-native`. React Native delivers native events (`appStateDidChange` * among them) to exactly one copy — the one whose `RCTDeviceEventEmitter` * is registered as a callable module, which is the copy your app's entry * file imported. A listener registered from any other copy sits on a dead * emitter: `addEventListener` returns a normal subscription object and it * simply never fires. The SDK detects that situation and reports it (see * the error message), and this is the escape hatch that fixes it without * touching your bundler: * * ```ts * import { AppState } from 'react-native'; * new LayersReactNative({ appId, environment, appState: AppState }); * ``` * * @default the SDK's own `require('react-native').AppState` */ appState?: AppStateLike; } /** * The slice of React Native's `AppState` the SDK uses. * * Structural, so a host can pass the real `AppState` (or a stand-in) without * the SDK depending on React Native's types. */ interface AppStateLike { addEventListener(type: 'change', handler: (state: string) => void): { remove: () => void; } | undefined; } type ErrorListener = (error: Error) => void; /** * Listener for SDK initialization timing metrics. * * @param mainThreadDurationMs Time spent in the synchronous portion of init * (core creation, device info collection, before background work). * @param totalDurationMs Total wall-clock time of the `init()` call, * including all async work (remote config fetch, attribution signals, app_open event). */ type InitListener = (mainThreadDurationMs: number, totalDurationMs: number) => void; declare class LayersReactNative { private core; private readonly eager; private get eagerPersistence(); private appUserId; private isOnline; private readonly enableDebug; private appStateSubscription; private exceptionUninstall; private persistenceSettle; private _persistTimer; private readonly _wrapperFlushes; private _lifecycleCheckTimer; private netInfoUnsubscribe; private readonly config; private userIdLocked; private readonly errorListeners; private _skanManager; private _skanArmed; private deepLinkUnsubscribe; private static readonly MAX_RECENT_EVENTS; private _recentEvents; private _isInitialized; private _initPromise; private _initGeneration; private _runtimeGeneration; private _configPollTimer; private static readonly CONFIG_POLL_INTERVAL_MS; private static readonly PERSIST_THROTTLE_MS; private _adServicesToken; private _installReferrer; private _gaid; private _installReferrerParams; private _hadPriorSdkState; private _hadPriorAppIdentity; private _deferredDeepLink; private _deferredDeepLinkDelivered; private _deferredDeepLinkRestore; private _deferredDeepLinkResolve; private readonly deferredDeepLinkListeners; private _clickResolveClaimed; private _attributionDeeplinkId; private _attributionGclid; private _attributionFbclid; private _attributionFbc; private _attributionTtclid; private _attributionMsclkid; private static readonly ATTRIBUTION_DEEPLINK_ID_KEY; private static readonly ATTRIBUTION_GCLID_KEY; private static readonly ATTRIBUTION_FBCLID_KEY; private static readonly ATTRIBUTION_FBC_KEY; private static readonly ATTRIBUTION_TTCLID_KEY; private static readonly ATTRIBUTION_MSCLKID_KEY; private _initListener; constructor(config: LayersRNConfig); /** * Best-effort device context available at construction time — no `await`, * so it can run in the constructor before any pre-init event is tracked. * Only the synchronously readable fields; the rest arrive with * initializeDeviceInfo(). */ private applyEagerDeviceContext; /** * The platform this instance runs on: 'ios' | 'android' | 'react-native'. * Reads the device context the constructor stamped (applyEagerDeviceContext) * and falls back to a direct `Platform.OS` read, so it answers correctly * before init() has completed. */ private currentPlatform; /** * Initialize the SDK. Idempotent: concurrent calls await the same in-flight * run, and a completed init() returns immediately. shutdown() releases the * latch, so shutdown() → init() re-initializes. */ init(): Promise; /** * True when a shutdown() landed after this init() run started. The caller * must return immediately. * * Without this gate init() ran to completion on an abandoned instance: it * built a second live core (the AsyncStorage rebuild below), installed the * AppState/NetInfo/deep-link listeners and the 300s config-poll timer on it, * and tracked a duplicate `app_open` — one orphaned SDK, with its own * timers, per remount. React 18 StrictMode mounts → unmounts → remounts * every effect, so the Expo `LayersProvider` (which calls shutdown() in * cleanup while init() is still awaiting) hit this on every dev launch. * * **This check has no side effects, deliberately.** It used to tear the * runtime down on any generation mismatch, which is wrong the moment two * runs overlap: init A → shutdown → init B completes → A finally reaches a * checkpoint and demolishes *B's* listeners, core and timer, leaving an SDK * that reported `isInitialized === true` and delivered nothing. Cleanup * belongs to shutdown() (which owns everything installed at the moment it * runs) and to {@link abandonRun} (which proves ownership by generation * first). */ private isRunAbandoned; /** * Abandon this run and release only the resources it installed itself. * * Every install site below is *synchronously* adjacent to its preceding * checkpoint — no `await` sits between "still current?" and the assignment — * so a shutdown() can never slip in between the two, and anything this run * did install was already torn down by that shutdown(). The ownership check * is what keeps that true if a future edit ever puts an await in between: * the run cleans up only while it is still the runtime's owner. */ private abandonRun; /** * Checkpoint used at every await boundary in runInit(): report whether this * run has been abandoned and, if so, release only what it still owns. */ private initAborted; private runInit; track(eventName: string, properties?: EventProperties): void; screen(screenName: string, properties?: EventProperties): void; /** * @internal Entry point for auto-capture integrations (the Expo provider's * router tracker). Identical to `screen()` except the view is kept out of * the SKAN engine: SKAN preset rules key off `screen_name`, and an * auto-captured stream of route views would otherwise change an app's iOS * conversion values the moment auto-capture turned on. Manual `screen()` * calls keep feeding SKAN exactly as before. */ _screenAutoCaptured(screenName: string, properties?: EventProperties): void; private screenImpl; setUserProperties(properties: UserProperties): Promise; setUserPropertiesOnce(properties: UserProperties): Promise; setConsent(consent: ConsentState): Promise; /** * Whether the app has explicitly denied advertising consent * (`adStorage: false`, or the legacy `advertising: false`). Unset means * allowed, the same default as the engine's ad gate and the Kotlin SDK. */ private advertisingConsentDenied; /** * Keep the Google Advertising ID on the device context unless the app has * explicitly denied advertising consent, and take it off when it has. * * The GAID is sent by default, as mobile measurement partners and the * Kotlin SDK do. It honours two opt-outs: the user's Limit Ad Tracking * setting (applied where the GAID is read) and the app's consent API. * Revoking removes it from later events and re-granting restores it. No * `att_status` is set on Android: that field carries Apple's ATT answer. */ private applyGaidConsent; /** Evaluate a feature flag. Emits `$feature_flag_called` (deduped per session). */ getFeatureFlag(flagKey: string): FeatureFlagValue | undefined; /** Convenience: returns true iff `getFeatureFlag(flagKey)` is truthy. */ isFeatureEnabled(flagKey: string): boolean; /** Look up the JSON payload attached to a flag. Does NOT emit exposure events. */ getFeatureFlagPayload(flagKey: string): T | undefined; /** Snapshot every accessible flag's current value. */ getAllFlags(): Record; /** Force a /config refresh and re-fire any registered listeners. */ reloadFeatureFlags(): Promise; /** Override person properties used for flag evaluation only. */ setPersonPropertiesForFlags(properties: Record): void; /** Merge additional person properties into the flag-evaluation map. */ mergePersonPropertiesForFlags(properties: Record): void; /** Subscribe to feature-flag refreshes. Returns an unsubscribe function. */ onFeatureFlags(callback: FeatureFlagsListener): () => void; /** Seed bootstrap flag values + payloads after init. */ setFeatureFlagBootstrap(bootstrap: FeatureFlagBootstrap): void; /** * Register one or more super-properties — auto-merged into every track / * screen call until cleared. Super-properties carry app-wide context * (plan tier, region, experiment bucket). */ setSuperProperties(properties: Record): void; /** Register super-properties only if their keys have not been set before. */ setSuperPropertiesOnce(properties: Record): void; /** Remove a single super-property by key. */ unregisterSuperProperty(key: string): void; /** Clear all registered super-properties. */ clearSuperProperties(): void; /** Snapshot the currently-registered super-properties. */ getSuperProperties(): Record; /** Start a duration timer for the next `track(name)` call. */ timeEvent(eventName: string): void; /** Cancel a timed event without emitting it. Returns elapsed ms (0 if none). */ cancelTimedEvent(eventName: string): number; /** Set membership for a single group_type. Empty `groupId` removes the type. */ setGroup(groupType: string, groupId: string): void; /** Add a group membership without overwriting other types (alias for setGroup). */ addGroup(groupType: string, groupId: string): void; /** Remove a group_type from the membership map. */ removeGroup(groupType: string): void; /** Snapshot the current $groups membership map. */ getGroups(): GroupsState; /** Increment a numeric user property by `delta`. */ increment(key: string, delta?: number): void; /** Append a single value to a list-typed user property. */ append(key: string, value: unknown): void; /** Union (set-add) values into a list-typed user property. */ union(key: string, values: unknown[]): void; /** Remove a user property by key. */ unset(key: string): void; /** Returns the current anonymous ID, rotated on `reset()`. */ getAnonymousId(): string | null; /** Returns the current device ID (stable per install, rotated on `reset()`). */ getDeviceId(): string | null; /** Returns the monotonically-increasing session number. */ getSessionNumber(): number; /** Returns the SDK first-open RFC3339 timestamp, or null. */ getFirstOpenTime(): string | null; /** * Register a synchronous filter callback called for every track / screen * before the event reaches the queue. Return `null` to drop, or a modified * `BeforeSendEvent` to forward. * * Pass `null` to clear the hook. */ setBeforeSend(hook: BeforeSendHook | null): void; /** * Request App Tracking Transparency authorization (iOS only). * After the user responds, this method automatically: * - Collects IDFA if authorized * - Updates device info with IDFA and ATT status * * ATT controls IDFA availability only. This method does not change Layers * consent; use setConsent() explicitly when the app wants to do that. * * Returns the ATT status string. */ requestTrackingPermission(): Promise; /** * Set the app user ID. Uses set-user-once semantics: * once set, subsequent calls are ignored until clearAppUserId() is called. */ setAppUserId(appUserId: string): void; clearAppUserId(): void; /** * Reset the SDK state: clears the user ID, unlocks set-user-once, drops * super-properties + multi-group memberships, rotates device / anonymous * IDs, and starts a new session. * * After reset(), the SDK behaves as if no user has been identified. Events * tracked after reset() will not carry the previous user's identity. */ reset(): void; /** * Associate all subsequent events with a group (company, team, organization). * Pass `undefined` or empty string to clear the group association. */ group(groupId: string | undefined, properties?: EventProperties): void; getAppUserId(): string | undefined; getSessionId(): string; getConsentState(): ConsentState; setDeviceInfo(deviceInfo: DeviceInfoUpdate): void; flush(): Promise; /** * Flush all queued events synchronously (blocking). * * Drains all batches from the queue and sends them via HTTP, awaiting * each batch before proceeding. Does not return until all batches have * been sent or failed. * * This is useful for AppState background transitions where the app may * be suspended shortly after the call returns. */ flushBlocking(): Promise; /** * Register an error listener. Errors from track/screen/flush * that would otherwise be silently dropped are forwarded here. */ on(event: 'error', listener: ErrorListener): this; /** * Remove a previously registered error listener. */ off(event: 'error', listener: ErrorListener): this; /** * Set a listener to receive SDK initialization timing metrics. * Must be called **before** `init()` to receive the callback. * Pass `null` to clear the listener. * * @param listener A function receiving `(mainThreadDurationMs, totalDurationMs)`. * `mainThreadDurationMs` is the time spent before background work begins. * `totalDurationMs` is the total wall-clock time of the `init()` call. */ setInitListener(listener: InitListener | null): void; /** * Returns the AdServices attribution token (iOS only), or null if not * available. Collected automatically during init() on iOS 14.3+. * Does NOT require ATT consent. */ getAdServicesToken(): string | null; /** * Returns the Google Play install referrer data (Android only), or null * if not available. Collected automatically during init() on Android. */ getInstallReferrer(): InstallReferrerData | null; /** * The deferred deep link for this install — the payload of the Layers * tracking link (`in.layers.com/l/tlnk_…`) the user tapped before installing — * or null. * * One-shot per install: the first call (or {@link onDeferredDeepLink} * listener) that receives it marks it delivered, persistently, and every * later call on this install returns null. A link found on one launch and * not yet taken stays pending across launches until something takes it. * * Waits for an in-flight `init()` or {@link resolveDeferredDeepLink} first, * so calling it straight after `init()` sees what init found. */ getDeferredDeepLink(): Promise; /** * Call `listener` once, when this install's deferred deep link becomes known * — immediately (asynchronously) if it was already known and not yet * delivered when you subscribed. Fires at most once per install across all * listeners and {@link getDeferredDeepLink}: whichever receives it first * marks it delivered. Returns an unsubscribe function. */ onDeferredDeepLink(listener: DeferredDeepLinkListener): () => void; /** * Look for this install's deferred deep link, then return it as * {@link getDeferredDeepLink} does (consuming it). * * `init()` already does this work when `autoTrackAppOpen` is on (the * default). Apps that turn `autoTrackAppOpen` off call this after `init()`: * * 1. Android: reads the Play install referrer's `deeplink` param (local, no * network). * 2. Only where the existing gates already allow it: `POST /clicks/resolve` * on a fresh install (installed within 24 hours, no prior probe, no click * id already known). That probe sends device signals, so it runs only when * the server's `fingerprint_resolve_enabled` remote config is `true` * (default off) AND the core's consent / DNT / Retry-After / circuit * breaker gate is open. This method opens neither gate. * * Safe to call more than once; concurrent calls share one run, and the * probe goes out at most once per install. */ resolveDeferredDeepLink(): Promise; private runDeferredDeepLinkResolve; /** * Claim this install's single `/clicks/resolve` probe. The in-memory flag is * taken synchronously, before any await, so two overlapping callers cannot * both pass; the persisted flag carries the claim across launches and is * written before the request, so a timeout or a kill mid-flight cannot earn * the install a second fingerprint. */ private claimClickResolveProbe; /** The server-driven privacy switches, read from the core's cached config. */ private readPrivacySwitches; private ensureDeferredDeepLinkRestored; private restoreDeferredDeepLink; private persistDeferredDeepLink; /** Record the referrer's `deeplink` payload, if it carries one. Never throws. */ private recordReferrerDeepLink; /** * Keep the first deferred deep link this install finds. A later source never * replaces it, and an install that already delivered one records nothing. */ private recordDeferredDeepLink; private deliverDeferredDeepLinkToListeners; /** * Returns the auto-configured SKANManager instance, or null if SKAN was not * configured by the server's remote config. * On iOS, when remote config contains a `skan` section with `preset` or * `customRules`, the SDK automatically creates and configures a SKANManager. * Every `track()` call is automatically forwarded to SKAN rule evaluation. */ getSkanManager(): SKANManager | null; /** Whether the SDK has completed async initialization. */ get isInitialized(): boolean; /** The configured environment ('development' | 'staging' | 'production'). */ get environment(): Environment; /** The configured app ID. */ get appId(): string; /** Number of events currently queued. */ getQueueDepth(): number; /** * Return the per-device DebugView token, or null if `debug` was not enabled * in the constructor config. * * When debug mode is on the SDK persists a stable UUID via AsyncStorage * and sends it in the `X-Debug-Token` header on every event upload. This * lets dashboard UIs filter the live tail to events from this device only. */ getDebugToken(): string | null; /** Current device context from the core. */ getDeviceContext(): DeviceContext; /** Whether the SDK believes the device is online. */ getNetworkStatus(): boolean; /** SDK version string. */ getSdkVersion(): string; /** * Returns the most recent tracked events (newest first). * Each entry is a formatted string like "12:34:56 event_name (3 props)". */ getRecentEvents(): readonly string[]; /** * POST a device fingerprint to /clicks/resolve so the server can match this * first-launch install to a recent /c/:appId click captured in the browser * before the App Store / Play Store redirect. On a successful match, persist * the returned click IDs via setAttributionData so every subsequent event * (app_open, purchase_success, etc.) carries fbclid / gclid / ttclid / etc. * * This is the primary iOS web-to-app attribution path, since iOS has no * equivalent of Android's Play Install Referrer. Also serves as a fallback * for Android installs where the Install Referrer API returned nothing * (sideload, Amazon Appstore, testing). * * Best-effort: all errors are swallowed — this must never block app_open. * * Two gates, both required. The caller checks the `fingerprint_resolve_enabled` * remote-config switch; this method checks the core's delivery policy via * `shouldAttemptSideRequest()`, so a device that denied analytics consent, is * sending DNT, is inside a server Retry-After window, or is behind an open * circuit breaker sends no fingerprint. The gate lives HERE, not only at the * call site, so a future caller inherits it. */ private resolveClickFromFingerprint; /** * Fire-and-forget POST to /users/properties. * Best-effort: errors are silently swallowed. */ private sendUserPropertiesAsync; private recordRecentEvent; private emitError; shutdown(): void; /** * Make a fatal error's `$exception` durable before React Native's crash * path ends the process. The core's `flush()` snapshots the queue to the * persistence backend, whose AsyncStorage write is otherwise fire-and- * forget; this waits for that write to land. Delivery is left to the next * launch, which re-hydrates the snapshot and sends it, so the crashed app is * held open only for the write. */ private persistForCrash; /** * Resolves once every AsyncStorage write started so far has landed: the * durable backend's, and the eager store's mirror (the only durable copy of * pre-init events until the core swap). */ private settleWrites; /** * Snapshot the queue to disk within {@link PERSIST_THROTTLE_MS} of an * accepted event. * * The engine writes its crash-safety snapshot only at the flush threshold, * on a delivery verdict, on background and at shutdown. A short session * (the typical lost Android install: 13 events in 13 seconds) reached none * of those before the process died, so its events never left memory. * Throttled rather than per event: each snapshot serializes the whole * queue, and one write per burst also keeps same-key writes apart on * AsyncStorage v3, which does not order them. */ private schedulePersist; private cancelPersist; /** * Start a send owned by the wrapper: optionally wait on `before` (the * background path waits for its snapshot to land), then deliver if online. * Failures are reported through the error listeners and never thrown into * the AppState callback that started it. */ private startWrapperFlush; private waitForWrapperFlush; /** * Make the queue durable, then try to send it. Used at the end of init() * and on the background transition. * * The snapshot is taken synchronously, and the send starts only once that * write has landed. The order matters: the send drains the batch out of the * queue, and a process killed mid-request must still find it on disk next * launch. Before this the background path took the snapshot and stopped, * so nothing was delivered until the app came back to the foreground, and * an Android app is usually killed rather than resumed. */ private persistThenSend; /** * Remove every listener and timer this instance installed, then shut the * core down. Shared by shutdown() and the mid-init abort path, so it must be * safe to call twice — every handle is nulled and the core's own shutdown() * is a no-op once it has run. */ private teardownRuntime; /** * Store attribution data that will be attached to all subsequent events. * * The values are persisted in AsyncStorage so they survive app restarts. * Pass `null` to clear a value. * * When set, click IDs (`gclid`, `fbclid`, `ttclid`, `msclkid`) are included * in every event's properties. For fbclid, a formatted `$fbc` parameter * (fb.1.{timestamp}.{fbclid}) is also included. * * @param deeplinkId Deep link identifier for server-side attribution matching. * @param gclid Google Click Identifier from ad click URLs. * @param fbclid Facebook Click Identifier from ad click URLs. * @param ttclid TikTok Click Identifier from ad click URLs. * @param msclkid Microsoft Click Identifier from ad click URLs. */ setAttributionData(deeplinkId?: string | null, gclid?: string | null, fbclid?: string | null, ttclid?: string | null, msclkid?: string | null): Promise; /** * Merge attribution properties (click IDs) into the given event properties, * if any attribution data is set. `deeplink_id` flows through DeviceContext * on the Rust core (set via `setAttributionData`), not through properties. */ private mergeAttributionProperties; /** * Restore persisted attribution data from AsyncStorage. * Called during initialization to survive app restarts. */ private restoreAttributionData; private _trackedDeepLinkUrls; private setupDeepLinkAutoTracking; /** * Record that this install's first launch has been decided, whichever way. * Best-effort: without AsyncStorage nothing persists anyway. */ private persistFirstLaunchHandled; /** * The package's first install time, for the 24-hour install gate. A method * so tests can drive the gate: the native module sits behind a real CJS * `require('react-native')` (same seam as `collectInstallReferrer`). */ private readFirstInstallTime; private initializeDeviceInfo; /** * Read the Google Play install referrer. * * A method rather than a direct call to `getInstallReferrer()` so tests can * drive the Android branch: the SDK reaches React Native through a real CJS * `require('react-native')`, which no module mock intercepts — that is why * this path had no test coverage until now. Same seam * `requireReactNative()` provides for the AppState listener. */ private collectInstallReferrer; /** Read the Google Advertising ID. A method for the same test seam. */ private collectGoogleAdvertisingId; /** * Emit the canonical `install_referrer` event (Android only). * * Wire parity with Kotlin (`InstallReferrerTracker`) and Unity * (`AndroidModule.GetInstallReferrer`): one dedicated event carrying the raw * referrer under `referrer`, the ReferrerDetails timestamps/version/instant * flag, and every parsed attribution param — pinned by * `schema/fixtures/install-referrer-event.json`. * * Emitted at most once per install (see `installReferrerTrackedKey`), before * `app_install` / `app_open`, so the attribution signal reaches the server * ahead of the events it explains. * * The click-ID hand-off to `setAttributionData` that Kotlin performs after * emitting already happens in `initializeDeviceInfo`, which must run before * `restoreAttributionData`'s values are consumed by any event. */ private emitInstallReferrerEvent; /** * The `react-native` module as this package resolves it. * * A method rather than 16 scattered `require` calls at the lifecycle sites, * so the module the listener binds to and the module the reachability check * inspects are provably the same one. */ private requireReactNative; private setupAppStateListener; /** * Ask, one tick later, whether the listener just registered can ever fire. * * `AppState.addEventListener` hands back a subscription object no matter * which copy of react-native you call it on, and a duplicate copy's * `RCTDeviceEventEmitter` is never fed by the native side — so the * subscription is real, well-formed, and permanently silent. That is the * exact state a release build of examples/expo was in: registration logged * success on every launch while `$app_background`, `$app_foreground` and * the crash-safety snapshot had not run once. Nothing in the SDK could see * it, and nothing in the app could either. * * The check is DEFERRED by a tick because it reads `AppRegistry`, which the * host populates from its entry file. An SDK constructed at module scope * ahead of `AppRegistry.registerComponent` would otherwise look like a * duplicate to itself — a false alarm about a healthy integration, which is * a worse failure than the one being fixed. */ private scheduleLifecycleReachabilityCheck; private cancelLifecycleReachabilityCheck; private configureSkanFromRemoteConfig; private startConfigPolling; private stopConfigPolling; private pollRemoteConfig; private setupNetInfoListener; } declare function getOrSetInstallId(): Promise; /** * Read the app's first install time from the native module. * Returns milliseconds since epoch, or null if not available. * * - Android: reads PackageInfo.firstInstallTime via PackageManager * - iOS: reads the Documents directory creation date as a proxy */ declare function getFirstInstallTime(): Promise; /** * Determine whether this is a genuine new install or an existing app that * just added the Layers SDK. * * This is a pure function that takes pre-resolved inputs — the caller is * responsible for reading and persisting the `FIRST_LAUNCH_TRACKED_KEY` * flag in AsyncStorage. This mirrors the Android/Swift implementations * where the caller manages persistence. * * Logic (mirrors Android's `shouldTreatAsNewInstall`): * 1. If `isFirstLaunchByFlag` is `false` (flag already tracked), return `false`. * 2. If the SDK had prior state (`install_id` already existed in * AsyncStorage), trust the flag — this is a returning user whose * first-launch flag was not yet written (e.g. a first launch that died * before the flag write). Return `true` — UNLESS `priorLaunchDecided` * says an earlier launch already reached the decision and did not record * it (a launch on 3.3.2 or earlier that suppressed `app_install`, see the * caller). Then return `false`. * 3. If the SDK had NO prior state AND the app was installed more than * 24 hours ago, this is an existing app getting the SDK for the first * time — suppress `is_first_launch`. Return `false`. * 4. If the SDK had no prior state AND the app was installed within 24 * hours, this is a genuine new install — allow `is_first_launch`. * Return `true`. * * @param isFirstLaunchByFlag Whether the first_launch flag has NOT been set yet (true = not yet tracked). * @param hadPriorSdkState Whether `install_id` already existed before init. * @param enableDebug Whether to log debug info. * @param priorLaunchDecided Whether an earlier launch already made the * install decision without writing the flag. */ declare function shouldTreatAsNewInstall(isFirstLaunchByFlag: boolean, hadPriorSdkState: boolean, enableDebug?: boolean, _getInstallTime?: () => Promise, priorLaunchDecided?: boolean): Promise; interface ClipboardAttribution { clickUrl: string; clickId: string; } declare function readClipboardAttribution(): Promise; interface GoogleAdvertisingInfo { id: string; isLimitAdTrackingEnabled: boolean; } /** * Fetch the Google Advertising ID (GAID) on Android via NativeModules. * Returns null on iOS or when the native module is not available. */ declare function getGoogleAdvertisingId(): Promise; /** * Request the AdServices attribution token on iOS 14.3+. * Uses the LayersAdServices native module which calls AAAttribution.attributionToken(). * Does NOT require ATT consent. * Returns null on Android or when the native module is not available. */ declare function getAdServicesToken(): Promise; interface InstallReferrerData { referrerUrl: string; referrerClickTimestamp?: number; installBeginTimestamp?: number; /** ReferrerDetails.getReferrerClickTimestampServerSeconds() */ referrerClickTimestampServer?: number; /** ReferrerDetails.getInstallBeginTimestampServerSeconds() */ installBeginTimestampServer?: number; /** ReferrerDetails.getInstallVersion() */ installVersion?: string; /** ReferrerDetails.getGooglePlayInstantParam() */ googlePlayInstant?: boolean; } /** * Parse a raw Play Install Referrer string into a map of recognized attribution * params. Accepts either a bare query string (`a=1&b=2`) or a full URL. * Returns an empty object on parse failure. */ declare function parseInstallReferrerUrl(referrer: string): Record; /** * Build the canonical `install_referrer` event properties. * * This is the same shape Kotlin's `InstallReferrerTracker.buildReferrerProperties` * and Unity's `InstallReferrerResult.ToEventProperties` produce: seven base * fields from `ReferrerDetails`, then every recognized attribution param parsed * out of the raw referrer merged on top. * * All seven base fields are always present, with the same defaults the Android * API hands back when a value is absent (`0`, `""`, `false`) — an SDK that * omitted a key on one install and included it on the next would give the * server two shapes for one event, which is the drift this unification exists * to end. `schema/fixtures/install-referrer-event.json` pins it, and the Kotlin * contract test asserts against the same file. */ declare function buildInstallReferrerProperties(referrer: InstallReferrerData): Record; /** * Where a deferred deep link came from. * * - `install_referrer` — the `deeplink` param the Layers click page writes into * the Google Play install referrer (Android). * - `click_resolve` — the `deeplink` field `POST /clicks/resolve` returns on a * match. Only runs where the existing `fingerprint_resolve_enabled` remote * config switch and the core's consent/delivery gate already allow the probe; * both are off by default, and nothing here turns them on. * - `clipboard` — reserved. The iOS pasteboard carries only a click id * (`https://in.layers.com/c/`), and no server endpoint yet turns a * pasted click id into a payload, so no current path produces this source. * Clipboard attribution remains opt-in and off. */ type DeferredDeepLinkSource = 'install_referrer' | 'click_resolve' | 'clipboard'; /** * The payload of the Layers tracking link (`in.layers.com/l/tlnk_…`) that led * to this install, delivered once per install. * * Treat `payload` as untrusted input, exactly like any URL the app is opened * with: route on known keys and validate values before acting on them. */ interface DeferredDeepLink { /** The link's flat key/value payload (at most 24 keys, 512 bytes server-side). */ payload: Record; source: DeferredDeepLinkSource; /** The Layers click id, when the source carried one. */ clickId?: string; } type DeferredDeepLinkListener = (link: DeferredDeepLink) => void; /** * The tracking-link payload carried in a Play Install Referrer string's * `deeplink` param (JSON, form-urlencoded, as the Layers click page writes it), * or null when there is none. * * Kept apart from {@link parseInstallReferrerUrl} on purpose: that map feeds * the `install_referrer` event and `app_open` properties, whose shape is pinned * by `schema/fixtures/install-referrer-event.json` across every SDK. */ declare function parseInstallReferrerDeepLink(referrer: string): Record | null; /** * Fetch the Google Play install referrer on Android via NativeModules. * Requires the `com.android.installreferrer` library on the native side. * Returns null on iOS or when the native module is not available. */ declare function getInstallReferrer(): Promise; /** * Format a raw fbclid into the Meta Conversions API `$fbc` parameter format. * * The format is: `fb.{subdomainIndex}.{creationTime}.{fbclid}` * - `subdomainIndex`: always `1` for app SDKs * - `creationTime`: Unix timestamp in milliseconds when the fbclid was captured * - `fbclid`: the raw Facebook Click Identifier * * The timestamp is captured once at format time so that every event carries * the same `$fbc` value (the capture time, not the event time). * * @see https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc */ declare function formatFbc(fbclid: string, timestampMs?: number): string; interface DeepLinkData { url: string; scheme: string; host: string; path: string; queryParams: Record; timestamp: number; } declare function setupDeepLinkListener(onDeepLink: (data: DeepLinkData) => void): () => void; declare function parseDeepLink(url: string): DeepLinkData | null; interface ExpoRouterTrackingOptions { /** * `'auto'` marks views recorded by an auto-capture integration (the Expo * provider's router tracker). They are tracked like any screen view but * are kept out of the SKAN engine, so turning auto-capture on cannot change * an app's iOS conversion values. Defaults to `'manual'`. */ source?: 'auto' | 'manual'; /** * The resolved pathname (`/profile/lin`) when `usePathname` returns * something else, such as the route pattern the Expo provider names screens * by. It is the deduplication key, so the provider's auto-captured view and * a manual hook call on the same route count once. Defaults to the * `usePathname` value. */ resolvedPath?: string; } /** * Hook that emits `screen_view` for every Expo Router pathname change. * * One view per route per SDK instance: the deduplication key is the resolved * pathname alone. A param-only change on the same route (a search box or * filter chips mirrored into the URL) is the same screen and is not * re-tracked, and the Expo provider's auto-captured view and a manual call on * the same route count once, whichever effect runs first. * * The hook also tracks the previous screen name so the emitted event carries * `previous_screen_name`, matching the React Navigation observer in * `useLayersNavigationTracking`. This makes funnel analysis trivial * server-side: every screen event has a self-contained pair `(from, to)` * — no need to join adjacent rows. */ declare function useLayersExpoRouterTracking(sdkInstance: LayersReactNative | null | undefined, usePathname: () => string, useGlobalSearchParams: () => Record, options?: ExpoRouterTrackingOptions): void; //#endregion export { buildInstallReferrerProperties as $, trackBeginCheckout as $t, LayersError as A, screenViewEvent as An, resolveActiveRoute as At, SurveyDefinition$1 as B, connectRevenueCat as Bt, FeatureFlagValue$1 as C, levelStartEvent as Cn, ExceptionInstallOptions as Ct, GroupsState$1 as D, paywallShowEvent as Dn, installExceptionAutoCapture as Dt, GoogleAdvertisingInfo as E, onboardingStartEvent as En, buildExceptionProperties as Et, PurchaseEventParams as F, subscribeEvent as Fn, createBackgroundFlushHandler as Ft, SurveyTargeting as G, CartItem as Gt, SurveyPosition as H, resetRevenueCatForTesting as Ht, RefundEventParams as I, tutorialCompleteEvent as In, registerExpoBackgroundFlush as It, TypedEventPayload as J, PurchaseFailedParams as Jt, SurveyTargetingContext as K, CommerceTracker as Kt, StartTrialEventParams as L, viewContentEvent as Ln, RevenueCatConfig as Lt, LayersEventName as M, shareEvent as Mn, BACKGROUND_FLUSH_MIN_INTERVAL_MINUTES as Mt, LayersRNConfig as N, signUpEvent as Nn, BACKGROUND_FLUSH_TASK_NAME as Nt, InitListener as O, purchaseEvent as On, NavigationTrackingHooks as Ot, LayersReactNative as P, startTrialEvent as Pn, BackgroundFlushResult as Pt, buildBeginCheckoutEvent as Q, trackAddToCart as Qt, SubscribeEventParams as R, viewItemEvent as Rn, RevenueCatCustomerInfo as Rt, FeatureFlagDefinition as S, levelCompleteEvent as Sn, SurveysModuleOptions as St, FeatureFlagsListener$1 as T, onboardingCompleteEvent as Tn, ExceptionSink as Tt, SurveyQuestion as U, syncRevenueCatAttributes as Ut, SurveyDisplay as V, isRevenueCatConnected as Vt, SurveyResponse$1 as W, trackRevenueCatPurchase as Wt, ViewItemEventParams as X, RefundParams as Xt, UserProperties$1 as Y, PurchaseParams as Yt, buildAddToCartEvent as Z, SubscriptionParams as Zt, EventProperties$1 as _, StandardEventPayload as _n, readClipboardAttribution as _t, BeginCheckoutEventParams as a, trackSubscription as an, buildStartTrialEvent as at, FeatureFlagBootstrapData as b, addToWishlistEvent as bn, useLayersExpoRouterTracking as bt, DeepLinkData as c, SuperwallPaywallInfo as cn, formatFbc as ct, DeferredDeepLinkSource as d, superwallTrackDismiss as dn, getGoogleAdvertisingId as dt, trackOrder as en, buildOnboardingCompleteEvent as et, DeviceContext$1 as f, superwallTrackPresentation as fn, getInstallReferrer as ft, ErrorListener as g, StandardEventName as gn, parseInstallReferrerUrl as gt, Environment$1 as h, superwallUserAttributes as hn, parseInstallReferrerDeepLink as ht, BeforeSendHook$1 as i, trackRemoveFromCart as in, buildRefundEvent as it, LayersEvent as j, searchEvent as jn, useLayersNavigationTracking as jt, InstallReferrerData as k, registerEvent as kn, createNavigationListener as kt, DeferredDeepLink as l, SuperwallProduct as ln, getAdServicesToken as lt, EcommerceItem as m, superwallTrackSkip as mn, parseDeepLink as mt, AppStateLike as n, trackPurchaseFailed as nn, buildPaywallShowEvent as nt, ClipboardAttribution as o, trackViewProduct as on, buildSubscribeEvent as ot, DeviceInfoUpdate as p, superwallTrackPurchase as pn, getOrSetInstallId as pt, SurveyType as q, OrderParams as qt, BeforeSendEvent as r, trackRefund as rn, buildPurchaseEvent as rt, ConsentState$1 as s, SuperwallEventInfo as sn, buildViewItemEvent as st, AddToCartEventParams as t, trackPurchase as tn, buildOnboardingStartEvent as tt, DeferredDeepLinkListener as u, superwallOnEvent as un, getFirstInstallTime as ut, ExpoRouterTrackingOptions as v, StandardEvents as vn, setupDeepLinkListener as vt, FeatureFlagVariant as w, loginEvent as wn, ExceptionProperties as wt, FeatureFlagCondition as x, initiateCheckoutEvent as xn, SurveysModule as xt, FeatureFlagBootstrap$1 as y, addToCartEvent as yn, shouldTreatAsNewInstall as yt, SurveyAnswer as z, RevenueCatPackage as zt }; //# sourceMappingURL=index-CodK28vr.d.mts.map