@atmosfer/impressions 0.1.6 → 0.1.8

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/dist/index.js CHANGED
@@ -89,14 +89,6 @@ function createTracker(cfg) {
89
89
  keepalive: true
90
90
  });
91
91
  }
92
- function beacon(ev) {
93
- var _a;
94
- try {
95
- const blob = new Blob([JSON.stringify(ev)], { type: "application/json" });
96
- (_a = navigator.sendBeacon) == null ? void 0 : _a.call(navigator, state.endpoint, blob);
97
- } catch {
98
- }
99
- }
100
92
  function build(eventType, detail) {
101
93
  return {
102
94
  schema_version: 1,
@@ -108,21 +100,6 @@ function createTracker(cfg) {
108
100
  event_detail: { ...baseDetail(anonId), ...detail ?? {} }
109
101
  };
110
102
  }
111
- window.addEventListener("pagehide", () => {
112
- if (state.disabled) return;
113
- beacon(build("page_view"));
114
- });
115
- const origPush = history.pushState;
116
- history.pushState = function(...args) {
117
- origPush.apply(history, args);
118
- void post(build("page_view"));
119
- };
120
- const origReplace = history.replaceState;
121
- history.replaceState = function(...args) {
122
- origReplace.apply(history, args);
123
- void post(build("page_view"));
124
- };
125
- window.addEventListener("popstate", () => void post(build("page_view")));
126
103
  return {
127
104
  page: async (detail) => post(build("page_view", detail)),
128
105
  track: async (eventType, detail) => post(build(eventType, detail)),
@@ -147,8 +124,24 @@ function useImpressions(config, options = {}) {
147
124
  react.useEffect(() => {
148
125
  if (options.trackOnMount === false) return;
149
126
  if (didTrackMount.current) return;
150
- didTrackMount.current = true;
151
- void tracker.page();
127
+ const sendPageView = () => {
128
+ if (didTrackMount.current) return;
129
+ didTrackMount.current = true;
130
+ void tracker.page();
131
+ };
132
+ if (document.visibilityState === "visible") {
133
+ sendPageView();
134
+ return;
135
+ }
136
+ const onVisibility = () => {
137
+ if (document.visibilityState !== "visible") return;
138
+ document.removeEventListener("visibilitychange", onVisibility);
139
+ sendPageView();
140
+ };
141
+ document.addEventListener("visibilitychange", onVisibility);
142
+ return () => {
143
+ document.removeEventListener("visibilitychange", onVisibility);
144
+ };
152
145
  }, []);
153
146
  return tracker;
154
147
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tracker.ts","../src/hook.ts"],"names":["useMemo","useRef","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, useRef } from \"react\";\nimport { createTracker } from \"./tracker\";\nimport type { TrackerConfig } from \"./tracker\";\n\nexport type UseImpressionsOptions = {\n trackOnMount?: boolean;\n};\n\nexport function useImpressions(\n config: TrackerConfig,\n options: UseImpressionsOptions = {}\n) {\n const tracker = useMemo(\n () => createTracker(config),\n [config.endpoint, config.restaurantId, config.tableNo]\n );\n const didTrackMount = useRef(false);\n\n useEffect(() => {\n if (options.trackOnMount === false) return;\n if (didTrackMount.current) return;\n didTrackMount.current = true;\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","useRef","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 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 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, useRef } from \"react\";\nimport { createTracker } from \"./tracker\";\nimport type { TrackerConfig } from \"./tracker\";\n\nexport type UseImpressionsOptions = {\n trackOnMount?: boolean;\n};\n\nexport function useImpressions(\n config: TrackerConfig,\n options: UseImpressionsOptions = {}\n) {\n const tracker = useMemo(\n () => createTracker(config),\n [config.endpoint, config.restaurantId, config.tableNo]\n );\n const didTrackMount = useRef(false);\n\n useEffect(() => {\n if (options.trackOnMount === false) return;\n if (didTrackMount.current) return;\n\n const sendPageView = () => {\n if (didTrackMount.current) return;\n didTrackMount.current = true;\n void tracker.page();\n };\n\n if (document.visibilityState === \"visible\") {\n sendPageView();\n return;\n }\n\n const onVisibility = () => {\n if (document.visibilityState !== \"visible\") return;\n document.removeEventListener(\"visibilitychange\", onVisibility);\n sendPageView();\n };\n\n document.addEventListener(\"visibilitychange\", onVisibility);\n return () => {\n document.removeEventListener(\"visibilitychange\", onVisibility);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return tracker;\n}\n"]}
package/dist/index.mjs CHANGED
@@ -87,14 +87,6 @@ function createTracker(cfg) {
87
87
  keepalive: true
88
88
  });
