@chordcommerce/analytics 1.23.1 → 1.24.1

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
@@ -121,8 +121,16 @@ export const cartFormatter: CartFormatter<StorefrontCart> = (props) => {
121
121
  addSourceMiddleware(func: AnalyticsMiddleware)
122
122
 
123
123
  Registers a middleware function to modify event properties before they are
124
- sent to analytics providers. Middleware functions are executed
125
- synchronously in the order they were added during event tracking.
124
+ sent to analytics providers. Middleware functions are invoked in the order they
125
+ were added during event tracking, and each one's properties are merged into the
126
+ event in that order.
127
+
128
+ A middleware may be synchronous or asynchronous. Synchronous middleware is never
129
+ awaited, so it adds no delay to event delivery. Asynchronous middleware is
130
+ awaited before the event is sent, capped at one second — a middleware that has
131
+ not resolved by then is abandoned, a warning is logged, and the event is sent
132
+ without its properties. A middleware that throws or rejects is logged and
133
+ skipped. A middleware never prevents an event from being sent.
126
134
 
127
135
  Parameters
128
136
 
@@ -135,6 +143,15 @@ The middleware function can return:
135
143
 
136
144
  - Partial<EventProperties> - Properties to merge into the event payload
137
145
  - void - No additional properties to add
146
+ - A Promise of either of the above - Awaited before the event is sent
147
+
148
+ The `payload` argument carries `payload.obj` (the event's properties, name and
149
+ page context) plus `payload.userId` and `payload.anonymousId` as the CDP
150
+ currently knows them — either may be undefined.
151
+
152
+ Note that middleware contributes properties by **returning** them. Mutating the
153
+ `payload` argument has no effect; `next` is accepted for signature
154
+ compatibility and does nothing.
138
155
 
139
156
  Useage Example
140
157
 
@@ -156,6 +173,15 @@ Useage Example
156
173
  timestamp: Date.now()
157
174
  };
158
175
  });
