@affiliateo/web 4.7.0 → 4.9.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/README.md +312 -238
- package/dist/bot-detect.d.ts +4 -4
- package/dist/bot-detect.js +6 -6
- package/dist/chat.d.ts +43 -0
- package/dist/chat.js +117 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/provider.d.ts +1 -1
- package/dist/provider.js +143 -80
- package/dist/queue.d.ts +7 -3
- package/dist/queue.js +32 -15
- package/dist/turnstile.d.ts +3 -3
- package/dist/turnstile.js +6 -6
- package/dist/types.d.ts +49 -15
- package/dist/user-id.d.ts +18 -0
- package/dist/user-id.js +40 -0
- package/package.json +46 -44
package/dist/queue.js
CHANGED
|
@@ -88,7 +88,7 @@ export class EventQueue {
|
|
|
88
88
|
this.maxQueueSize = Math.max(10, Math.min(1000, opts.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE));
|
|
89
89
|
// Hydrate from localStorage so events from a prior page load survive
|
|
90
90
|
// a refresh / navigation. Guards against corrupt JSON or wrong-shape
|
|
91
|
-
// entries by resetting to empty if anything looks off
|
|
91
|
+
// entries by resetting to empty if anything looks off - better to
|
|
92
92
|
// lose a few stuck events than crash the host app on init.
|
|
93
93
|
try {
|
|
94
94
|
const raw = lsGet(STORAGE_KEY);
|
|
@@ -125,8 +125,8 @@ export class EventQueue {
|
|
|
125
125
|
id,
|
|
126
126
|
endpoint,
|
|
127
127
|
// Delivery metadata is baked into the PERSISTED payload rather than
|
|
128
|
-
// added at flush time, so every retry
|
|
129
|
-
// page reload
|
|
128
|
+
// added at flush time, so every retry - and every recovery after a
|
|
129
|
+
// page reload - sends a byte-identical body:
|
|
130
130
|
//
|
|
131
131
|
// event_id makes the server insert idempotent. Without it, a 5xx
|
|
132
132
|
// raised after the row was already written, or a network timeout
|
|
@@ -137,7 +137,7 @@ export class EventQueue {
|
|
|
137
137
|
//
|
|
138
138
|
// client_ts is when the event HAPPENED. The server used to stamp
|
|
139
139
|
// arrival time, but the 5s flush cadence, the size-threshold
|
|
140
|
-
// burst flush, and retries all reorder delivery freely
|
|
140
|
+
// burst flush, and retries all reorder delivery freely - so
|
|
141
141
|
// arrival order is not user order. Stamping at enqueue is the
|
|
142
142
|
// only point that still knows the real sequence.
|
|
143
143
|
payload: { ...payload, event_id: id, client_ts: Date.now() },
|
|
@@ -153,7 +153,7 @@ export class EventQueue {
|
|
|
153
153
|
this.persist();
|
|
154
154
|
// Burst protection: when many events fire in quick succession
|
|
155
155
|
// (auto-pageview + immediate track + identify on app boot), don't
|
|
156
|
-
// wait for the 5s timer
|
|
156
|
+
// wait for the 5s timer - flush right away. The isFlushing guard
|
|
157
157
|
// inside flush() makes overlapping calls safe.
|
|
158
158
|
if (this.queue.length >= SIZE_FLUSH_THRESHOLD) {
|
|
159
159
|
void this.flush();
|
|
@@ -170,13 +170,21 @@ export class EventQueue {
|
|
|
170
170
|
if (this.isFlushing || this.queue.length === 0)
|
|
171
171
|
return;
|
|
172
172
|
this.isFlushing = true;
|
|
173
|
-
// Snapshot the queue so we iterate a stable list
|
|
173
|
+
// Snapshot the queue so we iterate a stable list - new enqueue()
|
|
174
174
|
// calls during the flush land in the live queue and get picked up
|
|
175
175
|
// by the next flush rather than this one (avoids infinite loops
|
|
176
176
|
// and reasoning headaches).
|
|
177
177
|
const batch = this.queue.slice();
|
|
178
178
|
for (const event of batch) {
|
|
179
|
+
// Dropped or cleared since the batch was taken (optOut(), or a
|
|
180
|
+
// sign-in replacing a sign-out still waiting here): not sent.
|
|
181
|
+
if (!this.queue.some((e) => e.id === event.id))
|
|
182
|
+
continue;
|
|
179
183
|
let ok = false;
|
|
184
|
+
// A 400 says the request itself is wrong (a placeholder user id, a
|
|
185
|
+
// malformed body), so sending it again cannot succeed: dropped
|
|
186
|
+
// rather than retried (4.8.0).
|
|
187
|
+
let rejected = false;
|
|
180
188
|
try {
|
|
181
189
|
const res = await fetch(event.endpoint, {
|
|
182
190
|
method: 'POST',
|
|
@@ -186,16 +194,16 @@ export class EventQueue {
|
|
|
186
194
|
// with the queue so even if this fetch errors, we'll retry.
|
|
187
195
|
keepalive: true,
|
|
188
196
|
});
|
|
189
|
-
// 2xx = success.
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
// dropping is the right move — keeps the queue clean.
|
|
197
|
+
// 2xx = success. A 400 is dropped at once (above). Anything else
|
|
198
|
+
// (other 4xx, 5xx) counts as a failure and bumps retries; after
|
|
199
|
+
// 3 it is dropped, which keeps the queue clean.
|
|
193
200
|
ok = res.ok;
|
|
201
|
+
rejected = res.status === 400;
|
|
194
202
|
}
|
|
195
203
|
catch {
|
|
196
204
|
ok = false;
|
|
197
205
|
}
|
|
198
|
-
if (ok) {
|
|
206
|
+
if (ok || rejected) {
|
|
199
207
|
// Remove from live queue (not the snapshot) by id
|
|
200
208
|
this.queue = this.queue.filter((e) => e.id !== event.id);
|
|
201
209
|
}
|
|
@@ -216,19 +224,19 @@ export class EventQueue {
|
|
|
216
224
|
this.isFlushing = false;
|
|
217
225
|
}
|
|
218
226
|
/** Best-effort sync flush triggered on page unload. Uses sendBeacon
|
|
219
|
-
* for each event so it survives the tab closing. No retry
|
|
227
|
+
* for each event so it survives the tab closing. No retry - this is
|
|
220
228
|
* the last chance.
|
|
221
229
|
*
|
|
222
230
|
* Called from the provider's pagehide / visibilitychange=hidden
|
|
223
231
|
* handlers so any events still queued at unload get one last shot.
|
|
224
232
|
* After this call, the in-memory queue is cleared (events that
|
|
225
|
-
* sendBeacon couldn't queue are lost
|
|
233
|
+
* sendBeacon couldn't queue are lost - that's the cost of the
|
|
226
234
|
* guaranteed-fast unload path). */
|
|
227
235
|
flushOnUnload() {
|
|
228
236
|
if (this.queue.length === 0)
|
|
229
237
|
return;
|
|
230
238
|
if (typeof navigator === 'undefined' || !navigator.sendBeacon) {
|
|
231
|
-
// No sendBeacon (very old browser)
|
|
239
|
+
// No sendBeacon (very old browser) - try a sync fetch with
|
|
232
240
|
// keepalive as a fallback. Won't actually finish, but it's our
|
|
233
241
|
// last shot.
|
|
234
242
|
for (const event of this.queue) {
|
|
@@ -255,7 +263,16 @@ export class EventQueue {
|
|
|
255
263
|
this.queue = [];
|
|
256
264
|
this.persist();
|
|
257
265
|
}
|
|
258
|
-
/**
|
|
266
|
+
/** Remove the queued events `predicate` picks (4.8.0). A sign-in uses it
|
|
267
|
+
* to drop a sign-out still waiting to be delivered: the sign-in replaces
|
|
268
|
+
* it, and a sign-out retried after it would undo it. */
|
|
269
|
+
drop(predicate) {
|
|
270
|
+
const before = this.queue.length;
|
|
271
|
+
this.queue = this.queue.filter((e) => !predicate(e));
|
|
272
|
+
if (this.queue.length !== before)
|
|
273
|
+
this.persist();
|
|
274
|
+
}
|
|
275
|
+
/** Wipe the queue. Called by optOut(). Removes the
|
|
259
276
|
* localStorage entry entirely so a reload doesn't re-hydrate the
|
|
260
277
|
* wiped events. */
|
|
261
278
|
clear() {
|
package/dist/turnstile.d.ts
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* Cloudflare Turnstile token minting. Mirrors the hosted t.js tracker:
|
|
3
3
|
* mint ONE invisible token at SDK init and attach it to the first pageview.
|
|
4
4
|
* The server verifies the token before creating the visitor row (and before
|
|
5
|
-
* counting a pay-per-click web_traffic click
|
|
5
|
+
* counting a pay-per-click web_traffic click - the billable path REQUIRES a
|
|
6
6
|
* genuine pass when TURNSTILE_SECRET is configured). Subsequent events from
|
|
7
7
|
* the same visitor_id are trusted because the row was created behind a
|
|
8
8
|
* verified token, so the cost is one mint + one verify per session.
|
|
9
9
|
*
|
|
10
10
|
* Tokens are single-use at Cloudflare's siteverify, so consumeToken()
|
|
11
|
-
* hands the token out exactly once
|
|
11
|
+
* hands the token out exactly once - re-sending a used token would get the
|
|
12
12
|
* event rejected server-side, not warn-allowed.
|
|
13
13
|
*
|
|
14
14
|
* Failure modes: if the Turnstile script is blocked (ad blockers, strict
|
|
@@ -28,7 +28,7 @@ declare global {
|
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Load the Turnstile script (once) and run the invisible challenge. Resolves
|
|
31
|
-
* when a token is available OR the mint failed/timed out
|
|
31
|
+
* when a token is available OR the mint failed/timed out - never rejects, so
|
|
32
32
|
* callers can unconditionally `.then(sendPageview)`.
|
|
33
33
|
*/
|
|
34
34
|
export declare function mintTurnstileToken(): Promise<void>;
|
package/dist/turnstile.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* Cloudflare Turnstile token minting. Mirrors the hosted t.js tracker:
|
|
3
3
|
* mint ONE invisible token at SDK init and attach it to the first pageview.
|
|
4
4
|
* The server verifies the token before creating the visitor row (and before
|
|
5
|
-
* counting a pay-per-click web_traffic click
|
|
5
|
+
* counting a pay-per-click web_traffic click - the billable path REQUIRES a
|
|
6
6
|
* genuine pass when TURNSTILE_SECRET is configured). Subsequent events from
|
|
7
7
|
* the same visitor_id are trusted because the row was created behind a
|
|
8
8
|
* verified token, so the cost is one mint + one verify per session.
|
|
9
9
|
*
|
|
10
10
|
* Tokens are single-use at Cloudflare's siteverify, so consumeToken()
|
|
11
|
-
* hands the token out exactly once
|
|
11
|
+
* hands the token out exactly once - re-sending a used token would get the
|
|
12
12
|
* event rejected server-side, not warn-allowed.
|
|
13
13
|
*
|
|
14
14
|
* Failure modes: if the Turnstile script is blocked (ad blockers, strict
|
|
@@ -17,20 +17,20 @@
|
|
|
17
17
|
* those through; commission-on-sale (web_affiliate) tracking never depends
|
|
18
18
|
* on the token at all.
|
|
19
19
|
*/
|
|
20
|
-
// Public sitekey (safe to expose)
|
|
20
|
+
// Public sitekey (safe to expose) - the same one public/t.js ships. The
|
|
21
21
|
// matching secret lives in TURNSTILE_SECRET on the server.
|
|
22
22
|
const TURNSTILE_SITEKEY = '0x4AAAAAADR5UW2x6axFd13p';
|
|
23
23
|
const TURNSTILE_SCRIPT_SRC = 'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=__affiliateoTSLoaded&render=explicit';
|
|
24
24
|
// Hard deadline for the whole mint (script load + challenge). t.js relies on
|
|
25
25
|
// Turnstile's own timeout-callback, but a script tag that never loads never
|
|
26
|
-
// fires ANY callback
|
|
26
|
+
// fires ANY callback - this timer makes sure the first pageview can't be
|
|
27
27
|
// held hostage by a blocked challenges.cloudflare.com.
|
|
28
28
|
const MINT_TIMEOUT_MS = 3000;
|
|
29
29
|
let token = null;
|
|
30
30
|
let minting = null;
|
|
31
31
|
/**
|
|
32
32
|
* Load the Turnstile script (once) and run the invisible challenge. Resolves
|
|
33
|
-
* when a token is available OR the mint failed/timed out
|
|
33
|
+
* when a token is available OR the mint failed/timed out - never rejects, so
|
|
34
34
|
* callers can unconditionally `.then(sendPageview)`.
|
|
35
35
|
*/
|
|
36
36
|
export function mintTurnstileToken() {
|
|
@@ -60,7 +60,7 @@ export function mintTurnstileToken() {
|
|
|
60
60
|
document.body.appendChild(holder);
|
|
61
61
|
window.turnstile.render(holder, {
|
|
62
62
|
sitekey: TURNSTILE_SITEKEY,
|
|
63
|
-
// Invisible widget
|
|
63
|
+
// Invisible widget - never shows UI. Real browsers pass the
|
|
64
64
|
// challenge in 50-200ms with zero user interaction.
|
|
65
65
|
size: 'invisible',
|
|
66
66
|
callback: (tok) => {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export interface AffiliateoConfig {
|
|
2
|
-
/** Your Affiliateo app ID
|
|
2
|
+
/** Your Affiliateo app ID - shown in the dashboard when you create a
|
|
3
3
|
* Web App Affiliate app. One of `appId` / `campaignId` is required;
|
|
4
4
|
* `appId` wins when both are set. */
|
|
5
5
|
appId?: string;
|
|
6
|
-
/** @deprecated Affiliateo campaigns are now called apps
|
|
6
|
+
/** @deprecated Affiliateo campaigns are now called apps - use `appId`.
|
|
7
7
|
* Kept working so existing integrations don't break. */
|
|
8
8
|
campaignId?: string;
|
|
9
9
|
/** Base URL for the API. Defaults to https://affiliateo.com */
|
|
@@ -55,7 +55,7 @@ export interface AffiliateoConfig {
|
|
|
55
55
|
* true, the SDK logs every action it takes (init, pageview fired,
|
|
56
56
|
* goal tracked, blocked by bot detection, blocked by localhost, queue
|
|
57
57
|
* flush attempted, etc.). Only the merchant or anyone manually opening
|
|
58
|
-
* DevTools sees these
|
|
58
|
+
* DevTools sees these - normal visitors never look at the console.
|
|
59
59
|
*
|
|
60
60
|
* Turn on temporarily when investigating "why aren't my events
|
|
61
61
|
* showing up?". Leave off in production.
|
|
@@ -80,6 +80,17 @@ export interface AffiliateoConfig {
|
|
|
80
80
|
/** Pageview auto-capture behavior. Pass `false` to disable entirely
|
|
81
81
|
* (you'll call `trackPageview()` yourself). Pass an object to fine-tune.
|
|
82
82
|
* Defaults to `true` (auto-capture everything). */
|
|
83
|
+
/** Live Chat (4.9.0). Once the business switches Live Chat on for this
|
|
84
|
+
* app in its Affiliateo dashboard, a chat bubble shows on the site.
|
|
85
|
+
* `true` (the default) shows it; `'hidden'` loads the chat without a
|
|
86
|
+
* bubble, for a site that calls `openChat()` from its own button; `false`
|
|
87
|
+
* never loads it. */
|
|
88
|
+
chat?: boolean | 'hidden';
|
|
89
|
+
/** Your own look for the chat bubble (4.15.0), over the Live Chat settings
|
|
90
|
+
* in your dashboard: anything left out keeps the dashboard's choice.
|
|
91
|
+
* sendColor colors the send button and the person's own messages;
|
|
92
|
+
* placeholder is the message box text, in your own words and language. */
|
|
93
|
+
chatLook?: ChatLook;
|
|
83
94
|
autoCapturePageviews?: boolean | {
|
|
84
95
|
/** Fire the initial pageview on mount. Defaults to true. Set false
|
|
85
96
|
* for cookie-consent flows where you want to defer until the user
|
|
@@ -121,7 +132,14 @@ export interface AffiliateoContextValue {
|
|
|
121
132
|
* both events resolve to the same user_id and the funnel reports a
|
|
122
133
|
* single converted person.
|
|
123
134
|
*
|
|
124
|
-
*
|
|
135
|
+
* Since 4.8.0 a different user signing in on this browser switches it
|
|
136
|
+
* to them (it used to be refused). The SDK stores the id and re-sends it
|
|
137
|
+
* once per visit; calling it again in the same visit sends nothing. Does
|
|
138
|
+
* nothing while opted out, and skips ids that mean "nobody" ("null",
|
|
139
|
+
* "undefined", "guest", "0", RevenueCat's $RCAnonymousID:...) that the
|
|
140
|
+
* server would refuse.
|
|
141
|
+
*
|
|
142
|
+
* Idempotent: safe to call on every page load. user_id only -
|
|
125
143
|
* the SDK does not accept or transmit email or any other PII.
|
|
126
144
|
*
|
|
127
145
|
* Example:
|
|
@@ -142,22 +160,25 @@ export interface AffiliateoContextValue {
|
|
|
142
160
|
* useEffect(() => { if (open) trackPageview('/pricing-modal') }, [open])
|
|
143
161
|
* } */
|
|
144
162
|
trackPageview: (path?: string) => Promise<void>;
|
|
145
|
-
/**
|
|
146
|
-
*
|
|
147
|
-
* is
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* user's funnel. Without this, shared family computers / kiosks /
|
|
152
|
-
* hot-desks silently pollute attribution.
|
|
163
|
+
/** The user signed out (4.8.0). Call it on sign-out: this browser starts
|
|
164
|
+
* over as a new anonymous customer of the app, so whoever signs in next
|
|
165
|
+
* (or nobody) is not counted as the person who just left, and everything
|
|
166
|
+
* already recorded stays with them. Forgets the stored user id, and
|
|
167
|
+
* their Live Chat: the next person to open it starts a new thread. The
|
|
168
|
+
* visitor id, the affiliate ref and tracking carry on unchanged.
|
|
153
169
|
*
|
|
154
170
|
* Example:
|
|
155
|
-
* const {
|
|
171
|
+
* const { logout } = useAffiliateRef()
|
|
156
172
|
* async function handleLogout() {
|
|
157
173
|
* await supabase.auth.signOut()
|
|
158
|
-
* await
|
|
174
|
+
* await logout()
|
|
159
175
|
* router.push('/')
|
|
160
176
|
* } */
|
|
177
|
+
logout: () => Promise<void>;
|
|
178
|
+
/** The same as logout() since 4.8.0. It used to mint a fresh visitor id
|
|
179
|
+
* and clear the affiliate ref, which split one person into two visitors
|
|
180
|
+
* and lost their affiliate. Kept as an alias so existing integrations
|
|
181
|
+
* get the sign-out they meant. */
|
|
161
182
|
reset: () => Promise<void>;
|
|
162
183
|
/** Stop tracking this visitor. Sets a persistent opt-out flag in
|
|
163
184
|
* localStorage and silences all subsequent track / identify / pageview
|
|
@@ -196,7 +217,20 @@ export interface AffiliateoContextValue {
|
|
|
196
217
|
* }
|
|
197
218
|
*
|
|
198
219
|
* Returns a Promise that resolves when the flush attempt completes
|
|
199
|
-
* (success OR failure
|
|
220
|
+
* (success OR failure - failed events stay in the queue for the next
|
|
200
221
|
* flush). Safe to await without timeout concerns. */
|
|
201
222
|
flush: () => Promise<void>;
|
|
202
223
|
}
|
|
224
|
+
/** The chat bubble's look, set in code (4.15.0). Colors are hex ("#7C3AED"). */
|
|
225
|
+
export interface ChatLook {
|
|
226
|
+
color?: string;
|
|
227
|
+
iconColor?: string;
|
|
228
|
+
/** The chat's send button and the person's own messages (iMessage blue unless the dashboard says otherwise). */
|
|
229
|
+
sendColor?: string;
|
|
230
|
+
/** The message box text, in your own words and language (up to 60 characters): pass your app's own translation. */
|
|
231
|
+
placeholder?: string;
|
|
232
|
+
style?: 'solid' | 'glass';
|
|
233
|
+
corner?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
234
|
+
/** In pixels, 40 to 80. */
|
|
235
|
+
size?: number;
|
|
236
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which user ids may name a signed-in person (4.8.0).
|
|
3
|
+
*
|
|
4
|
+
* Every device that signs in with the same user id becomes ONE customer of
|
|
5
|
+
* the app on Affiliateo's side, so an id that really means "nobody" ("null",
|
|
6
|
+
* "undefined", "guest", "0") would merge every stranger whose site sent it
|
|
7
|
+
* into one person. The server refuses these with a 400; this is the same
|
|
8
|
+
* list, checked here so a wrong call is skipped (and says so with
|
|
9
|
+
* debug={true}) instead of costing a request.
|
|
10
|
+
*
|
|
11
|
+
* KEEP IN SYNC with src/lib/customers/user-id.ts in the Affiliateo web repo,
|
|
12
|
+
* and with public/t.js, which the vanilla snippet loads.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MAX_USER_ID_LENGTH = 128;
|
|
15
|
+
/** The trimmed id when it can name a person, otherwise null. A number (a
|
|
16
|
+
* database row id passed straight through) is read as its digits, the way
|
|
17
|
+
* the server reads it. */
|
|
18
|
+
export declare function usableUserId(value: unknown): string | null;
|
package/dist/user-id.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which user ids may name a signed-in person (4.8.0).
|
|
3
|
+
*
|
|
4
|
+
* Every device that signs in with the same user id becomes ONE customer of
|
|
5
|
+
* the app on Affiliateo's side, so an id that really means "nobody" ("null",
|
|
6
|
+
* "undefined", "guest", "0") would merge every stranger whose site sent it
|
|
7
|
+
* into one person. The server refuses these with a 400; this is the same
|
|
8
|
+
* list, checked here so a wrong call is skipped (and says so with
|
|
9
|
+
* debug={true}) instead of costing a request.
|
|
10
|
+
*
|
|
11
|
+
* KEEP IN SYNC with src/lib/customers/user-id.ts in the Affiliateo web repo,
|
|
12
|
+
* and with public/t.js, which the vanilla snippet loads.
|
|
13
|
+
*/
|
|
14
|
+
export const MAX_USER_ID_LENGTH = 128;
|
|
15
|
+
const NOT_A_USER = new Set([
|
|
16
|
+
'null', '(null)', '<null>', 'nil', 'none', 'no', 'undefined', 'unidentified',
|
|
17
|
+
'unknown', 'anonymous', 'anon', 'guest', 'nan', 'false', 'true',
|
|
18
|
+
'0', '-1', '[]', '{}', '[object object]',
|
|
19
|
+
]);
|
|
20
|
+
/** The trimmed id when it can name a person, otherwise null. A number (a
|
|
21
|
+
* database row id passed straight through) is read as its digits, the way
|
|
22
|
+
* the server reads it. */
|
|
23
|
+
export function usableUserId(value) {
|
|
24
|
+
let raw;
|
|
25
|
+
if (typeof value === 'string')
|
|
26
|
+
raw = value;
|
|
27
|
+
else if (typeof value === 'number' && Number.isFinite(value))
|
|
28
|
+
raw = String(value);
|
|
29
|
+
else
|
|
30
|
+
return null;
|
|
31
|
+
const id = raw.trim();
|
|
32
|
+
if (id.length < 1 || id.length > MAX_USER_ID_LENGTH)
|
|
33
|
+
return null;
|
|
34
|
+
if (NOT_A_USER.has(id.toLowerCase()))
|
|
35
|
+
return null;
|
|
36
|
+
// RevenueCat's own anonymous id names an install, not a user.
|
|
37
|
+
if (id.startsWith('$RCAnonymousID:'))
|
|
38
|
+
return null;
|
|
39
|
+
return id;
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,44 +1,46 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@affiliateo/web",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Affiliateo web SDK
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"module": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js",
|
|
12
|
-
"require": "./dist/index.js"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@affiliateo/web",
|
|
3
|
+
"version": "4.9.0",
|
|
4
|
+
"description": "Affiliateo web SDK: affiliate attribution and tracking for web apps",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"prebuild": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"prepack": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"react": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"peerDependenciesMeta": {
|
|
25
|
+
"react": {
|
|
26
|
+
"optional": true
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/react": "^19.0.0",
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"affiliateo",
|
|
35
|
+
"affiliate",
|
|
36
|
+
"attribution",
|
|
37
|
+
"stripe",
|
|
38
|
+
"web",
|
|
39
|
+
"session",
|
|
40
|
+
"tracking"
|
|
41
|
+
],
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
]
|
|
46
|
+
}
|