@affiliateo/web 4.4.0 → 4.5.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 CHANGED
@@ -11,7 +11,7 @@ npm install @affiliateo/web
11
11
  ## Setup
12
12
 
13
13
  1. Connect your Stripe account on the Affiliateo dashboard
14
- 2. Wrap your app in `<AffiliateoProvider>` with your campaign ID
14
+ 2. Wrap your app in `<AffiliateoProvider>` with your app ID
15
15
  3. At checkout time, call `getAttribution()` and pass the result to Stripe metadata
16
16
 
17
17
  ### Step 1. wrap your app
@@ -22,7 +22,7 @@ import { AffiliateoProvider } from '@affiliateo/web';
22
22
 
23
23
  export default function RootLayout({ children }) {
24
24
  return (
25
- <AffiliateoProvider campaignId="YOUR_CAMPAIGN_ID">
25
+ <AffiliateoProvider appId="YOUR_APP_ID">
26
26
  {children}
27
27
  </AffiliateoProvider>
28
28
  );
@@ -53,7 +53,7 @@ async function handleCheckout() {
53
53
  }
54
54
  ```
55
55
 
56
- `getAttribution()` reads `affiliateo_ref`, `affiliateo_visitor_id`, and `affiliateo_campaign_id` from `localStorage` and returns them as an object, ready to pass to Stripe.
56
+ `getAttribution()` reads the stored attribution from `localStorage` and returns `affiliateo_ref`, `affiliateo_visitor_id`, and `affiliateo_app_id` as an object, ready to pass to Stripe.
57
57
 
58
58
  ### Step 3. in your checkout API route (server-side)
59
59
 
@@ -107,7 +107,7 @@ If you're not using React, use the tracking script directly instead of the provi
107
107
 
108
108
  ```html
109
109
  <script defer
110
- data-campaign="YOUR_CAMPAIGN_ID"
110
+ data-app="YOUR_APP_ID"
111
111
  src="https://affiliateo.com/t.js"
112
112
  ></script>
113
113
  ```
@@ -118,7 +118,7 @@ Then in your checkout flow (vanilla JS):
118
118
  const attribution = {
119
119
  affiliateo_ref: localStorage.getItem('affiliateo_ref'),
120
120
  affiliateo_visitor_id: localStorage.getItem('affiliateo_visitor_id'),
121
- affiliateo_campaign_id: localStorage.getItem('affiliateo_campaign_id'),
121
+ affiliateo_app_id: localStorage.getItem('affiliateo_campaign_id'), // stored under its legacy key
122
122
  };
123
123
 
124
124
  // Send to your server with the rest of your checkout payload
@@ -210,12 +210,12 @@ or any other PII.
210
210
 
211
211
  ## API
212
212
 
213
- ### `<AffiliateoProvider campaignId="..." apiUrl?="...">`
213
+ ### `<AffiliateoProvider appId="..." apiUrl?="...">`
214
214
 
215
215
  React provider. Wrap your app once. Reads `?ref=` from URL, persists to localStorage, sends pageview events, sets up data-attribute + scroll listeners.
216
216
 
217
217
  Props:
218
- - `campaignId` (required): your Affiliateo campaign ID
218
+ - `appId` (required): your Affiliateo app ID (`campaignId` still works as a deprecated alias)
219
219
  - `apiUrl` (optional): defaults to `https://affiliateo.com`
220
220
 
221
221
  ### `useAffiliateRef()`
@@ -227,7 +227,7 @@ React hook that returns `{ refCode, visitorId, track, identify }`.
227
227
 
228
228
  ### `getAttribution()`
229
229
 
230
- Client-side function. Returns an object with `affiliateo_ref`, `affiliateo_visitor_id`, and `affiliateo_campaign_id` from localStorage. Pass directly as Stripe `metadata`.
230
+ Client-side function. Returns an object with `affiliateo_ref`, `affiliateo_visitor_id`, and `affiliateo_app_id` from localStorage. Pass directly as Stripe `metadata`.
231
231
 
232
232
  ## Framework support
233
233
 
@@ -18,6 +18,6 @@ export declare function useAffiliateRef(): AffiliateoContextValue;
18
18
  * ```
19
19
  */
20
20
  export declare function getAttribution(): Record<string, string>;
21
- export declare function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allowLocalhost, allowIframe, allowFileProtocol, debug, flushInterval, maxQueueSize, autoCapturePageviews, children, }: AffiliateoConfig & {
21
+ export declare function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames, allowLocalhost, allowIframe, allowFileProtocol, debug, flushInterval, maxQueueSize, autoCapturePageviews, children, }: AffiliateoConfig & {
22
22
  children: React.ReactNode;
23
23
  }): import("react/jsx-runtime").JSX.Element;
package/dist/provider.js CHANGED
@@ -7,7 +7,10 @@ import { isDevHost } from './dev-host';
7
7
  const DEFAULT_API_URL = 'https://affiliateo.com';
8
8
  const LS_VISITOR_ID = 'affiliateo_visitor_id';
9
9
  const LS_REF = 'affiliateo_ref';
10
- const LS_CAMPAIGN_ID = 'affiliateo_campaign_id';
10
+ // Storage key keeps its historical name: t.js and the WooCommerce tracker
11
+ // write the same key, and renaming it would orphan existing visitors'
12
+ // attribution mid-session.
13
+ const LS_APP_ID = 'affiliateo_campaign_id';
11
14
  // Persistent opt-out flag (affiliateo_opt_out) —
12
15
  // when set to "true" the SDK silently noops every outbound call. Survives
13
16
  // page reloads. removed by optIn(). Checked at init time so a previously
@@ -149,13 +152,15 @@ export function getAttribution() {
149
152
  const data = {};
150
153
  const vid = lsGet(LS_VISITOR_ID);
151
154
  const ref = lsGet(LS_REF);
152
- const cid = lsGet(LS_CAMPAIGN_ID);
155
+ const cid = lsGet(LS_APP_ID);
153
156
  if (vid)
154
157
  data.affiliateo_visitor_id = vid;
155
158
  if (ref)
156
159
  data.affiliateo_ref = ref;
160
+ // Key renamed from affiliateo_campaign_id in 4.5.0 — the Affiliateo
161
+ // webhook accepts both, so checkouts created by older SDKs keep working.
157
162
  if (cid)
158
- data.affiliateo_campaign_id = cid;
163
+ data.affiliateo_app_id = cid;
159
164
  return data;
160
165
  }
161
166
  // Double-mount detection. The host app should mount AffiliateoProvider
@@ -165,7 +170,7 @@ export function getAttribution() {
165
170
  // mount (host has the call sites) but we can shout in dev so the
166
171
  // mistake is visible.
167
172
  let providerMountCount = 0;
168
- export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allowLocalhost, allowIframe, allowFileProtocol, debug, flushInterval, maxQueueSize, autoCapturePageviews, children, }) {
173
+ export function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames, allowLocalhost, allowIframe, allowFileProtocol, debug, flushInterval, maxQueueSize, autoCapturePageviews, children, }) {
169
174
  // Normalize autoCapturePageviews. Three shapes:
170
175
  // - undefined / true / object → auto-capture ON (with object overrides)
171
176
  // - false → auto-capture OFF entirely (host must call trackPageview)
@@ -211,12 +216,15 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
211
216
  console.log('[Affiliateo] ' + msg);
212
217
  }, [debug]);
213
218
  const apiBase = (apiUrl || DEFAULT_API_URL).replace(/\/$/, '');
214
- // campaignIdRef stays current so callbacks defined inside useEffect
219
+ // appId is the documented prop; campaignId is the pre-4.5.0 name kept as
220
+ // a working alias so upgrading the package never breaks an integration.
221
+ const resolvedAppId = appId ?? campaignId ?? '';
222
+ // appIdRef stays current so callbacks defined inside useEffect
215
223
  // always read the latest value, even after a prop change. Closures
216
224
  // captured by host-side `useRef` no longer drift to a stale id.
217
- const campaignIdRef = useRef(campaignId);
218
- campaignIdRef.current = campaignId;
219
- // Mint visitorId once and put it in a ref so it survives campaignId
225
+ const appIdRef = useRef(resolvedAppId);
226
+ appIdRef.current = resolvedAppId;
227
+ // Mint visitorId once and put it in a ref so it survives appId
220
228
  // prop changes without forcing a re-fetch / re-mint cycle. Same UUID
221
229
  // for the lifetime of the localStorage entry across the whole tab.
222
230
  const visitorIdRef = useRef(null);
@@ -230,7 +238,7 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
230
238
  // handlers, which fire after mount.
231
239
  const queueRef = useRef(null);
232
240
  // track + identify are defined OUTSIDE the route-effect so they
233
- // close over refs (campaignIdRef, visitorIdRef) rather than the
241
+ // close over refs (appIdRef, visitorIdRef) rather than the
234
242
  // captured-once values from inside the effect. A consumer caching
235
243
  // `track` keeps firing under the LATEST (campaignId, visitorId).
236
244
  const track = useCallback(async (eventName, metadata) => {
@@ -246,7 +254,7 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
246
254
  const name = eventName.trim().toLowerCase();
247
255
  if (!name || name.length > 64 || !/^[a-z0-9_:-]+$/.test(name))
248
256
  return;
249
- const cid = campaignIdRef.current;
257
+ const cid = appIdRef.current;
250
258
  const vid = visitorIdRef.current;
251
259
  if (!cid || !vid)
252
260
  return;
@@ -285,7 +293,7 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
285
293
  // firebase uid, supabase uuid.
286
294
  if (!uid || uid.length > 128)
287
295
  return;
288
- const cid = campaignIdRef.current;
296
+ const cid = appIdRef.current;
289
297
  const vid = visitorIdRef.current;
290
298
  if (!cid || !vid)
291
299
  return;
@@ -315,7 +323,7 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
315
323
  return;
316
324
  if (isOptedOut())
317
325
  return;
318
- const cid = campaignIdRef.current;
326
+ const cid = appIdRef.current;
319
327
  const vid = visitorIdRef.current;
320
328
  if (!cid || !vid)
321
329
  return;
@@ -425,6 +433,13 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
425
433
  useEffect(() => {
426
434
  if (typeof window === 'undefined')
427
435
  return;
436
+ // Neither appId nor the legacy campaignId alias was passed — nothing to
437
+ // attribute events to, so stay dormant instead of firing junk.
438
+ if (!appIdRef.current) {
439
+ // eslint-disable-next-line no-console
440
+ console.error('[Affiliateo] Missing appId — pass your app ID to <AffiliateoProvider appId="...">.');
441
+ return;
442
+ }
428
443
  // Mount-count warning. Fires only when more than one Provider has
429
444
  // ever mounted in the page lifetime. Dev-only; production swallows
430
445
  // the warning to avoid noise in real merchant consoles.
@@ -613,7 +628,7 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
613
628
  vid = uuid();
614
629
  }
615
630
  lsSet(LS_VISITOR_ID, vid);
616
- lsSet(LS_CAMPAIGN_ID, campaignIdRef.current);
631
+ lsSet(LS_APP_ID, appIdRef.current);
617
632
  visitorIdRef.current = vid;
618
633
  // Read ref. Cross-domain inbound wins, then fresh affiliate link
619
634
  // params (?ref / ?via / ?source), then stored value.
@@ -628,7 +643,7 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
628
643
  visitorId: vid,
629
644
  track, identify, trackPageview, reset, optOut, optIn, flush,
630
645
  });
631
- log('init', { campaign: campaignIdRef.current, visitor: vid, ref: ref || null });
646
+ log('init', { app: appIdRef.current, visitor: vid, ref: ref || null });
632
647
  // lastSendTime drives the visibility-refire heuristic below (refire a
633
648
  // pageview when a tab returns after the session window). Duplicate
634
649
  // suppression itself is the persisted same-URL throttle inside
@@ -661,7 +676,7 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
661
676
  ended = false;
662
677
  queue.enqueue(endpoint, {
663
678
  type: 'pageview',
664
- campaign_id: campaignIdRef.current,
679
+ campaign_id: appIdRef.current,
665
680
  visitor_id: vid,
666
681
  session_id: getSessionId(),
667
682
  url: href,
@@ -702,7 +717,7 @@ export function AffiliateoProvider({ campaignId, apiUrl, allowedHostnames, allow
702
717
  queue.flushOnUnload();
703
718
  const data = JSON.stringify({
704
719
  type: 'session_end',
705
- campaign_id: campaignIdRef.current,
720
+ campaign_id: appIdRef.current,
706
721
  visitor_id: vid,
707
722
  ref: ref || lsGet(LS_REF) || '',
708
723
  });
package/dist/types.d.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  export interface AffiliateoConfig {
2
- campaignId: string;
2
+ /** Your Affiliateo app ID — shown in the dashboard when you create a
3
+ * Web App Affiliate app. One of `appId` / `campaignId` is required;
4
+ * `appId` wins when both are set. */
5
+ appId?: string;
6
+ /** @deprecated Affiliateo campaigns are now called apps — use `appId`.
7
+ * Kept working so existing integrations don't break. */
8
+ campaignId?: string;
3
9
  /** Base URL for the API. Defaults to https://affiliateo.com */
4
10
  apiUrl?: string;
5
11
  /** Other domains that are part of the same merchant property and should
@@ -22,7 +28,7 @@ export interface AffiliateoConfig {
22
28
  *
23
29
  * Example:
24
30
  * <AffiliateoProvider
25
- * campaignId="..."
31
+ * appId="..."
26
32
  * allowedHostnames={['app.merchant.com', 'docs.merchant.com']}
27
33
  * > */
28
34
  allowedHostnames?: string[];
@@ -36,7 +42,7 @@ export interface AffiliateoConfig {
36
42
  * land server-side).
37
43
  *
38
44
  * Example:
39
- * <AffiliateoProvider campaignId="..." allowLocalhost> */
45
+ * <AffiliateoProvider appId="..." allowLocalhost> */
40
46
  allowLocalhost?: boolean;
41
47
  /** Allow tracking when the page is rendered inside an iframe. Defaults
42
48
  * to false so scrapers, preview tools, and other sites embedding yours
@@ -55,7 +61,7 @@ export interface AffiliateoConfig {
55
61
  * showing up?". Leave off in production.
56
62
  *
57
63
  * Example:
58
- * <AffiliateoProvider campaignId="..." debug> */
64
+ * <AffiliateoProvider appId="..." debug> */
59
65
  debug?: boolean;
60
66
  /** Allow tracking from pages served via the `file://` protocol (someone
61
67
  * double-clicking a built HTML file instead of running a server).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@affiliateo/web",
3
- "version": "4.4.0",
3
+ "version": "4.5.0",
4
4
  "description": "Affiliateo web SDK — affiliate attribution and tracking for web apps",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",