@blotoutio/edgetag-sdk-js 0.5.1 → 0.6.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..esm.js ADDED
@@ -0,0 +1,569 @@
1
+ import { v4 } from 'uuid';
2
+
3
+ /******************************************************************************
4
+ Copyright (c) Microsoft Corporation.
5
+
6
+ Permission to use, copy, modify, and/or distribute this software for any
7
+ purpose with or without fee is hereby granted.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
16
+ ***************************************************************************** */
17
+
18
+ function __awaiter(thisArg, _arguments, P, generator) {
19
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
20
+ return new (P || (P = Promise))(function (resolve, reject) {
21
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
22
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
23
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
24
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
25
+ });
26
+ }
27
+
28
+ const tagStorage = 'edgeTag';
29
+ const consentKey = 'consent';
30
+ const providersKey = 'providers';
31
+ const keyPrefix = `_worker`;
32
+
33
+ const initKey = `${keyPrefix}Store`;
34
+ const getCookieValue = (key) => {
35
+ if (!document || !document.cookie) {
36
+ return '';
37
+ }
38
+ const name = `${key}=`;
39
+ const decodedCookie = decodeURIComponent(document.cookie);
40
+ const ca = decodedCookie.split(';');
41
+ for (let i = 0; i < ca.length; i++) {
42
+ let c = ca[i];
43
+ while (c.charAt(0) === ' ') {
44
+ c = c.substring(1);
45
+ }
46
+ if (c.indexOf(name) === 0) {
47
+ return c.substring(name.length, c.length);
48
+ }
49
+ }
50
+ return '';
51
+ };
52
+ const saveDataPerKey = (persistType, provider, value, key) => {
53
+ const storage = getData$1(persistType);
54
+ if (!storage['data']) {
55
+ storage['data'] = {};
56
+ }
57
+ if (!storage['data'][provider]) {
58
+ storage['data'][provider] = {};
59
+ }
60
+ storage['data'][provider][key] = value;
61
+ saveData(persistType, storage);
62
+ };
63
+ const savePerKey = (persistType, provider, value, key) => {
64
+ const storage = getData$1(persistType);
65
+ if (!storage[provider]) {
66
+ storage[provider] = {};
67
+ }
68
+ storage[provider][key] = value;
69
+ saveData(persistType, storage);
70
+ };
71
+ const getDataPerKey = (persistType, provider, key) => {
72
+ const storage = getData$1(persistType);
73
+ if (!storage[provider]) {
74
+ return undefined;
75
+ }
76
+ return storage[provider][key];
77
+ };
78
+ const saveData = (persistType, value, key = initKey) => {
79
+ if (persistType === 'session') {
80
+ saveSession(value, key);
81
+ return;
82
+ }
83
+ saveLocal(value, key);
84
+ };
85
+ const getData$1 = (persistType, key = initKey) => {
86
+ if (persistType === 'session') {
87
+ return getSession(key);
88
+ }
89
+ return getLocal(key);
90
+ };
91
+ const saveKV = (data) => {
92
+ let currentSession = getData$1('session');
93
+ if (!currentSession) {
94
+ currentSession = {};
95
+ }
96
+ if (!currentSession['kv']) {
97
+ currentSession['kv'] = {};
98
+ }
99
+ currentSession['kv'] = Object.assign(Object.assign({}, currentSession['kv']), data);
100
+ saveData('session', currentSession);
101
+ };
102
+ const saveLocal = (value, key) => {
103
+ localStorage.setItem(key, JSON.stringify(value));
104
+ };
105
+ const getLocal = (key) => {
106
+ const data = localStorage.getItem(key);
107
+ if (!data) {
108
+ return {};
109
+ }
110
+ return JSON.parse(data) || {};
111
+ };
112
+ const saveSession = (value, key) => {
113
+ sessionStorage.setItem(key, JSON.stringify(value));
114
+ };
115
+ const getSession = (key) => {
116
+ const data = sessionStorage.getItem(key);
117
+ if (!data) {
118
+ return {};
119
+ }
120
+ return JSON.parse(data) || {};
121
+ };
122
+
123
+ let endpointUrl = '';
124
+ const generateUrl = (path) => {
125
+ const endpoint = getUrl();
126
+ if (!endpoint) {
127
+ console.log('URL is not valid');
128
+ return '';
129
+ }
130
+ return `${endpoint}${path}`;
131
+ };
132
+ const getUrl = () => {
133
+ return endpointUrl;
134
+ };
135
+ const setUrl = (url) => {
136
+ if (url == null) {
137
+ return;
138
+ }
139
+ endpointUrl = url;
140
+ };
141
+ const getTagURL = () => {
142
+ return generateUrl('/tag');
143
+ };
144
+ const getInitURL = () => {
145
+ return generateUrl('/init');
146
+ };
147
+ const getConsentURL = () => {
148
+ return generateUrl('/consent');
149
+ };
150
+ const getUserURL = () => {
151
+ return generateUrl('/user');
152
+ };
153
+ const getDataURL = () => {
154
+ return generateUrl(`/data`);
155
+ };
156
+ const getGetDataURL = (keys) => {
157
+ return generateUrl(`/data?keys=${encodeURIComponent(keys.join(','))}`);
158
+ };
159
+ const getKeysURL = () => {
160
+ return generateUrl(`/keys`);
161
+ };
162
+
163
+ let consentDisabled = false;
164
+ let providers = [];
165
+ const setPreferences = (preferences) => {
166
+ if (!preferences) {
167
+ return false;
168
+ }
169
+ if (!preferences.edgeURL) {
170
+ console.error('Please provide URL for EdgeTag');
171
+ return false;
172
+ }
173
+ consentDisabled = !!preferences.disableConsentCheck;
174
+ providers = preferences.providers || [];
175
+ if (window && window.edgetagPackages) {
176
+ providers = [...window.edgetagPackages, ...providers];
177
+ }
178
+ setUrl(preferences.edgeURL);
179
+ return true;
180
+ };
181
+ const isConsentDisabled = () => consentDisabled;
182
+ const getProvidersPackage = () => providers;
183
+
184
+ const getUserAgent = () => {
185
+ const nav = navigator;
186
+ let ua = nav.userAgent;
187
+ ua += nav.brave ? `${ua} Brave` : '';
188
+ return ua;
189
+ };
190
+ const allowTag = (providers) => {
191
+ const consent = getDataPerKey('local', tagStorage, consentKey);
192
+ if (isConsentDisabled()) {
193
+ return true;
194
+ }
195
+ if (!consent) {
196
+ return false;
197
+ }
198
+ if (consent['all']) {
199
+ return true;
200
+ }
201
+ if (!providers) {
202
+ return (Object.values(consent).find((isAllowed) => isAllowed) || false);
203
+ }
204
+ const allProviders = (getDataPerKey('local', tagStorage, providersKey) ||
205
+ []);
206
+ for (const provider of allProviders) {
207
+ const tagProviderSetting = providers[provider];
208
+ if (tagProviderSetting ||
209
+ (providers['all'] === true && tagProviderSetting === undefined) ||
210
+ (providers['all'] === false && tagProviderSetting === true)) {
211
+ if (consent[provider]) {
212
+ return true;
213
+ }
214
+ }
215
+ }
216
+ return false;
217
+ };
218
+ const allowProvider = (providers, providerId) => {
219
+ const consent = getDataPerKey('local', tagStorage, consentKey);
220
+ if (isConsentDisabled()) {
221
+ return true;
222
+ }
223
+ if (!consent) {
224
+ return false;
225
+ }
226
+ if (consent['all']) {
227
+ return true;
228
+ }
229
+ if (!providers) {
230
+ return consent[providerId];
231
+ }
232
+ const tagProvider = providers[providerId];
233
+ if (tagProvider ||
234
+ (providers['all'] === true && tagProvider === undefined) ||
235
+ (providers['all'] === false && tagProvider === true)) {
236
+ if (consent[providerId]) {
237
+ return true;
238
+ }
239
+ }
240
+ return false;
241
+ };
242
+
243
+ const beacon = (url, payload) => {
244
+ let blob;
245
+ if (payload) {
246
+ blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
247
+ }
248
+ return navigator.sendBeacon(url, blob);
249
+ };
250
+ const ajax = (method, url, payload) => __awaiter(void 0, void 0, void 0, function* () {
251
+ return yield fetch(url, {
252
+ method,
253
+ headers: {
254
+ 'Content-type': 'application/json; charset=utf-8',
255
+ Accept: 'application/json; charset=utf-8',
256
+ },
257
+ body: JSON.stringify(payload),
258
+ credentials: 'include',
259
+ })
260
+ .then((response) => response.json().then((data) => ({ status: response.status, body: data })))
261
+ .then((data) => {
262
+ if (data.status < 200 || data.status >= 300) {
263
+ // Q: do we need to retry?
264
+ return Promise.reject(new Error(JSON.stringify(data.body)));
265
+ }
266
+ return Promise.resolve(data.body);
267
+ })
268
+ .catch((error) => {
269
+ // Q: do we need to retry?
270
+ return Promise.reject(new Error(error));
271
+ });
272
+ });
273
+ const getStandardPayload = (payload) => {
274
+ const data = Object.assign({ pageUrl: window.location.href, userAgent: getUserAgent() }, (payload || {}));
275
+ let storage = {};
276
+ const session = getData$1('session');
277
+ if (session) {
278
+ storage = Object.assign(Object.assign({}, storage), session);
279
+ }
280
+ const local = getData$1('local');
281
+ if (local) {
282
+ storage = Object.assign(Object.assign({}, storage), local);
283
+ }
284
+ data.storage = storage;
285
+ return data;
286
+ };
287
+ function postRequest(url, data, options) {
288
+ return __awaiter(this, void 0, void 0, function* () {
289
+ if (!url) {
290
+ return Promise.reject(new Error('URL is empty'));
291
+ }
292
+ const payload = getStandardPayload(data);
293
+ if (options && options.method === 'beacon') {
294
+ return Promise.resolve(beacon(url, payload));
295
+ }
296
+ return yield ajax('POST', url, payload);
297
+ });
298
+ }
299
+ function getRequest(url, options) {
300
+ return __awaiter(this, void 0, void 0, function* () {
301
+ if (!url) {
302
+ return Promise.reject(new Error('URL is empty'));
303
+ }
304
+ if (options && options.method === 'beacon') {
305
+ return {
306
+ result: Promise.resolve(beacon(url)),
307
+ };
308
+ }
309
+ return yield ajax('GET', url);
310
+ });
311
+ }
312
+
313
+ const info = (data) => {
314
+ };
315
+
316
+ const saveConsent = (consent) => {
317
+ savePerKey('local', tagStorage, consent, consentKey);
318
+ };
319
+ const handleConsent = (consent) => {
320
+ const payload = {
321
+ consentString: consent,
322
+ };
323
+ saveConsent(consent);
324
+ postRequest(getConsentURL(), payload).catch(info);
325
+ };
326
+
327
+ const generateEventId = (name) => {
328
+ let time = Date.now().toString();
329
+ if (typeof performance !== 'undefined' &&
330
+ typeof performance.now === 'function') {
331
+ const perf = performance.now();
332
+ if (perf) {
333
+ time = perf.toFixed(4);
334
+ }
335
+ }
336
+ return `${btoa(name)}-${v4()}-${time}`;
337
+ };
338
+
339
+ const handleGetUserId = () => {
340
+ return getCookieValue('tag_user_id');
341
+ };
342
+
343
+ const manifestVariables = {};
344
+ const addProviderVariable = (name, variables) => {
345
+ manifestVariables[name] = variables;
346
+ };
347
+ const getProviderVariables = (name) => manifestVariables[name] || {};
348
+
349
+ const sendTag = ({ eventName, eventId, data, providerData, providers, options, }) => {
350
+ const payload = {
351
+ eventName,
352
+ eventId,
353
+ timestamp: Date.now(),
354
+ data,
355
+ providerData,
356
+ };
357
+ if (providers) {
358
+ payload.providers = providers;
359
+ }
360
+ postRequest(getTagURL(), payload, options).catch(info);
361
+ };
362
+ const handleTag = (eventName, data = {}, providers, options) => {
363
+ if (!allowTag(providers)) {
364
+ console.log('No consent');
365
+ return;
366
+ }
367
+ let eventId = data['eventId'];
368
+ if (!eventId) {
369
+ eventId = generateEventId(eventName);
370
+ }
371
+ const providerPackages = getProvidersPackage();
372
+ const userId = handleGetUserId();
373
+ const providerData = {};
374
+ if (providerPackages) {
375
+ providerPackages.forEach((pkg) => {
376
+ if (!allowProvider(providers, pkg.name)) {
377
+ return;
378
+ }
379
+ if (pkg && pkg.tag) {
380
+ const result = pkg.tag({
381
+ userId,
382
+ eventName,
383
+ eventId,
384
+ data,
385
+ sendTag,
386
+ manifestVariables: getProviderVariables(pkg.name),
387
+ });
388
+ if (result) {
389
+ providerData[pkg.name] = result;
390
+ }
391
+ }
392
+ });
393
+ }
394
+ sendTag({
395
+ eventName,
396
+ eventId,
397
+ data,
398
+ providerData,
399
+ providers,
400
+ options,
401
+ });
402
+ };
403
+
404
+ const handleCaptureQuery = (provider, key, persistType) => {
405
+ if (!window) {
406
+ return;
407
+ }
408
+ const params = new URLSearchParams(window.location.search);
409
+ if (!params || !params.get(key)) {
410
+ return;
411
+ }
412
+ saveDataPerKey(persistType, provider, params.get(key), key);
413
+ };
414
+ const handleCaptureStorage = (provider, key, persistType, location) => {
415
+ let data;
416
+ switch (location) {
417
+ case 'cookie': {
418
+ data = getCookieValue(key);
419
+ break;
420
+ }
421
+ case 'local': {
422
+ data = sessionStorage && sessionStorage.getItem(key);
423
+ break;
424
+ }
425
+ case 'session': {
426
+ data = localStorage && localStorage.getItem(key);
427
+ break;
428
+ }
429
+ }
430
+ if (!data) {
431
+ return;
432
+ }
433
+ saveDataPerKey(persistType, provider, data, key);
434
+ };
435
+ const handleCapture = (provider, params) => {
436
+ params.forEach((param) => {
437
+ switch (param.type) {
438
+ case 'query': {
439
+ handleCaptureQuery(provider, param.key, param.persist);
440
+ break;
441
+ }
442
+ case 'storage': {
443
+ handleCaptureStorage(provider, param.key, param.persist, param.location);
444
+ break;
445
+ }
446
+ }
447
+ });
448
+ };
449
+
450
+ const handleManifest = (manifest) => {
451
+ const providers = [];
452
+ const providerPackages = getProvidersPackage();
453
+ const userId = handleGetUserId();
454
+ manifest.forEach((provider) => {
455
+ providers.push(provider.package);
456
+ addProviderVariable(provider.package, provider.variables);
457
+ if (provider.rules) {
458
+ Object.entries(provider.rules).forEach(([name, recipe]) => {
459
+ switch (name) {
460
+ case 'capture': {
461
+ handleCapture(provider.package, recipe);
462
+ return;
463
+ }
464
+ }
465
+ });
466
+ }
467
+ if (providerPackages) {
468
+ const pkg = providerPackages.find((pkg) => pkg.name === provider.package);
469
+ if (pkg && pkg.init) {
470
+ pkg.init({ userId, manifest: provider, sendTag });
471
+ }
472
+ }
473
+ });
474
+ savePerKey('local', tagStorage, providers, providersKey);
475
+ };
476
+
477
+ const handleInit = (preferences) => {
478
+ const success = setPreferences(preferences);
479
+ if (!success) {
480
+ return;
481
+ }
482
+ const url = new URL(getInitURL());
483
+ if (preferences.disableConsentCheck) {
484
+ url.searchParams.set('consentDisabled', 'true');
485
+ saveConsent({ all: true });
486
+ }
487
+ if (preferences.userId) {
488
+ url.searchParams.set('userId', preferences.userId);
489
+ }
490
+ getRequest(url.href)
491
+ .then((result) => {
492
+ if (!result) {
493
+ console.log('init failed');
494
+ return;
495
+ }
496
+ handleManifest(result.result);
497
+ })
498
+ .catch(info);
499
+ };
500
+
501
+ const handleUser = (key, value, options) => {
502
+ if (!key || !value) {
503
+ console.error('Key or Value is missing in user API.');
504
+ return;
505
+ }
506
+ saveKV({
507
+ [key]: value,
508
+ });
509
+ postRequest(getUserURL(), {
510
+ key,
511
+ value,
512
+ }, options).catch(info);
513
+ };
514
+
515
+ const handleData = (data, options) => {
516
+ if (!data || Object.keys(data).length === 0) {
517
+ console.error('Provide data for data API.');
518
+ return;
519
+ }
520
+ saveKV(data);
521
+ postRequest(getDataURL(), { data }, options).catch(info);
522
+ };
523
+
524
+ const handleGetData = (keys, callback) => {
525
+ if (!keys || keys.length === 0) {
526
+ console.error('Provide keys for get data API.');
527
+ return;
528
+ }
529
+ getRequest(getGetDataURL(keys))
530
+ .then((result) => {
531
+ callback((result === null || result === void 0 ? void 0 : result.result) || {});
532
+ })
533
+ .catch(info);
534
+ };
535
+
536
+ const handleKeys = (callback) => {
537
+ getRequest(getKeysURL())
538
+ .then((result) => {
539
+ callback((result === null || result === void 0 ? void 0 : result.result) || []);
540
+ })
541
+ .catch(info);
542
+ };
543
+
544
+ const init = (preferences) => {
545
+ handleInit(preferences);
546
+ };
547
+ const tag = (name, data, providers, options) => {
548
+ handleTag(name, data, providers, options);
549
+ };
550
+ const consent = (consent) => {
551
+ handleConsent(consent);
552
+ };
553
+ const user = (key, value, options) => {
554
+ handleUser(key, value, options);
555
+ };
556
+ const data = (data, options) => {
557
+ handleData(data, options);
558
+ };
559
+ const getData = (keys, callback) => {
560
+ handleGetData(keys, callback);
561
+ };
562
+ const keys = (callback) => {
563
+ handleKeys(callback);
564
+ };
565
+ const getUserId = () => {
566
+ return handleGetUserId();
567
+ };
568
+
569
+ export { consent, data, getData, getUserId, init, keys, tag, user };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blotoutio/edgetag-sdk-js",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "JS SDK for EdgeTag",
5
5
  "author": "Blotout",
