@chordcommerce/analytics 1.22.1-beta.1 → 1.22.2

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.
@@ -31,7 +31,7 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
31
31
  */
32
32
  private cdpReadyPromise;
33
33
  private consentConfig;
34
- private resetInProgress;
34
+ private resetCompletePromise;
35
35
  /**
36
36
  * Queue for events waiting for consent to be ready
37
37
  */
@@ -73,6 +73,7 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
73
73
  ccdp: () => AnyAnalytics;
74
74
  logger: (message?: any, ...optionalParams: any[]) => void;
75
75
  isResetting: () => boolean;
76
+ private waitForReadyState;
76
77
  /**
77
78
  * Wait for consent manager to be ready (no timeout - queues indefinitely).
78
79
  * Uses a shared promise so multiple events share the same watcher.
@@ -151,6 +152,7 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
151
152
  */
152
153
  private sendPageEvent;
153
154
  reset: () => Promise<void>;
155
+ private performReset;
154
156
  /**
155
157
  * Handler for postMessage events from the Shopify Web Pixel sandbox.
156
158
  * Bound to the instance for proper `this` context and cleanup.
@@ -87,7 +87,9 @@ var ChordAnalytics = /** @class */ (function () {
87
87
  */
88
88
  this.cdpReadyPromise = null;
89
89
  this.consentConfig = null;
90
- this.resetInProgress = false;
90
+ // Non-null while reset() is in flight. Acts as both the coalescing handle and
91
+ // the gate that track/identify/page await before executing.
92
+ this.resetCompletePromise = null;
91
93
  /**
92
94
  * Queue for events waiting for consent to be ready
93
95
  */
@@ -260,7 +262,21 @@ var ChordAnalytics = /** @class */ (function () {
260
262
  console.log.apply(console, __spreadArray([message], optionalParams, false));
261
263
  };
262
264
  this.isResetting = function () {
263
- return _this.resetInProgress;
265
+ return _this.resetCompletePromise !== null;
266
+ };
267
+ // Single gate for all outgoing operations. Returns a Promise only when there
268
+ // is actually something to wait for, preserving synchronous execution in the
269
+ // common case where CDP is ready and no reset is in flight.
270
+ this.waitForReadyState = function () {
271
+ var cdp = _this.cdpReadyPromise;
272
+ var reset = _this.resetCompletePromise;
273
+ if (!cdp && !reset)
274
+ return undefined;
275
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
276
+ if (cdp && reset)
277
+ return Promise.all([cdp, reset]).then(function () { });
278
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
279
+ return cdp !== null && cdp !== void 0 ? cdp : reset;
264
280
  };
265
281
  /**
266
282
  * Wait for consent manager to be ready (no timeout - queues indefinitely).
@@ -453,6 +469,7 @@ var ChordAnalytics = /** @class */ (function () {
453
469
  * If consent is not ready, the event is queued until consent is available.
454
470
  */
455
471
  this.track = function (event, props, options) { return __awaiter(_this, void 0, void 0, function () {
472
+ var gate;
456
473
  return __generator(this, function (_a) {
457
474
  switch (_a.label) {
458
475
  case 0:
@@ -462,18 +479,13 @@ var ChordAnalytics = /** @class */ (function () {
462
479
  }
463
480
  if (!event)
464
481
  this.logger('No event name provided');
465
- if (!this.cdpReadyPromise) return [3 /*break*/, 2];
466
- if (this.options.enableLogging && this.options.debug) {
467
- // eslint-disable-next-line no-console
468
- console.log("[Chord Analytics DEBUG]: track(\"".concat(event, "\") waiting for cdpReadyPromise"));
469
- }
470
- return [4 /*yield*/, this.cdpReadyPromise];
482
+ gate = this.waitForReadyState();
483
+ if (!gate) return [3 /*break*/, 2];
484
+ return [4 /*yield*/, gate
485
+ // If consent is configured but not ready, queue the event
486
+ ];
471
487
  case 1:
472
488
  _a.sent();
473
- if (this.options.enableLogging && this.options.debug) {
474
- // eslint-disable-next-line no-console
475
- console.log("[Chord Analytics DEBUG]: track(\"".concat(event, "\") cdpReadyPromise resolved"));
476
- }
477
489
  _a.label = 2;
478
490
  case 2:
479
491
  // If consent is configured but not ready, queue the event
@@ -583,11 +595,15 @@ var ChordAnalytics = /** @class */ (function () {
583
595
  * If consent is not ready, the event is queued until consent is available.
584
596
  */
585
597
  this.identify = function (userIdOrTraits, traitsOrOptions, options) { return __awaiter(_this, void 0, void 0, function () {
598
+ var gate;
586
599
  return __generator(this, function (_a) {
587
600
  switch (_a.label) {
588
601
  case 0:
589
- if (!this.cdpReadyPromise) return [3 /*break*/, 2];
590
- return [4 /*yield*/, this.cdpReadyPromise];
602
+ gate = this.waitForReadyState();
603
+ if (!gate) return [3 /*break*/, 2];
604
+ return [4 /*yield*/, gate
605
+ // If consent is configured but not ready, queue the event
606
+ ];
591
607
  case 1:
592
608
  _a.sent();
593
609
  _a.label = 2;
@@ -670,11 +686,15 @@ var ChordAnalytics = /** @class */ (function () {
670
686
  this.page = function (options, props) {
671
687
  if (props === void 0) { props = {}; }
672
688
  return __awaiter(_this, void 0, void 0, function () {
689
+ var gate;
673
690
  return __generator(this, function (_a) {
674
691
  switch (_a.label) {
675
692
  case 0:
676
- if (!this.cdpReadyPromise) return [3 /*break*/, 2];
677
- return [4 /*yield*/, this.cdpReadyPromise];
693
+ gate = this.waitForReadyState();
694
+ if (!gate) return [3 /*break*/, 2];
695
+ return [4 /*yield*/, gate
696
+ // If consent is configured but not ready, queue the event
697
+ ];
678
698
  case 1:
679
699
  _a.sent();
680
700
  _a.label = 2;
@@ -742,14 +762,21 @@ var ChordAnalytics = /** @class */ (function () {
742
762
  });
743
763
  });
744
764
  };
745
- this.reset = function () { return __awaiter(_this, void 0, void 0, function () {
765
+ // Coalesces concurrent reset() calls — a second caller gets the same in-flight
766
+ // Promise rather than triggering a duplicate reset.
767
+ this.reset = function () {
768
+ if (!_this.resetCompletePromise) {
769
+ _this.resetCompletePromise = _this.performReset();
770
+ }
771
+ return _this.resetCompletePromise;
772
+ };
773
+ this.performReset = function () { return __awaiter(_this, void 0, void 0, function () {
746
774
  var a, b, maxAttempts, attempts, userState, error_3;
747
775
  var _a;
748
776
  return __generator(this, function (_b) {
749
777
  switch (_b.label) {
750
778
  case 0:
751
779
  _b.trys.push([0, 4, 5, 6]);
752
- this.resetInProgress = true;
753
780
  a = this.ccdp();
754
781
  if (a && typeof a.reset === 'function') {
755
782
  a.reset();
@@ -785,7 +812,7 @@ var ChordAnalytics = /** @class */ (function () {
785
812
  this.logger(error_3);
786
813
  return [3 /*break*/, 6];
787
814
  case 5:
788
- this.resetInProgress = false;
815
+ this.resetCompletePromise = null;
789
816
  return [7 /*endfinally*/];
790
817
  case 6: return [2 /*return*/];
791
818
  }
@@ -31,7 +31,7 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
31
31
  */
32
32
  private cdpReadyPromise;
33
33
  private consentConfig;
34
- private resetInProgress;
34
+ private resetCompletePromise;
35
35
  /**
36
36
  * Queue for events waiting for consent to be ready
37
37
  */
@@ -73,6 +73,7 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
73
73
  ccdp: () => AnyAnalytics;
74
74
  logger: (message?: any, ...optionalParams: any[]) => void;
75
75
  isResetting: () => boolean;
76
+ private waitForReadyState;
76
77
  /**
77
78
  * Wait for consent manager to be ready (no timeout - queues indefinitely).
78
79
  * Uses a shared promise so multiple events share the same watcher.
@@ -151,6 +152,7 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
151
152
  */
152
153
  private sendPageEvent;
153
154
  reset: () => Promise<void>;
155
+ private performReset;
154
156
  /**
155
157
  * Handler for postMessage events from the Shopify Web Pixel sandbox.
156
158
  * Bound to the instance for proper `this` context and cleanup.
@@ -36,7 +36,9 @@ export class ChordAnalytics {
36
36
  */
37
37
  this.cdpReadyPromise = null;
38
38
  this.consentConfig = null;
39
- this.resetInProgress = false;
39
+ // Non-null while reset() is in flight. Acts as both the coalescing handle and
40
+ // the gate that track/identify/page await before executing.
41
+ this.resetCompletePromise = null;
40
42
  /**
41
43
  * Queue for events waiting for consent to be ready
42
44
  */
@@ -205,7 +207,21 @@ export class ChordAnalytics {
205
207
  console.log(message, ...optionalParams);
206
208
  };
207
209
  this.isResetting = () => {
208
- return this.resetInProgress;
210
+ return this.resetCompletePromise !== null;
211
+ };
212
+ // Single gate for all outgoing operations. Returns a Promise only when there
213
+ // is actually something to wait for, preserving synchronous execution in the
214
+ // common case where CDP is ready and no reset is in flight.
215
+ this.waitForReadyState = () => {
216
+ const cdp = this.cdpReadyPromise;
217
+ const reset = this.resetCompletePromise;
218
+ if (!cdp && !reset)
219
+ return undefined;
220
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
221
+ if (cdp && reset)
222
+ return Promise.all([cdp, reset]).then(() => { });
223
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
224
+ return cdp !== null && cdp !== void 0 ? cdp : reset;
209
225
  };
210
226
  /**
211
227
  * Wait for consent manager to be ready (no timeout - queues indefinitely).
@@ -359,18 +375,9 @@ export class ChordAnalytics {
359
375
  }
360
376
  if (!event)
361
377
  this.logger('No event name provided');
362
- // Wait for CDP to be ready before sending track events
363
- if (this.cdpReadyPromise) {
364
- if (this.options.enableLogging && this.options.debug) {
365
- // eslint-disable-next-line no-console
366
- console.log(`[Chord Analytics DEBUG]: track("${event}") waiting for cdpReadyPromise`);
367
- }
368
- yield this.cdpReadyPromise;
369
- if (this.options.enableLogging && this.options.debug) {
370
- // eslint-disable-next-line no-console
371
- console.log(`[Chord Analytics DEBUG]: track("${event}") cdpReadyPromise resolved`);
372
- }
373
- }
378
+ const gate = this.waitForReadyState();
379
+ if (gate)
380
+ yield gate;
374
381
  // If consent is configured but not ready, queue the event
375
382
  if (this.consentConfig && !this.consentConfig.isConsentReady()) {
376
383
  if (this.options.enableLogging && this.options.debug) {
@@ -466,10 +473,9 @@ export class ChordAnalytics {
466
473
  * If consent is not ready, the event is queued until consent is available.
467
474
  */
468
475
  this.identify = (userIdOrTraits, traitsOrOptions, options) => __awaiter(this, void 0, void 0, function* () {
469
- // Wait for CDP to be ready before sending identify events
470
- if (this.cdpReadyPromise) {
471
- yield this.cdpReadyPromise;
472
- }
476
+ const gate = this.waitForReadyState();
477
+ if (gate)
478
+ yield gate;
473
479
  // If consent is configured but not ready, queue the event
474
480
  if (this.consentConfig && !this.consentConfig.isConsentReady()) {
475
481
  return this.queueEventForConsent('identify', [
@@ -536,10 +542,9 @@ export class ChordAnalytics {
536
542
  * If consent is not ready, the event is queued until consent is available.
537
543
  */
538
544
  this.page = (options, props = {}) => __awaiter(this, void 0, void 0, function* () {
539
- // Wait for CDP to be ready before sending page events
540
- if (this.cdpReadyPromise) {
541
- yield this.cdpReadyPromise;
542
- }
545
+ const gate = this.waitForReadyState();
546
+ if (gate)
547
+ yield gate;
543
548
  // If consent is configured but not ready, queue the event
544
549
  if (this.consentConfig && !this.consentConfig.isConsentReady()) {
545
550
  return this.queueEventForConsent('page', [options, props]);
@@ -584,10 +589,17 @@ export class ChordAnalytics {
584
589
  }
585
590
  });
586
591
  });
587
- this.reset = () => __awaiter(this, void 0, void 0, function* () {
592
+ // Coalesces concurrent reset() calls — a second caller gets the same in-flight
593
+ // Promise rather than triggering a duplicate reset.
594
+ this.reset = () => {
595
+ if (!this.resetCompletePromise) {
596
+ this.resetCompletePromise = this.performReset();
597
+ }
598
+ return this.resetCompletePromise;
599
+ };
600
+ this.performReset = () => __awaiter(this, void 0, void 0, function* () {
588
601
  var _e;
589
602
  try {
590
- this.resetInProgress = true;
591
603
  const a = this.ccdp();
592
604
  if (a && typeof a.reset === 'function') {
593
605
  a.reset();
@@ -617,7 +629,7 @@ export class ChordAnalytics {
617
629
  this.logger(error);
618
630
  }
619
631
  finally {
620
- this.resetInProgress = false;
632
+ this.resetCompletePromise = null;
621
633
  }
622
634
  });
623
635
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chordcommerce/analytics",
3
3
  "description": "Chord Commerce event tracking",
4
- "version": "1.22.1-beta.1",
4
+ "version": "1.22.2",
5
5
  "author": "Chord Commerce <help@chord.co> (https://www.chord.co)",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",