@atmosfer/impressions 0.1.3 → 0.1.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.
- package/README.md +115 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +15 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @atmosfer/impressions
|
|
2
|
+
|
|
3
|
+
Lightweight impressions tracker with a small React hook wrapper. The tracker
|
|
4
|
+
captures page views and custom events, enriches them with basic page/device
|
|
5
|
+
context, and sends them to a configured endpoint.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @atmosfer/impressions
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Peer dependency: React 18+ (for the hook).
|
|
14
|
+
|
|
15
|
+
## Usage (vanilla)
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createTracker } from "@atmosfer/impressions";
|
|
19
|
+
|
|
20
|
+
const tracker = createTracker({
|
|
21
|
+
endpoint: "https://example.com/impressions",
|
|
22
|
+
restaurantId: "resto_123",
|
|
23
|
+
tableNo: 12,
|
|
24
|
+
headers: { "x-api-key": "secret" },
|
|
25
|
+
debug: true,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Track a page view
|
|
29
|
+
await tracker.page();
|
|
30
|
+
|
|
31
|
+
// Track a custom event
|
|
32
|
+
await tracker.track("menu_opened", { section: "drinks" });
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage (React)
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useImpressions } from "@atmosfer/impressions";
|
|
39
|
+
|
|
40
|
+
export function MenuPage() {
|
|
41
|
+
const tracker = useImpressions({
|
|
42
|
+
endpoint: "https://example.com/impressions",
|
|
43
|
+
restaurantId: "resto_123",
|
|
44
|
+
tableNo: 12,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<button onClick={() => void tracker.track("cta_click", { id: "order" })}>
|
|
49
|
+
Order now
|
|
50
|
+
</button>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## API
|
|
56
|
+
|
|
57
|
+
### createTracker(config)
|
|
58
|
+
|
|
59
|
+
Creates a tracker instance. In non-browser environments (no `window`), the
|
|
60
|
+
tracker becomes a no-op, which keeps SSR safe.
|
|
61
|
+
|
|
62
|
+
Config fields:
|
|
63
|
+
- `endpoint` (string, required): where events are POSTed.
|
|
64
|
+
- `restaurantId` (string, required): logical tenant identifier.
|
|
65
|
+
- `tableNo` (number, optional): table number context.
|
|
66
|
+
- `headers` (object, optional): extra headers for the POST request.
|
|
67
|
+
- `debug` (boolean, optional): logs events to console when true.
|
|
68
|
+
- `disable` (boolean, optional): start in disabled mode.
|
|
69
|
+
- `anonIdKey` (string, optional): localStorage key for anonymous user id.
|
|
70
|
+
|
|
71
|
+
Returns a `Tracker` with:
|
|
72
|
+
- `page(detail?)`: send a `page_view`.
|
|
73
|
+
- `track(eventType, detail?)`: send a custom event.
|
|
74
|
+
- `setContext({ restaurantId, tableNo })`: update context.
|
|
75
|
+
- `disable()` / `enable()`: toggle event sending.
|
|
76
|
+
|
|
77
|
+
### useImpressions(config)
|
|
78
|
+
|
|
79
|
+
React hook that memoizes a tracker and sends a `page_view` on mount.
|
|
80
|
+
|
|
81
|
+
## Event payload
|
|
82
|
+
|
|
83
|
+
Each event is sent as JSON:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"schema_version": 1,
|
|
88
|
+
"event_id": "uuid",
|
|
89
|
+
"ts": "2025-01-01T00:00:00.000Z",
|
|
90
|
+
"restaurant_id": "resto_123",
|
|
91
|
+
"table_no": 12,
|
|
92
|
+
"event_type": "page_view",
|
|
93
|
+
"event_detail": {
|
|
94
|
+
"path": "/menu?utm_source=qr",
|
|
95
|
+
"referrer": "https://example.com",
|
|
96
|
+
"utm": { "source": "qr", "medium": null, "campaign": null },
|
|
97
|
+
"anon_id": "uuid",
|
|
98
|
+
"language": "en-US",
|
|
99
|
+
"timezone": "Europe/Istanbul",
|
|
100
|
+
"device": { "type": "mobile" },
|
|
101
|
+
"screen": { "w": 390, "h": 844 },
|
|
102
|
+
"page": { "title": "Menu" }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Additional detail fields passed to `page()` or `track()` are merged into
|
|
108
|
+
`event_detail`.
|
|
109
|
+
|
|
110
|
+
## Build
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm run build
|
|
114
|
+
```
|
|
115
|
+
|
package/dist/index.js
CHANGED
|
@@ -54,6 +54,21 @@ function baseDetail(anonId) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
function createTracker(cfg) {
|
|
57
|
+
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined" && typeof navigator !== "undefined" && typeof location !== "undefined";
|
|
58
|
+
if (!isBrowser) {
|
|
59
|
+
return {
|
|
60
|
+
page: async () => {
|
|
61
|
+
},
|
|
62
|
+
track: async () => {
|
|
63
|
+
},
|
|
64
|
+
setContext: () => {
|
|
65
|
+
},
|
|
66
|
+
disable: () => {
|
|
67
|
+
},
|
|
68
|
+
enable: () => {
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
57
72
|
const anonKey = cfg.anonIdKey ?? "sa_anon_id";
|
|
58
73
|
const anonId = getAnonId(anonKey);
|
|
59
74
|
const state = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tracker.ts","../src/hook.ts"],"names":["useMemo","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["export type RestaurantId = string;\n\nexport type ImpressionEvent = {\n schema_version: 1;\n event_id: string;\n ts: string;\n\n restaurant_id: RestaurantId;\n table_no?: number | null;\n\n event_type: string;\n event_detail: Record<string, any>;\n};\n\nexport type TrackerConfig = {\n endpoint: string;\n restaurantId: RestaurantId;\n tableNo?: number;\n headers?: Record<string, string>;\n debug?: boolean;\n disable?: boolean;\n anonIdKey?: string;\n};\n\nfunction uuidv4(): string {\n return crypto.randomUUID();\n}\n\nfunction safeGetLS(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\nfunction safeSetLS(key: string, val: string) {\n try {\n localStorage.setItem(key, val);\n } catch {}\n}\n\nfunction getAnonId(key: string) {\n const existing = safeGetLS(key);\n if (existing) return existing;\n const id = uuidv4();\n safeSetLS(key, id);\n return id;\n}\n\nfunction parseUtm() {\n const p = new URLSearchParams(location.search);\n const source = p.get(\"utm_source\");\n const medium = p.get(\"utm_medium\");\n const campaign = p.get(\"utm_campaign\");\n if (!source && !medium && !campaign) return undefined;\n return { source, medium, campaign };\n}\n\nfunction deviceType(): \"mobile\" | \"tablet\" | \"desktop\" {\n const w = Math.min(screen.width, screen.height);\n if (w <= 480) return \"mobile\";\n if (w <= 1024) return \"tablet\";\n return \"desktop\";\n}\n\nfunction baseDetail(anonId: string) {\n return {\n path: location.pathname + location.search,\n referrer: document.referrer || undefined,\n utm: parseUtm(),\n anon_id: anonId,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n device: { type: deviceType() },\n screen: { w: window.innerWidth, h: window.innerHeight },\n page: { title: document.title },\n };\n}\n\nexport type Tracker = {\n page: (detail?: Record<string, any>) => Promise<void>;\n track: (eventType: string, detail?: Record<string, any>) => Promise<void>;\n setContext: (\n ctx: Partial<{ restaurantId: RestaurantId; tableNo: number }>\n ) => void;\n disable: () => void;\n enable: () => void;\n};\n\nexport function createTracker(cfg: TrackerConfig): Tracker {\n const anonKey = cfg.anonIdKey ?? \"sa_anon_id\";\n const anonId = getAnonId(anonKey);\n\n const state = {\n endpoint: cfg.endpoint,\n restaurantId: cfg.restaurantId,\n tableNo: cfg.tableNo,\n headers: cfg.headers ?? {},\n disabled: !!cfg.disable,\n debug: !!cfg.debug,\n };\n\n async function post(ev: ImpressionEvent) {\n if (state.disabled) return;\n\n if (state.debug) console.log(\"[impr] sending\", ev);\n\n await fetch(state.endpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...state.headers },\n body: JSON.stringify(ev),\n keepalive: true,\n });\n }\n\n function beacon(ev: ImpressionEvent) {\n try {\n const blob = new Blob([JSON.stringify(ev)], { type: \"application/json\" });\n navigator.sendBeacon?.(state.endpoint, blob);\n } catch {}\n }\n\n function build(\n eventType: string,\n detail?: Record<string, any>\n ): ImpressionEvent {\n return {\n schema_version: 1,\n event_id: uuidv4(),\n ts: new Date().toISOString(),\n restaurant_id: state.restaurantId,\n table_no: state.tableNo ?? null,\n event_type: eventType,\n event_detail: { ...baseDetail(anonId), ...(detail ?? {}) },\n };\n }\n\n // Unload best-effort\n window.addEventListener(\"pagehide\", () => {\n if (state.disabled) return;\n beacon(build(\"page_view\"));\n });\n\n // SPA navigation tracking (framework-agnostic)\n const origPush = history.pushState;\n history.pushState = function (...args: any) {\n origPush.apply(history, args as any);\n void post(build(\"page_view\"));\n } as any;\n\n const origReplace = history.replaceState;\n history.replaceState = function (...args: any) {\n origReplace.apply(history, args as any);\n void post(build(\"page_view\"));\n } as any;\n\n window.addEventListener(\"popstate\", () => void post(build(\"page_view\")));\n\n return {\n page: async (detail) => post(build(\"page_view\", detail)),\n track: async (eventType, detail) => post(build(eventType, detail)),\n setContext: (ctx) => {\n if (ctx.restaurantId !== undefined) state.restaurantId = ctx.restaurantId;\n if (ctx.tableNo !== undefined) state.tableNo = ctx.tableNo;\n },\n disable: () => {\n state.disabled = true;\n },\n enable: () => {\n state.disabled = false;\n },\n };\n}\n","import { useEffect, useMemo } from \"react\";\nimport { createTracker } from \"./tracker\";\nimport type { TrackerConfig } from \"./tracker\";\n\nexport function useImpressions(config: TrackerConfig) {\n const tracker = useMemo(\n () => createTracker(config),\n [config.endpoint, config.restaurantId, config.tableNo]\n );\n\n useEffect(() => {\n void tracker.page();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return tracker;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/tracker.ts","../src/hook.ts"],"names":["useMemo","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["export type RestaurantId = string;\n\nexport type ImpressionEvent = {\n schema_version: 1;\n event_id: string;\n ts: string;\n\n restaurant_id: RestaurantId;\n table_no?: number | null;\n\n event_type: string;\n event_detail: Record<string, any>;\n};\n\nexport type TrackerConfig = {\n endpoint: string;\n restaurantId: RestaurantId;\n tableNo?: number;\n headers?: Record<string, string>;\n debug?: boolean;\n disable?: boolean;\n anonIdKey?: string;\n};\n\nfunction uuidv4(): string {\n return crypto.randomUUID();\n}\n\nfunction safeGetLS(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\nfunction safeSetLS(key: string, val: string) {\n try {\n localStorage.setItem(key, val);\n } catch {}\n}\n\nfunction getAnonId(key: string) {\n const existing = safeGetLS(key);\n if (existing) return existing;\n const id = uuidv4();\n safeSetLS(key, id);\n return id;\n}\n\nfunction parseUtm() {\n const p = new URLSearchParams(location.search);\n const source = p.get(\"utm_source\");\n const medium = p.get(\"utm_medium\");\n const campaign = p.get(\"utm_campaign\");\n if (!source && !medium && !campaign) return undefined;\n return { source, medium, campaign };\n}\n\nfunction deviceType(): \"mobile\" | \"tablet\" | \"desktop\" {\n const w = Math.min(screen.width, screen.height);\n if (w <= 480) return \"mobile\";\n if (w <= 1024) return \"tablet\";\n return \"desktop\";\n}\n\nfunction baseDetail(anonId: string) {\n return {\n path: location.pathname + location.search,\n referrer: document.referrer || undefined,\n utm: parseUtm(),\n anon_id: anonId,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n device: { type: deviceType() },\n screen: { w: window.innerWidth, h: window.innerHeight },\n page: { title: document.title },\n };\n}\n\nexport type Tracker = {\n page: (detail?: Record<string, any>) => Promise<void>;\n track: (eventType: string, detail?: Record<string, any>) => Promise<void>;\n setContext: (\n ctx: Partial<{ restaurantId: RestaurantId; tableNo: number }>\n ) => void;\n disable: () => void;\n enable: () => void;\n};\n\nexport function createTracker(cfg: TrackerConfig): Tracker {\n const isBrowser =\n typeof window !== \"undefined\" &&\n typeof document !== \"undefined\" &&\n typeof navigator !== \"undefined\" &&\n typeof location !== \"undefined\";\n\n if (!isBrowser) {\n return {\n page: async () => {},\n track: async () => {},\n setContext: () => {},\n disable: () => {},\n enable: () => {},\n };\n }\n\n const anonKey = cfg.anonIdKey ?? \"sa_anon_id\";\n const anonId = getAnonId(anonKey);\n\n const state = {\n endpoint: cfg.endpoint,\n restaurantId: cfg.restaurantId,\n tableNo: cfg.tableNo,\n headers: cfg.headers ?? {},\n disabled: !!cfg.disable,\n debug: !!cfg.debug,\n };\n\n async function post(ev: ImpressionEvent) {\n if (state.disabled) return;\n\n if (state.debug) console.log(\"[impr] sending\", ev);\n\n await fetch(state.endpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...state.headers },\n body: JSON.stringify(ev),\n keepalive: true,\n });\n }\n\n function beacon(ev: ImpressionEvent) {\n try {\n const blob = new Blob([JSON.stringify(ev)], { type: \"application/json\" });\n navigator.sendBeacon?.(state.endpoint, blob);\n } catch {}\n }\n\n function build(\n eventType: string,\n detail?: Record<string, any>\n ): ImpressionEvent {\n return {\n schema_version: 1,\n event_id: uuidv4(),\n ts: new Date().toISOString(),\n restaurant_id: state.restaurantId,\n table_no: state.tableNo ?? null,\n event_type: eventType,\n event_detail: { ...baseDetail(anonId), ...(detail ?? {}) },\n };\n }\n\n // Unload best-effort\n window.addEventListener(\"pagehide\", () => {\n if (state.disabled) return;\n beacon(build(\"page_view\"));\n });\n\n // SPA navigation tracking (framework-agnostic)\n const origPush = history.pushState;\n history.pushState = function (...args: any) {\n origPush.apply(history, args as any);\n void post(build(\"page_view\"));\n } as any;\n\n const origReplace = history.replaceState;\n history.replaceState = function (...args: any) {\n origReplace.apply(history, args as any);\n void post(build(\"page_view\"));\n } as any;\n\n window.addEventListener(\"popstate\", () => void post(build(\"page_view\")));\n\n return {\n page: async (detail) => post(build(\"page_view\", detail)),\n track: async (eventType, detail) => post(build(eventType, detail)),\n setContext: (ctx) => {\n if (ctx.restaurantId !== undefined) state.restaurantId = ctx.restaurantId;\n if (ctx.tableNo !== undefined) state.tableNo = ctx.tableNo;\n },\n disable: () => {\n state.disabled = true;\n },\n enable: () => {\n state.disabled = false;\n },\n };\n}\n","import { useEffect, useMemo } from \"react\";\nimport { createTracker } from \"./tracker\";\nimport type { TrackerConfig } from \"./tracker\";\n\nexport function useImpressions(config: TrackerConfig) {\n const tracker = useMemo(\n () => createTracker(config),\n [config.endpoint, config.restaurantId, config.tableNo]\n );\n\n useEffect(() => {\n void tracker.page();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return tracker;\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -52,6 +52,21 @@ function baseDetail(anonId) {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
function createTracker(cfg) {
|
|
55
|
+
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined" && typeof navigator !== "undefined" && typeof location !== "undefined";
|
|
56
|
+
if (!isBrowser) {
|
|
57
|
+
return {
|
|
58
|
+
page: async () => {
|
|
59
|
+
},
|
|
60
|
+
track: async () => {
|
|
61
|
+
},
|
|
62
|
+
setContext: () => {
|
|
63
|
+
},
|
|
64
|
+
disable: () => {
|
|
65
|
+
},
|
|
66
|
+
enable: () => {
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
55
70
|
const anonKey = cfg.anonIdKey ?? "sa_anon_id";
|
|
56
71
|
const anonId = getAnonId(anonKey);
|
|
57
72
|
const state = {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tracker.ts","../src/hook.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export type RestaurantId = string;\n\nexport type ImpressionEvent = {\n schema_version: 1;\n event_id: string;\n ts: string;\n\n restaurant_id: RestaurantId;\n table_no?: number | null;\n\n event_type: string;\n event_detail: Record<string, any>;\n};\n\nexport type TrackerConfig = {\n endpoint: string;\n restaurantId: RestaurantId;\n tableNo?: number;\n headers?: Record<string, string>;\n debug?: boolean;\n disable?: boolean;\n anonIdKey?: string;\n};\n\nfunction uuidv4(): string {\n return crypto.randomUUID();\n}\n\nfunction safeGetLS(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\nfunction safeSetLS(key: string, val: string) {\n try {\n localStorage.setItem(key, val);\n } catch {}\n}\n\nfunction getAnonId(key: string) {\n const existing = safeGetLS(key);\n if (existing) return existing;\n const id = uuidv4();\n safeSetLS(key, id);\n return id;\n}\n\nfunction parseUtm() {\n const p = new URLSearchParams(location.search);\n const source = p.get(\"utm_source\");\n const medium = p.get(\"utm_medium\");\n const campaign = p.get(\"utm_campaign\");\n if (!source && !medium && !campaign) return undefined;\n return { source, medium, campaign };\n}\n\nfunction deviceType(): \"mobile\" | \"tablet\" | \"desktop\" {\n const w = Math.min(screen.width, screen.height);\n if (w <= 480) return \"mobile\";\n if (w <= 1024) return \"tablet\";\n return \"desktop\";\n}\n\nfunction baseDetail(anonId: string) {\n return {\n path: location.pathname + location.search,\n referrer: document.referrer || undefined,\n utm: parseUtm(),\n anon_id: anonId,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n device: { type: deviceType() },\n screen: { w: window.innerWidth, h: window.innerHeight },\n page: { title: document.title },\n };\n}\n\nexport type Tracker = {\n page: (detail?: Record<string, any>) => Promise<void>;\n track: (eventType: string, detail?: Record<string, any>) => Promise<void>;\n setContext: (\n ctx: Partial<{ restaurantId: RestaurantId; tableNo: number }>\n ) => void;\n disable: () => void;\n enable: () => void;\n};\n\nexport function createTracker(cfg: TrackerConfig): Tracker {\n const anonKey = cfg.anonIdKey ?? \"sa_anon_id\";\n const anonId = getAnonId(anonKey);\n\n const state = {\n endpoint: cfg.endpoint,\n restaurantId: cfg.restaurantId,\n tableNo: cfg.tableNo,\n headers: cfg.headers ?? {},\n disabled: !!cfg.disable,\n debug: !!cfg.debug,\n };\n\n async function post(ev: ImpressionEvent) {\n if (state.disabled) return;\n\n if (state.debug) console.log(\"[impr] sending\", ev);\n\n await fetch(state.endpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...state.headers },\n body: JSON.stringify(ev),\n keepalive: true,\n });\n }\n\n function beacon(ev: ImpressionEvent) {\n try {\n const blob = new Blob([JSON.stringify(ev)], { type: \"application/json\" });\n navigator.sendBeacon?.(state.endpoint, blob);\n } catch {}\n }\n\n function build(\n eventType: string,\n detail?: Record<string, any>\n ): ImpressionEvent {\n return {\n schema_version: 1,\n event_id: uuidv4(),\n ts: new Date().toISOString(),\n restaurant_id: state.restaurantId,\n table_no: state.tableNo ?? null,\n event_type: eventType,\n event_detail: { ...baseDetail(anonId), ...(detail ?? {}) },\n };\n }\n\n // Unload best-effort\n window.addEventListener(\"pagehide\", () => {\n if (state.disabled) return;\n beacon(build(\"page_view\"));\n });\n\n // SPA navigation tracking (framework-agnostic)\n const origPush = history.pushState;\n history.pushState = function (...args: any) {\n origPush.apply(history, args as any);\n void post(build(\"page_view\"));\n } as any;\n\n const origReplace = history.replaceState;\n history.replaceState = function (...args: any) {\n origReplace.apply(history, args as any);\n void post(build(\"page_view\"));\n } as any;\n\n window.addEventListener(\"popstate\", () => void post(build(\"page_view\")));\n\n return {\n page: async (detail) => post(build(\"page_view\", detail)),\n track: async (eventType, detail) => post(build(eventType, detail)),\n setContext: (ctx) => {\n if (ctx.restaurantId !== undefined) state.restaurantId = ctx.restaurantId;\n if (ctx.tableNo !== undefined) state.tableNo = ctx.tableNo;\n },\n disable: () => {\n state.disabled = true;\n },\n enable: () => {\n state.disabled = false;\n },\n };\n}\n","import { useEffect, useMemo } from \"react\";\nimport { createTracker } from \"./tracker\";\nimport type { TrackerConfig } from \"./tracker\";\n\nexport function useImpressions(config: TrackerConfig) {\n const tracker = useMemo(\n () => createTracker(config),\n [config.endpoint, config.restaurantId, config.tableNo]\n );\n\n useEffect(() => {\n void tracker.page();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return tracker;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/tracker.ts","../src/hook.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export type RestaurantId = string;\n\nexport type ImpressionEvent = {\n schema_version: 1;\n event_id: string;\n ts: string;\n\n restaurant_id: RestaurantId;\n table_no?: number | null;\n\n event_type: string;\n event_detail: Record<string, any>;\n};\n\nexport type TrackerConfig = {\n endpoint: string;\n restaurantId: RestaurantId;\n tableNo?: number;\n headers?: Record<string, string>;\n debug?: boolean;\n disable?: boolean;\n anonIdKey?: string;\n};\n\nfunction uuidv4(): string {\n return crypto.randomUUID();\n}\n\nfunction safeGetLS(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\nfunction safeSetLS(key: string, val: string) {\n try {\n localStorage.setItem(key, val);\n } catch {}\n}\n\nfunction getAnonId(key: string) {\n const existing = safeGetLS(key);\n if (existing) return existing;\n const id = uuidv4();\n safeSetLS(key, id);\n return id;\n}\n\nfunction parseUtm() {\n const p = new URLSearchParams(location.search);\n const source = p.get(\"utm_source\");\n const medium = p.get(\"utm_medium\");\n const campaign = p.get(\"utm_campaign\");\n if (!source && !medium && !campaign) return undefined;\n return { source, medium, campaign };\n}\n\nfunction deviceType(): \"mobile\" | \"tablet\" | \"desktop\" {\n const w = Math.min(screen.width, screen.height);\n if (w <= 480) return \"mobile\";\n if (w <= 1024) return \"tablet\";\n return \"desktop\";\n}\n\nfunction baseDetail(anonId: string) {\n return {\n path: location.pathname + location.search,\n referrer: document.referrer || undefined,\n utm: parseUtm(),\n anon_id: anonId,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n device: { type: deviceType() },\n screen: { w: window.innerWidth, h: window.innerHeight },\n page: { title: document.title },\n };\n}\n\nexport type Tracker = {\n page: (detail?: Record<string, any>) => Promise<void>;\n track: (eventType: string, detail?: Record<string, any>) => Promise<void>;\n setContext: (\n ctx: Partial<{ restaurantId: RestaurantId; tableNo: number }>\n ) => void;\n disable: () => void;\n enable: () => void;\n};\n\nexport function createTracker(cfg: TrackerConfig): Tracker {\n const isBrowser =\n typeof window !== \"undefined\" &&\n typeof document !== \"undefined\" &&\n typeof navigator !== \"undefined\" &&\n typeof location !== \"undefined\";\n\n if (!isBrowser) {\n return {\n page: async () => {},\n track: async () => {},\n setContext: () => {},\n disable: () => {},\n enable: () => {},\n };\n }\n\n const anonKey = cfg.anonIdKey ?? \"sa_anon_id\";\n const anonId = getAnonId(anonKey);\n\n const state = {\n endpoint: cfg.endpoint,\n restaurantId: cfg.restaurantId,\n tableNo: cfg.tableNo,\n headers: cfg.headers ?? {},\n disabled: !!cfg.disable,\n debug: !!cfg.debug,\n };\n\n async function post(ev: ImpressionEvent) {\n if (state.disabled) return;\n\n if (state.debug) console.log(\"[impr] sending\", ev);\n\n await fetch(state.endpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...state.headers },\n body: JSON.stringify(ev),\n keepalive: true,\n });\n }\n\n function beacon(ev: ImpressionEvent) {\n try {\n const blob = new Blob([JSON.stringify(ev)], { type: \"application/json\" });\n navigator.sendBeacon?.(state.endpoint, blob);\n } catch {}\n }\n\n function build(\n eventType: string,\n detail?: Record<string, any>\n ): ImpressionEvent {\n return {\n schema_version: 1,\n event_id: uuidv4(),\n ts: new Date().toISOString(),\n restaurant_id: state.restaurantId,\n table_no: state.tableNo ?? null,\n event_type: eventType,\n event_detail: { ...baseDetail(anonId), ...(detail ?? {}) },\n };\n }\n\n // Unload best-effort\n window.addEventListener(\"pagehide\", () => {\n if (state.disabled) return;\n beacon(build(\"page_view\"));\n });\n\n // SPA navigation tracking (framework-agnostic)\n const origPush = history.pushState;\n history.pushState = function (...args: any) {\n origPush.apply(history, args as any);\n void post(build(\"page_view\"));\n } as any;\n\n const origReplace = history.replaceState;\n history.replaceState = function (...args: any) {\n origReplace.apply(history, args as any);\n void post(build(\"page_view\"));\n } as any;\n\n window.addEventListener(\"popstate\", () => void post(build(\"page_view\")));\n\n return {\n page: async (detail) => post(build(\"page_view\", detail)),\n track: async (eventType, detail) => post(build(eventType, detail)),\n setContext: (ctx) => {\n if (ctx.restaurantId !== undefined) state.restaurantId = ctx.restaurantId;\n if (ctx.tableNo !== undefined) state.tableNo = ctx.tableNo;\n },\n disable: () => {\n state.disabled = true;\n },\n enable: () => {\n state.disabled = false;\n },\n };\n}\n","import { useEffect, useMemo } from \"react\";\nimport { createTracker } from \"./tracker\";\nimport type { TrackerConfig } from \"./tracker\";\n\nexport function useImpressions(config: TrackerConfig) {\n const tracker = useMemo(\n () => createTracker(config),\n [config.endpoint, config.restaurantId, config.tableNo]\n );\n\n useEffect(() => {\n void tracker.page();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return tracker;\n}\n"]}
|