6
6
  "license": "MIT",
@@ -8,19 +8,12 @@
8
8
  "publishConfig": {
9
9
  "access": "public"
10
10
  },
11
+ "main": "./index.esm.js",
12
+ "peerDependencies": {
13
+ "uuid": "*"
14
+ },
11
15
  "repository": {
12
16
  "type": "git",
13
17
  "url": "git+https://github.com/blotoutio/edgetag-sdk.git"
14
- },
15
- "module": "./index.js",
16
- "main": "./index.cjs",
17
- "type": "module",
18
- "types": "./index.d.ts",
19
- "exports": {
20
- ".": {
21
- "types": "./index.d.ts",
22
- "import": "./index.js",
23
- "require": "./index.cjs"
24
- }
25
18
  }
26
19
  }
@@ -1,3 +1,58 @@
1
+ // TODO this all located in '@blotoutio/shared/utility-sdk'
2
+
3
+ type PersistType = 'local' | 'session'
4
+
5
+ type EventOptions = {
6
+ method?: 'beacon'
7
+ }
8
+
9
+ type Data = Record<string, unknown>
10
+
11
+ export type SendTag = {
12
+ eventName: string
13
+ eventId: string
14
+ data: Data
15
+ providerData?: Data
16
+ providers?: Data
17
+ options?: EventOptions
18
+ }
19
+
20
+ type ManifestRule = {
21
+ type: string
22
+ key: string
23
+ persist: PersistType
24
+ location?: string
25
+ }
26
+
27
+ type Manifest = {
28
+ package: string
29
+ variables?: Record<string, string>
30
+ rules: Record<string, ManifestRule[]>
31
+ }
32
+
33
+ type InitParams = {
34
+ userId: string
35
+ manifest: Manifest
36
+ sendTag: (params: SendTag) => void
37
+ }
38
+
39
+ type TagParams = {
40
+ userId: string
41
+ eventId: string
42
+ eventName: string
43
+ data: Data
44
+ manifestVariables: Record<string, string>
45
+ sendTag: (params: SendTag) => void
46
+ }
47
+
48
+ type ProviderInit = {
49
+ name: string
50
+ init?: (params: InitParams) => void
51
+ tag?: (params: TagParams) => unknown
52
+ }
53
+
54
+ // end TODO
55
+
1
56
  type UserKey =
