react-native-revnix 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +766 -0
  3. package/dist/blocks/background.d.ts +101 -0
  4. package/dist/blocks/background.js +165 -0
  5. package/dist/blocks/color.d.ts +88 -0
  6. package/dist/blocks/color.js +329 -0
  7. package/dist/blocks/locale.d.ts +42 -0
  8. package/dist/blocks/locale.js +263 -0
  9. package/dist/blocks/selection.d.ts +49 -0
  10. package/dist/blocks/selection.js +109 -0
  11. package/dist/blocks/style.d.ts +93 -0
  12. package/dist/blocks/style.js +365 -0
  13. package/dist/blocks/tags.d.ts +12 -0
  14. package/dist/blocks/tags.js +121 -0
  15. package/dist/blocks/types.d.ts +301 -0
  16. package/dist/blocks/types.js +44 -0
  17. package/dist/client.d.ts +402 -0
  18. package/dist/client.js +1653 -0
  19. package/dist/device.d.ts +29 -0
  20. package/dist/device.js +137 -0
  21. package/dist/errors.d.ts +39 -0
  22. package/dist/errors.js +68 -0
  23. package/dist/esm/blocks/background.js +160 -0
  24. package/dist/esm/blocks/color.js +318 -0
  25. package/dist/esm/blocks/locale.js +255 -0
  26. package/dist/esm/blocks/selection.js +100 -0
  27. package/dist/esm/blocks/style.js +356 -0
  28. package/dist/esm/blocks/tags.js +117 -0
  29. package/dist/esm/blocks/types.js +40 -0
  30. package/dist/esm/client.js +1649 -0
  31. package/dist/esm/device.js +130 -0
  32. package/dist/esm/errors.js +59 -0
  33. package/dist/esm/iap.js +101 -0
  34. package/dist/esm/implicit.js +63 -0
  35. package/dist/esm/index.js +29 -0
  36. package/dist/esm/package.json +1 -0
  37. package/dist/esm/storage.js +78 -0
  38. package/dist/esm/types.js +4 -0
  39. package/dist/esm/ui/RevnixBlockPaywall.js +706 -0
  40. package/dist/esm/ui/RevnixPaywall.js +890 -0
  41. package/dist/esm/ui/index.js +6 -0
  42. package/dist/esm/version.js +4 -0
  43. package/dist/iap.d.ts +20 -0
  44. package/dist/iap.js +104 -0
  45. package/dist/implicit.d.ts +69 -0
  46. package/dist/implicit.js +67 -0
  47. package/dist/index.d.ts +18 -0
  48. package/dist/index.js +50 -0
  49. package/dist/storage.d.ts +14 -0
  50. package/dist/storage.js +85 -0
  51. package/dist/types.d.ts +463 -0
  52. package/dist/types.js +5 -0
  53. package/dist/ui/RevnixBlockPaywall.d.ts +96 -0
  54. package/dist/ui/RevnixBlockPaywall.js +742 -0
  55. package/dist/ui/RevnixPaywall.d.ts +148 -0
  56. package/dist/ui/RevnixPaywall.js +927 -0
  57. package/dist/ui/index.d.ts +7 -0
  58. package/dist/ui/index.js +25 -0
  59. package/dist/version.d.ts +1 -0
  60. package/dist/version.js +7 -0
  61. package/package.json +79 -0
  62. package/src/blocks/background.ts +232 -0
  63. package/src/blocks/color.ts +352 -0
  64. package/src/blocks/locale.ts +271 -0
  65. package/src/blocks/paywall-background-wire.json +12 -0
  66. package/src/blocks/paywall-selection-wire.json +149 -0
  67. package/src/blocks/selection.ts +161 -0
  68. package/src/blocks/style.ts +411 -0
  69. package/src/blocks/tags.ts +125 -0
  70. package/src/blocks/types.ts +352 -0
  71. package/src/client.ts +1993 -0
  72. package/src/device.ts +146 -0
  73. package/src/errors.ts +69 -0
  74. package/src/iap.ts +137 -0
  75. package/src/implicit.ts +129 -0
  76. package/src/index.ts +82 -0
  77. package/src/storage.ts +92 -0
  78. package/src/types.ts +528 -0
  79. package/src/ui/RevnixBlockPaywall.tsx +1423 -0
  80. package/src/ui/RevnixPaywall.tsx +1542 -0
  81. package/src/ui/index.ts +43 -0
  82. package/src/version.ts +4 -0
  83. package/ui.d.ts +1 -0
  84. package/ui.js +5 -0
