react-fathom 0.1.10 → 0.2.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.
Files changed (80) hide show
  1. package/README.md +941 -25
  2. package/dist/cjs/index.cjs +55 -9
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs/native/index.cjs +1079 -0
  5. package/dist/cjs/native/index.cjs.map +1 -0
  6. package/dist/cjs/next/index.cjs +89 -5
  7. package/dist/cjs/next/index.cjs.map +1 -1
  8. package/dist/es/index.js +55 -9
  9. package/dist/es/index.js.map +1 -1
  10. package/dist/es/native/index.js +1071 -0
  11. package/dist/es/native/index.js.map +1 -0
  12. package/dist/es/next/index.js +90 -6
  13. package/dist/es/next/index.js.map +1 -1
  14. package/dist/react-fathom.js +55 -9
  15. package/dist/react-fathom.js.map +1 -1
  16. package/dist/react-fathom.min.js +2 -2
  17. package/dist/react-fathom.min.js.map +1 -1
  18. package/package.json +28 -5
  19. package/src/FathomContext.tsx +30 -1
  20. package/src/FathomProvider.test.tsx +115 -15
  21. package/src/FathomProvider.tsx +10 -2
  22. package/src/components/TrackClick.test.tsx +7 -7
  23. package/src/components/TrackClick.tsx +1 -1
  24. package/src/components/TrackVisible.test.tsx +7 -7
  25. package/src/components/TrackVisible.tsx +1 -1
  26. package/src/hooks/useFathom.test.tsx +14 -3
  27. package/src/hooks/useTrackOnClick.test.tsx +4 -4
  28. package/src/hooks/useTrackOnClick.ts +1 -1
  29. package/src/hooks/useTrackOnVisible.test.tsx +4 -4
  30. package/src/hooks/useTrackOnVisible.ts +1 -1
  31. package/src/index.ts +1 -0
  32. package/src/native/FathomWebView.test.tsx +410 -0
  33. package/src/native/FathomWebView.tsx +297 -0
  34. package/src/native/NativeFathomProvider.test.tsx +372 -0
  35. package/src/native/NativeFathomProvider.tsx +113 -0
  36. package/src/native/createWebViewClient.test.ts +380 -0
  37. package/src/native/createWebViewClient.ts +271 -0
  38. package/src/native/index.ts +29 -0
  39. package/src/native/react-native.d.ts +74 -0
  40. package/src/native/types.ts +145 -0
  41. package/src/native/useAppStateTracking.test.ts +249 -0
  42. package/src/native/useAppStateTracking.ts +66 -0
  43. package/src/native/useNavigationTracking.test.ts +446 -0
  44. package/src/native/useNavigationTracking.ts +177 -0
  45. package/src/next/NextFathomProviderApp.client.tsx +5 -0
  46. package/src/next/NextFathomProviderApp.test.tsx +154 -0
  47. package/src/next/NextFathomProviderApp.tsx +62 -0
  48. package/src/next/index.ts +3 -0
  49. package/src/types.ts +36 -9
  50. package/types/FathomContext.d.ts +1 -1
  51. package/types/FathomContext.d.ts.map +1 -1
  52. package/types/FathomProvider.d.ts.map +1 -1
  53. package/types/components/TrackClick.d.ts +1 -1
  54. package/types/components/TrackVisible.d.ts +1 -1
  55. package/types/hooks/useTrackOnClick.d.ts +1 -1
  56. package/types/hooks/useTrackOnVisible.d.ts +1 -1
  57. package/types/index.d.ts +1 -0
  58. package/types/index.d.ts.map +1 -1
  59. package/types/native/FathomWebView.d.ts +59 -0
  60. package/types/native/FathomWebView.d.ts.map +1 -0
  61. package/types/native/NativeFathomProvider.d.ts +36 -0
  62. package/types/native/NativeFathomProvider.d.ts.map +1 -0
  63. package/types/native/createWebViewClient.d.ts +51 -0
  64. package/types/native/createWebViewClient.d.ts.map +1 -0
  65. package/types/native/index.d.ts +10 -0
  66. package/types/native/index.d.ts.map +1 -0
  67. package/types/native/types.d.ts +125 -0
  68. package/types/native/types.d.ts.map +1 -0
  69. package/types/native/useAppStateTracking.d.ts +25 -0
  70. package/types/native/useAppStateTracking.d.ts.map +1 -0
  71. package/types/native/useNavigationTracking.d.ts +30 -0
  72. package/types/native/useNavigationTracking.d.ts.map +1 -0
  73. package/types/next/NextFathomProviderApp.client.d.ts +3 -0
  74. package/types/next/NextFathomProviderApp.client.d.ts.map +1 -0
  75. package/types/next/NextFathomProviderApp.d.ts +38 -4
  76. package/types/next/NextFathomProviderApp.d.ts.map +1 -1
  77. package/types/next/index.d.ts +1 -0
  78. package/types/next/index.d.ts.map +1 -1
  79. package/types/types.d.ts +34 -9
  80. package/types/types.d.ts.map +1 -1
