@pageviews/tracker 0.4.0 → 0.4.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
@@ -37,6 +37,8 @@ track('signup', { props: { plan: 'pro' } })
37
37
  identify({ userId: 'user-123' })
38
38
  ```
39
39
 
40
+ `track()` and `identify()` are safe to call before `init()` has finished, or even before it has been called: up to 100 calls are queued and sent, in order, as soon as tracking starts. Awaiting `init()` is optional.
41
+
40
42
  Your site ID is available in the Pageviews dashboard under **Settings → Tracking**.
41
43
 
42
44
  ## Framework Examples
@@ -476,6 +476,23 @@ const CACHE_KEY = 'pageviews_config';
476
476
  const CACHE_TTL = 1000 * 60 * 60; // 1 hour
477
477
  let instance = null;
478
478
  let initialized = false;
479
+ /** Bumped by destroy(), so a config fetch that resolves afterwards does not start tracking. */
480
+ let generation = 0;
481
+ const MAX_QUEUED_CALLS = 100;
482
+ /** track()/identify() calls made before init() has finished, replayed once tracking starts. */
483
+ let queued = [];
484
+ function enqueue(call) {
485
+ if (queued.length < MAX_QUEUED_CALLS)
486
+ queued.push(call);
487
+ }
488
+ function replayQueued() {
489
+ if (!instance)
490
+ return;
491
+ const calls = queued;
492
+ queued = [];
493
+ for (const call of calls)
494
+ call(instance.tracker);
495
+ }
479
496
  function getCachedConfig(siteId) {
480
497
  try {
481
498
  const raw = localStorage.getItem(`${CACHE_KEY}_${siteId}`);
@@ -506,6 +523,7 @@ async function fetchConfig(endpoint, siteId) {
506
523
  }
507
524
  function startTracking(config) {
508
525
  instance = initTracking(config);
526
+ replayQueued();
509
527
  }
510
528
  async function init(options) {
511
529
  if (initialized)
@@ -513,6 +531,7 @@ async function init(options) {
513
531
  initialized = true;
514
532
  const ingestEndpoint = (options.endpoint || 'https://data.pageviews.ai') + '/api/event';
515
533
  const cfgEndpoint = options.configEndpoint || 'https://pageviews.ai';
534
+ const current = generation;
516
535
  const cached = getCachedConfig(options.siteId);
517
536
  if (cached) {
518
537
  startTracking(Object.assign(Object.assign({}, cached), { siteId: options.siteId, endpoint: ingestEndpoint }));
@@ -522,26 +541,46 @@ async function init(options) {
522
541
  }).catch(() => { });
523
542
  return;
524
543
  }
525
- const configData = await fetchConfig(cfgEndpoint, options.siteId);
544
+ let configData;
545
+ try {
546
+ configData = await fetchConfig(cfgEndpoint, options.siteId);
547
+ }
548
+ catch (error) {
549
+ if (current === generation) {
550
+ initialized = false;
551
+ queued = [];
552
+ }
553
+ throw error;
554
+ }
555
+ if (current !== generation)
556
+ return;
526
557
  setCachedConfig(options.siteId, configData);
527
558
  startTracking(Object.assign(Object.assign({}, configData), { siteId: options.siteId, endpoint: ingestEndpoint }));
528
559
  }
560
+ /** Sends a custom event. Calls made before init() has finished are queued and sent once it has. */
529
561
  function track(eventName, options) {
530
- if (!instance)
531
- throw new Error('pageviews.track() requires init() to be called first');
562
+ if (!instance) {
563
+ enqueue((tracker) => tracker.track(eventName, options));
564
+ return;
565
+ }
532
566
  instance.tracker.track(eventName, options);
533
567
  }
568
+ /** Identifies the visitor. Calls made before init() has finished are queued and sent once it has. */
534
569
  function identify(data) {
535
- if (!instance)
536
- throw new Error('pageviews.identify() requires init() to be called first');
570
+ if (!instance) {
571
+ enqueue((tracker) => tracker.identify(data));
572
+ return;
573
+ }
537
574
  instance.tracker.identify(data);
538
575
  }
539
576
  function destroy() {
577
+ generation++;
578
+ queued = [];
579
+ initialized = false;
540
580
  if (!instance)
541
581
  return;
542
582
  instance.destroy();
543
583
  instance = null;
544
- initialized = false;
545
584
  }
546
585
 
547
586
  exports.destroy = destroy;
@@ -38,7 +38,9 @@ interface InitOptions {
38
38
  configEndpoint?: string;
39
39
  }
40
40
  declare function init(options: InitOptions): Promise<void>;
41
+ /** Sends a custom event. Calls made before init() has finished are queued and sent once it has. */
41
42
  declare function track(eventName: string, options?: TrackOptions): void;
43
+ /** Identifies the visitor. Calls made before init() has finished are queued and sent once it has. */
42
44
  declare function identify(data: IdentifyData): void;
43
45
  declare function destroy(): void;
44
46
 
@@ -474,6 +474,23 @@ const CACHE_KEY = 'pageviews_config';
474
474
  const CACHE_TTL = 1000 * 60 * 60; // 1 hour
475
475
  let instance = null;
476
476
  let initialized = false;
477
+ /** Bumped by destroy(), so a config fetch that resolves afterwards does not start tracking. */
478
+ let generation = 0;
479
+ const MAX_QUEUED_CALLS = 100;
480
+ /** track()/identify() calls made before init() has finished, replayed once tracking starts. */
481
+ let queued = [];
482
+ function enqueue(call) {
483
+ if (queued.length < MAX_QUEUED_CALLS)
484
+ queued.push(call);
485
+ }
486
+ function replayQueued() {
487
+ if (!instance)
488
+ return;
489
+ const calls = queued;
490
+ queued = [];
491
+ for (const call of calls)
492
+ call(instance.tracker);
493
+ }
477
494
  function getCachedConfig(siteId) {
478
495
  try {
479
496
  const raw = localStorage.getItem(`${CACHE_KEY}_${siteId}`);
@@ -504,6 +521,7 @@ async function fetchConfig(endpoint, siteId) {
504
521
  }
505
522
  function startTracking(config) {
506
523
  instance = initTracking(config);
524
+ replayQueued();
507
525
  }
508
526
  async function init(options) {
509
527
  if (initialized)
@@ -511,6 +529,7 @@ async function init(options) {
511
529
  initialized = true;
512
530
  const ingestEndpoint = (options.endpoint || 'https://data.pageviews.ai') + '/api/event';
513
531
  const cfgEndpoint = options.configEndpoint || 'https://pageviews.ai';
532
+ const current = generation;
514
533
  const cached = getCachedConfig(options.siteId);
515
534
  if (cached) {
516
535
  startTracking(Object.assign(Object.assign({}, cached), { siteId: options.siteId, endpoint: ingestEndpoint }));
@@ -520,26 +539,46 @@ async function init(options) {
520
539
  }).catch(() => { });
521
540
  return;
522
541
  }
523
- const configData = await fetchConfig(cfgEndpoint, options.siteId);
542
+ let configData;
543
+ try {
544
+ configData = await fetchConfig(cfgEndpoint, options.siteId);
545
+ }
546
+ catch (error) {
547
+ if (current === generation) {
548
+ initialized = false;
549
+ queued = [];
550
+ }
551
+ throw error;
552
+ }
553
+ if (current !== generation)
554
+ return;
524
555
  setCachedConfig(options.siteId, configData);
525
556
  startTracking(Object.assign(Object.assign({}, configData), { siteId: options.siteId, endpoint: ingestEndpoint }));
526
557
  }
558
+ /** Sends a custom event. Calls made before init() has finished are queued and sent once it has. */
527
559
  function track(eventName, options) {
528
- if (!instance)
529
- throw new Error('pageviews.track() requires init() to be called first');
560
+ if (!instance) {
561
+ enqueue((tracker) => tracker.track(eventName, options));
562
+ return;
563
+ }
530
564
  instance.tracker.track(eventName, options);
531
565
  }
566
+ /** Identifies the visitor. Calls made before init() has finished are queued and sent once it has. */
532
567
  function identify(data) {
533
- if (!instance)
534
- throw new Error('pageviews.identify() requires init() to be called first');
568
+ if (!instance) {
569
+ enqueue((tracker) => tracker.identify(data));
570
+ return;
571
+ }
535
572
  instance.tracker.identify(data);
536
573
  }
537
574
  function destroy() {
575
+ generation++;
576
+ queued = [];
577
+ initialized = false;
538
578
  if (!instance)
539
579
  return;
540
580
  instance.destroy();
541
581
  instance = null;
542
- initialized = false;
543
582
  }
544
583
 
545
584
  export { destroy, identify, init, track };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pageviews/tracker",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Lightweight, cookie-free analytics tracker for PageViews.ai",
5
5
  "author": "Pageviews <hello@pageviews.ai>",
6
6
  "homepage": "https://pageviews.ai",