@reforma/site 0.0.2 → 0.0.4

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.
@@ -0,0 +1,24 @@
1
+ type AnalyticsOptions = {
2
+ /** Test-only: allow re-init and clear history hooks. */
3
+ reset?: boolean;
4
+ /** Default false — skip localhost / 127.0.0.1. */
5
+ captureOnLocalhost?: boolean;
6
+ };
7
+ type TrackOptions = {
8
+ url?: string;
9
+ referrer?: string;
10
+ props?: Record<string, string>;
11
+ };
12
+ /**
13
+ * Start first-party pageview collection for a published Reforma site.
14
+ * Reads `NEXT_PUBLIC_REFORMA_ANALYTICS*` env baked on publish. Safe to call
15
+ * multiple times — only the first call installs history hooks.
16
+ */
17
+ export declare function initAnalytics(options?: AnalyticsOptions): void;
18
+ /**
19
+ * Send a named event. `pageview` is traffic (deduped by path); any other name
20
+ * is a custom event / goal conversion. Optional `props` are string key/values.
21
+ */
22
+ export declare function track(name: string, options?: TrackOptions): void;
23
+ export {};
24
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAAA,KAAK,gBAAgB,GAAG;IACpB,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kDAAkD;IAClD,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,KAAK,YAAY,GAAG;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AASF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,gBAAqB,GAAG,IAAI,CAqClE;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,IAAI,CA6DpE"}
@@ -0,0 +1,132 @@
1
+ let started = false;
2
+ let enabled = false;
3
+ let writeKey = '';
4
+ let endpoint = '';
5
+ let lastPath = '';
6
+ let unhook = null;
7
+ /**
8
+ * Start first-party pageview collection for a published Reforma site.
9
+ * Reads `NEXT_PUBLIC_REFORMA_ANALYTICS*` env baked on publish. Safe to call
10
+ * multiple times — only the first call installs history hooks.
11
+ */
12
+ export function initAnalytics(options = {}) {
13
+ if (typeof window === 'undefined') {
14
+ return;
15
+ }
16
+ if (options.reset) {
17
+ unhook?.();
18
+ unhook = null;
19
+ started = false;
20
+ lastPath = '';
21
+ }
22
+ if (started) {
23
+ return;
24
+ }
25
+ started = true;
26
+ enabled = process.env.NEXT_PUBLIC_REFORMA_ANALYTICS === '1';
27
+ writeKey = process.env.NEXT_PUBLIC_REFORMA_ANALYTICS_KEY?.trim() ?? '';
28
+ endpoint = process.env.NEXT_PUBLIC_REFORMA_ANALYTICS_ENDPOINT?.trim() ?? '';
29
+ if (!enabled || !writeKey || !endpoint) {
30
+ enabled = false;
31
+ return;
32
+ }
33
+ if (!options.captureOnLocalhost && isLocalHost(window.location.hostname)) {
34
+ enabled = false;
35
+ return;
36
+ }
37
+ track('pageview');
38
+ unhook = installHistoryHook(() => {
39
+ track('pageview');
40
+ });
41
+ }
42
+ /**
43
+ * Send a named event. `pageview` is traffic (deduped by path); any other name
44
+ * is a custom event / goal conversion. Optional `props` are string key/values.
45
+ */
46
+ export function track(name, options = {}) {
47
+ if (!enabled || typeof window === 'undefined') {
48
+ return;
49
+ }
50
+ const eventName = name.trim();
51
+ if (!eventName) {
52
+ return;
53
+ }
54
+ const url = options.url ?? window.location.href;
55
+ const path = pathOf(url);
56
+ if (eventName === 'pageview') {
57
+ if (path === lastPath) {
58
+ return;
59
+ }
60
+ lastPath = path;
61
+ }
62
+ const payload = {
63
+ key: writeKey,
64
+ name: eventName,
65
+ url,
66
+ referrer: options.referrer ?? (document.referrer || undefined),
67
+ };
68
+ if (eventName !== 'pageview' && options.props && typeof options.props === 'object') {
69
+ payload.props = options.props;
70
+ }
71
+ const bodyText = JSON.stringify(payload);
72
+ const body = typeof Blob === 'undefined'
73
+ ? bodyText
74
+ : new Blob([bodyText], { type: 'application/json' });
75
+ try {
76
+ if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
77
+ const ok = navigator.sendBeacon(endpoint, body);
78
+ if (ok) {
79
+ return;
80
+ }
81
+ }
82
+ void fetch(endpoint, {
83
+ method: 'POST',
84
+ body: bodyText,
85
+ headers: { 'Content-Type': 'application/json' },
86
+ keepalive: true,
87
+ mode: 'cors',
88
+ credentials: 'omit',
89
+ }).catch(() => {
90
+ // Fire-and-forget — never break the host site.
91
+ });
92
+ }
93
+ catch {
94
+ // Ignore beacon failures.
95
+ }
96
+ }
97
+ function installHistoryHook(onNavigate) {
98
+ const { pushState, replaceState } = history;
99
+ const notify = () => {
100
+ queueMicrotask(onNavigate);
101
+ };
102
+ history.pushState = function (...args) {
103
+ const result = pushState.apply(this, args);
104
+ notify();
105
+ return result;
106
+ };
107
+ history.replaceState = function (...args) {
108
+ const result = replaceState.apply(this, args);
109
+ notify();
110
+ return result;
111
+ };
112
+ window.addEventListener('popstate', notify);
113
+ return () => {
114
+ history.pushState = pushState;
115
+ history.replaceState = replaceState;
116
+ window.removeEventListener('popstate', notify);
117
+ };
118
+ }
119
+ function isLocalHost(hostname) {
120
+ return hostname === 'localhost'
121
+ || hostname === '127.0.0.1'
122
+ || hostname === '[::1]'
123
+ || hostname.endsWith('.local');
124
+ }
125
+ function pathOf(url) {
126
+ try {
127
+ return new URL(url, window.location.origin).pathname;
128
+ }
129
+ catch {
130
+ return '/';
131
+ }
132
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { initAnalytics, track } from './analytics.js';
1
2
  export { projectMetadata, projectRobots } from './metadata.js';
2
3
  export { SiteRoot, useMode } from './root.js';
3
4
  export type { Appearance, ModeAppearances, ModeDocument, SystemModes } from './root.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
+ export { initAnalytics, track } from './analytics.js';
1
2
  export { projectMetadata, projectRobots } from './metadata.js';
2
3
  export { SiteRoot, useMode } from './root.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reforma/site",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Visitor runtime for a Reforma site: mode, publish metadata, and the PostCSS url guard.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",