176
+
177
+ // Asynchronous middleware: the event waits for the lookup, up to one second.
178
+ analytics.addSourceMiddleware(async ({ payload }) => {
179
+ if (payload.obj.event !== 'Order Completed') return;
180
+
181
+ const isNewCustomer = await fetchIsNewCustomer();
182
+
183
+ return { is_new_customer: isNewCustomer ? 1 : 0 };
184
+ });
159
185
  ```
160
186
 
161
187
  ## API Reference
@@ -71,6 +71,7 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
71
71
  setDebugMode: (enabled: boolean) => void;
72
72
  cdp: () => AnyAnalytics;
73
73
  ccdp: () => AnyAnalytics;
74
+ private currentUser;
74
75
  logger: (message?: any, ...optionalParams: any[]) => void;
75
76
  isResetting: () => boolean;
76
77
  private waitForReadyState;
@@ -249,6 +249,32 @@ var ChordAnalytics = /** @class */ (function () {
249
249
  return null;
250
250
  return (window === null || window === void 0 ? void 0 : window["_".concat(namespace)]) || (0, cdp_js_1.cdpQueue)(namespace);
251
251
  };
252
+ /*
253
+ * The current user and anonymous ids as the CDP knows them.
254
+ *
255
+ * Exposed to source middleware, which on Segment could read these from the
256
+ * event facade. Either CDP may be absent or may not implement `user()`, so
257
+ * every step is guarded and an unknown id is simply undefined.
258
+ */
259
+ this.currentUser = function () {
260
+ var read = function (analytics) {
261
+ var _a, _b, _c;
262
+ try {
263
+ var user = (_a = analytics === null || analytics === void 0 ? void 0 : analytics.user) === null || _a === void 0 ? void 0 : _a.call(analytics);
264
+ if (!user)
265
+ return undefined;
266
+ var userId = (_b = user.userId) !== null && _b !== void 0 ? _b : undefined;
267
+ var anonymousId = (_c = user.anonymousId) !== null && _c !== void 0 ? _c : undefined;
268
+ if (userId === undefined && anonymousId === undefined)
269
+ return undefined;
270
+ return { userId: userId, anonymousId: anonymousId };
271
+ }
272
+ catch (_d) {
273
+ return undefined;
274
+ }
275
+ };
276
+ return read(_this.ccdp()) || read(_this.cdp()) || {};
277
+ };
252
278
  /*
253
279
  * Log a message to the console if `options.enableLogging` is true.
254
280
  */
@@ -510,44 +536,77 @@ var ChordAnalytics = /** @class */ (function () {
510
536
  * Internal method to actually send a track event to CDP.
511
537
  */
512
538
  this.sendTrackEvent = function (event, props, options) { return __awaiter(_this, void 0, void 0, function () {
513
- var formattedProps, middlewareProps, searchParams_1, urlSearchParams, searchData_1, finalEventProps;
539
+ var formattedProps, middlewareProps, searchParams_1, urlSearchParams, searchData_1, _a, userId_1, anonymousId_1, invoke, settle, returned, results, _b, finalEventProps;
514
540
  var _this = this;
515
- return __generator(this, function (_a) {
516
- switch (_a.label) {
541
+ return __generator(this, function (_c) {
542
+ switch (_c.label) {
517
543
  case 0:
518
544
  formattedProps = this.options.stripNull && props ? (0, utils_js_1.pruneNullValues)(props) : props;
519
545
  middlewareProps = {};
520
- if (this.middleware.length > 0) {
521
- searchParams_1 = typeof window !== 'undefined' ? window.location.search : '';
522
- urlSearchParams = new URLSearchParams(searchParams_1);
523
- searchData_1 = Object.fromEntries(urlSearchParams.entries());
524
- // process middleware synchronously
525
- this.middleware.forEach(function (func) {
526
- try {
527
- var result = func({
528
- payload: {
529
- obj: {
530
- properties: props,
531
- event: event,
532
- context: {
533
- page: {
534
- search: searchParams_1,
535
- searchParams: searchData_1,
536
- },
546
+ if (!(this.middleware.length > 0)) return [3 /*break*/, 4];
547
+ searchParams_1 = typeof window !== 'undefined' ? window.location.search : '';
548
+ urlSearchParams = new URLSearchParams(searchParams_1);
549
+ searchData_1 = Object.fromEntries(urlSearchParams.entries());
550
+ _a = this.currentUser(), userId_1 = _a.userId, anonymousId_1 = _a.anonymousId;
551
+ invoke = function (func) {
552
+ try {
553
+ return func({
554
+ payload: {
555
+ userId: userId_1,
556
+ anonymousId: anonymousId_1,
557
+ obj: {
558
+ properties: props,
559
+ event: event,
560
+ context: {
561
+ page: {
562
+ search: searchParams_1,
563
+ searchParams: searchData_1,
537
564
  },
538
565
  },
539
566
  },
540
- next: function (_) { return _; },
541
- });
542
- if (result && typeof result === 'object') {
543
- middlewareProps = __assign(__assign({}, middlewareProps), result);
544
- }
545
- }
546
- catch (error) {
547
- console.warn('Middleware error:', error);
567
+ },
568
+ next: function (_) { return _; },
569
+ });
570
+ }
571
+ catch (error) {
572
+ console.warn('Middleware error:', error);
573
+ return undefined;
574
+ }
575
+ };
576
+ settle = function (returned) {
577
+ if (!(0, utils_js_1.isPromise)(returned))
578
+ return returned;
579
+ return (0, utils_js_1.withTimeout)(returned, utils_js_1.MIDDLEWARE_TIMEOUT_MS)
580
+ .then(function (settled) {
581
+ if (settled === utils_js_1.TIMED_OUT) {
582
+ console.warn("Middleware timed out after ".concat(utils_js_1.MIDDLEWARE_TIMEOUT_MS, "ms; its properties were dropped"));
583
+ return undefined;
548
584
  }
585
+ return settled;
586
+ })
587
+ .catch(function (error) {
588
+ console.warn('Middleware error:', error);
589
+ return undefined;
549
590
  });
550
- }
591
+ };
592
+ returned = this.middleware.map(invoke);
593
+ if (!returned.some(utils_js_1.isPromise)) return [3 /*break*/, 2];
594
+ return [4 /*yield*/, Promise.all(returned.map(settle))];
595
+ case 1:
596
+ _b = _c.sent();
597
+ return [3 /*break*/, 3];
598
+ case 2:
599
+ _b = returned;
600
+ _c.label = 3;
601
+ case 3:
602
+ results = _b;
603
+ results.forEach(function (result) {
604
+ if (result && typeof result === 'object') {
605
+ middlewareProps = __assign(__assign({}, middlewareProps), result);
606
+ }
607
+ });
608
+ _c.label = 4;
609
+ case 4:
551
610
  finalEventProps = __assign(__assign(__assign({}, formattedProps), middlewareProps), { meta: this.meta() });
552
611
  if (this.options.debug) {
553
612
  this.validate(event, finalEventProps).forEach(function (valid) {
@@ -585,8 +644,8 @@ var ChordAnalytics = /** @class */ (function () {
585
644
  resolve();
586
645
  }
587
646
  })];
588
- case 1:
589
- _a.sent();
647
+ case 5:
648
+ _c.sent();
590
649
  return [2 /*return*/];
591
650
  }
592
651
  });
@@ -88,10 +88,23 @@ type MiddlewarePayload = {
88
88
  page: PageContext;
89
89
  };
90
90
  };
91
+ /**
92
+ * The current user and anonymous ids, read from the CDP at send time.
93
+ * Segment's middleware payload exposes these as `userId()`/`anonymousId()`
94
+ * methods on a facade; they are plain values here.
95
+ */
96
+ userId?: string;
97
+ anonymousId?: string;
91
98
  };
92
99
  type MiddlewareArgs = {
93
100
  payload: MiddlewarePayload;
94
101
  next: <T>(arg: T) => T;
95
102
  };
96
- export type AnalyticsMiddleware = (args: MiddlewareArgs) => Partial<EventProperties> | void;
103
+ export type MiddlewareResult = Partial<EventProperties> | void;
104
+ /**
105
+ * A source middleware may be synchronous or asynchronous. An asynchronous one
106
+ * is awaited before the event is dispatched, bounded by a timeout — see
107
+ * `addSourceMiddleware` in the README.
108
+ */
109
+ export type AnalyticsMiddleware = (args: MiddlewareArgs) => MiddlewareResult | Promise<MiddlewareResult>;
97
110
  export {};
@@ -1,2 +1,21 @@
1
1
  import type { EventProperties } from './types/index.js';
2
2
  export declare const pruneNullValues: (props: EventProperties) => EventProperties;
3
+ /**
4
+ * How long a single asynchronous source middleware may take before its
5
+ * contribution is abandoned.
6
+ *
7
+ * Middleware runs on the path to the CDP, so an unbounded wait would let one
8
+ * slow lookup hold up delivery indefinitely. `Order Completed` in particular
9
+ * fires on a confirmation page shoppers close, so a late event is a lost one.
10
+ */
11
+ export declare const MIDDLEWARE_TIMEOUT_MS = 1000;
12
+ /** Returned by {@link withTimeout} when the promise did not settle in time. */
13
+ export declare const TIMED_OUT: unique symbol;
14
+ export declare const isPromise: (value: unknown) => value is Promise<unknown>;
15
+ /**
16
+ * Resolve `promise`, or {@link TIMED_OUT} if it has not settled within `ms`.
17
+ *
18
+ * The timer is cleared either way, so a promise that settles quickly does not
19
+ * leave a pending timeout behind.
20
+ */
21
+ export declare const withTimeout: <T>(promise: Promise<T>, ms: number) => Promise<typeof TIMED_OUT | T>;
package/dist/cjs/utils.js CHANGED
@@ -1,6 +1,42 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
2
38
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.pruneNullValues = void 0;
39
+ exports.withTimeout = exports.isPromise = exports.TIMED_OUT = exports.MIDDLEWARE_TIMEOUT_MS = exports.pruneNullValues = void 0;
4
40
  var pruneNullValues = function (props) {
5
41
  if (typeof props !== 'object' || props === null)
6
42
  return props;
@@ -23,3 +59,45 @@ var pruneNullValues = function (props) {
23
59
  return props;
24
60
  };
25
61
  exports.pruneNullValues = pruneNullValues;
62
+ /**
63
+ * How long a single asynchronous source middleware may take before its
64
+ * contribution is abandoned.
65
+ *
66
+ * Middleware runs on the path to the CDP, so an unbounded wait would let one
67
+ * slow lookup hold up delivery indefinitely. `Order Completed` in particular
68
+ * fires on a confirmation page shoppers close, so a late event is a lost one.
69
+ */
70
+ exports.MIDDLEWARE_TIMEOUT_MS = 1000;
71
+ /** Returned by {@link withTimeout} when the promise did not settle in time. */
72
+ exports.TIMED_OUT = Symbol('timed-out');
73
+ var isPromise = function (value) {
74
+ return typeof (value === null || value === void 0 ? void 0 : value.then) === 'function';
75
+ };
76
+ exports.isPromise = isPromise;
77
+ /**
78
+ * Resolve `promise`, or {@link TIMED_OUT} if it has not settled within `ms`.
79
+ *
80
+ * The timer is cleared either way, so a promise that settles quickly does not
81
+ * leave a pending timeout behind.
82
+ */
83
+ var withTimeout = function (promise, ms) { return __awaiter(void 0, void 0, void 0, function () {
84
+ var timer, timeout;
85
+ return __generator(this, function (_a) {
86
+ switch (_a.label) {
87
+ case 0:
88
+ timeout = new Promise(function (resolve) {
89
+ timer = setTimeout(function () { return resolve(exports.TIMED_OUT); }, ms);
90
+ });
91
+ _a.label = 1;
92
+ case 1:
93
+ _a.trys.push([1, , 3, 4]);
94
+ return [4 /*yield*/, Promise.race([promise, timeout])];
95
+ case 2: return [2 /*return*/, _a.sent()];
96
+ case 3:
97
+ clearTimeout(timer);
98
+ return [7 /*endfinally*/];
99
+ case 4: return [2 /*return*/];
100
+ }
101
+ });
102
+ }); };
103
+ exports.withTimeout = withTimeout;
@@ -71,6 +71,7 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
71
71
  setDebugMode: (enabled: boolean) => void;
72
72
  cdp: () => AnyAnalytics;
73
73
  ccdp: () => AnyAnalytics;
74
+ private currentUser;
74
75
  logger: (message?: any, ...optionalParams: any[]) => void;
75
76
  isResetting: () => boolean;
76
77
  private waitForReadyState;
@@ -11,7 +11,7 @@ import { addCdpSnippet, cdpQueue } from './cdp.js';
11
11
  import { addCdpConsent } from './consent/index.js';
12
12
  import { claimConsentEmit, commitConsentEmit, forgetConsent, releaseConsentEmit, } from './consent/persistence.js';
13
13
  import { eventSchemas } from '../validators/index.js';
14
- import { pruneNullValues } from '../utils.js';
14
+ import { isPromise, MIDDLEWARE_TIMEOUT_MS, pruneNullValues, TIMED_OUT, withTimeout, } from '../utils.js';
15
15
  const DEBUG_MODE_STORAGE_KEY = 'chordcommerce/analytics/debug';
16
16
  const CONSENT_QUEUE_STORAGE_KEY = 'chordcommerce/analytics/consent-queue';
17
17
  const CONSENT_QUEUE_TTL_MS = 5 * 60 * 1000; // 5 minutes
@@ -198,6 +198,32 @@ export class ChordAnalytics {
198
198
  return null;
199
199
  return (window === null || window === void 0 ? void 0 : window[`_${namespace}`]) || cdpQueue(namespace);
200
200
  };
201
+ /*
202
+ * The current user and anonymous ids as the CDP knows them.
203
+ *
204
+ * Exposed to source middleware, which on Segment could read these from the
205
+ * event facade. Either CDP may be absent or may not implement `user()`, so
206
+ * every step is guarded and an unknown id is simply undefined.
207
+ */
208
+ this.currentUser = () => {
209
+ const read = (analytics) => {
210
+ var _a, _b, _c;
211
+ try {
212
+ const user = (_a = analytics === null || analytics === void 0 ? void 0 : analytics.user) === null || _a === void 0 ? void 0 : _a.call(analytics);
213
+ if (!user)
214
+ return undefined;
215
+ const userId = (_b = user.userId) !== null && _b !== void 0 ? _b : undefined;
216
+ const anonymousId = (_c = user.anonymousId) !== null && _c !== void 0 ? _c : undefined;
217
+ if (userId === undefined && anonymousId === undefined)
218
+ return undefined;
219
+ return { userId, anonymousId };
220
+ }
221
+ catch (_d) {
222
+ return undefined;
223
+ }
224
+ };
225
+ return read(this.ccdp()) || read(this.cdp()) || {};
226
+ };
201
227
  /*
202
228
  * Log a message to the console if `options.enableLogging` is true.
203
229
  */
@@ -404,11 +430,13 @@ export class ChordAnalytics {
404
430
  const searchParams = typeof window !== 'undefined' ? window.location.search : '';
405
431
  const urlSearchParams = new URLSearchParams(searchParams);
406
432
  const searchData = Object.fromEntries(urlSearchParams.entries());
407
- // process middleware synchronously
408
- this.middleware.forEach((func) => {
433
+ const { userId, anonymousId } = this.currentUser();
434
+ const invoke = (func) => {
409
435
  try {
410
- const result = func({
436
+ return func({
411
437
  payload: {
438
+ userId,
439
+ anonymousId,
412
440
  obj: {
413
441
  properties: props,
414
442
  event,
@@ -422,12 +450,44 @@ export class ChordAnalytics {
422
450
  },
423
451
  next: (_) => _,
424
452
  });
425
- if (result && typeof result === 'object') {
426
- middlewareProps = Object.assign(Object.assign({}, middlewareProps), result);
427
- }
428
453
  }
429
454
  catch (error) {
430
455
  console.warn('Middleware error:', error);
456
+ return undefined;
457
+ }
458
+ };
459
+ /**
460
+ * Give an asynchronous middleware until MIDDLEWARE_TIMEOUT_MS to produce
461
+ * its properties. Neither a timeout nor a rejection is allowed to reach
462
+ * the caller: a middleware must never cost the event.
463
+ */
464
+ const settle = (returned) => {
465
+ if (!isPromise(returned))
466
+ return returned;
467
+ return withTimeout(returned, MIDDLEWARE_TIMEOUT_MS)
468
+ .then((settled) => {
469
+ if (settled === TIMED_OUT) {
470
+ console.warn(`Middleware timed out after ${MIDDLEWARE_TIMEOUT_MS}ms; its properties were dropped`);
471
+ return undefined;
472
+ }
473
+ return settled;
474
+ })
475
+ .catch((error) => {
476
+ console.warn('Middleware error:', error);
477
+ return undefined;
478
+ });
479
+ };
480
+ // Every middleware is invoked in registration order, as before. Only if
481
+ // one actually returned a promise is anything awaited -- so a set of
482
+ // purely synchronous middleware reaches the CDP on the same tick it
483
+ // always has, and callers that never await `track()` are unaffected.
484
+ const returned = this.middleware.map(invoke);
485
+ const results = returned.some(isPromise)
486
+ ? yield Promise.all(returned.map(settle))
487
+ : returned;
488
+ results.forEach((result) => {
489
+ if (result && typeof result === 'object') {
490
+ middlewareProps = Object.assign(Object.assign({}, middlewareProps), result);
431
491
  }
432
492
  });
433
493
  }
@@ -88,10 +88,23 @@ type MiddlewarePayload = {
88
88
  page: PageContext;
89
89
  };
90
90
  };
91
+ /**
92
+ * The current user and anonymous ids, read from the CDP at send time.
93
+ * Segment's middleware payload exposes these as `userId()`/`anonymousId()`
94
+ * methods on a facade; they are plain values here.
95
+ */
96
+ userId?: string;
97
+ anonymousId?: string;
91
98
  };
92
99
  type MiddlewareArgs = {
93
100
  payload: MiddlewarePayload;
94
101
  next: <T>(arg: T) => T;
95
102
  };
96
- export type AnalyticsMiddleware = (args: MiddlewareArgs) => Partial<EventProperties> | void;
103
+ export type MiddlewareResult = Partial<EventProperties> | void;
104
+ /**
105
+ * A source middleware may be synchronous or asynchronous. An asynchronous one
106
+ * is awaited before the event is dispatched, bounded by a timeout — see
107
+ * `addSourceMiddleware` in the README.
108
+ */
109
+ export type AnalyticsMiddleware = (args: MiddlewareArgs) => MiddlewareResult | Promise<MiddlewareResult>;
97
110
  export {};
package/dist/utils.d.ts CHANGED
@@ -1,2 +1,21 @@
1
1
  import type { EventProperties } from './types/index.js';
2
2
  export declare const pruneNullValues: (props: EventProperties) => EventProperties;
3
+ /**
4
+ * How long a single asynchronous source middleware may take before its
5
+ * contribution is abandoned.
6
+ *
7
+ * Middleware runs on the path to the CDP, so an unbounded wait would let one
8
+ * slow lookup hold up delivery indefinitely. `Order Completed` in particular
9
+ * fires on a confirmation page shoppers close, so a late event is a lost one.
10
+ */
11
+ export declare const MIDDLEWARE_TIMEOUT_MS = 1000;
12
+ /** Returned by {@link withTimeout} when the promise did not settle in time. */
13
+ export declare const TIMED_OUT: unique symbol;
14
+ export declare const isPromise: (value: unknown) => value is Promise<unknown>;
15
+ /**
16
+ * Resolve `promise`, or {@link TIMED_OUT} if it has not settled within `ms`.
17
+ *
18
+ * The timer is cleared either way, so a promise that settles quickly does not
19
+ * leave a pending timeout behind.
20
+ */
21
+ export declare const withTimeout: <T>(promise: Promise<T>, ms: number) => Promise<typeof TIMED_OUT | T>;
package/dist/utils.js CHANGED
@@ -1,3 +1,12 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  export const pruneNullValues = (props) => {
2
11
  if (typeof props !== 'object' || props === null)
3
12
  return props;
@@ -19,3 +28,33 @@ export const pruneNullValues = (props) => {
19
28
  }
20
29
  return props;
21
30
  };
31
+ /**
32
+ * How long a single asynchronous source middleware may take before its
33
+ * contribution is abandoned.
34
+ *
35
+ * Middleware runs on the path to the CDP, so an unbounded wait would let one
36
+ * slow lookup hold up delivery indefinitely. `Order Completed` in particular
37
+ * fires on a confirmation page shoppers close, so a late event is a lost one.
38
+ */
39
+ export const MIDDLEWARE_TIMEOUT_MS = 1000;
40
+ /** Returned by {@link withTimeout} when the promise did not settle in time. */
41
+ export const TIMED_OUT = Symbol('timed-out');
42
+ export const isPromise = (value) => typeof (value === null || value === void 0 ? void 0 : value.then) === 'function';
43
+ /**
44
+ * Resolve `promise`, or {@link TIMED_OUT} if it has not settled within `ms`.
45
+ *
46
+ * The timer is cleared either way, so a promise that settles quickly does not
47
+ * leave a pending timeout behind.
48
+ */
49
+ export const withTimeout = (promise, ms) => __awaiter(void 0, void 0, void 0, function* () {
50
+ let timer;
51
+ const timeout = new Promise((resolve) => {
52
+ timer = setTimeout(() => resolve(TIMED_OUT), ms);
53
+ });
54
+ try {
55
+ return yield Promise.race([promise, timeout]);
56
+ }
57
+ finally {
58
+ clearTimeout(timer);
59
+ }
60
+ });
package/package.json CHANGED
@@ -1,8 +1,18 @@
1
1
  {
2
2
  "name": "@chordcommerce/analytics",
3
3
  "description": "Chord Commerce event tracking",
4
- "version": "1.23.1",
4
+ "version": "1.24.1",
5
5
  "author": "Chord Commerce <help@chord.co> (https://www.chord.co)",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/chordcommerce/chord-pixel.git",
9
+ "directory": "packages/core"
10
+ },
11
+ "publishConfig": {
12
+ "registry": "https://registry.npmjs.org/",
13
+ "@chordcommerce:registry": "https://registry.npmjs.org/",
14
+ "provenance": false
15
+ },
6
16
  "type": "module",
7
17
  "main": "dist/index.js",
8
18
  "types": "dist/index.d.ts",