@affiliateo/web 1.6.0 → 2.0.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/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { AffiliateoProvider, useAffiliateRef } from './provider';
1
+ export { AffiliateoProvider, useAffiliateRef, getAttribution } from './provider';
2
2
  export type { AffiliateoConfig, AffiliateoContextValue } from './types';
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  'use client';
2
- export { AffiliateoProvider, useAffiliateRef } from './provider';
2
+ export { AffiliateoProvider, useAffiliateRef, getAttribution } from './provider';
@@ -4,6 +4,20 @@ import type { AffiliateoConfig, AffiliateoContextValue } from './types';
4
4
  * Returns the affiliate ref code and visitor ID.
5
5
  */
6
6
  export declare function useAffiliateRef(): AffiliateoContextValue;
7
+ /**
8
+ * Returns attribution data from localStorage for checkout flows.
9
+ * Call this client-side and include the result in your checkout request body.
10
+ *
11
+ * Example:
12
+ * ```ts
13
+ * const attribution = getAttribution()
14
+ * const res = await fetch('/api/checkout', {
15
+ * method: 'POST',
16
+ * body: JSON.stringify({ ...checkoutData, ...attribution }),
17
+ * })
18
+ * ```
19
+ */
20
+ export declare function getAttribution(): Record<string, string>;
7
21
  export declare function AffiliateoProvider({ campaignId, apiUrl, children, }: AffiliateoConfig & {
8
22
  children: React.ReactNode;
9
23
  }): import("react/jsx-runtime").JSX.Element;
package/dist/provider.js CHANGED
@@ -2,20 +2,23 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useContext, useEffect, useRef, useState } from 'react';
4
4
  const DEFAULT_API_URL = 'https://affiliateo.com';
