@blotoutio/providers-dataflywheel-sdk 1.68.1 → 1.69.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/index.cjs.js +172 -2
- package/index.js +172 -2
- package/index.mjs +172 -2
- package/package.json +1 -1
package/index.cjs.js
CHANGED
|
@@ -1,9 +1,139 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const packageName = 'dataFlywheel';
|
|
4
|
+
// Browser identifiers the edge persists against the EdgeTag user id, the way
|
|
5
|
+
// Facebook stores _fbp/_fbc. The identify request carries no providerData and
|
|
6
|
+
// dfw_nid is third-party to the merchant, so this is n_id's only route there.
|
|
7
|
+
const storedNIdKey = 'n_id';
|
|
8
|
+
const storedSessionIdKey = 'session_id';
|
|
9
|
+
const storedSessionExpiryKey = 'session_expires_at';
|
|
10
|
+
// The same identifiers published into the EdgeTag SDK's kv store, which rides
|
|
11
|
+
// on every request including identify. Namespaced the way the edge splits them
|
|
12
|
+
// back apart, as Facebook does with facebook::_fbp.
|
|
13
|
+
const kvKey = (key) => `${packageName}::${key}`;
|
|
4
14
|
const sessionStateKeyPrefix = 'dfw_sdk_state';
|
|
5
15
|
const sessionBootstrapPath = 'v1/session';
|
|
6
16
|
|
|
17
|
+
const keyPrefix = `_worker`;
|
|
18
|
+
|
|
19
|
+
const canLog = () => {
|
|
20
|
+
try {
|
|
21
|
+
return localStorage.getItem('edgeTagDebug') === '1';
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const prefix = `[EdgeTag]`;
|
|
28
|
+
const logger = {
|
|
29
|
+
log: (...args) => {
|
|
30
|
+
if (canLog()) {
|
|
31
|
+
console.log(prefix, ...args);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
error: (...args) => {
|
|
35
|
+
if (canLog()) {
|
|
36
|
+
console.error(prefix, ...args);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
info: (...args) => {
|
|
40
|
+
if (canLog()) {
|
|
41
|
+
console.info(prefix, ...args);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
trace: (...args) => {
|
|
45
|
+
if (canLog()) {
|
|
46
|
+
console.trace(prefix, ...args);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const initKey = `${keyPrefix}StoreMultiple`;
|
|
52
|
+
const saveKV = (destination, data) => {
|
|
53
|
+
const currentSession = getData(destination, 'session');
|
|
54
|
+
if (!currentSession['kv']) {
|
|
55
|
+
currentSession['kv'] = {};
|
|
56
|
+
}
|
|
57
|
+
currentSession['kv'] = {
|
|
58
|
+
...currentSession['kv'],
|
|
59
|
+
...data,
|
|
60
|
+
};
|
|
61
|
+
saveData(destination, 'session', currentSession);
|
|
62
|
+
};
|
|
63
|
+
const saveData = (destination, persistType, value, key = initKey) => {
|
|
64
|
+
if (persistType === 'session') {
|
|
65
|
+
const data = getSession(key);
|
|
66
|
+
data[destination] = value;
|
|
67
|
+
saveSession(data, key);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const data = getLocal(key);
|
|
71
|
+
data[destination] = value;
|
|
72
|
+
saveLocal(data, key);
|
|
73
|
+
};
|
|
74
|
+
const getData = (destination, persistType, key = initKey) => {
|
|
75
|
+
let data;
|
|
76
|
+
if (persistType === 'session') {
|
|
77
|
+
data = getSession(key);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
data = getLocal(key);
|
|
81
|
+
}
|
|
82
|
+
return (data === null || data === void 0 ? void 0 : data[destination]) || {};
|
|
83
|
+
};
|
|
84
|
+
const saveLocal = (value, key) => {
|
|
85
|
+
try {
|
|
86
|
+
if (!localStorage) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
logger.log('Local storage not supported.');
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const getLocal = (key) => {
|
|
96
|
+
try {
|
|
97
|
+
if (!localStorage) {
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
const data = localStorage.getItem(key);
|
|
101
|
+
if (!data) {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
return JSON.parse(data) || {};
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const saveSession = (value, key) => {
|
|
111
|
+
try {
|
|
112
|
+
if (!sessionStorage) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
sessionStorage.setItem(key, JSON.stringify(value));
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
logger.log('Session storage not supported.');
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const getSession = (key) => {
|
|
122
|
+
try {
|
|
123
|
+
if (!sessionStorage) {
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
const data = sessionStorage.getItem(key);
|
|
127
|
+
if (!data) {
|
|
128
|
+
return {};
|
|
129
|
+
}
|
|
130
|
+
return JSON.parse(data) || {};
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
7
137
|
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
8
138
|
// Coalesces concurrent cold bootstraps per merchant: events firing in the same
|
|
9
139
|
// tick share one in-flight /v1/session call instead of racing N handshakes.
|
|
@@ -92,6 +222,38 @@ function expiresAtMs(state) {
|
|
|
92
222
|
const expiresAt = Date.parse(state.expires_at);
|
|
93
223
|
return Number.isFinite(expiresAt) ? expiresAt : null;
|
|
94
224
|
}
|
|
225
|
+
// The dfw_nid cookie belongs to the DataFlywheel identity host, so the merchant's
|
|
226
|
+
// edge never receives it. Publishing into the SDK's kv store puts the same values
|
|
227
|
+
// on every request body instead -- identify included, which providerData is not.
|
|
228
|
+
const published = new Map();
|
|
229
|
+
function publishToKv(destination, state) {
|
|
230
|
+
if (!destination || !state)
|
|
231
|
+
return;
|
|
232
|
+
// saveKV rewrites the whole multi-destination blob synchronously, and the
|
|
233
|
+
// cached path runs on every event, so skip when nothing moved.
|
|
234
|
+
const last = published.get(destination);
|
|
235
|
+
if (last &&
|
|
236
|
+
last.n_id === state.n_id &&
|
|
237
|
+
last.session_id === state.session_id &&
|
|
238
|
+
last.expires_at === state.expires_at) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
published.set(destination, {
|
|
242
|
+
n_id: state.n_id,
|
|
243
|
+
session_id: state.session_id,
|
|
244
|
+
expires_at: state.expires_at,
|
|
245
|
+
});
|
|
246
|
+
try {
|
|
247
|
+
saveKV(destination, {
|
|
248
|
+
[kvKey(storedNIdKey)]: state.n_id,
|
|
249
|
+
[kvKey(storedSessionIdKey)]: state.session_id,
|
|
250
|
+
[kvKey(storedSessionExpiryKey)]: state.expires_at,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Storage can be unavailable; the providerData and provider-key routes remain.
|
|
255
|
+
}
|
|
256
|
+
}
|
|
95
257
|
function bootstrapUrl(input) {
|
|
96
258
|
const base = input.dataFlywheelUrl.endsWith('/')
|
|
97
259
|
? input.dataFlywheelUrl
|
|
@@ -151,6 +313,7 @@ function ensureSession(input) {
|
|
|
151
313
|
const now = (_c = (_b = input.now) === null || _b === void 0 ? void 0 : _b.call(input)) !== null && _c !== void 0 ? _c : Date.now();
|
|
152
314
|
const existing = readSessionState(storage, merchantId);
|
|
153
315
|
if (!shouldBootstrap(now, expiresAtMs(existing))) {
|
|
316
|
+
publishToKv(input.destination, existing);
|
|
154
317
|
return Promise.resolve(existing);
|
|
155
318
|
}
|
|
156
319
|
// Opaque origins (sandboxed iframes like Shopify pixels) serialize to the
|
|
@@ -174,7 +337,12 @@ function ensureSession(input) {
|
|
|
174
337
|
storage,
|
|
175
338
|
fetcher,
|
|
176
339
|
now,
|
|
177
|
-
})
|
|
340
|
+
})
|
|
341
|
+
.then((state) => {
|
|
342
|
+
publishToKv(input.destination, state);
|
|
343
|
+
return state;
|
|
344
|
+
})
|
|
345
|
+
.finally(() => {
|
|
178
346
|
inFlight.delete(key);
|
|
179
347
|
});
|
|
180
348
|
inFlight.set(key, bootstrap);
|
|
@@ -194,13 +362,15 @@ const init = (params) => {
|
|
|
194
362
|
void ensureSession({
|
|
195
363
|
variables: params.manifest.variables,
|
|
196
364
|
pageUrl: params.pageUrl,
|
|
365
|
+
destination: params.destination,
|
|
197
366
|
});
|
|
198
367
|
};
|
|
199
368
|
|
|
200
|
-
const tag = async ({ manifestVariables, pageUrl, }) => {
|
|
369
|
+
const tag = async ({ manifestVariables, pageUrl, destination, }) => {
|
|
201
370
|
const state = await ensureSession({
|
|
202
371
|
variables: manifestVariables,
|
|
203
372
|
pageUrl,
|
|
373
|
+
destination,
|
|
204
374
|
});
|
|
205
375
|
return toProviderData(state);
|
|
206
376
|
};
|
package/index.js
CHANGED
|
@@ -2,9 +2,139 @@ var ProvidersDataflywheelSdk = (function () {
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const packageName = 'dataFlywheel';
|
|
5
|
+
// Browser identifiers the edge persists against the EdgeTag user id, the way
|
|
6
|
+
// Facebook stores _fbp/_fbc. The identify request carries no providerData and
|
|
7
|
+
// dfw_nid is third-party to the merchant, so this is n_id's only route there.
|
|
8
|
+
const storedNIdKey = 'n_id';
|
|
9
|
+
const storedSessionIdKey = 'session_id';
|
|
10
|
+
const storedSessionExpiryKey = 'session_expires_at';
|
|
11
|
+
// The same identifiers published into the EdgeTag SDK's kv store, which rides
|
|
12
|
+
// on every request including identify. Namespaced the way the edge splits them
|
|
13
|
+
// back apart, as Facebook does with facebook::_fbp.
|
|
14
|
+
const kvKey = (key) => `${packageName}::${key}`;
|
|
5
15
|
const sessionStateKeyPrefix = 'dfw_sdk_state';
|
|
6
16
|
const sessionBootstrapPath = 'v1/session';
|
|
7
17
|
|
|
18
|
+
const keyPrefix = `_worker`;
|
|
19
|
+
|
|
20
|
+
const canLog = () => {
|
|
21
|
+
try {
|
|
22
|
+
return localStorage.getItem('edgeTagDebug') === '1';
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const prefix = `[EdgeTag]`;
|
|
29
|
+
const logger = {
|
|
30
|
+
log: (...args) => {
|
|
31
|
+
if (canLog()) {
|
|
32
|
+
console.log(prefix, ...args);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
error: (...args) => {
|
|
36
|
+
if (canLog()) {
|
|
37
|
+
console.error(prefix, ...args);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
info: (...args) => {
|
|
41
|
+
if (canLog()) {
|
|
42
|
+
console.info(prefix, ...args);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
trace: (...args) => {
|
|
46
|
+
if (canLog()) {
|
|
47
|
+
console.trace(prefix, ...args);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const initKey = `${keyPrefix}StoreMultiple`;
|
|
53
|
+
const saveKV = (destination, data) => {
|
|
54
|
+
const currentSession = getData(destination, 'session');
|
|
55
|
+
if (!currentSession['kv']) {
|
|
56
|
+
currentSession['kv'] = {};
|
|
57
|
+
}
|
|
58
|
+
currentSession['kv'] = {
|
|
59
|
+
...currentSession['kv'],
|
|
60
|
+
...data,
|
|
61
|
+
};
|
|
62
|
+
saveData(destination, 'session', currentSession);
|
|
63
|
+
};
|
|
64
|
+
const saveData = (destination, persistType, value, key = initKey) => {
|
|
65
|
+
if (persistType === 'session') {
|
|
66
|
+
const data = getSession(key);
|
|
67
|
+
data[destination] = value;
|
|
68
|
+
saveSession(data, key);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const data = getLocal(key);
|
|
72
|
+
data[destination] = value;
|
|
73
|
+
saveLocal(data, key);
|
|
74
|
+
};
|
|
75
|
+
const getData = (destination, persistType, key = initKey) => {
|
|
76
|
+
let data;
|
|
77
|
+
if (persistType === 'session') {
|
|
78
|
+
data = getSession(key);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
data = getLocal(key);
|
|
82
|
+
}
|
|
83
|
+
return (data === null || data === void 0 ? void 0 : data[destination]) || {};
|
|
84
|
+
};
|
|
85
|
+
const saveLocal = (value, key) => {
|
|
86
|
+
try {
|
|
87
|
+
if (!localStorage) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
logger.log('Local storage not supported.');
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const getLocal = (key) => {
|
|
97
|
+
try {
|
|
98
|
+
if (!localStorage) {
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
const data = localStorage.getItem(key);
|
|
102
|
+
if (!data) {
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
return JSON.parse(data) || {};
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return {};
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const saveSession = (value, key) => {
|
|
112
|
+
try {
|
|
113
|
+
if (!sessionStorage) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
sessionStorage.setItem(key, JSON.stringify(value));
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
logger.log('Session storage not supported.');
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const getSession = (key) => {
|
|
123
|
+
try {
|
|
124
|
+
if (!sessionStorage) {
|
|
125
|
+
return {};
|
|
126
|
+
}
|
|
127
|
+
const data = sessionStorage.getItem(key);
|
|
128
|
+
if (!data) {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
return JSON.parse(data) || {};
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return {};
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
8
138
|
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
9
139
|
// Coalesces concurrent cold bootstraps per merchant: events firing in the same
|
|
10
140
|
// tick share one in-flight /v1/session call instead of racing N handshakes.
|
|
@@ -93,6 +223,38 @@ var ProvidersDataflywheelSdk = (function () {
|
|
|
93
223
|
const expiresAt = Date.parse(state.expires_at);
|
|
94
224
|
return Number.isFinite(expiresAt) ? expiresAt : null;
|
|
95
225
|
}
|
|
226
|
+
// The dfw_nid cookie belongs to the DataFlywheel identity host, so the merchant's
|
|
227
|
+
// edge never receives it. Publishing into the SDK's kv store puts the same values
|
|
228
|
+
// on every request body instead -- identify included, which providerData is not.
|
|
229
|
+
const published = new Map();
|
|
230
|
+
function publishToKv(destination, state) {
|
|
231
|
+
if (!destination || !state)
|
|
232
|
+
return;
|
|
233
|
+
// saveKV rewrites the whole multi-destination blob synchronously, and the
|
|
234
|
+
// cached path runs on every event, so skip when nothing moved.
|
|
235
|
+
const last = published.get(destination);
|
|
236
|
+
if (last &&
|
|
237
|
+
last.n_id === state.n_id &&
|
|
238
|
+
last.session_id === state.session_id &&
|
|
239
|
+
last.expires_at === state.expires_at) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
published.set(destination, {
|
|
243
|
+
n_id: state.n_id,
|
|
244
|
+
session_id: state.session_id,
|
|
245
|
+
expires_at: state.expires_at,
|
|
246
|
+
});
|
|
247
|
+
try {
|
|
248
|
+
saveKV(destination, {
|
|
249
|
+
[kvKey(storedNIdKey)]: state.n_id,
|
|
250
|
+
[kvKey(storedSessionIdKey)]: state.session_id,
|
|
251
|
+
[kvKey(storedSessionExpiryKey)]: state.expires_at,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// Storage can be unavailable; the providerData and provider-key routes remain.
|
|
256
|
+
}
|
|
257
|
+
}
|
|
96
258
|
function bootstrapUrl(input) {
|
|
97
259
|
const base = input.dataFlywheelUrl.endsWith('/')
|
|
98
260
|
? input.dataFlywheelUrl
|
|
@@ -152,6 +314,7 @@ var ProvidersDataflywheelSdk = (function () {
|
|
|
152
314
|
const now = (_c = (_b = input.now) === null || _b === void 0 ? void 0 : _b.call(input)) !== null && _c !== void 0 ? _c : Date.now();
|
|
153
315
|
const existing = readSessionState(storage, merchantId);
|
|
154
316
|
if (!shouldBootstrap(now, expiresAtMs(existing))) {
|
|
317
|
+
publishToKv(input.destination, existing);
|
|
155
318
|
return Promise.resolve(existing);
|
|
156
319
|
}
|
|
157
320
|
// Opaque origins (sandboxed iframes like Shopify pixels) serialize to the
|
|
@@ -175,7 +338,12 @@ var ProvidersDataflywheelSdk = (function () {
|
|
|
175
338
|
storage,
|
|
176
339
|
fetcher,
|
|
177
340
|
now,
|
|
178
|
-
})
|
|
341
|
+
})
|
|
342
|
+
.then((state) => {
|
|
343
|
+
publishToKv(input.destination, state);
|
|
344
|
+
return state;
|
|
345
|
+
})
|
|
346
|
+
.finally(() => {
|
|
179
347
|
inFlight.delete(key);
|
|
180
348
|
});
|
|
181
349
|
inFlight.set(key, bootstrap);
|
|
@@ -195,13 +363,15 @@ var ProvidersDataflywheelSdk = (function () {
|
|
|
195
363
|
void ensureSession({
|
|
196
364
|
variables: params.manifest.variables,
|
|
197
365
|
pageUrl: params.pageUrl,
|
|
366
|
+
destination: params.destination,
|
|
198
367
|
});
|
|
199
368
|
};
|
|
200
369
|
|
|
201
|
-
const tag = async ({ manifestVariables, pageUrl, }) => {
|
|
370
|
+
const tag = async ({ manifestVariables, pageUrl, destination, }) => {
|
|
202
371
|
const state = await ensureSession({
|
|
203
372
|
variables: manifestVariables,
|
|
204
373
|
pageUrl,
|
|
374
|
+
destination,
|
|
205
375
|
});
|
|
206
376
|
return toProviderData(state);
|
|
207
377
|
};
|
package/index.mjs
CHANGED
|
@@ -1,7 +1,137 @@
|
|
|
1
1
|
const packageName = 'dataFlywheel';
|
|
2
|
+
// Browser identifiers the edge persists against the EdgeTag user id, the way
|
|
3
|
+
// Facebook stores _fbp/_fbc. The identify request carries no providerData and
|
|
4
|
+
// dfw_nid is third-party to the merchant, so this is n_id's only route there.
|
|
5
|
+
const storedNIdKey = 'n_id';
|
|
6
|
+
const storedSessionIdKey = 'session_id';
|
|
7
|
+
const storedSessionExpiryKey = 'session_expires_at';
|
|
8
|
+
// The same identifiers published into the EdgeTag SDK's kv store, which rides
|
|
9
|
+
// on every request including identify. Namespaced the way the edge splits them
|
|
10
|
+
// back apart, as Facebook does with facebook::_fbp.
|
|
11
|
+
const kvKey = (key) => `${packageName}::${key}`;
|
|
2
12
|
const sessionStateKeyPrefix = 'dfw_sdk_state';
|
|
3
13
|
const sessionBootstrapPath = 'v1/session';
|
|
4
14
|
|
|
15
|
+
const keyPrefix = `_worker`;
|
|
16
|
+
|
|
17
|
+
const canLog = () => {
|
|
18
|
+
try {
|
|
19
|
+
return localStorage.getItem('edgeTagDebug') === '1';
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const prefix = `[EdgeTag]`;
|
|
26
|
+
const logger = {
|
|
27
|
+
log: (...args) => {
|
|
28
|
+
if (canLog()) {
|
|
29
|
+
console.log(prefix, ...args);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
error: (...args) => {
|
|
33
|
+
if (canLog()) {
|
|
34
|
+
console.error(prefix, ...args);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
info: (...args) => {
|
|
38
|
+
if (canLog()) {
|
|
39
|
+
console.info(prefix, ...args);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
trace: (...args) => {
|
|
43
|
+
if (canLog()) {
|
|
44
|
+
console.trace(prefix, ...args);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const initKey = `${keyPrefix}StoreMultiple`;
|
|
50
|
+
const saveKV = (destination, data) => {
|
|
51
|
+
const currentSession = getData(destination, 'session');
|
|
52
|
+
if (!currentSession['kv']) {
|
|
53
|
+
currentSession['kv'] = {};
|
|
54
|
+
}
|
|
55
|
+
currentSession['kv'] = {
|
|
56
|
+
...currentSession['kv'],
|
|
57
|
+
...data,
|
|
58
|
+
};
|
|
59
|
+
saveData(destination, 'session', currentSession);
|
|
60
|
+
};
|
|
61
|
+
const saveData = (destination, persistType, value, key = initKey) => {
|
|
62
|
+
if (persistType === 'session') {
|
|
63
|
+
const data = getSession(key);
|
|
64
|
+
data[destination] = value;
|
|
65
|
+
saveSession(data, key);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const data = getLocal(key);
|
|
69
|
+
data[destination] = value;
|
|
70
|
+
saveLocal(data, key);
|
|
71
|
+
};
|
|
72
|
+
const getData = (destination, persistType, key = initKey) => {
|
|
73
|
+
let data;
|
|
74
|
+
if (persistType === 'session') {
|
|
75
|
+
data = getSession(key);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
data = getLocal(key);
|
|
79
|
+
}
|
|
80
|
+
return (data === null || data === void 0 ? void 0 : data[destination]) || {};
|
|
81
|
+
};
|
|
82
|
+
const saveLocal = (value, key) => {
|
|
83
|
+
try {
|
|
84
|
+
if (!localStorage) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
logger.log('Local storage not supported.');
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const getLocal = (key) => {
|
|
94
|
+
try {
|
|
95
|
+
if (!localStorage) {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
const data = localStorage.getItem(key);
|
|
99
|
+
if (!data) {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
return JSON.parse(data) || {};
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const saveSession = (value, key) => {
|
|
109
|
+
try {
|
|
110
|
+
if (!sessionStorage) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
sessionStorage.setItem(key, JSON.stringify(value));
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
logger.log('Session storage not supported.');
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const getSession = (key) => {
|
|
120
|
+
try {
|
|
121
|
+
if (!sessionStorage) {
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
const data = sessionStorage.getItem(key);
|
|
125
|
+
if (!data) {
|
|
126
|
+
return {};
|
|
127
|
+
}
|
|
128
|
+
return JSON.parse(data) || {};
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return {};
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
5
135
|
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
6
136
|
// Coalesces concurrent cold bootstraps per merchant: events firing in the same
|
|
7
137
|
// tick share one in-flight /v1/session call instead of racing N handshakes.
|
|
@@ -90,6 +220,38 @@ function expiresAtMs(state) {
|
|
|
90
220
|
const expiresAt = Date.parse(state.expires_at);
|
|
91
221
|
return Number.isFinite(expiresAt) ? expiresAt : null;
|
|
92
222
|
}
|
|
223
|
+
// The dfw_nid cookie belongs to the DataFlywheel identity host, so the merchant's
|
|
224
|
+
// edge never receives it. Publishing into the SDK's kv store puts the same values
|
|
225
|
+
// on every request body instead -- identify included, which providerData is not.
|
|
226
|
+
const published = new Map();
|
|
227
|
+
function publishToKv(destination, state) {
|
|
228
|
+
if (!destination || !state)
|
|
229
|
+
return;
|
|
230
|
+
// saveKV rewrites the whole multi-destination blob synchronously, and the
|
|
231
|
+
// cached path runs on every event, so skip when nothing moved.
|
|
232
|
+
const last = published.get(destination);
|
|
233
|
+
if (last &&
|
|
234
|
+
last.n_id === state.n_id &&
|
|
235
|
+
last.session_id === state.session_id &&
|
|
236
|
+
last.expires_at === state.expires_at) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
published.set(destination, {
|
|
240
|
+
n_id: state.n_id,
|
|
241
|
+
session_id: state.session_id,
|
|
242
|
+
expires_at: state.expires_at,
|
|
243
|
+
});
|
|
244
|
+
try {
|
|
245
|
+
saveKV(destination, {
|
|
246
|
+
[kvKey(storedNIdKey)]: state.n_id,
|
|
247
|
+
[kvKey(storedSessionIdKey)]: state.session_id,
|
|
248
|
+
[kvKey(storedSessionExpiryKey)]: state.expires_at,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// Storage can be unavailable; the providerData and provider-key routes remain.
|
|
253
|
+
}
|
|
254
|
+
}
|
|
93
255
|
function bootstrapUrl(input) {
|
|
94
256
|
const base = input.dataFlywheelUrl.endsWith('/')
|
|
95
257
|
? input.dataFlywheelUrl
|
|
@@ -149,6 +311,7 @@ function ensureSession(input) {
|
|
|
149
311
|
const now = (_c = (_b = input.now) === null || _b === void 0 ? void 0 : _b.call(input)) !== null && _c !== void 0 ? _c : Date.now();
|
|
150
312
|
const existing = readSessionState(storage, merchantId);
|
|
151
313
|
if (!shouldBootstrap(now, expiresAtMs(existing))) {
|
|
314
|
+
publishToKv(input.destination, existing);
|
|
152
315
|
return Promise.resolve(existing);
|
|
153
316
|
}
|
|
154
317
|
// Opaque origins (sandboxed iframes like Shopify pixels) serialize to the
|
|
@@ -172,7 +335,12 @@ function ensureSession(input) {
|
|
|
172
335
|
storage,
|
|
173
336
|
fetcher,
|
|
174
337
|
now,
|
|
175
|
-
})
|
|
338
|
+
})
|
|
339
|
+
.then((state) => {
|
|
340
|
+
publishToKv(input.destination, state);
|
|
341
|
+
return state;
|
|
342
|
+
})
|
|
343
|
+
.finally(() => {
|
|
176
344
|
inFlight.delete(key);
|
|
177
345
|
});
|
|
178
346
|
inFlight.set(key, bootstrap);
|
|
@@ -192,13 +360,15 @@ const init = (params) => {
|
|
|
192
360
|
void ensureSession({
|
|
193
361
|
variables: params.manifest.variables,
|
|
194
362
|
pageUrl: params.pageUrl,
|
|
363
|
+
destination: params.destination,
|
|
195
364
|
});
|
|
196
365
|
};
|
|
197
366
|
|
|
198
|
-
const tag = async ({ manifestVariables, pageUrl, }) => {
|
|
367
|
+
const tag = async ({ manifestVariables, pageUrl, destination, }) => {
|
|
199
368
|
const state = await ensureSession({
|
|
200
369
|
variables: manifestVariables,
|
|
201
370
|
pageUrl,
|
|
371
|
+
destination,
|
|
202
372
|
});
|
|
203
373
|
return toProviderData(state);
|
|
204
374
|
};
|