@@ -0,0 +1,1079 @@
1
+ /*! react-fathom - 0.2.0 !*/
2
+ 'use strict';
3
+
4
+ var React = require('react');
5
+ var reactNative = require('react-native');
6
+ var reactNativeWebview = require('react-native-webview');
7
+
8
+ function _arrayWithHoles(r) {
9
+ if (Array.isArray(r)) return r;
10
+ }
11
+
12
+ function _iterableToArrayLimit(r, l) {
13
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
14
+ if (null != t) {
15
+ var e,
16
+ n,
17
+ i,
18
+ u,
19
+ a = [],
20
+ f = true,
21
+ o = false;
22
+ try {
23
+ if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
24
+ } catch (r) {
25
+ o = true, n = r;
26
+ } finally {
27
+ try {
28
+ if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return;
29
+ } finally {
30
+ if (o) throw n;
31
+ }
32
+ }
33
+ return a;
34
+ }
35
+ }
36
+
37
+ function _arrayLikeToArray(r, a) {
38
+ (null == a || a > r.length) && (a = r.length);
39
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
40
+ return n;
41
+ }
42
+
43
+ function _unsupportedIterableToArray(r, a) {
44
+ if (r) {
45
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
46
+ var t = {}.toString.call(r).slice(8, -1);
47
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
48
+ }
49
+ }
50
+
51
+ function _nonIterableRest() {
52
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
53
+ }
54
+
55
+ function _slicedToArray(r, e) {
56
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
57
+ }
58
+
59
+ /**
60
+ * Hidden WebView component that loads and manages the Fathom Analytics script.
61
+ *
62
+ * This component renders an invisible WebView that loads the official Fathom
63
+ * tracking script, allowing React Native apps to use Fathom's full functionality.
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * const fathomRef = useRef<FathomWebViewRef>(null)
68
+ *
69
+ * <FathomWebView
70
+ * ref={fathomRef}
71
+ * siteId="ABCDEFGH"
72
+ * onReady={() => console.log('Fathom ready!')}
73
+ * />
74
+ *
75
+ * // Later, track events:
76
+ * fathomRef.current?.trackPageview({ url: '/home' })
77
+ * ```
78
+ */
79
+ var FathomWebView = /*#__PURE__*/React.forwardRef(function FathomWebView(_ref, ref) {
80
+ var siteId = _ref.siteId,
81
+ _ref$loadOptions = _ref.loadOptions,
82
+ loadOptions = _ref$loadOptions === void 0 ? {} : _ref$loadOptions,
83
+ _ref$scriptDomain = _ref.scriptDomain,
84
+ scriptDomain = _ref$scriptDomain === void 0 ? 'cdn.usefathom.com' : _ref$scriptDomain,
85
+ onReady = _ref.onReady,
86
+ _onError = _ref.onError,
87
+ _ref$debug = _ref.debug,
88
+ debug = _ref$debug === void 0 ? false : _ref$debug;
89
+ var webViewRef = React.useRef(null);
90
+ var _useState = React.useState(false),
91
+ _useState2 = _slicedToArray(_useState, 2),
92
+ _isReady = _useState2[0],
93
+ setIsReady = _useState2[1];
94
+ var log = React.useCallback(function () {
95
+ if (debug) {
96
+ var _console;
97
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
98
+ args[_key] = arguments[_key];
99
+ }
100
+ (_console = console).log.apply(_console, ['[react-fathom/webview]'].concat(args));
101
+ }
102
+ }, [debug]);
103
+
104
+ // Build data attributes for load options
105
+ var buildDataAttributes = React.useCallback(function () {
106
+ var attrs = ["data-site=\"".concat(siteId, "\"")];
107
+ if (loadOptions.auto === false) {
108
+ attrs.push('data-auto="false"');
109
+ }
110
+ if (loadOptions.honorDNT) {
111
+ attrs.push('data-honor-dnt="true"');
112
+ }
113
+ if (loadOptions.canonical === false) {
114
+ attrs.push('data-canonical="false"');
115
+ }
116
+ if (loadOptions.spa) {
117
+ attrs.push("data-spa=\"".concat(loadOptions.spa, "\""));
118
+ }
119
+ return attrs.join(' ');
120
+ }, [siteId, loadOptions]);
121
+
122
+ // HTML content that loads the Fathom script
123
+ var htmlContent = "\n <!DOCTYPE html>\n <html>\n <head>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <script src=\"https://".concat(scriptDomain, "/script.js\" ").concat(buildDataAttributes(), " defer></script>\n <script>\n // Wait for Fathom to be available\n function waitForFathom(callback, maxAttempts = 50) {\n let attempts = 0;\n const check = () => {\n attempts++;\n if (typeof window.fathom !== 'undefined') {\n callback();\n } else if (attempts < maxAttempts) {\n setTimeout(check, 100);\n } else {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'error',\n message: 'Fathom script failed to load after ' + (maxAttempts * 100) + 'ms'\n }));\n }\n };\n check();\n }\n\n // Initialize when DOM is ready\n document.addEventListener('DOMContentLoaded', () => {\n waitForFathom(() => {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'ready'\n }));\n });\n });\n\n // Handle commands from React Native\n window.handleCommand = function(command) {\n if (typeof window.fathom === 'undefined') {\n console.warn('Fathom not loaded yet');\n return;\n }\n\n try {\n switch (command.action) {\n case 'trackPageview':\n window.fathom.trackPageview(command.options || {});\n break;\n case 'trackEvent':\n window.fathom.trackEvent(command.eventName, command.options || {});\n break;\n case 'trackGoal':\n window.fathom.trackGoal(command.code, command.cents);\n break;\n case 'blockTrackingForMe':\n window.fathom.blockTrackingForMe();\n break;\n case 'enableTrackingForMe':\n window.fathom.enableTrackingForMe();\n break;\n default:\n console.warn('Unknown command:', command.action);\n }\n } catch (error) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'error',\n message: error.message\n }));\n }\n };\n </script>\n </head>\n <body></body>\n </html>\n ");
124
+ var injectCommand = React.useCallback(function (command) {
125
+ if (!webViewRef.current) {
126
+ log('WebView not available');
127
+ return;
128
+ }
129
+ var script = "window.handleCommand(".concat(JSON.stringify(command), "); true;");
130
+ webViewRef.current.injectJavaScript(script);
131
+ log('Injected command:', command);
132
+ }, [log]);
133
+ var handleMessage = React.useCallback(function (event) {
134
+ try {
135
+ var data = JSON.parse(event.nativeEvent.data);
136
+ switch (data.type) {
137
+ case 'ready':
138
+ log('Fathom script loaded and ready');
139
+ setIsReady(true);
140
+ onReady === null || onReady === void 0 || onReady();
141
+ break;
142
+ case 'error':
143
+ log('Error from WebView:', data.message);
144
+ _onError === null || _onError === void 0 || _onError(data.message);
145
+ break;
146
+ default:
147
+ log('Unknown message type:', data.type);
148
+ }
149
+ } catch (_unused) {
150
+ log('Failed to parse WebView message:', event.nativeEvent.data);
151
+ }
152
+ }, [log, onReady, _onError]);
153
+
154
+ // Expose methods via ref
155
+ React.useImperativeHandle(ref, function () {
156
+ return {
157
+ trackPageview: function trackPageview(opts) {
158
+ injectCommand({
159
+ action: 'trackPageview',
160
+ options: opts
161
+ });
162
+ },
163
+ trackEvent: function trackEvent(eventName, opts) {
164
+ injectCommand({
165
+ action: 'trackEvent',
166
+ eventName: eventName,
167
+ options: opts
168
+ });
169
+ },
170
+ trackGoal: function trackGoal(code, cents) {
171
+ injectCommand({
172
+ action: 'trackGoal',
173
+ code: code,
174
+ cents: cents
175
+ });
176
+ },
177
+ blockTrackingForMe: function blockTrackingForMe() {
178
+ injectCommand({
179
+ action: 'blockTrackingForMe'
180
+ });
181
+ },
182
+ enableTrackingForMe: function enableTrackingForMe() {
183
+ injectCommand({
184
+ action: 'enableTrackingForMe'
185
+ });
186
+ },
187
+ isReady: function isReady() {
188
+ return _isReady;
189
+ }
190
+ };
191
+ }, [injectCommand, _isReady]);
192
+ return /*#__PURE__*/React.createElement(reactNative.View, {
193
+ style: styles.container
194
+ }, /*#__PURE__*/React.createElement(reactNativeWebview.WebView, {
195
+ ref: webViewRef,
196
+ source: {
197
+ html: htmlContent
198
+ },
199
+ onMessage: handleMessage,
200
+ onError: function onError(syntheticEvent) {
201
+ var nativeEvent = syntheticEvent.nativeEvent;
202
+ log('WebView error:', nativeEvent);
203
+ _onError === null || _onError === void 0 || _onError(nativeEvent.description || 'WebView error');
204
+ },
205
+ javaScriptEnabled: true,
206
+ domStorageEnabled: true
207
+ // Hide the WebView completely
208
+ ,
209
+ style: styles.webview
210
+ // Prevent any user interaction
211
+ ,
212
+ scrollEnabled: false,
213
+ bounces: false
214
+ // Optimize for background operation
215
+ ,
216
+ cacheEnabled: true,
217
+ incognito: false
218
+ }));
219
+ });
220
+ var styles = reactNative.StyleSheet.create({
221
+ container: {
222
+ position: 'absolute',
223
+ width: 0,
224
+ height: 0,
225
+ overflow: 'hidden'
226
+ },
227
+ webview: {
228
+ width: 1,
229
+ height: 1,
230
+ opacity: 0
231
+ }
232
+ });
233
+
234
+ /**
235
+ * Creates a Fathom client that communicates with a FathomWebView component.
236
+ *
237
+ * This client queues commands until the WebView is ready, then flushes them.
238
+ * It implements the standard FathomClient interface for compatibility with
239
+ * the FathomProvider component.
240
+ *
241
+ * @example
242
+ * ```tsx
243
+ * import { createWebViewClient, FathomWebView } from 'react-fathom/native'
244
+ *
245
+ * function App() {
246
+ * const webViewRef = useRef<FathomWebViewRef>(null)
247
+ * const client = useMemo(
248
+ * () => createWebViewClient(() => webViewRef.current, { debug: __DEV__ }),
249
+ * []
250
+ * )
251
+ *
252
+ * return (
253
+ * <FathomProvider client={client} siteId="YOUR_SITE_ID">
254
+ * <FathomWebView ref={webViewRef} siteId="YOUR_SITE_ID" />
255
+ * <YourApp />
256
+ * </FathomProvider>
257
+ * )
258
+ * }
259
+ * ```
260
+ */
261
+
262
+ function createWebViewClient(getWebViewRef) {
263
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
264
+ var _options$debug = options.debug,
265
+ debug = _options$debug === void 0 ? false : _options$debug,
266
+ _options$enableQueue = options.enableQueue,
267
+ enableQueue = _options$enableQueue === void 0 ? true : _options$enableQueue,
268
+ _options$maxQueueSize = options.maxQueueSize,
269
+ maxQueueSize = _options$maxQueueSize === void 0 ? 100 : _options$maxQueueSize;
270
+ var isTrackingBlocked = false;
271
+
272
+ // Queue for commands sent before WebView is ready
273
+ var commandQueue = [];
274
+ var log = function log() {
275
+ if (debug) {
276
+ var _console;
277
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
278
+ args[_key] = arguments[_key];
279
+ }
280
+ (_console = console).log.apply(_console, ['[react-fathom/webview-client]'].concat(args));
281
+ }
282
+ };
283
+ var warn = function warn() {
284
+ if (debug) {
285
+ var _console2;
286
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
287
+ args[_key2] = arguments[_key2];
288
+ }
289
+ (_console2 = console).warn.apply(_console2, ['[react-fathom/webview-client]'].concat(args));
290
+ }
291
+ };
292
+
293
+ /**
294
+ * Queue a command for later execution
295
+ */
296
+ var queueCommand = function queueCommand(type, args) {
297
+ if (!enableQueue) {
298
+ warn('Queue disabled, dropping command:', type);
299
+ return;
300
+ }
301
+ if (commandQueue.length >= maxQueueSize) {
302
+ commandQueue.shift();
303
+ log('Queue full, removed oldest command');
304
+ }
305
+ commandQueue.push({
306
+ type: type,
307
+ args: args,
308
+ timestamp: Date.now()
309
+ });
310
+ log("Command queued (".concat(commandQueue.length, "/").concat(maxQueueSize, "):"), type);
311
+ };
312
+
313
+ /**
314
+ * Process all queued commands
315
+ */
316
+ var processQueue = function processQueue() {
317
+ var ref = getWebViewRef();
318
+ if (!(ref !== null && ref !== void 0 && ref.isReady())) {
319
+ return 0;
320
+ }
321
+ log("Processing ".concat(commandQueue.length, " queued commands"));
322
+ var processed = 0;
323
+ while (commandQueue.length > 0) {
324
+ var command = commandQueue.shift();
325
+ switch (command.type) {
326
+ case 'pageview':
327
+ ref.trackPageview(command.args[0]);
328
+ break;
329
+ case 'event':
330
+ ref.trackEvent(command.args[0], command.args[1]);
331
+ break;
332
+ case 'goal':
333
+ ref.trackGoal(command.args[0], command.args[1]);
334
+ break;
335
+ case 'block':
336
+ ref.blockTrackingForMe();
337
+ break;
338
+ case 'enable':
339
+ ref.enableTrackingForMe();
340
+ break;
341
+ }
342
+ processed++;
343
+ }
344
+ log("Processed ".concat(processed, " queued commands"));
345
+ return processed;
346
+ };
347
+
348
+ /**
349
+ * Execute a command immediately or queue it
350
+ */
351
+ var executeOrQueue = function executeOrQueue(type, args, executor) {
352
+ var ref = getWebViewRef();
353
+ if (ref !== null && ref !== void 0 && ref.isReady()) {
354
+ executor();
355
+ } else {
356
+ queueCommand(type, args);
357
+ }
358
+ };
359
+ var client = {
360
+ load: function load(siteId, opts) {
361
+ log('Client loaded with site ID:', siteId);
362
+
363
+ // Process any queued commands now that we're "loaded"
364
+ // (actual WebView readiness is separate)
365
+ processQueue();
366
+ },
367
+ trackPageview: function trackPageview(opts) {
368
+ if (isTrackingBlocked) {
369
+ log('Tracking blocked, skipping pageview');
370
+ return;
371
+ }
372
+ executeOrQueue('pageview', [opts], function () {
373
+ var ref = getWebViewRef();
374
+ ref === null || ref === void 0 || ref.trackPageview(opts);
375
+ log('Tracked pageview:', opts);
376
+ });
377
+ },
378
+ trackEvent: function trackEvent(eventName, opts) {
379
+ if (isTrackingBlocked) {
380
+ log('Tracking blocked, skipping event');
381
+ return;
382
+ }
383
+ executeOrQueue('event', [eventName, opts], function () {
384
+ var ref = getWebViewRef();
385
+ ref === null || ref === void 0 || ref.trackEvent(eventName, opts);
386
+ log('Tracked event:', eventName, opts);
387
+ });
388
+ },
389
+ trackGoal: function trackGoal(code, cents) {
390
+ if (isTrackingBlocked) {
391
+ log('Tracking blocked, skipping goal');
392
+ return;
393
+ }
394
+ executeOrQueue('goal', [code, cents], function () {
395
+ var ref = getWebViewRef();
396
+ ref === null || ref === void 0 || ref.trackGoal(code, cents);
397
+ log('Tracked goal:', code, cents);
398
+ });
399
+ },
400
+ setSite: function setSite(id) {
401
+ log('Site ID changed to:', id);
402
+ // Note: The WebView loads with a specific site ID, so changing it
403
+ // at runtime would require reloading the WebView
404
+ warn('setSite() called but WebView was initialized with a different site ID. ' + 'Consider re-mounting the FathomWebView component.');
405
+ },
406
+ blockTrackingForMe: function blockTrackingForMe() {
407
+ isTrackingBlocked = true;
408
+ executeOrQueue('block', [], function () {
409
+ var ref = getWebViewRef();
410
+ ref === null || ref === void 0 || ref.blockTrackingForMe();
411
+ });
412
+ log('Tracking blocked');
413
+ },
414
+ enableTrackingForMe: function enableTrackingForMe() {
415
+ isTrackingBlocked = false;
416
+ executeOrQueue('enable', [], function () {
417
+ var ref = getWebViewRef();
418
+ ref === null || ref === void 0 || ref.enableTrackingForMe();
419
+ });
420
+ log('Tracking enabled');
421
+
422
+ // Process queue when tracking is re-enabled
423
+ processQueue();
424
+ },
425
+ isTrackingEnabled: function isTrackingEnabled() {
426
+ return !isTrackingBlocked;
427
+ },
428
+ // Additional methods for React Native
429
+ processQueue: processQueue,
430
+ getQueueLength: function getQueueLength() {
431
+ return commandQueue.length;
432
+ },
433
+ /**
434
+ * Call this when the WebView signals it's ready.
435
+ * This will flush any queued commands.
436
+ */
437
+ setWebViewReady: function setWebViewReady() {
438
+ log('WebView ready, processing queue');
439
+ processQueue();
440
+ }
441
+ };
442
+ return client;
443
+ }
444
+
445
+ function _typeof(o) {
446
+ "@babel/helpers - typeof";
447
+
448
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
449
+ return typeof o;
450
+ } : function (o) {
451
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
452
+ }, _typeof(o);
453
+ }
454
+
455
+ function toPrimitive(t, r) {
456
+ if ("object" != _typeof(t) || !t) return t;
457
+ var e = t[Symbol.toPrimitive];
458
+ if (void 0 !== e) {
459
+ var i = e.call(t, r);
460
+ if ("object" != _typeof(i)) return i;
461
+ throw new TypeError("@@toPrimitive must return a primitive value.");
462
+ }
463
+ return ("string" === r ? String : Number)(t);
464
+ }
465
+
466
+ function toPropertyKey(t) {
467
+ var i = toPrimitive(t, "string");
468
+ return "symbol" == _typeof(i) ? i : i + "";
469
+ }
470
+
471
+ function _defineProperty(e, r, t) {
472
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
473
+ value: t,
474
+ enumerable: true,
475
+ configurable: true,
476
+ writable: true
477
+ }) : e[r] = t, e;
478
+ }
479
+
480
+ // src/index.ts
481
+ var enqueue = (command) => {
482
+ window.__fathomClientQueue = window.__fathomClientQueue || [];
483
+ window.__fathomClientQueue.push(command);
484
+ };
485
+ var flushQueue = () => {
486
+ window.__fathomIsLoading = false;
487
+ window.__fathomClientQueue = window.__fathomClientQueue || [];
488
+ window.__fathomClientQueue.forEach((command) => {
489
+ switch (command.type) {
490
+ case "trackPageview":
491
+ trackPageview(command.opts);
492
+ return;
493
+ case "trackGoal":
494
+ trackGoal(command.code, command.cents);
495
+ return;
496
+ case "trackEvent":
497
+ trackEvent(command.eventName, command.opts);
498
+ return;
499
+ case "enableTrackingForMe":
500
+ enableTrackingForMe();
501
+ return;
502
+ case "blockTrackingForMe":
503
+ blockTrackingForMe();
504
+ return;
505
+ case "setSite":
506
+ setSite(command.id);
507
+ return;
508
+ }
509
+ });
510
+ window.__fathomClientQueue = [];
511
+ };
512
+ var checkDomainsAndWarn = (domains) => {
513
+ const regex = /(https?)(?=:|\/|$)/;
514
+ domains.forEach((domain) => {
515
+ if (regex.exec(domain) !== null)
516
+ console.warn(
517
+ `The include domain ${domain} might fail to work as intended as it begins with a transfer protocol (http://, https://). Consider removing the protocol portion of the string.`
518
+ );
519
+ });
520
+ };
521
+ var load = (siteId, opts) => {
522
+ if (window.__fathomIsLoading || window.fathom) return;
523
+ window.__fathomIsLoading = true;
524
+ let tracker = document.createElement("script");
525
+ let firstScript = document.getElementsByTagName("script")[0] || document.querySelector("body");
526
+ tracker.id = "fathom-script";
527
+ tracker.async = true;
528
+ tracker.setAttribute("data-site", siteId);
529
+ tracker.src = opts && opts.url ? opts.url : "https://cdn.usefathom.com/script.js";
530
+ if (opts) {
531
+ if (opts.auto !== void 0)
532
+ tracker.setAttribute("data-auto", `${opts.auto}`);
533
+ if (opts.honorDNT !== void 0)
534
+ tracker.setAttribute("data-honor-dnt", `${opts.honorDNT}`);
535
+ if (opts.canonical !== void 0)
536
+ tracker.setAttribute("data-canonical", `${opts.canonical}`);
537
+ if (opts.includedDomains) {
538
+ checkDomainsAndWarn(opts.includedDomains);
539
+ tracker.setAttribute(
540
+ "data-included-domains",
541
+ opts.includedDomains.join(",")
542
+ );
543
+ }
544
+ if (opts.excludedDomains) {
545
+ checkDomainsAndWarn(opts.excludedDomains);
546
+ tracker.setAttribute(
547
+ "data-excluded-domains",
548
+ opts.excludedDomains.join(",")
549
+ );
550
+ }
551
+ if (opts.spa) tracker.setAttribute("data-spa", opts.spa);
552
+ }
553
+ tracker.onload = flushQueue;
554
+ firstScript.parentNode.insertBefore(tracker, firstScript);
555
+ };
556
+ var trackPageview = (opts) => {
557
+ if (window.fathom) {
558
+ if (opts) {
559
+ window.fathom.trackPageview(opts);
560
+ } else {
561
+ window.fathom.trackPageview();
562
+ }
563
+ } else {
564
+ enqueue({ type: "trackPageview", opts });
565
+ }
566
+ };
567
+ var trackGoal = (code, cents) => {
568
+ if (window.fathom) {
569
+ window.fathom.trackGoal(code, cents);
570
+ } else {
571
+ enqueue({ type: "trackGoal", code, cents });
572
+ }
573
+ };
574
+ var trackEvent = (eventName, opts) => {
575
+ if (window.fathom) {
576
+ window.fathom.trackEvent(eventName, opts);
577
+ } else {
578
+ enqueue({ type: "trackEvent", eventName, opts });
579
+ }
580
+ };
581
+ var blockTrackingForMe = () => {
582
+ if (window.fathom) {
583
+ window.fathom.blockTrackingForMe();
584
+ } else {
585
+ enqueue({ type: "blockTrackingForMe" });
586
+ }
587
+ };
588
+ var enableTrackingForMe = () => {
589
+ if (window.fathom) {
590
+ window.fathom.enableTrackingForMe();
591
+ } else {
592
+ enqueue({ type: "enableTrackingForMe" });
593
+ }
594
+ };
595
+ var isTrackingEnabled = () => {
596
+ const preferenceStorage = localStorage.getItem(
597
+ "blockFathomTracking"
598
+ );
599
+ return preferenceStorage !== null ? preferenceStorage !== "true" : true;
600
+ };
601
+ var setSite = (id) => {
602
+ if (window.fathom) {
603
+ window.fathom.setSite(id);
604
+ } else {
605
+ enqueue({ type: "setSite", id });
606
+ }
607
+ };
608
+
609
+ var Fathom = /*#__PURE__*/Object.freeze({
610
+ __proto__: null,
611
+ blockTrackingForMe: blockTrackingForMe,
612
+ enableTrackingForMe: enableTrackingForMe,
613
+ isTrackingEnabled: isTrackingEnabled,
614
+ load: load,
615
+ setSite: setSite,
616
+ trackEvent: trackEvent,
617
+ trackGoal: trackGoal,
618
+ trackPageview: trackPageview
619
+ });
620
+
621
+ var warnMissingProvider = function warnMissingProvider(methodName) {
622
+ if (process.env.NODE_ENV !== 'production') {
623
+ console.warn("[react-fathom] ".concat(methodName, "() called without a FathomProvider. ") + 'Wrap your app with <FathomProvider> to enable analytics tracking.');
624
+ }
625
+ };
626
+
627
+ /**
628
+ * Default context value with stub methods that warn in development.
629
+ * These allow useFathom() to be called without optional chaining,
630
+ * while still informing developers when the provider is missing.
631
+ */
632
+ var defaultContextValue = {
633
+ blockTrackingForMe: function blockTrackingForMe() {
634
+ return warnMissingProvider('blockTrackingForMe');
635
+ },
636
+ enableTrackingForMe: function enableTrackingForMe() {
637
+ return warnMissingProvider('enableTrackingForMe');
638
+ },
639
+ isTrackingEnabled: function isTrackingEnabled() {
640
+ warnMissingProvider('isTrackingEnabled');
641
+ return false;
642
+ },
643
+ load: function load() {
644
+ return warnMissingProvider('load');
645
+ },
646
+ setSite: function setSite() {
647
+ return warnMissingProvider('setSite');
648
+ },
649
+ trackPageview: function trackPageview() {
650
+ return warnMissingProvider('trackPageview');
651
+ },
652
+ trackEvent: function trackEvent() {
653
+ return warnMissingProvider('trackEvent');
654
+ },
655
+ trackGoal: function trackGoal() {
656
+ return warnMissingProvider('trackGoal');
657
+ }
658
+ };
659
+ var FathomContext = /*#__PURE__*/React.createContext(defaultContextValue);
660
+
661
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
662
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
663
+ var FathomProvider = function FathomProvider(_ref) {
664
+ var children = _ref.children,
665
+ providedClient = _ref.client,
666
+ clientRef = _ref.clientRef,
667
+ clientOptions = _ref.clientOptions,
668
+ siteId = _ref.siteId,
669
+ providedDefaultPageviewOptions = _ref.defaultPageviewOptions,
670
+ providedDefaultEventOptions = _ref.defaultEventOptions;
671
+ // Read parent context if it exists
672
+ var parentContext = React.useContext(FathomContext);
673
+
674
+ // Use provided client or fall back to parent client or default Fathom
675
+ var client = React.useMemo(function () {
676
+ var _ref2;
677
+ return (_ref2 = providedClient !== null && providedClient !== void 0 ? providedClient : parentContext.client) !== null && _ref2 !== void 0 ? _ref2 : Fathom;
678
+ }, [providedClient, parentContext.client]);
679
+
680
+ // Merge defaultPageviewOptions: provided > parent > undefined
681
+ var defaultPageviewOptions = React.useMemo(function () {
682
+ return providedDefaultPageviewOptions !== null && providedDefaultPageviewOptions !== void 0 ? providedDefaultPageviewOptions : parentContext.defaultPageviewOptions;
683
+ }, [providedDefaultPageviewOptions, parentContext.defaultPageviewOptions]);
684
+
685
+ // Merge defaultEventOptions: provided > parent > undefined
686
+ var defaultEventOptions = React.useMemo(function () {
687
+ return providedDefaultEventOptions !== null && providedDefaultEventOptions !== void 0 ? providedDefaultEventOptions : parentContext.defaultEventOptions;
688
+ }, [providedDefaultEventOptions, parentContext.defaultEventOptions]);
689
+ var blockTrackingForMe = React.useCallback(function () {
690
+ client.blockTrackingForMe();
691
+ }, [client]);
692
+ var enableTrackingForMe = React.useCallback(function () {
693
+ client.enableTrackingForMe();
694
+ }, [client]);
695
+ var isTrackingEnabled = React.useCallback(function () {
696
+ var _client$isTrackingEna;
697
+ return (_client$isTrackingEna = client.isTrackingEnabled()) !== null && _client$isTrackingEna !== void 0 ? _client$isTrackingEna : false;
698
+ }, [client]);
699
+ var load = React.useCallback(function (siteId, clientOptions) {
700
+ client.load(siteId, clientOptions);
701
+ }, [client]);
702
+ var setSite = React.useCallback(function (siteId) {
703
+ client.setSite(siteId);
704
+ }, [client]);
705
+ var trackEvent = React.useCallback(function (eventName, options) {
706
+ client.trackEvent(eventName, _objectSpread(_objectSpread({}, defaultEventOptions), options));
707
+ }, [client, defaultEventOptions]);
708
+ var trackPageview = React.useCallback(function (options) {
709
+ client.trackPageview(_objectSpread(_objectSpread({}, defaultPageviewOptions), options));
710
+ }, [client, defaultPageviewOptions]);
711
+ var trackGoal = React.useCallback(function (code, cents) {
712
+ client.trackGoal(code, cents);
713
+ }, [client]);
714
+ React.useEffect(function () {
715
+ if (siteId !== undefined) {
716
+ load(siteId, clientOptions);
717
+ }
718
+ }, [clientOptions, load, siteId]);
719
+
720
+ // Populate the clientRef so the parent component can access the client
721
+ React.useEffect(function () {
722
+ if (clientRef) {
723
+ clientRef.current = client;
724
+ }
725
+ }, [client, clientRef]);
726
+ return /*#__PURE__*/React.createElement(FathomContext.Provider, {
727
+ value: {
728
+ blockTrackingForMe: blockTrackingForMe,
729
+ enableTrackingForMe: enableTrackingForMe,
730
+ isTrackingEnabled: isTrackingEnabled,
731
+ load: load,
732
+ setSite: setSite,
733
+ trackEvent: trackEvent,
734
+ trackGoal: trackGoal,
735
+ trackPageview: trackPageview,
736
+ client: client,
737
+ defaultPageviewOptions: defaultPageviewOptions,
738
+ defaultEventOptions: defaultEventOptions
739
+ }
740
+ }, children);
741
+ };
742
+ FathomProvider.displayName = 'FathomProvider';
743
+
744
+ var useFathom = function useFathom() {
745
+ var context = React.useContext(FathomContext);
746
+ return context;
747
+ };
748
+ useFathom.displayName = 'useFathom';
749
+
750
+ /**
751
+ * Hook that tracks app state changes (foreground/background) as Fathom events.
752
+ *
753
+ * This is useful for understanding user engagement patterns and session behavior.
754
+ *
755
+ * @example
756
+ * ```tsx
757
+ * import { useAppStateTracking } from 'react-fathom/native'
758
+ *
759
+ * function App() {
760
+ * useAppStateTracking({
761
+ * foregroundEventName: 'app-resumed',
762
+ * backgroundEventName: 'app-paused',
763
+ * onStateChange: (state) => {
764
+ * console.log('App state:', state)
765
+ * },
766
+ * })
767
+ *
768
+ * return <YourApp />
769
+ * }
770
+ * ```
771
+ */
772
+ function useAppStateTracking() {
773
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
774
+ var _options$foregroundEv = options.foregroundEventName,
775
+ foregroundEventName = _options$foregroundEv === void 0 ? 'app-foreground' : _options$foregroundEv,
776
+ _options$backgroundEv = options.backgroundEventName,
777
+ backgroundEventName = _options$backgroundEv === void 0 ? 'app-background' : _options$backgroundEv,
778
+ eventOptions = options.eventOptions,
779
+ onStateChange = options.onStateChange;
780
+ var _useFathom = useFathom(),
781
+ trackEvent = _useFathom.trackEvent;
782
+ var appStateRef = React.useRef(reactNative.AppState.currentState);
783
+ React.useEffect(function () {
784
+ var handleAppStateChange = function handleAppStateChange(nextAppState) {
785
+ var previousState = appStateRef.current;
786
+
787
+ // Track when app comes to foreground
788
+ if (previousState.match(/inactive|background/) && nextAppState === 'active') {
789
+ trackEvent === null || trackEvent === void 0 || trackEvent(foregroundEventName, eventOptions);
790
+ }
791
+
792
+ // Track when app goes to background
793
+ if (previousState === 'active' && nextAppState.match(/inactive|background/)) {
794
+ trackEvent === null || trackEvent === void 0 || trackEvent(backgroundEventName, eventOptions);
795
+ }
796
+
797
+ // Call the optional state change callback
798
+ onStateChange === null || onStateChange === void 0 || onStateChange(nextAppState);
799
+ appStateRef.current = nextAppState;
800
+ };
801
+ var subscription = reactNative.AppState.addEventListener('change', handleAppStateChange);
802
+ return function () {
803
+ subscription.remove();
804
+ };
805
+ }, [trackEvent, foregroundEventName, backgroundEventName, eventOptions, onStateChange]);
806
+ }
807
+
808
+ /**
809
+ * Internal component that handles app state tracking
810
+ */
811
+ var AppStateTracker = function AppStateTracker() {
812
+ useAppStateTracking({
813
+ foregroundEventName: 'app-foreground',
814
+ backgroundEventName: 'app-background'
815
+ });
816
+ return null;
817
+ };
818
+
819
+ /**
820
+ * A convenience provider for React Native apps that uses a hidden WebView
821
+ * to load the official Fathom Analytics script.
822
+ *
823
+ * This approach ensures full compatibility with Fathom's tracking by using
824
+ * their official JavaScript client, while providing a native React API.
825
+ *
826
+ * @example
827
+ * ```tsx
828
+ * import { NativeFathomProvider } from 'react-fathom/native'
829
+ *
830
+ * function App() {
831
+ * return (
832
+ * <NativeFathomProvider
833
+ * siteId="YOUR_SITE_ID"
834
+ * debug={__DEV__}
835
+ * trackAppState
836
+ * >
837
+ * <YourApp />
838
+ * </NativeFathomProvider>
839
+ * )
840
+ * }
841
+ * ```
842
+ *
843
+ * @remarks
844
+ * This component renders a hidden WebView that loads Fathom's tracking script.
845
+ * Events are queued until the WebView is ready, then automatically flushed.
846
+ *
847
+ * The WebView approach is used because Fathom Analytics does not currently
848
+ * provide a public API for server-side or mobile event tracking. This ensures
849
+ * your analytics are recorded correctly using Fathom's official client.
850
+ */
851
+ var NativeFathomProvider = function NativeFathomProvider(_ref) {
852
+ var siteId = _ref.siteId,
853
+ loadOptions = _ref.loadOptions,
854
+ scriptDomain = _ref.scriptDomain,
855
+ defaultPageviewOptions = _ref.defaultPageviewOptions,
856
+ defaultEventOptions = _ref.defaultEventOptions,
857
+ _ref$trackAppState = _ref.trackAppState,
858
+ trackAppState = _ref$trackAppState === void 0 ? false : _ref$trackAppState,
859
+ _ref$debug = _ref.debug,
860
+ debug = _ref$debug === void 0 ? false : _ref$debug,
861
+ onReady = _ref.onReady,
862
+ onError = _ref.onError,
863
+ clientRef = _ref.clientRef,
864
+ children = _ref.children;
865
+ var webViewRef = React.useRef(null);
866
+
867
+ // Create the WebView-based client
868
+ var client = React.useMemo(function () {
869
+ return createWebViewClient(function () {
870
+ return webViewRef.current;
871
+ }, {
872
+ debug: debug,
873
+ enableQueue: true,
874
+ maxQueueSize: 100
875
+ });
876
+ }, [debug]);
877
+
878
+ // Populate the clientRef so the parent component can access the client
879
+ React.useEffect(function () {
880
+ if (clientRef) {
881
+ clientRef.current = client;
882
+ }
883
+ }, [client, clientRef]);
884
+
885
+ // Handle WebView ready event
886
+ var handleReady = React.useCallback(function () {
887
+ // Flush any queued commands
888
+ client.setWebViewReady();
889
+ onReady === null || onReady === void 0 || onReady();
890
+ }, [client, onReady]);
891
+ return /*#__PURE__*/React.createElement(FathomProvider, {
892
+ client: client,
893
+ siteId: siteId,
894
+ defaultPageviewOptions: defaultPageviewOptions,
895
+ defaultEventOptions: defaultEventOptions
896
+ }, /*#__PURE__*/React.createElement(FathomWebView, {
897
+ ref: webViewRef,
898
+ siteId: siteId,
899
+ loadOptions: loadOptions,
900
+ scriptDomain: scriptDomain,
901
+ debug: debug,
902
+ onReady: handleReady,
903
+ onError: onError
904
+ }), trackAppState && /*#__PURE__*/React.createElement(AppStateTracker, null), children);
905
+ };
906
+
907
+ /**
908
+ * Hook that tracks screen navigation as pageviews using React Navigation.
909
+ *
910
+ * This integrates with React Navigation's navigation container to automatically
911
+ * track screen changes as Fathom pageviews.
912
+ *
913
+ * @example
914
+ * ```tsx
915
+ * import { NavigationContainer } from '@react-navigation/native'
916
+ * import { useNavigationTracking } from 'react-fathom/native'
917
+ *
918
+ * function App() {
919
+ * const navigationRef = useNavigationContainerRef()
920
+ *
921
+ * useNavigationTracking({
922
+ * navigationRef,
923
+ * transformRouteName: (name) => `/screens/${name}`,
924
+ * })
925
+ *
926
+ * return (
927
+ * <NavigationContainer ref={navigationRef}>
928
+ * <Navigator />
929
+ * </NavigationContainer>
930
+ * )
931
+ * }
932
+ * ```
933
+ */
934
+ function useNavigationTracking(options) {
935
+ var navigationRef = options.navigationRef,
936
+ transformRouteName = options.transformRouteName,
937
+ shouldTrackRoute = options.shouldTrackRoute,
938
+ _options$includeParam = options.includeParams,
939
+ includeParams = _options$includeParam === void 0 ? false : _options$includeParam;
940
+ var _useFathom = useFathom(),
941
+ trackPageview = _useFathom.trackPageview;
942
+ var routeNameRef = React.useRef(undefined);
943
+
944
+ /**
945
+ * Get the current route name from the navigation state
946
+ */
947
+ var getCurrentRouteName = React.useCallback(function () {
948
+ var _navigationRef$curren, _navigationRef$curren2, _currentState$routes$2;
949
+ if (!navigationRef.current) {
950
+ return undefined;
951
+ }
952
+ var state = (_navigationRef$curren = (_navigationRef$curren2 = navigationRef.current).getRootState) === null || _navigationRef$curren === void 0 ? void 0 : _navigationRef$curren.call(_navigationRef$curren2);
953
+ if (!state) {
954
+ return undefined;
955
+ }
956
+
957
+ // Navigate through nested navigators to get the deepest route
958
+ var currentState = state;
959
+ while ((_currentState$routes$ = currentState.routes[currentState.index]) !== null && _currentState$routes$ !== void 0 && _currentState$routes$.state) {
960
+ var _currentState$routes$;
961
+ currentState = currentState.routes[currentState.index].state;
962
+ }
963
+ return (_currentState$routes$2 = currentState.routes[currentState.index]) === null || _currentState$routes$2 === void 0 ? void 0 : _currentState$routes$2.name;
964
+ }, [navigationRef]);
965
+
966
+ /**
967
+ * Get the current route params from the navigation state
968
+ */
969
+ var getCurrentRouteParams = React.useCallback(function () {
970
+ var _navigationRef$curren3, _navigationRef$curren4, _currentState$routes$4;
971
+ if (!navigationRef.current) {
972
+ return undefined;
973
+ }
974
+ var state = (_navigationRef$curren3 = (_navigationRef$curren4 = navigationRef.current).getRootState) === null || _navigationRef$curren3 === void 0 ? void 0 : _navigationRef$curren3.call(_navigationRef$curren4);
975
+ if (!state) {
976
+ return undefined;
977
+ }
978
+
979
+ // Navigate through nested navigators to get the deepest route
980
+ var currentState = state;
981
+ while ((_currentState$routes$3 = currentState.routes[currentState.index]) !== null && _currentState$routes$3 !== void 0 && _currentState$routes$3.state) {
982
+ var _currentState$routes$3;
983
+ currentState = currentState.routes[currentState.index].state;
984
+ }
985
+ return (_currentState$routes$4 = currentState.routes[currentState.index]) === null || _currentState$routes$4 === void 0 ? void 0 : _currentState$routes$4.params;
986
+ }, [navigationRef]);
987
+
988
+ /**
989
+ * Build the URL to track
990
+ */
991
+ var buildTrackingUrl = React.useCallback(function (routeName, params) {
992
+ var url = transformRouteName ? transformRouteName(routeName) : "/".concat(routeName);
993
+ if (includeParams && params && Object.keys(params).length > 0) {
994
+ var queryString = Object.entries(params).filter(function (_ref) {
995
+ var _ref2 = _slicedToArray(_ref, 2);
996
+ _ref2[0];
997
+ var value = _ref2[1];
998
+ return value !== undefined && value !== null;
999
+ }).map(function (_ref3) {
1000
+ var _ref4 = _slicedToArray(_ref3, 2),
1001
+ key = _ref4[0],
1002
+ value = _ref4[1];
1003
+ return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(String(value)));
1004
+ }).join('&');
1005
+ if (queryString) {
1006
+ url += "?".concat(queryString);
1007
+ }
1008
+ }
1009
+ return url;
1010
+ }, [transformRouteName, includeParams]);
1011
+
1012
+ /**
1013
+ * Handle navigation state change
1014
+ */
1015
+ var handleStateChange = React.useCallback(function () {
1016
+ var currentRouteName = getCurrentRouteName();
1017
+ var previousRouteName = routeNameRef.current;
1018
+ if (currentRouteName && currentRouteName !== previousRouteName) {
1019
+ var params = includeParams ? getCurrentRouteParams() : undefined;
1020
+
1021
+ // Check if this route should be tracked
1022
+ if (shouldTrackRoute && !shouldTrackRoute(currentRouteName, params)) {
1023
+ routeNameRef.current = currentRouteName;
1024
+ return;
1025
+ }
1026
+ var url = buildTrackingUrl(currentRouteName, params);
1027
+ trackPageview === null || trackPageview === void 0 || trackPageview({
1028
+ url: url,
1029
+ referrer: previousRouteName ? buildTrackingUrl(previousRouteName) : undefined
1030
+ });
1031
+ routeNameRef.current = currentRouteName;
1032
+ }
1033
+ }, [getCurrentRouteName, getCurrentRouteParams, shouldTrackRoute, buildTrackingUrl, trackPageview, includeParams]);
1034
+
1035
+ // Track initial route on mount
1036
+ React.useEffect(function () {
1037
+ // Small delay to ensure navigation is ready
1038
+ var timeout = setTimeout(function () {
1039
+ var initialRoute = getCurrentRouteName();
1040
+ if (initialRoute) {
1041
+ var params = includeParams ? getCurrentRouteParams() : undefined;
1042
+ if (!shouldTrackRoute || shouldTrackRoute(initialRoute, params)) {
1043
+ var url = buildTrackingUrl(initialRoute, params);
1044
+ trackPageview === null || trackPageview === void 0 || trackPageview({
1045
+ url: url
1046
+ });
1047
+ routeNameRef.current = initialRoute;
1048
+ }
1049
+ }
1050
+ }, 0);
1051
+ return function () {
1052
+ return clearTimeout(timeout);
1053
+ };
1054
+ }, []); // Only on mount
1055
+
1056
+ // Set up navigation state change listener
1057
+ React.useEffect(function () {
1058
+ var _navigationRef$curren5, _navigationRef$curren6;
1059
+ if (!navigationRef.current) {
1060
+ return;
1061
+ }
1062
+
1063
+ // Listen for navigation state changes
1064
+ var unsubscribe = (_navigationRef$curren5 = (_navigationRef$curren6 = navigationRef.current).addListener) === null || _navigationRef$curren5 === void 0 ? void 0 : _navigationRef$curren5.call(_navigationRef$curren6, 'state', handleStateChange);
1065
+ return function () {
1066
+ unsubscribe === null || unsubscribe === void 0 || unsubscribe();
1067
+ };
1068
+ }, [navigationRef, handleStateChange]);
1069
+ }
1070
+
1071
+ exports.FathomProvider = FathomProvider;
1072
+ exports.FathomWebView = FathomWebView;
1073
+ exports.NativeFathomProvider = NativeFathomProvider;
1074
+ exports.createWebViewClient = createWebViewClient;
1075
+ exports.useAppStateTracking = useAppStateTracking;
1076
+ exports.useFathom = useFathom;
1077
+ exports.useNavigationTracking = useNavigationTracking;
1078
+ /* Copyright 2026 - Ryan Hefner <hi@ryanhefner.com> (https://www.ryanhefner.com) */
1079
+ //# sourceMappingURL=index.cjs.map