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.
- package/LICENSE +21 -0
- package/README.md +766 -0
- package/dist/blocks/background.d.ts +101 -0
- package/dist/blocks/background.js +165 -0
- package/dist/blocks/color.d.ts +88 -0
- package/dist/blocks/color.js +329 -0
- package/dist/blocks/locale.d.ts +42 -0
- package/dist/blocks/locale.js +263 -0
- package/dist/blocks/selection.d.ts +49 -0
- package/dist/blocks/selection.js +109 -0
- package/dist/blocks/style.d.ts +93 -0
- package/dist/blocks/style.js +365 -0
- package/dist/blocks/tags.d.ts +12 -0
- package/dist/blocks/tags.js +121 -0
- package/dist/blocks/types.d.ts +301 -0
- package/dist/blocks/types.js +44 -0
- package/dist/client.d.ts +402 -0
- package/dist/client.js +1653 -0
- package/dist/device.d.ts +29 -0
- package/dist/device.js +137 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.js +68 -0
- package/dist/esm/blocks/background.js +160 -0
- package/dist/esm/blocks/color.js +318 -0
- package/dist/esm/blocks/locale.js +255 -0
- package/dist/esm/blocks/selection.js +100 -0
- package/dist/esm/blocks/style.js +356 -0
- package/dist/esm/blocks/tags.js +117 -0
- package/dist/esm/blocks/types.js +40 -0
- package/dist/esm/client.js +1649 -0
- package/dist/esm/device.js +130 -0
- package/dist/esm/errors.js +59 -0
- package/dist/esm/iap.js +101 -0
- package/dist/esm/implicit.js +63 -0
- package/dist/esm/index.js +29 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/storage.js +78 -0
- package/dist/esm/types.js +4 -0
- package/dist/esm/ui/RevnixBlockPaywall.js +706 -0
- package/dist/esm/ui/RevnixPaywall.js +890 -0
- package/dist/esm/ui/index.js +6 -0
- package/dist/esm/version.js +4 -0
- package/dist/iap.d.ts +20 -0
- package/dist/iap.js +104 -0
- package/dist/implicit.d.ts +69 -0
- package/dist/implicit.js +67 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +50 -0
- package/dist/storage.d.ts +14 -0
- package/dist/storage.js +85 -0
- package/dist/types.d.ts +463 -0
- package/dist/types.js +5 -0
- package/dist/ui/RevnixBlockPaywall.d.ts +96 -0
- package/dist/ui/RevnixBlockPaywall.js +742 -0
- package/dist/ui/RevnixPaywall.d.ts +148 -0
- package/dist/ui/RevnixPaywall.js +927 -0
- package/dist/ui/index.d.ts +7 -0
- package/dist/ui/index.js +25 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +7 -0
- package/package.json +79 -0
- package/src/blocks/background.ts +232 -0
- package/src/blocks/color.ts +352 -0
- package/src/blocks/locale.ts +271 -0
- package/src/blocks/paywall-background-wire.json +12 -0
- package/src/blocks/paywall-selection-wire.json +149 -0
- package/src/blocks/selection.ts +161 -0
- package/src/blocks/style.ts +411 -0
- package/src/blocks/tags.ts +125 -0
- package/src/blocks/types.ts +352 -0
- package/src/client.ts +1993 -0
- package/src/device.ts +146 -0
- package/src/errors.ts +69 -0
- package/src/iap.ts +137 -0
- package/src/implicit.ts +129 -0
- package/src/index.ts +82 -0
- package/src/storage.ts +92 -0
- package/src/types.ts +528 -0
- package/src/ui/RevnixBlockPaywall.tsx +1423 -0
- package/src/ui/RevnixPaywall.tsx +1542 -0
- package/src/ui/index.ts +43 -0
- package/src/version.ts +4 -0
- package/ui.d.ts +1 -0
- package/ui.js +5 -0
package/dist/client.d.ts
ADDED
|
@@ -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
|
+
}
|