89
89
  }
90
- function beacon(ev) {
91
- var _a;
92
- try {
93
- const blob = new Blob([JSON.stringify(ev)], { type: "application/json" });
94
- (_a = navigator.sendBeacon) == null ? void 0 : _a.call(navigator, state.endpoint, blob);
95
- } catch {
96
- }
97
- }
98
90
  function build(eventType, detail) {
99
91
  return {
100
92
  schema_version: 1,
@@ -106,21 +98,6 @@ function createTracker(cfg) {
106
98
  event_detail: { ...baseDetail(anonId), ...detail ?? {} }
107
99
  };
108
100
  }
109
- window.addEventListener("pagehide", () => {
110
- if (state.disabled) return;
111
- beacon(build("page_view"));
112
- });
113
- const origPush = history.pushState;
114
- history.pushState = function(...args) {
115
- origPush.apply(history, args);
116
- void post(build("page_view"));
117
- };
118
- const origReplace = history.replaceState;
119
- history.replaceState = function(...args) {
120
- origReplace.apply(history, args);
121
- void post(build("page_view"));
122
- };
123
- window.addEventListener("popstate", () => void post(build("page_view")));
124
101
  return {
125
102
  page: async (detail) => post(build("page_view", detail)),
126
103
  track: async (eventType, detail) => post(build(eventType, detail)),
@@ -145,8 +122,24 @@ function useImpressions(config, options = {}) {
145
122
  useEffect(() => {
146
123
  if (options.trackOnMount === false) return;
147
124
  if (didTrackMount.current) return;
148
- didTrackMount.current = true;
149
- void tracker.page();
125
+ const sendPageView = () => {
126
+ if (didTrackMount.current) return;
127
+ didTrackMount.current = true;
128
+ void tracker.page();
129
+ };
130
+ if (document.visibilityState === "visible") {
131
+ sendPageView();
132
+ return;
133
+ }
134
+ const onVisibility = () => {
135
+ if (document.visibilityState !== "visible") return;
136
+ document.removeEventListener("visibilitychange", onVisibility);
137
+ sendPageView();
138
+ };
139
+ document.addEventListener("visibilitychange", onVisibility);
140
+ return () => {
141
+ document.removeEventListener("visibilitychange", onVisibility);
142
+ };
150
143
  }, []);
151
144
  return tracker;
152
145
  }
@@ -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 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, useRef } from \"react\";\nimport { createTracker } from \"./tracker\";\nimport type { TrackerConfig } from \"./tracker\";\n\nexport type UseImpressionsOptions = {\n trackOnMount?: boolean;\n};\n\nexport function useImpressions(\n config: TrackerConfig,\n options: UseImpressionsOptions = {}\n) {\n const tracker = useMemo(\n () => createTracker(config),\n [config.endpoint, config.restaurantId, config.tableNo]\n );\n const didTrackMount = useRef(false);\n\n useEffect(() => {\n if (options.trackOnMount === false) return;\n if (didTrackMount.current) return;\n didTrackMount.current = true;\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 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 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, useRef } from \"react\";\nimport { createTracker } from \"./tracker\";\nimport type { TrackerConfig } from \"./tracker\";\n\nexport type UseImpressionsOptions = {\n trackOnMount?: boolean;\n};\n\nexport function useImpressions(\n config: TrackerConfig,\n options: UseImpressionsOptions = {}\n) {\n const tracker = useMemo(\n () => createTracker(config),\n [config.endpoint, config.restaurantId, config.tableNo]\n );\n const didTrackMount = useRef(false);\n\n useEffect(() => {\n if (options.trackOnMount === false) return;\n if (didTrackMount.current) return;\n\n const sendPageView = () => {\n if (didTrackMount.current) return;\n didTrackMount.current = true;\n void tracker.page();\n };\n\n if (document.visibilityState === \"visible\") {\n sendPageView();\n return;\n }\n\n const onVisibility = () => {\n if (document.visibilityState !== \"visible\") return;\n document.removeEventListener(\"visibilitychange\", onVisibility);\n sendPageView();\n };\n\n document.addEventListener(\"visibilitychange\", onVisibility);\n return () => {\n document.removeEventListener(\"visibilitychange\", onVisibility);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return tracker;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atmosfer/impressions",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",