5
- // Cookie helpers
6
- function getCookie(name) {
7
- if (typeof document === 'undefined')
5
+ const LS_VISITOR_ID = 'affiliateo_visitor_id';
6
+ const LS_REF = 'affiliateo_ref';
7
+ const LS_CAMPAIGN_ID = 'affiliateo_campaign_id';
8
+ // localStorage helpers
9
+ function lsGet(key) {
10
+ try {
11
+ return localStorage.getItem(key);
12
+ }
13
+ catch {
8
14
  return null;
9
- const m = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
10
- return m ? m[2] : null;
15
+ }
11
16
  }
12
- function setCookie(name, value, days) {
13
- if (typeof document === 'undefined')
14
- return;
15
- const dt = new Date();
16
- dt.setTime(dt.getTime() + days * 864e5);
17
- const secure = window.location.protocol === 'https:' ? ';Secure' : '';
18
- document.cookie = `${name}=${value};expires=${dt.toUTCString()};path=/;SameSite=Lax${secure}`;
17
+ function lsSet(key, value) {
18
+ try {
19
+ localStorage.setItem(key, value);
20
+ }
21
+ catch { }
19
22
  }
20
23
  // Generate UUID
21
24
  function uuid() {
@@ -37,6 +40,32 @@ const AffiliateoContext = createContext({
37
40
  export function useAffiliateRef() {
38
41
  return useContext(AffiliateoContext);
39
42
  }
43
+ /**
44
+ * Returns attribution data from localStorage for checkout flows.
45
+ * Call this client-side and include the result in your checkout request body.
46
+ *
47
+ * Example:
48
+ * ```ts
49
+ * const attribution = getAttribution()
50
+ * const res = await fetch('/api/checkout', {
51
+ * method: 'POST',
52
+ * body: JSON.stringify({ ...checkoutData, ...attribution }),
53
+ * })
54
+ * ```
55
+ */
56
+ export function getAttribution() {
57
+ const data = {};
58
+ const vid = lsGet(LS_VISITOR_ID);
59
+ const ref = lsGet(LS_REF);
60
+ const cid = lsGet(LS_CAMPAIGN_ID);
61
+ if (vid)
62
+ data.affiliateo_visitor_id = vid;
63
+ if (ref)
64
+ data.affiliateo_ref = ref;
65
+ if (cid)
66
+ data.affiliateo_campaign_id = cid;
67
+ return data;
68
+ }
40
69
  export function AffiliateoProvider({ campaignId, apiUrl, children, }) {
41
70
  const [state, setState] = useState({
42
71
  refCode: null,
@@ -49,22 +78,22 @@ export function AffiliateoProvider({ campaignId, apiUrl, children, }) {
49
78
  if (typeof window === 'undefined')
50
79
  return;
51
80
  // Get or create visitor ID
52
- let vid = getCookie('affiliateo_visitor_id');
81
+ let vid = lsGet(LS_VISITOR_ID);
53
82
  if (!vid) {
54
83
  vid = uuid();
55
84
  }
56
- setCookie('affiliateo_visitor_id', vid, 90);
57
- setCookie('affiliateo_campaign_id', campaignIdRef.current, 90);
58
- // Read ref from URL params
85
+ lsSet(LS_VISITOR_ID, vid);
86
+ lsSet(LS_CAMPAIGN_ID, campaignIdRef.current);
87
+ // Read ref from URL params or localStorage
59
88
  const params = new URLSearchParams(window.location.search);
60
- const ref = params.get('ref') || params.get('via') || params.get('source') || getCookie('affiliateo_ref') || null;
89
+ const ref = params.get('ref') || params.get('via') || params.get('source')
90
+ || lsGet(LS_REF) || null;
61
91
  if (ref) {
62
- setCookie('affiliateo_ref', ref, 90);
92
+ lsSet(LS_REF, ref);
63
93
  }
64
94
  setState({ refCode: ref, visitorId: vid });
65
95
  // Throttle: max 1 pageview per second
66
96
  let lastSendTime = 0;
67
- // Send pageview
68
97
  function sendPageview() {
69
98
  const now = Date.now();
70
99
  if (now - lastSendTime < 1000)
@@ -78,31 +107,32 @@ export function AffiliateoProvider({ campaignId, apiUrl, children, }) {
78
107
  path: window.location.pathname,
79
108
  hostname: window.location.hostname,
80
109
  referrer: document.referrer || '',
81
- ref: ref || getCookie('affiliateo_ref') || '',
110
+ ref: ref || lsGet(LS_REF) || '',
82
111
  utm_source: params.get('utm_source') || '',
83
112
  utm_medium: params.get('utm_medium') || '',
84
113
  utm_campaign: params.get('utm_campaign') || '',
85
114
  screen_width: window.innerWidth,
86
115
  };
87
- const data = JSON.stringify(payload);
88
- if (navigator.sendBeacon) {
89
- navigator.sendBeacon(`${apiBase}/api/v1/web/event`, new Blob([data], { type: 'application/json' }));
90
- }
91
- else {
92
- fetch(`${apiBase}/api/v1/web/event`, {
93
- method: 'POST',
94
- headers: { 'Content-Type': 'application/json' },
95
- body: data,
96
- keepalive: true,
97
- }).catch(() => { });
98
- }
116
+ fetch(`${apiBase}/api/v1/web/event`, {
117
+ method: 'POST',
118
+ headers: { 'Content-Type': 'application/json' },
119
+ body: JSON.stringify(payload),
120
+ keepalive: true,
121
+ }).then(async (r) => {
122
+ if (!r.ok) {
123
+ const text = await r.text().catch(() => '');
124
+ console.error(`[Affiliateo] Event failed (${r.status}):`, text);
125
+ }
126
+ }).catch((err) => {
127
+ console.error('[Affiliateo] Event send error:', err);
128
+ });
99
129
  }
100
- // Send pageview on mount (first load or page navigation)
130
+ // Send pageview on mount
101
131
  if (!sentRef.current) {
102
132
  sentRef.current = true;
103
133
  sendPageview();
104
134
  }
105
- // For SPA: listen for URL changes (popstate + pushState/replaceState)
135
+ // SPA support: detect URL changes
106
136
  let lastPath = window.location.pathname;
107
137
  function onUrlChange() {
108
138
  if (window.location.pathname !== lastPath) {
@@ -110,9 +140,7 @@ export function AffiliateoProvider({ campaignId, apiUrl, children, }) {
110
140
  sendPageview();
111
141
  }
112
142
  }
113
- // Listen for browser back/forward
114
143
  window.addEventListener('popstate', onUrlChange);
115
- // Patch pushState/replaceState to detect SPA navigations
116
144
  const origPushState = history.pushState;
117
145
  const origReplaceState = history.replaceState;
118
146
  history.pushState = function (...args) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@affiliateo/web",
3
- "version": "1.6.0",
4
- "description": "Affiliateo web SDK — affiliate attribution and session tracking for web apps",
3
+ "version": "2.0.0",
4
+ "description": "Affiliateo web SDK — affiliate attribution and tracking for web apps",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -10,11 +10,6 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.js",
12
12
  "require": "./dist/index.js"
13
- },
14
- "./server": {
15
- "types": "./dist/server.d.ts",
16
- "import": "./dist/server.js",
17
- "require": "./dist/server.js"
18
13
  }
19
14
  },
20
15
  "scripts": {
@@ -1,21 +0,0 @@
1
- /**
2
- * Extract affiliateo cookies from an incoming request.
3
- * Works with Next.js (App Router & Pages Router), Express, and any framework
4
- * that exposes cookies on the request object.
5
- *
6
- * Returns metadata object to pass to Stripe checkout session.
7
- */
8
- export declare function getMetadata(req: {
9
- cookies?: Record<string, string> | {
10
- get?: (name: string) => {
11
- value: string;
12
- } | undefined;
13
- };
14
- headers?: {
15
- cookie?: string;
16
- } | Headers;
17
- }): {
18
- affiliateo_visitor_id?: string;
19
- affiliateo_ref?: string;
20
- campaign_id?: string;
21
- };
package/dist/metadata.js DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * Extract affiliateo cookies from an incoming request.
3
- * Works with Next.js (App Router & Pages Router), Express, and any framework
4
- * that exposes cookies on the request object.
5
- *
6
- * Returns metadata object to pass to Stripe checkout session.
7
- */
8
- export function getMetadata(req) {
9
- const metadata = {};
10
- // Try Next.js App Router cookies (ReadonlyRequestCookies with .get())
11
- if (req.cookies && typeof req.cookies === 'object' && 'get' in req.cookies && typeof req.cookies.get === 'function') {
12
- const visitorId = req.cookies.get('affiliateo_visitor_id');
13
- const ref = req.cookies.get('affiliateo_ref');
14
- const campaignId = req.cookies.get('affiliateo_campaign_id');
15
- if (visitorId?.value)
16
- metadata.affiliateo_visitor_id = visitorId.value;
17
- if (ref?.value)
18
- metadata.affiliateo_ref = ref.value;
19
- if (campaignId?.value)
20
- metadata.campaign_id = campaignId.value;
21
- return metadata;
22
- }
23
- // Try plain object cookies (Express, Pages Router, etc.)
24
- if (req.cookies && typeof req.cookies === 'object') {
25
- const cookies = req.cookies;
26
- if (cookies.affiliateo_visitor_id)
27
- metadata.affiliateo_visitor_id = cookies.affiliateo_visitor_id;
28
- if (cookies.affiliateo_ref)
29
- metadata.affiliateo_ref = cookies.affiliateo_ref;
30
- if (cookies.affiliateo_campaign_id)
31
- metadata.campaign_id = cookies.affiliateo_campaign_id;
32
- return metadata;
33
- }
34
- // Fallback: parse cookie header manually
35
- const cookieHeader = req.headers instanceof Headers
36
- ? req.headers.get('cookie')
37
- : req.headers?.cookie;
38
- if (cookieHeader) {
39
- const cookies = parseCookies(cookieHeader);
40
- if (cookies.affiliateo_visitor_id)
41
- metadata.affiliateo_visitor_id = cookies.affiliateo_visitor_id;
42
- if (cookies.affiliateo_ref)
43
- metadata.affiliateo_ref = cookies.affiliateo_ref;
44
- if (cookies.affiliateo_campaign_id)
45
- metadata.campaign_id = cookies.affiliateo_campaign_id;
46
- }
47
- return metadata;
48
- }
49
- /**
50
- * Parse a cookie header string into key-value pairs.
51
- */
52
- function parseCookies(cookieHeader) {
53
- const cookies = {};
54
- for (const pair of cookieHeader.split(';')) {
55
- const [key, ...rest] = pair.trim().split('=');
56
- if (key)
57
- cookies[key.trim()] = rest.join('=').trim();
58
- }
59
- return cookies;
60
- }
package/dist/server.d.ts DELETED
@@ -1 +0,0 @@
1
- export { getMetadata } from './metadata';
package/dist/server.js DELETED
@@ -1 +0,0 @@
1
- export { getMetadata } from './metadata';