@@ -0,0 +1,402 @@
1
+ import type { AdRevenue, AliasResult, CustomerEntitlements, IdentifyResult, IdentifyStrategy, IdentityInfo, LastDeepLink, MmpAttribution, PaywallInteraction, PlacementResolution, RegisterPurchaseInput, RegisterPurchaseResult, RevnixAttribution, RevnixConfig } from "./types.js";
2
+ export declare class RevnixClient {
3
+ private readonly apiKey;
4
+ private readonly baseUrl;
5
+ private readonly storage;
6
+ private readonly fetchImpl;
7
+ private readonly reportInstalls;
8
+ private readonly persistentStorage;
9
+ /** True while the current id is a generated anon id living on
10
+ * NON-persistent storage — it changes every launch, so sending it to
11
+ * the experiments path would mint a phantom enrollment per launch
12
+ * (same hazard class the M5 install guard covers). */
13
+ private ephemeralAnonId;
14
+ private readonly installMetadata?;
15
+ private readonly platform;
16
+ private readonly deviceHeaderPromise;
17
+ private readonly requestTimeoutMs;
18
+ private readonly readYourWritesDelaysMs;
19
+ private readonly offlineMaxCacheAgeMs;
20
+ private readonly entitlementsTtlMs;
21
+ private readonly onDiagnostic?;
22
+ private bgFailures;
23
+ private disposed;
24
+ private customerIdPromise;
25
+ private readonly onImplicitPaywall?;
26
+ private readonly onDeferredDeepLink?;
27
+ private readonly onAttribution?;
28
+ private deferredDeepLinkClaimed;
29
+ private landingUrl;
30
+ private lastDeepLinkWrite;
31
+ private readonly implicitEnabled;
32
+ private readonly sessionTimeoutMs;
33
+ private implicitConfigPromise;
34
+ private implicitConfigRetryAt;
35
+ private lifecycleUnsubscribe;
36
+ /** The cold-start batch while it runs, resolving to whether it presented a
37
+ * paywall. A deep link delivered on the first frame — SwiftUI's onOpenURL,
38
+ * RN's getInitialURL — must wait for it, or the customer gets the launch
39
+ * paywall AND the link paywall stacked. Null once the batch is over. */
40
+ private launchBatch;
41
+ /** When the app last went to the BACKGROUND, or null while it has not.
42
+ * A return after `sessionTimeoutMs` away is a new session; anything sooner
43
+ * is an app switch. Measured from the background transition, not from the
44
+ * last return — the latter would mint a session after 35 minutes of
45
+ * continuous use plus a three-second switch. */
46
+ private lastBackgroundAt;
47
+ /** LOOP GUARD: view ids of displays that came FROM an implicit trigger. A
48
+ * paywall shown because a paywall was dismissed must not itself fire
49
+ * `paywall_decline`, or the customer is handed the same screen forever.
50
+ * Recognised from the placementKey the host reports on logPaywallShown —
51
+ * a host that reports no placementKey cannot be protected here, which is
52
+ * why the server keeps its own same-paywall backstop. */
53
+ private readonly implicitViewIds;
54
+ /** True when this launch generated a fresh anonymous id — the same signal
55
+ * /v1/installs uses, and independent of the device header (which an app
56
+ * can turn off entirely). */
57
+ private freshInstall;
58
+ constructor(config: RevnixConfig);
59
+ /** The id every call is made as: the stored identified/canonical id, or a
60
+ * generated-and-persisted anonymous id (rvx_anon_…) on first launch. */
61
+ getCustomerId(): Promise<string>;
62
+ private requireSecretKey;
63
+ /** Merges the current (usually anonymous) customer into `userId`. The
64
+ * canonical id from the response becomes the id all subsequent calls use.
65
+ * Safe to call repeatedly — a no-op when already identified as `userId`.
66
+ *
67
+ * Secret-key only — call this from YOUR BACKEND on login, not from the
68
+ * device. On a publishable key it throws RevnixAuthError locally, before
69
+ * any network call: identity merges from a device would let any caller
70
+ * claim any customer. Device flow: send `getCustomerId()` and your user id
71
+ * to your server, identify there with an rvx_sk_ key, and the device picks
72
+ * up the canonical id on its next registerPurchase() (its response always
73
+ * carries the caller's canonical id). */
74
+ identify(userId: string, options?: {
75
+ strategy?: IdentifyStrategy;
76
+ }): Promise<IdentifyResult>;
77
+ /** Attach an additional external id (legacy id, email hash, …) to the
78
+ * current customer without switching to it.
79
+ *
80
+ * Secret-key only, like identify() — throws RevnixAuthError locally on a
81
+ * publishable key; call it from your backend instead. */
82
+ alias(aliasId: string): Promise<AliasResult>;
83
+ /** Call on logout: forget the identified customer and start a fresh
84
+ * anonymous one. Never merges — purchases made while logged out belong
85
+ * to the new anonymous customer until the next identify(). */
86
+ logout(): Promise<string>;
87
+ /** Call right after a successful store purchase: binds the store token to
88
+ * the current customer and appends the purchase to the ledger, so
89
+ * entitlement never waits on the store webhook. Throws
90
+ * PurchaseBlockedError when the token belongs to another customer and the
91
+ * app's transfer behavior forbids reassignment. */
92
+ registerPurchase(input: RegisterPurchaseInput): Promise<RegisterPurchaseResult>;
93
+ private postPurchase;
94
+ /** C7: retry purchases that failed to register and were persisted locally.
95
+ * Runs automatically at construction; call again when connectivity returns.
96
+ * Idempotent and safe to call repeatedly. */
97
+ retryPendingPurchases(): Promise<void>;
98
+ private freshEntitlements;
99
+ private inflightEntitlements;
100
+ /** Current entitlements for this customer, merged across every id linked to
101
+ * them. Served from memory when a fresh network read is younger than
102
+ * `entitlementsTtlMs` (REV-199 — pass 0 to disable); otherwise reads the
103
+ * network (concurrent callers share one request) and caches the result; if
104
+ * that read FAILS transiently (offline / incident) it falls back to the
105
+ * last cached entitlements with `stale: true`, so a paying customer is
106
+ * never shown as unsubscribed just because the network is down. Throws on
107
+ * a deliberate server rejection (revoked key, …) or when there is nothing
108
+ * cached. */
109
+ getEntitlements(): Promise<CustomerEntitlements>;
110
+ /** The authoritative network read: no TTL, no cache fallback — throws so
111
+ * the caller sees the real failure (waitForEntitlements needs 429s). */
112
+ private fetchFreshEntitlements;
113
+ private fetchEntitlementsWithFallback;
114
+ /** M9: read-your-writes — poll getEntitlements until the response's ledger
115
+ * cursor is at least `seq` (i.e. the read provably includes that write),
116
+ * then resolve with it. Resolves with the LAST read if the delays run out
117
+ * or reads only come from the stale cache — never spins forever. Called
118
+ * automatically by registerPurchase; public for manual flows (e.g. after
119
+ * a webhook-driven grant the app learns about out-of-band). */
120
+ waitForEntitlements(seq: number): Promise<CustomerEntitlements>;
121
+ /** C7: the last entitlements cached for the current customer, with NO network
122
+ * call — for an instant, offline-safe gate at launch. Always `stale: true`;
123
+ * follow with getEntitlements() to revalidate. Null if nothing is cached. */
124
+ getCachedEntitlements(): Promise<CustomerEntitlements | null>;
125
+ /** REV-157: cached entitlements are a snapshot — re-evaluate expiry against
126
+ * the local clock when serving them, so a subscription that ended long ago
127
+ * never stays "active" offline forever. Active entitlements keep access
128
+ * for LOCAL_EXPIRY_GRACE_MS past expiresAt (the renewal an offline device
129
+ * cannot see yet). Inactive stays inactive; no expiresAt (lifetime/grant)
130
+ * is untouched. */
131
+ private withLocalExpiry;
132
+ /** Convenience: is one entitlement active right now? */
133
+ isEntitled(entitlementId: string): Promise<boolean>;
134
+ /** How the current id resolves: canonical id, alias status, linked ids.
135
+ *
136
+ * Secret-key only — the response is an identity graph, so the server 403s
137
+ * a publishable key (REV-184). On an rvx_pk_ key this throws
138
+ * RevnixAuthError locally, before any network call, like identify(). */
139
+ getIdentity(): Promise<IdentityInfo>;
140
+ /** Resolve a named in-app location to the currently published offering —
141
+ * the one-line paywall call. REV-157: reads the network first and caches
142
+ * the result; if that read FAILS (offline / incident) it falls back to the
143
+ * last cached resolution with `stale: true`, so the paywall renders the
144
+ * last published version rather than not at all. Throws only when there
145
+ * is nothing cached. */
146
+ resolvePlacement(placementKey: string): Promise<PlacementResolution>;
147
+ /** REV-157: the last resolution cached for `placementKey`, with NO network
148
+ * call — for an instant, offline-safe paywall at launch. Always
149
+ * `stale: true`; follow with resolvePlacement() to revalidate. Null if
150
+ * nothing is cached yet. */
151
+ getCachedPlacement(placementKey: string): Promise<PlacementResolution | null>;
152
+ /** Report that a paywall was displayed (REV-094). Fire-and-forget like
153
+ * install reporting — never blocks rendering and never throws; each call
154
+ * is one view (retries of the same display are deduped by viewId).
155
+ * RevnixPaywall calls this automatically when given a `client`; headless
156
+ * paywall UIs call it on their own show event. Powers the analytics
157
+ * funnel's "Paywall displayed" stage and View → Trial/Paid conversions.
158
+ *
159
+ * Returns the view id it generated (REV-252). Hold onto it and hand it to
160
+ * logPaywallClosed when the customer dismisses this display: the pair
161
+ * sharing one id is what lets the ledger tie a close to the display it
162
+ * ended, rather than guessing from timestamps. Ignoring the return value
163
+ * is still valid — it stays a fire-and-forget beacon. */
164
+ logPaywallShown(options?: {
165
+ placementKey?: string;
166
+ paywallId?: string;
167
+ }): string;
168
+ /** Report that the customer dismissed a paywall (REV-252) — the other half
169
+ * of a display's life. Fire-and-forget and idempotent per view id, exactly
170
+ * like logPaywallShown.
171
+ *
172
+ * Pass the id that logPaywallShown returned for THIS display. The two
173
+ * events then share a view id, so a reader can pair them, and the gap
174
+ * between their timestamps is the customer's dwell on the screen. Calling
175
+ * it with a fresh id still records a dismissal; it just cannot be tied
176
+ * back to the display it ended. RevnixPaywall does all of this for you.
177
+ *
178
+ * A close is a DECLINE. Do not report one for a display that ended in a
179
+ * purchase — with implicit placements on, a close is also the
180
+ * `paywall_decline` moment, and a win-back offer seconds after a successful
181
+ * purchase is the one thing an operator never means. RevnixPaywall only
182
+ * reports the close affordances, never a purchase-driven dismissal. */
183
+ logPaywallClosed(viewId: string, options?: {
184
+ placementKey?: string;
185
+ paywallId?: string;
186
+ }): void;
187
+ /** Report one of the six paywall interactions (REV-263) — what the customer
188
+ * DID on a display, between the `logPaywallShown` that opened it and the
189
+ * `logPaywallClosed` (or purchase) that ended it:
190
+ *
191
+ * - `selected` — a package was picked
192
+ * - `purchase_started` — checkout was started
193
+ * - `purchase_abandoned` — the customer backed out at the store sheet
194
+ * - `purchase_failed` — the store refused the payment
195
+ * - `restore` — restore purchases was tapped
196
+ * - `error` — the paywall itself failed (config, products, render)
197
+ *
198
+ * Fire-and-forget like the other beacons: never blocks, never throws.
199
+ *
200
+ * `viewId` is the id `logPaywallShown` returned for THIS display. Passing
201
+ * it is what threads the whole life of one impression together and puts
202
+ * the event on the paywall's own analytics row — a fresh id still records
203
+ * the event, it just cannot be tied back to the display.
204
+ *
205
+ * RevnixPaywall reports `selected`, `purchase_started`, `restore` and a
206
+ * no-products `error` for you. The purchase OUTCOME is yours: only the
207
+ * host performs the store call, so report `purchase_abandoned` /
208
+ * `purchase_failed` from your own StoreKit / Play error handling — or let
209
+ * RevnixPaywall do it by returning the outcome from `onPurchase`.
210
+ *
211
+ * `eventId` is the idempotency key and defaults to the display's `viewId`,
212
+ * which caps the report at one per display per event type. Pass one per
213
+ * occurrence (and reuse it across your own retries) to record every
214
+ * occurrence — e.g. a customer who fails, retries and fails again. */
215
+ logPaywallEvent(event: PaywallInteraction, viewId: string, options?: {
216
+ placementKey?: string;
217
+ paywallId?: string;
218
+ productId?: string;
219
+ /** Store error code — `purchase_failed` / `error`. */
220
+ code?: string;
221
+ message?: string;
222
+ /** Idempotency key for this occurrence; defaults to `viewId`. */
223
+ eventId?: string;
224
+ }): void;
225
+ /** Reports one ad impression's payout (POST /v1/ad-revenue).
226
+ * Fire-and-forget like the other beacons: never blocks, never throws. */
227
+ logAdRevenue(input: AdRevenue): void;
228
+ /** Reports one custom in-app event (POST /v1/events), not for purchases.
229
+ * Fire-and-forget like the other beacons: never blocks, never throws. */
230
+ track(event: string, properties?: Record<string, string | number | boolean>, options?: {
231
+ eventId?: string;
232
+ }): void;
233
+ /** PT11: forward an MMP's attribution callback (Adjust, AppsFlyer, …) so
234
+ * Revnix credits revenue to the right network/campaign. */
235
+ setAttribution(attribution: MmpAttribution): void;
236
+ /** Register this device's push token for uninstall measurement: Revnix
237
+ * sends a daily silent probe and records `app.uninstalled` when APNs/FCM
238
+ * report the token dead. iOS/Android only (no-op on web),
239
+ * fire-and-forget. */
240
+ setPushToken(token: string): void;
241
+ /** REV-272: the two moments that happen ON a paywall, with the loop guard
242
+ * applied. `fromPaywallId` travels so the server can refuse to hand back
243
+ * the very paywall being dismissed — the backstop for a host that reports
244
+ * no placementKey, and for an operator who pointed the rule at the source
245
+ * paywall by hand. */
246
+ private fireImplicitFromPaywall;
247
+ /** Set attributes on the current customer (REV-033 v2). Attributes are what
248
+ * A/B-test audiences target, so this is how a mobile-only app becomes able
249
+ * to run "US + UK users" tests: set `country`, `app_version`, `locale`, or
250
+ * any custom key you want to segment on.
251
+ *
252
+ * Pass `null` as a value to delete a key. Awaits the write — unlike the
253
+ * fire-and-forget telemetry calls — because the very next placement
254
+ * resolve may depend on it, and silently racing the audience check would
255
+ * make targeting look broken.
256
+ *
257
+ * Two server rules to know: `email` and `username` are reserved and only
258
+ * settable with a secret key from your own backend, and any attribute your
259
+ * backend already set is authoritative and cannot be changed from a device.
260
+ * Both throw rather than partially applying. */
261
+ setAttributes(attributes: Record<string, string | number | null>): Promise<void>;
262
+ /** REV-272: hand the SDK the URL that opened your app, from wherever you
263
+ * already receive it (Linking.getInitialURL / onNewIntent / your router).
264
+ * This is the one implicit moment the SDK cannot see for itself — the URL
265
+ * goes to your entry point, and an SDK intercepting it would be fighting
266
+ * your router.
267
+ *
268
+ * Fire-and-forget: never blocks, never throws. An ordinary link is
269
+ * always reported so its `link.*` facts land on the customer; it only
270
+ * presents a paywall when implicit placements are on AND `deeplink_open`
271
+ * is configured in the dashboard. A dashboard preview link is always
272
+ * handed to `onImplicitPaywall`. */
273
+ handleDeepLink(url: string): void;
274
+ /** Most recent deep link seen via `handleDeepLink` or a delivered
275
+ * deferred deep link. Null when none recorded. Never throws. */
276
+ getLastDeepLink(): Promise<LastDeepLink | null>;
277
+ /** AT11: the install-attribution verdict for this customer — `null` when
278
+ * none has been recorded yet (a normal cold-start race, matching the
279
+ * server's `installMatch: "unknown"`) or when the read fails. Fetches
280
+ * fresh on every call rather than caching in memory, since the point is
281
+ * to answer with whatever the server currently believes; a changed
282
+ * answer also reaches `onAttribution`, so a host that only wants updates
283
+ * need not call this at all. Never throws. */
284
+ getAttribution(): Promise<RevnixAttribution | null>;
285
+ private refreshAttribution;
286
+ private deliverAttribution;
287
+ private rememberDeepLink;
288
+ private presentPreview;
289
+ /**
290
+ * REV-283: hand over the Android Play Store install referrer.
291
+ *
292
+ * The deferred half of link attribution. A deep link only ever reaches an
293
+ * app that already exists, which leaves out the customers an ad actually
294
+ * won — they saw it, had no app, and installed one. Google Play carries a
295
+ * referrer string through that install, so on Android the answer is exact
296
+ * rather than guessed. (Apple ships no equivalent; that half stays an
297
+ * attribution provider's job.)
298
+ *
299
+ * Read it yourself with the Play Install Referrer library and pass the raw
300
+ * string — `utm_source=instagram&utm_medium=cpc` — exactly as you received
301
+ * it. The SDK cannot read it for you: it is an Android API behind a service
302
+ * binding, and a cross-platform SDK has no business linking it.
303
+ *
304
+ * Safe to call late, and safe to call twice. The Play API answers
305
+ * asynchronously, so the install has usually been reported already; the
306
+ * server absorbs the duplicate and seeds the campaign facts from it anyway.
307
+ * They are fill-only, so a deep link the customer has opened since is never
308
+ * overwritten.
309
+ *
310
+ * Fire-and-forget: never blocks, never throws.
311
+ */
312
+ handleInstallReferrer(referrer: string): void;
313
+ /**
314
+ * AT9: the Apple Search Ads half of install attribution. `AAAttribution
315
+ * .attributionToken()` is an iOS framework call with no JavaScript
316
+ * binding, so a React Native host mints the token in a native module and
317
+ * hands it here — this package cannot mint it itself.
318
+ *
319
+ * Unlike `handleInstallReferrer`, this resolves to the server's verdict
320
+ * (`InstallResponse.appleAttribution`) rather than returning void, because
321
+ * a freshly minted token is unregistered on Apple's side for a few
322
+ * seconds and the server reports that as `"pending"`. The native iOS SDK
323
+ * retries on its own in that case; a host driving this manually needs the
324
+ * verdict to know whether a fresh token on the next cold start is worth
325
+ * trying. `"resolved"` (Apple named a campaign) and `"organic"` (Apple
326
+ * answered, not an ASA install) are final. Resolves to `undefined` when a
327
+ * guard held or the request failed — safe to leave unawaited.
328
+ *
329
+ * Same guards as `handleInstallReferrer`, same reasons: bails when
330
+ * disposed, when the trimmed token is empty, and when `ephemeralAnonId` is
331
+ * set, since an id that changes every launch would file the campaign
332
+ * against a customer nobody will see again.
333
+ */
334
+ handleAttributionToken(token: string): Promise<string | undefined>;
335
+ /** Unwraps a link an email provider rewrote through its click-tracking
336
+ * domain into the deep link it points at. Never throws; returns `url`
337
+ * unchanged on any failure. The result can still be an http(s) URL if the
338
+ * chain could not be unwrapped, so check the scheme before routing on it. */
339
+ resolveDeepLink(url: string): Promise<string>;
340
+ private implicitConfig;
341
+ /** REV-272: report one implicit moment and present whatever it resolves to.
342
+ * Fire-and-forget in every direction — this runs on an app launch and on
343
+ * every return to the foreground, so it must never be able to throw into
344
+ * the host or delay a frame. */
345
+ private fireImplicit;
346
+ /** The trigger itself. Resolves to true when a paywall was handed to the
347
+ * host. `present` false still reports the moment (its ledger event is a
348
+ * fact either way) but hands nothing over — how the launch batch keeps a
349
+ * cold start to ONE paywall. Never throws past its caller's catch. */
350
+ private triggerImplicit;
351
+ private consumePreviousSessionMs;
352
+ /** REV-272: the launch-time moments, plus the foreground listener that
353
+ * `session_start` is built on.
354
+ *
355
+ * A cold start is always both a launch AND a session — an operator who
356
+ * configured only `session_start` still wants the first one — and it is an
357
+ * install too when this launch generated the anonymous id. All three are
358
+ * independent placements, so an app may have any subset configured and the
359
+ * others cost nothing. */
360
+ private startImplicit;
361
+ private onAppState;
362
+ /** REV-204 (L10): stop this client's background work (purchase retries,
363
+ * read-your-writes polls). Called automatically when Revnix.configure()
364
+ * replaces the singleton; call it yourself when discarding a manually
365
+ * constructed client. */
366
+ dispose(): void;
367
+ /** A block paywall reporting a paint string it could not read. Routed to the
368
+ * same sink as every other swallowed failure, so a host that already wired
369
+ * `onDiagnostic` needs no new wiring to see render fallbacks. */
370
+ reportRenderDiagnostic(message: string): void;
371
+ private diag;
372
+ private deliverDeferredDeepLink;
373
+ private loadCustomerId;
374
+ /** Fire-and-forget install report (REV-093). Never blocks startup and
375
+ * never throws — the backend absorbs retries idempotently per id. */
376
+ private reportInstall;
377
+ /** REV-268: assemble and encode the device facts once. Runtime-detected
378
+ * facts first, the app's `device` over them, then the three the SDK owns.
379
+ * `installedAt` is the first launch this storage ever saw — written on
380
+ * that launch and read back on every later one — and `firstOpen` is true
381
+ * for the whole of that first session. Both are skipped on non-persistent
382
+ * storage: memory-only storage would make every launch a first open,
383
+ * which is the same phantom-new-customer hazard the M5 install guard
384
+ * exists for. */
385
+ private buildDeviceHeader;
386
+ /** REV-268: the decoded facts this client sends — for tests and for apps
387
+ * that want to show what Revnix knows. `null` when disabled. */
388
+ getDeviceHeader(): Promise<string | null>;
389
+ private setCustomerId;
390
+ private readEntitlementsMap;
391
+ private writeEntitlementsCache;
392
+ private readEntitlementsCache;
393
+ private readPlacementMap;
394
+ private readPlacementCache;
395
+ private writePlacementCache;
396
+ private queueLock;
397
+ private mutateQueue;
398
+ private readPurchaseQueue;
399
+ private writePurchaseQueue;
400
+ private enqueuePurchase;
401
+ private request;
402
+ }