@affiliateo/web 4.7.0 → 4.8.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/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 — the billable path REQUIRES a
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 — re-sending a used token would get the
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) — the same one public/t.js ships. The
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 — this timer makes sure the first pageview can't be
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 — never rejects, so
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 — never shows UI. Real browsers pass the
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 — shown in the dashboard when you create a
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 — use `appId`.
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 — normal visitors never look at the console.
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.
@@ -121,7 +121,14 @@ export interface AffiliateoContextValue {
121
121
  * both events resolve to the same user_id and the funnel reports a
122
122
  * single converted person.
123
123
  *
124
- * Idempotent: safe to call on every page load. user_id only —
124
+ * Since 4.8.0 a different user signing in on this browser switches it
125
+ * to them (it used to be refused). The SDK stores the id and re-sends it
126
+ * once per visit; calling it again in the same visit sends nothing. Does
127
+ * nothing while opted out, and skips ids that mean "nobody" ("null",
128
+ * "undefined", "guest", "0", RevenueCat's $RCAnonymousID:...) that the
129
+ * server would refuse.
130
+ *
131
+ * Idempotent: safe to call on every page load. user_id only -
125
132
  * the SDK does not accept or transmit email or any other PII.
126
133
  *
127
134
  * Example:
@@ -142,22 +149,24 @@ export interface AffiliateoContextValue {
142
149
  * useEffect(() => { if (open) trackPageview('/pricing-modal') }, [open])
143
150
  * } */
144
151
  trackPageview: (path?: string) => Promise<void>;
145
- /** Wipe the visitor identity. Mints a fresh visitor_id and clears the
146
- * affiliate ref + campaign_id from localStorage so the next pageview
147
- * is treated as a brand-new anonymous visitor.
148
- *
149
- * Use case: call on user logout so a different person sitting down at
150
- * the same browser doesn't get their actions merged into the previous
151
- * user's funnel. Without this, shared family computers / kiosks /
152
- * hot-desks silently pollute attribution.
152
+ /** The user signed out (4.8.0). Call it on sign-out: this browser starts
153
+ * over as a new anonymous customer of the app, so whoever signs in next
154
+ * (or nobody) is not counted as the person who just left, and everything
155
+ * already recorded stays with them. Forgets the stored user id. The
156
+ * visitor id, the affiliate ref and tracking carry on unchanged.
153
157
  *
154
158
  * Example:
155
- * const { reset } = useAffiliateRef()
159
+ * const { logout } = useAffiliateRef()
156
160
  * async function handleLogout() {
157
161
  * await supabase.auth.signOut()
158
- * await reset()
162
+ * await logout()
159
163
  * router.push('/')
160
164
  * } */
165
+ logout: () => Promise<void>;
166
+ /** The same as logout() since 4.8.0. It used to mint a fresh visitor id
167
+ * and clear the affiliate ref, which split one person into two visitors
168
+ * and lost their affiliate. Kept as an alias so existing integrations
169
+ * get the sign-out they meant. */
161
170
  reset: () => Promise<void>;
162
171
  /** Stop tracking this visitor. Sets a persistent opt-out flag in
163
172
  * localStorage and silences all subsequent track / identify / pageview
@@ -196,7 +205,7 @@ export interface AffiliateoContextValue {
196
205
  * }
197
206
  *
198
207
  * Returns a Promise that resolves when the flush attempt completes
199
- * (success OR failure — failed events stay in the queue for the next
208
+ * (success OR failure - failed events stay in the queue for the next
200
209
  * flush). Safe to await without timeout concerns. */
201
210
  flush: () => Promise<void>;
202
211
  }
@@ -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;
@@ -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.7.0",
4
- "description": "Affiliateo web SDK \u2014 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
- "build": "tsc",
17
- "prepack": "npm run build"
18
- },
19
- "peerDependencies": {
20
- "react": ">=18.0.0"
21
- },
22
- "peerDependenciesMeta": {
23
- "react": {
24
- "optional": true
25
- }
26
- },
27
- "devDependencies": {
28
- "@types/react": "^19.0.0",
29
- "typescript": "^5.0.0"
30
- },
31
- "keywords": [
32
- "affiliateo",
33
- "affiliate",
34
- "attribution",
35
- "stripe",
36
- "web",
37
- "session",
38
- "tracking"
39
- ],
40
- "license": "MIT",
41
- "files": [
42
- "dist"
43
- ]
44
- }
1
+ {
2
+ "name": "@affiliateo/web",
3
+ "version": "4.8.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
+ }