2
57
  | 'email'
3
58
  | 'phone'
@@ -11,16 +66,11 @@ type UserKey =
11
66
  | 'zip'
12
67
  | 'address'
13
68
 
14
- type Data = Record<string, unknown>
15
-
16
- interface InitPreferences {
69
+ type InitPreferences = {
17
70
  edgeURL: string
18
71
  disableConsentCheck?: boolean
19
72
  userId?: string
20
- }
21
-
22
- interface EventOptions {
23
- method?: 'beacon'
73
+ providers?: ProviderInit[]
24
74
  }
25
75
 
26
76
  export declare const init: (preferences: InitPreferences) => void
@@ -39,3 +89,5 @@ export declare const getData: (
39
89
  ) => void
40
90
 
41
91
  export declare const keys: (callback: (keys: string[]) => void) => void
92
+
93
+ export declare const getUserId: () => string
@@ -1,6 +1,4 @@
1
- import { Data, UserKey } from './index'
2
-
3
- type PersistType = 'local' | 'session'
1
+ import { UserKey, Data } from './index'
4
2
 
5
3
  type Navigator = {
6
4
  userAgent: string
@@ -9,6 +7,9 @@ type Navigator = {
9
7
 
10
8
  type TagPayload = {
11
9
  eventName: string
10
+ eventId: string
11
+ timestamp: number
12
+ providerData?: Data
12
13
  data?: Data
13
14
  providers?: Data
14
15
  }
@@ -32,15 +33,3 @@ type PostPayload = {
32
33
  data?: Data
33
34
  storage?: Data
34
35
  }
35
-
36
- export type ManifestRule = {
37
- type: string
38
- key: string
39
- persist: PersistType
40
- location?: string
41
- }
42
-
43
- export type Manifest = {
44
- package: string
45
- rules: Record<string, ManifestRule[]>
46
- }
package/api/consent.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import { Data } from '../typings';
2
- export declare const saveConsent: (consent: Data) => void;
3
- export declare const handleConsent: (consent: Data) => void;