@affiliateo/web 4.6.0 → 4.7.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/provider.js +38 -5
- package/dist/queue.js +19 -2
- package/package.json +15 -5
package/dist/provider.js
CHANGED
|
@@ -171,6 +171,23 @@ export function getAttribution() {
|
|
|
171
171
|
// mount (host has the call sites) but we can shout in dev so the
|
|
172
172
|
// mistake is visible.
|
|
173
173
|
let providerMountCount = 0;
|
|
174
|
+
/**
|
|
175
|
+
* True only when we can SEE that this is a production build.
|
|
176
|
+
*
|
|
177
|
+
* Every bundler (webpack, Vite, Next, Rollup) replaces `process.env.NODE_ENV`
|
|
178
|
+
* with a literal before shipping, which is why reading it works for almost
|
|
179
|
+
* everyone. Someone importing the ESM directly has no `process` at all, and
|
|
180
|
+
* touching it there throws a ReferenceError rather than reading undefined.
|
|
181
|
+
*
|
|
182
|
+
* UNKNOWN COUNTS AS NOT-PRODUCTION, deliberately. The only caller is the
|
|
183
|
+
* double-mount warning, and a double mount double-fires every pageview — which
|
|
184
|
+
* corrupts the merchant's own numbers and the affiliate attribution built on
|
|
185
|
+
* them. One stray console line on an unbundled production page is a smaller
|
|
186
|
+
* cost than silently letting that run.
|
|
187
|
+
*/
|
|
188
|
+
function isProductionBuild() {
|
|
189
|
+
return typeof process !== 'undefined' && process?.env?.NODE_ENV === 'production';
|
|
190
|
+
}
|
|
174
191
|
export function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames, allowLocalhost, allowIframe, allowFileProtocol, debug, flushInterval, maxQueueSize, autoCapturePageviews, children, }) {
|
|
175
192
|
// Normalize autoCapturePageviews. Three shapes:
|
|
176
193
|
// - undefined / true / object → auto-capture ON (with object overrides)
|
|
@@ -209,10 +226,8 @@ export function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames
|
|
|
209
226
|
const log = useCallback((msg, data) => {
|
|
210
227
|
if (!debug)
|
|
211
228
|
return;
|
|
212
|
-
// eslint-disable-next-line no-console
|
|
213
229
|
if (data !== undefined)
|
|
214
230
|
console.log('[Affiliateo] ' + msg, data);
|
|
215
|
-
// eslint-disable-next-line no-console
|
|
216
231
|
else
|
|
217
232
|
console.log('[Affiliateo] ' + msg);
|
|
218
233
|
}, [debug]);
|
|
@@ -265,6 +280,18 @@ export function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames
|
|
|
265
280
|
visitor_id: vid,
|
|
266
281
|
session_id: getSessionId(),
|
|
267
282
|
event_name: name,
|
|
283
|
+
// Page context, same three fields the pageview sender carries.
|
|
284
|
+
// A goal event used to go out without them, and the server's
|
|
285
|
+
// `path || hostname || '[Web]'` fallback then overwrote the
|
|
286
|
+
// visitor's real current_page with the literal string '[Web]' —
|
|
287
|
+
// which both broke the tracking-page popover and made the visitor
|
|
288
|
+
// invisible to any funnel step matching on a page path. The server
|
|
289
|
+
// no longer clobbers on missing values, but sending the real path
|
|
290
|
+
// is what actually keeps current_page correct as a visitor fires
|
|
291
|
+
// goals while navigating a single-page app.
|
|
292
|
+
url: window.location.href,
|
|
293
|
+
path: window.location.pathname,
|
|
294
|
+
hostname: window.location.hostname,
|
|
268
295
|
};
|
|
269
296
|
if (metadata && typeof metadata === 'object')
|
|
270
297
|
payload.metadata = metadata;
|
|
@@ -443,7 +470,6 @@ export function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames
|
|
|
443
470
|
// Neither appId nor the legacy campaignId alias was passed — nothing to
|
|
444
471
|
// attribute events to, so stay dormant instead of firing junk.
|
|
445
472
|
if (!appIdRef.current) {
|
|
446
|
-
// eslint-disable-next-line no-console
|
|
447
473
|
console.error('[Affiliateo] Missing appId — pass your app ID to <AffiliateoProvider appId="...">.');
|
|
448
474
|
return;
|
|
449
475
|
}
|
|
@@ -451,8 +477,7 @@ export function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames
|
|
|
451
477
|
// ever mounted in the page lifetime. Dev-only; production swallows
|
|
452
478
|
// the warning to avoid noise in real merchant consoles.
|
|
453
479
|
providerMountCount += 1;
|
|
454
|
-
if (providerMountCount > 1 &&
|
|
455
|
-
// eslint-disable-next-line no-console
|
|
480
|
+
if (providerMountCount > 1 && !isProductionBuild()) {
|
|
456
481
|
console.warn('[Affiliateo] AffiliateoProvider mounted more than once. Mount it exactly once near the root of your app, or pageviews will double-fire.');
|
|
457
482
|
}
|
|
458
483
|
// Client-side bot detection. If the current browser context looks
|
|
@@ -952,6 +977,14 @@ export function AffiliateoProvider({ appId, campaignId, apiUrl, allowedHostnames
|
|
|
952
977
|
queueRef.current = null;
|
|
953
978
|
providerMountCount = Math.max(0, providerMountCount - 1);
|
|
954
979
|
};
|
|
980
|
+
// flushInterval, maxQueueSize, allowFileProtocol and the autoCfg.* fields
|
|
981
|
+
// are BOOT configuration, read once when the queue and listeners are
|
|
982
|
+
// constructed. Listing them would tear the whole pipeline down and rebuild
|
|
983
|
+
// it whenever a host app re-renders with a new inline object, and
|
|
984
|
+
// shutdown() drops the in-memory queue — so honoring the rule here would
|
|
985
|
+
// cost real events. A host that needs different settings should remount
|
|
986
|
+
// the provider.
|
|
987
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
955
988
|
}, [apiBase, allowedHostnames, allowLocalhost, allowIframe, log, track, identify, trackPageview, reset, optOut, optIn, flush]);
|
|
956
989
|
return (_jsx(AffiliateoContext.Provider, { value: state, children: children }));
|
|
957
990
|
}
|
package/dist/queue.js
CHANGED
|
@@ -120,10 +120,27 @@ export class EventQueue {
|
|
|
120
120
|
enqueue(endpoint, payload) {
|
|
121
121
|
if (this.shuttingDown)
|
|
122
122
|
return;
|
|
123
|
+
const id = uuid();
|
|
123
124
|
this.queue.push({
|
|
124
|
-
id
|
|
125
|
+
id,
|
|
125
126
|
endpoint,
|
|
126
|
-
payload
|
|
127
|
+
// Delivery metadata is baked into the PERSISTED payload rather than
|
|
128
|
+
// added at flush time, so every retry — and every recovery after a
|
|
129
|
+
// page reload — sends a byte-identical body:
|
|
130
|
+
//
|
|
131
|
+
// event_id makes the server insert idempotent. Without it, a 5xx
|
|
132
|
+
// raised after the row was already written, or a network timeout
|
|
133
|
+
// on a request the server actually processed, lands the same
|
|
134
|
+
// event twice: flush() only removes an entry on res.ok, so
|
|
135
|
+
// anything else bumps retries and sends it again (MAX_RETRIES=3).
|
|
136
|
+
// The server dedups on this via a unique index.
|
|
137
|
+
//
|
|
138
|
+
// client_ts is when the event HAPPENED. The server used to stamp
|
|
139
|
+
// arrival time, but the 5s flush cadence, the size-threshold
|
|
140
|
+
// burst flush, and retries all reorder delivery freely — so
|
|
141
|
+
// arrival order is not user order. Stamping at enqueue is the
|
|
142
|
+
// only point that still knows the real sequence.
|
|
143
|
+
payload: { ...payload, event_id: id, client_ts: Date.now() },
|
|
127
144
|
retries: 0,
|
|
128
145
|
});
|
|
129
146
|
// FIFO drop when over cap. Network outage edge case: if we're way
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@affiliateo/web",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Affiliateo web SDK
|
|
3
|
+
"version": "4.7.0",
|
|
4
|
+
"description": "Affiliateo web SDK \u2014 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",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc",
|
|
17
|
-
"
|
|
17
|
+
"prepack": "npm run build"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"react": ">=18.0.0"
|
|
@@ -28,7 +28,17 @@
|
|
|
28
28
|
"@types/react": "^19.0.0",
|
|
29
29
|
"typescript": "^5.0.0"
|
|
30
30
|
},
|
|
31
|
-
"keywords": [
|
|
31
|
+
"keywords": [
|
|
32
|
+
"affiliateo",
|
|
33
|
+
"affiliate",
|
|
34
|
+
"attribution",
|
|
35
|
+
"stripe",
|
|
36
|
+
"web",
|
|
37
|
+
"session",
|
|
38
|
+
"tracking"
|
|
39
|
+
],
|
|
32
40
|
"license": "MIT",
|
|
33
|
-
"files": [
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
]
|
|
34
44
|
}
|