@tiendanube/live-state 1.0.0-beta.13 → 1.0.0-beta.14
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 +54 -0
- package/dist/{LiveStateProvider-BH31kAuo.js → LiveStateProvider-DeRIGqmT.js} +15 -11
- package/dist/LiveStateProvider-DeRIGqmT.js.map +1 -0
- package/dist/LiveStateProvider-vcADappI.cjs +2 -0
- package/dist/LiveStateProvider-vcADappI.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/src/utils/analytics.d.ts +0 -14
- package/dist/src/utils/analytics.d.ts.map +1 -1
- package/dist/testing.cjs +1 -1
- package/dist/testing.js +1 -1
- package/package.json +6 -1
- package/dist/LiveStateProvider-BAkFYuZ2.cjs +0 -2
- package/dist/LiveStateProvider-BAkFYuZ2.cjs.map +0 -1
- package/dist/LiveStateProvider-BH31kAuo.js.map +0 -1
package/README.md
CHANGED
|
@@ -146,6 +146,60 @@ vi.mock('@tiendanube/live-state', async (importOriginal) => ({
|
|
|
146
146
|
|
|
147
147
|
---
|
|
148
148
|
|
|
149
|
+
## Disabling analytics (dev / test / staging)
|
|
150
|
+
|
|
151
|
+
Pass `disabled={true}` to `LiveStateProvider` to prevent the Amplitude and Clarity SDKs from being initialised. The prop value should come from your app's own environment variable — the library does not define or read any env var itself.
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
// Vite
|
|
155
|
+
<LiveStateProvider
|
|
156
|
+
fetcher={fetchLiveState}
|
|
157
|
+
disabled={import.meta.env.VITE_APP_ENV !== 'production'}
|
|
158
|
+
>
|
|
159
|
+
...
|
|
160
|
+
</LiveStateProvider>
|
|
161
|
+
|
|
162
|
+
// Create React App / Next.js
|
|
163
|
+
<LiveStateProvider
|
|
164
|
+
fetcher={fetchLiveState}
|
|
165
|
+
disabled={process.env.NODE_ENV !== 'production'}
|
|
166
|
+
>
|
|
167
|
+
...
|
|
168
|
+
</LiveStateProvider>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
When `disabled` is `true`, `onEvent` callbacks still fire normally so you can observe tracking without hitting the real SDKs.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Fetcher timeouts
|
|
176
|
+
|
|
177
|
+
The library does not impose a timeout on the fetcher — if the endpoint hangs, `isLoading` will remain `true` indefinitely. Because live state notifications are non-critical UI, you should configure a timeout directly in your fetcher so that a slow backend never blocks the page.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
// Using axios
|
|
181
|
+
const fetcher: LiveStateFetcher = async () => {
|
|
182
|
+
const { data } = await axios.get('/api/live-state', { timeout: 5000 });
|
|
183
|
+
return data ?? null;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Using native fetch + AbortSignal
|
|
187
|
+
const fetcher: LiveStateFetcher = async () => {
|
|
188
|
+
const controller = new AbortController();
|
|
189
|
+
const id = setTimeout(() => controller.abort(), 5000);
|
|
190
|
+
try {
|
|
191
|
+
const res = await fetch('/api/live-state', { signal: controller.signal });
|
|
192
|
+
return res.ok ? await res.json() : null;
|
|
193
|
+
} catch {
|
|
194
|
+
return null; // timeout or network error — fail silently
|
|
195
|
+
} finally {
|
|
196
|
+
clearTimeout(id);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
149
203
|
## Documentation
|
|
150
204
|
|
|
151
205
|
- [Implementation Guide](./docs/IMPLEMENTATION_GUIDE.md) — full integration guide with usage patterns, API reference, and troubleshooting
|
|
@@ -30,7 +30,7 @@ function u(e, t) {
|
|
|
30
30
|
async function d(e, t) {
|
|
31
31
|
let n = c(t);
|
|
32
32
|
try {
|
|
33
|
-
if (
|
|
33
|
+
if (v()) {
|
|
34
34
|
n("info", "Clarity already initialized by client");
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
@@ -48,7 +48,7 @@ function f() {
|
|
|
48
48
|
try {
|
|
49
49
|
let e = document.querySelector("script[src*=\"clarity.ms\"]");
|
|
50
50
|
if (e) return e.getAttribute("src")?.match(/\/([a-z0-9]+)$/)?.[1];
|
|
51
|
-
let t =
|
|
51
|
+
let t = v()?.q?.[0]?.[1];
|
|
52
52
|
if (typeof t == "string") return t;
|
|
53
53
|
} catch {}
|
|
54
54
|
}
|
|
@@ -66,8 +66,9 @@ function p(e, t, n) {
|
|
|
66
66
|
function m(e, t, n) {
|
|
67
67
|
let r = c(n);
|
|
68
68
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
let n = v();
|
|
70
|
+
n && (n("event", e), t && Object.entries(t).forEach(([e, t]) => {
|
|
71
|
+
n("set", e, String(t));
|
|
71
72
|
}));
|
|
72
73
|
} catch (t) {
|
|
73
74
|
r("error", "Failed to track Clarity event", {
|
|
@@ -89,10 +90,13 @@ function g(e) {
|
|
|
89
90
|
function _(e, t, n) {
|
|
90
91
|
p(e, t, n), m(e, t, n);
|
|
91
92
|
}
|
|
93
|
+
function v() {
|
|
94
|
+
return window.clarity;
|
|
95
|
+
}
|
|
92
96
|
//#endregion
|
|
93
97
|
//#region src/providers/LiveStateProvider.tsx
|
|
94
|
-
var
|
|
95
|
-
function
|
|
98
|
+
var y = e(null);
|
|
99
|
+
function b({ children: e, fetcher: t, analytics: a, onEvent: s, onLog: l, disabled: f = !1 }) {
|
|
96
100
|
let p = r(() => c(l), [l]);
|
|
97
101
|
n(() => {
|
|
98
102
|
f || (a?.amplitudeKey && u(a.amplitudeKey, p), a?.clarityProjectId && d(a.clarityProjectId, p));
|
|
@@ -116,18 +120,18 @@ function y({ children: e, fetcher: t, analytics: a, onEvent: s, onLog: l, disabl
|
|
|
116
120
|
]);
|
|
117
121
|
return /* @__PURE__ */ o(i, {
|
|
118
122
|
value: { provider: () => /* @__PURE__ */ new Map() },
|
|
119
|
-
children: /* @__PURE__ */ o(
|
|
123
|
+
children: /* @__PURE__ */ o(y.Provider, {
|
|
120
124
|
value: m,
|
|
121
125
|
children: e
|
|
122
126
|
})
|
|
123
127
|
});
|
|
124
128
|
}
|
|
125
|
-
function
|
|
126
|
-
let e = t(
|
|
129
|
+
function x() {
|
|
130
|
+
let e = t(y);
|
|
127
131
|
if (!e) throw Error("useLiveState must be used within LiveStateProvider");
|
|
128
132
|
return e;
|
|
129
133
|
}
|
|
130
134
|
//#endregion
|
|
131
|
-
export { _ as a, g as i,
|
|
135
|
+
export { _ as a, g as i, x as n, c as o, h as r, b as t };
|
|
132
136
|
|
|
133
|
-
//# sourceMappingURL=LiveStateProvider-
|
|
137
|
+
//# sourceMappingURL=LiveStateProvider-DeRIGqmT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveStateProvider-DeRIGqmT.js","names":[],"sources":["../src/utils/logger.ts","../src/utils/analytics.ts","../src/providers/LiveStateProvider.tsx"],"sourcesContent":["import type { LogLevel } from '../types';\n\n/**\n * Logger function type — matches the signature of LiveStateProviderProps.onLog.\n */\nexport type LoggerFn = (\n level: LogLevel,\n message: string,\n context?: Record<string, unknown>,\n) => void;\n\n/**\n * Default logger — falls back to console.* when no onLog is provided.\n * Uses console.warn for 'info' and 'warn', console.error for 'error'.\n */\nconst defaultLogger: LoggerFn = (level, message, context) => {\n const prefix = '[LiveState]';\n if (level === 'error') {\n console.error(prefix, message, ...(context ? [context] : []));\n } else {\n console.warn(prefix, message, ...(context ? [context] : []));\n }\n};\n\n/**\n * Creates a logger bound to an optional consumer-provided onLog callback.\n * When onLog is provided it is called exclusively — the default console.*\n * fallback does NOT run, giving the consumer full control over output.\n *\n * @example\n * const log = createLogger(onLog);\n * log('warn', 'Amplitude API key not provided');\n * log('error', 'Failed to fetch', { url: '/v1/live-state', status: 500 });\n */\nexport function createLogger(onLog?: LoggerFn): LoggerFn {\n return (level, message, context) => {\n if (onLog) {\n onLog(level, message, context);\n } else {\n defaultLogger(level, message, context);\n }\n };\n}\n","import * as amplitude from '@amplitude/analytics-browser';\nimport type { TrackingProperties, TrackingValue } from '../types';\nimport { createLogger, type LoggerFn } from './logger';\n\n/**\n * Isolated Amplitude instance for the lib — avoids conflicts with the\n * consumer app's own Amplitude instance when both coexist on the same page.\n */\nconst amplitudeInstance = amplitude.createInstance();\n\n/**\n * Initialize Amplitude (fail silently)\n */\nexport function initializeAmplitude(apiKey: string, log?: LoggerFn): void {\n const logger = createLogger(log);\n try {\n if (!apiKey) {\n logger('warn', 'Amplitude API key not provided');\n return;\n }\n\n amplitudeInstance.init(apiKey, {\n defaultTracking: false,\n });\n } catch (error) {\n logger('error', 'Failed to initialize Amplitude', { error });\n }\n}\n\n/**\n * Initialize Clarity (fail silently)\n */\nexport async function initializeClarity(\n projectId?: string,\n log?: LoggerFn,\n): Promise<void> {\n const logger = createLogger(log);\n try {\n // Check if Clarity is already loaded by the client\n if (getClarity()) {\n logger('info', 'Clarity already initialized by client');\n return;\n }\n\n // If client provided project ID, use it\n const finalProjectId = projectId || getClientClarityProjectId();\n\n if (!finalProjectId) {\n logger('warn', 'Clarity project ID not provided');\n return;\n }\n\n // Initialize Clarity with project ID\n const Clarity = (await import('@microsoft/clarity')).default;\n Clarity.init(finalProjectId);\n } catch (error) {\n logger('error', 'Failed to initialize Clarity', { error });\n }\n}\n\n/**\n * Try to extract Clarity project ID from client's installation\n */\nfunction getClientClarityProjectId(): string | undefined {\n try {\n // Check if Clarity script tag exists\n const clarityScript = document.querySelector('script[src*=\"clarity.ms\"]');\n if (clarityScript) {\n const src = clarityScript.getAttribute('src');\n const match = src?.match(/\\/([a-z0-9]+)$/);\n return match?.[1];\n }\n\n // Check global clarity config: shape is clarity.q = [['init', projectId, ...]]\n const projectId = getClarity()?.q?.[0]?.[1];\n if (typeof projectId === 'string') {\n return projectId;\n }\n } catch {\n // Fail silently\n }\n\n return undefined;\n}\n\n/**\n * Track event with Amplitude (fail silently)\n */\nexport function trackAmplitudeEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n const logger = createLogger(log);\n try {\n amplitudeInstance.track(eventName, properties);\n } catch (error) {\n logger('error', 'Failed to track Amplitude event', { eventName, error });\n }\n}\n\n/**\n * Track event with Clarity (fail silently)\n */\nexport function trackClarityEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n const logger = createLogger(log);\n try {\n const clarity = getClarity();\n if (clarity) {\n clarity('event', eventName);\n\n // Set custom tags for properties\n if (properties) {\n Object.entries(properties).forEach(([key, value]) => {\n clarity('set', key, String(value));\n });\n }\n }\n } catch (error) {\n logger('error', 'Failed to track Clarity event', { eventName, error });\n }\n}\n\n/**\n * Builds a tracking event name from prefix, page and event segments.\n * The prefix is expected to already include a trailing separator (e.g. 'nuvempago_lending_').\n * Page is optional — when absent the segment is omitted from the name.\n *\n * @example\n * buildEventName('nuvempago_lending_', 'dashboard', 'charge', 'view')\n * // → 'nuvempago_lending_dashboard_charge_view'\n *\n * buildEventName('nuvempago_lending_', undefined, 'charge', 'view')\n * // → 'nuvempago_lending_charge_view'\n */\nexport function buildEventName(\n prefix: string,\n page: string | undefined,\n context: string,\n event: string,\n): string {\n // Prefix already contains its trailing separator — join only the remaining segments\n const segments = [page, context, event].filter(Boolean);\n return `${prefix}${segments.join('_')}`;\n}\n\n/**\n * Builds a TrackingProperties object from a loose record, dropping entries\n * whose value is undefined so callers don't need to guard optional fields.\n */\nexport function buildTrackingProperties(\n source: Record<string, TrackingValue | undefined>,\n): TrackingProperties {\n return Object.fromEntries(\n Object.entries(source).filter(\n (entry): entry is [string, TrackingValue] => entry[1] !== undefined,\n ),\n );\n}\n\n/**\n * Track event with both Amplitude and Clarity (fail silently)\n */\nexport function trackEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n trackAmplitudeEvent(eventName, properties, log);\n trackClarityEvent(eventName, properties, log);\n}\n\n/**\n * Clarity command API shape — kept module-local to avoid polluting the\n * consumer's global Window type with a conflicting declaration.\n * https://learn.microsoft.com/en-us/clarity/setup-and-installation/clarity-api\n */\ntype ClarityCommand =\n | 'init'\n | 'event'\n | 'set'\n | 'identify'\n | 'consent'\n | 'upgrade';\n\ntype ClarityFn = {\n (command: ClarityCommand, ...args: string[]): void;\n q?: string[][];\n};\n\n/** Access window.clarity without declaring a global augmentation */\nfunction getClarity(): ClarityFn | undefined {\n return (window as unknown as Record<string, unknown>).clarity as\n | ClarityFn\n | undefined;\n}\n","import React, { createContext, useContext, useEffect, useMemo } from 'react';\nimport { SWRConfig } from 'swr';\nimport type {\n LiveStateFetcher,\n AnalyticsConfig,\n TrackingProperties,\n} from '../types';\nimport { initializeAmplitude, initializeClarity } from '../utils/analytics';\nimport { createLogger, type LoggerFn } from '../utils/logger';\n\ninterface LiveStateContextValue {\n fetcher: LiveStateFetcher;\n analytics?: AnalyticsConfig;\n onEvent?: (eventName: string, properties?: TrackingProperties) => void;\n disabled?: boolean;\n log: LoggerFn;\n}\n\nconst LiveStateContext = createContext<LiveStateContextValue | null>(null);\n\nexport interface LiveStateProviderProps {\n children: React.ReactNode;\n fetcher: LiveStateFetcher;\n analytics?: AnalyticsConfig;\n onEvent?: (eventName: string, properties?: TrackingProperties) => void;\n onLog?: LoggerFn;\n /**\n * When true, disables all analytics SDKs (Amplitude and Clarity).\n * Useful in development and test environments to suppress SDK warnings\n * and avoid polluting analytics data without needing to mock anything.\n * The `onEvent` and `onLog` callbacks still fire when disabled.\n *\n * @example\n * <LiveStateProvider disabled={process.env.NODE_ENV !== 'production'} />\n */\n disabled?: boolean;\n}\n\n/**\n * LiveStateProvider\n *\n * Root-level provider that configures fetcher function and analytics.\n * Initializes Amplitude and Clarity on mount.\n *\n * The optional `onEvent` callback is fired on every tracking event before\n * sending to Amplitude/Clarity — useful for simulators and debugging tools.\n *\n * The optional `onLog` callback receives all internal lib logs — route them\n * to your APM (Datadog, Sentry, etc.) to avoid relying on console.*.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateProvider({\n children,\n fetcher,\n analytics,\n onEvent,\n onLog,\n disabled = false,\n}: LiveStateProviderProps) {\n const log = useMemo(() => createLogger(onLog), [onLog]);\n\n useEffect(() => {\n if (disabled) return;\n\n if (analytics?.amplitudeKey) {\n initializeAmplitude(analytics.amplitudeKey, log);\n }\n\n if (analytics?.clarityProjectId) {\n initializeClarity(analytics.clarityProjectId, log);\n }\n }, [analytics, disabled, log]);\n\n const contextValue = useMemo(\n () => ({ fetcher, analytics, onEvent, disabled, log }),\n [fetcher, analytics, onEvent, disabled, log],\n );\n\n return (\n <SWRConfig value={{ provider: () => new Map() }}>\n <LiveStateContext.Provider value={contextValue}>\n {children}\n </LiveStateContext.Provider>\n </SWRConfig>\n );\n}\n\n/**\n * useLiveStateContext\n *\n * Internal hook to access LiveStateContext\n */\nexport function useLiveStateContext() {\n const context = useContext(LiveStateContext);\n\n if (!context) {\n throw new Error('useLiveState must be used within LiveStateProvider');\n }\n\n return context;\n}\n"],"mappings":";;;;;AAeA,IAAM,KAA2B,GAAO,GAAS,MAAY;CAC3D,IAAM,IAAS;AACf,CAAI,MAAU,UACZ,QAAQ,MAAM,GAAQ,GAAS,GAAI,IAAU,CAAC,EAAQ,GAAG,EAAE,CAAE,GAE7D,QAAQ,KAAK,GAAQ,GAAS,GAAI,IAAU,CAAC,EAAQ,GAAG,EAAE,CAAE;;AAchE,SAAgB,EAAa,GAA4B;AACvD,SAAQ,GAAO,GAAS,MAAY;AAClC,EAAI,IACF,EAAM,GAAO,GAAS,EAAQ,GAE9B,EAAc,GAAO,GAAS,EAAQ;;;;;AC/B5C,IAAM,IAAoB,EAAU,gBAAgB;AAKpD,SAAgB,EAAoB,GAAgB,GAAsB;CACxE,IAAM,IAAS,EAAa,EAAI;AAChC,KAAI;AACF,MAAI,CAAC,GAAQ;AACX,KAAO,QAAQ,iCAAiC;AAChD;;AAGF,IAAkB,KAAK,GAAQ,EAC7B,iBAAiB,IAClB,CAAC;UACK,GAAO;AACd,IAAO,SAAS,kCAAkC,EAAE,UAAO,CAAC;;;AAOhE,eAAsB,EACpB,GACA,GACe;CACf,IAAM,IAAS,EAAa,EAAI;AAChC,KAAI;AAEF,MAAI,GAAY,EAAE;AAChB,KAAO,QAAQ,wCAAwC;AACvD;;EAIF,IAAM,IAAiB,KAAa,GAA2B;AAE/D,MAAI,CAAC,GAAgB;AACnB,KAAO,QAAQ,kCAAkC;AACjD;;AAKF,GADiB,MAAM,OAAO,uBAAuB,QAC7C,KAAK,EAAe;UACrB,GAAO;AACd,IAAO,SAAS,gCAAgC,EAAE,UAAO,CAAC;;;AAO9D,SAAS,IAAgD;AACvD,KAAI;EAEF,IAAM,IAAgB,SAAS,cAAc,8BAA4B;AACzE,MAAI,EAGF,QAFY,EAAc,aAAa,MAAM,EAC1B,MAAM,iBAAiB,GAC3B;EAIjB,IAAM,IAAY,GAAY,EAAE,IAAI,KAAK;AACzC,MAAI,OAAO,KAAc,SACvB,QAAO;SAEH;;AAUV,SAAgB,EACd,GACA,GACA,GACM;CACN,IAAM,IAAS,EAAa,EAAI;AAChC,KAAI;AACF,IAAkB,MAAM,GAAW,EAAW;UACvC,GAAO;AACd,IAAO,SAAS,mCAAmC;GAAE;GAAW;GAAO,CAAC;;;AAO5E,SAAgB,EACd,GACA,GACA,GACM;CACN,IAAM,IAAS,EAAa,EAAI;AAChC,KAAI;EACF,IAAM,IAAU,GAAY;AAC5B,EAAI,MACF,EAAQ,SAAS,EAAU,EAGvB,KACF,OAAO,QAAQ,EAAW,CAAC,SAAS,CAAC,GAAK,OAAW;AACnD,KAAQ,OAAO,GAAK,OAAO,EAAM,CAAC;IAClC;UAGC,GAAO;AACd,IAAO,SAAS,iCAAiC;GAAE;GAAW;GAAO,CAAC;;;AAgB1E,SAAgB,EACd,GACA,GACA,GACA,GACQ;AAGR,QAAO,GAAG,IADO;EAAC;EAAM;EAAS;EAAM,CAAC,OAAO,QAAQ,CAC3B,KAAK,IAAI;;AAOvC,SAAgB,EACd,GACoB;AACpB,QAAO,OAAO,YACZ,OAAO,QAAQ,EAAO,CAAC,QACpB,MAA4C,EAAM,OAAO,KAAA,EAC3D,CACF;;AAMH,SAAgB,EACd,GACA,GACA,GACM;AAEN,CADA,EAAoB,GAAW,GAAY,EAAI,EAC/C,EAAkB,GAAW,GAAY,EAAI;;AAsB/C,SAAS,IAAoC;AAC3C,QAAQ,OAA8C;;;;AClLxD,IAAM,IAAmB,EAA4C,KAAK;AAkC1E,SAAgB,EAAkB,EAChC,aACA,YACA,cACA,YACA,UACA,cAAW,MACc;CACzB,IAAM,IAAM,QAAc,EAAa,EAAM,EAAE,CAAC,EAAM,CAAC;AAEvD,SAAgB;AACV,QAEA,GAAW,gBACb,EAAoB,EAAU,cAAc,EAAI,EAG9C,GAAW,oBACb,EAAkB,EAAU,kBAAkB,EAAI;IAEnD;EAAC;EAAW;EAAU;EAAI,CAAC;CAE9B,IAAM,IAAe,SACZ;EAAE;EAAS;EAAW;EAAS;EAAU;EAAK,GACrD;EAAC;EAAS;EAAW;EAAS;EAAU;EAAI,CAC7C;AAED,QACE,kBAAC,GAAD;EAAW,OAAO,EAAE,gCAAgB,IAAI,KAAK,EAAE;YAC7C,kBAAC,EAAiB,UAAlB;GAA2B,OAAO;GAC/B;GACyB,CAAA;EAClB,CAAA;;AAShB,SAAgB,IAAsB;CACpC,IAAM,IAAU,EAAW,EAAiB;AAE5C,KAAI,CAAC,EACH,OAAU,MAAM,qDAAqD;AAGvE,QAAO"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`react`);c=s(c,1);let l=require(`swr`),u=require(`@amplitude/analytics-browser`);u=s(u,1);let d=require(`react/jsx-runtime`);var f=(e,t,n)=>{let r=`[LiveState]`;e===`error`?console.error(r,t,...n?[n]:[]):console.warn(r,t,...n?[n]:[])};function p(e){return(t,n,r)=>{e?e(t,n,r):f(t,n,r)}}var m=u.createInstance();function h(e,t){let n=p(t);try{if(!e){n(`warn`,`Amplitude API key not provided`);return}m.init(e,{defaultTracking:!1})}catch(e){n(`error`,`Failed to initialize Amplitude`,{error:e})}}async function g(e,t){let n=p(t);try{if(C()){n(`info`,`Clarity already initialized by client`);return}let t=e||_();if(!t){n(`warn`,`Clarity project ID not provided`);return}(await import(`@microsoft/clarity`)).default.init(t)}catch(e){n(`error`,`Failed to initialize Clarity`,{error:e})}}function _(){try{let e=document.querySelector(`script[src*="clarity.ms"]`);if(e)return e.getAttribute(`src`)?.match(/\/([a-z0-9]+)$/)?.[1];let t=C()?.q?.[0]?.[1];if(typeof t==`string`)return t}catch{}}function v(e,t,n){let r=p(n);try{m.track(e,t)}catch(t){r(`error`,`Failed to track Amplitude event`,{eventName:e,error:t})}}function y(e,t,n){let r=p(n);try{let n=C();n&&(n(`event`,e),t&&Object.entries(t).forEach(([e,t])=>{n(`set`,e,String(t))}))}catch(t){r(`error`,`Failed to track Clarity event`,{eventName:e,error:t})}}function b(e,t,n,r){return`${e}${[t,n,r].filter(Boolean).join(`_`)}`}function x(e){return Object.fromEntries(Object.entries(e).filter(e=>e[1]!==void 0))}function S(e,t,n){v(e,t,n),y(e,t,n)}function C(){return window.clarity}var w=(0,c.createContext)(null);function T({children:e,fetcher:t,analytics:n,onEvent:r,onLog:i,disabled:a=!1}){let o=(0,c.useMemo)(()=>p(i),[i]);(0,c.useEffect)(()=>{a||(n?.amplitudeKey&&h(n.amplitudeKey,o),n?.clarityProjectId&&g(n.clarityProjectId,o))},[n,a,o]);let s=(0,c.useMemo)(()=>({fetcher:t,analytics:n,onEvent:r,disabled:a,log:o}),[t,n,r,a,o]);return(0,d.jsx)(l.SWRConfig,{value:{provider:()=>new Map},children:(0,d.jsx)(w.Provider,{value:s,children:e})})}function E(){let e=(0,c.useContext)(w);if(!e)throw Error(`useLiveState must be used within LiveStateProvider`);return e}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return T}});
|
|
2
|
+
//# sourceMappingURL=LiveStateProvider-vcADappI.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveStateProvider-vcADappI.cjs","names":[],"sources":["../src/utils/logger.ts","../src/utils/analytics.ts","../src/providers/LiveStateProvider.tsx"],"sourcesContent":["import type { LogLevel } from '../types';\n\n/**\n * Logger function type — matches the signature of LiveStateProviderProps.onLog.\n */\nexport type LoggerFn = (\n level: LogLevel,\n message: string,\n context?: Record<string, unknown>,\n) => void;\n\n/**\n * Default logger — falls back to console.* when no onLog is provided.\n * Uses console.warn for 'info' and 'warn', console.error for 'error'.\n */\nconst defaultLogger: LoggerFn = (level, message, context) => {\n const prefix = '[LiveState]';\n if (level === 'error') {\n console.error(prefix, message, ...(context ? [context] : []));\n } else {\n console.warn(prefix, message, ...(context ? [context] : []));\n }\n};\n\n/**\n * Creates a logger bound to an optional consumer-provided onLog callback.\n * When onLog is provided it is called exclusively — the default console.*\n * fallback does NOT run, giving the consumer full control over output.\n *\n * @example\n * const log = createLogger(onLog);\n * log('warn', 'Amplitude API key not provided');\n * log('error', 'Failed to fetch', { url: '/v1/live-state', status: 500 });\n */\nexport function createLogger(onLog?: LoggerFn): LoggerFn {\n return (level, message, context) => {\n if (onLog) {\n onLog(level, message, context);\n } else {\n defaultLogger(level, message, context);\n }\n };\n}\n","import * as amplitude from '@amplitude/analytics-browser';\nimport type { TrackingProperties, TrackingValue } from '../types';\nimport { createLogger, type LoggerFn } from './logger';\n\n/**\n * Isolated Amplitude instance for the lib — avoids conflicts with the\n * consumer app's own Amplitude instance when both coexist on the same page.\n */\nconst amplitudeInstance = amplitude.createInstance();\n\n/**\n * Initialize Amplitude (fail silently)\n */\nexport function initializeAmplitude(apiKey: string, log?: LoggerFn): void {\n const logger = createLogger(log);\n try {\n if (!apiKey) {\n logger('warn', 'Amplitude API key not provided');\n return;\n }\n\n amplitudeInstance.init(apiKey, {\n defaultTracking: false,\n });\n } catch (error) {\n logger('error', 'Failed to initialize Amplitude', { error });\n }\n}\n\n/**\n * Initialize Clarity (fail silently)\n */\nexport async function initializeClarity(\n projectId?: string,\n log?: LoggerFn,\n): Promise<void> {\n const logger = createLogger(log);\n try {\n // Check if Clarity is already loaded by the client\n if (getClarity()) {\n logger('info', 'Clarity already initialized by client');\n return;\n }\n\n // If client provided project ID, use it\n const finalProjectId = projectId || getClientClarityProjectId();\n\n if (!finalProjectId) {\n logger('warn', 'Clarity project ID not provided');\n return;\n }\n\n // Initialize Clarity with project ID\n const Clarity = (await import('@microsoft/clarity')).default;\n Clarity.init(finalProjectId);\n } catch (error) {\n logger('error', 'Failed to initialize Clarity', { error });\n }\n}\n\n/**\n * Try to extract Clarity project ID from client's installation\n */\nfunction getClientClarityProjectId(): string | undefined {\n try {\n // Check if Clarity script tag exists\n const clarityScript = document.querySelector('script[src*=\"clarity.ms\"]');\n if (clarityScript) {\n const src = clarityScript.getAttribute('src');\n const match = src?.match(/\\/([a-z0-9]+)$/);\n return match?.[1];\n }\n\n // Check global clarity config: shape is clarity.q = [['init', projectId, ...]]\n const projectId = getClarity()?.q?.[0]?.[1];\n if (typeof projectId === 'string') {\n return projectId;\n }\n } catch {\n // Fail silently\n }\n\n return undefined;\n}\n\n/**\n * Track event with Amplitude (fail silently)\n */\nexport function trackAmplitudeEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n const logger = createLogger(log);\n try {\n amplitudeInstance.track(eventName, properties);\n } catch (error) {\n logger('error', 'Failed to track Amplitude event', { eventName, error });\n }\n}\n\n/**\n * Track event with Clarity (fail silently)\n */\nexport function trackClarityEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n const logger = createLogger(log);\n try {\n const clarity = getClarity();\n if (clarity) {\n clarity('event', eventName);\n\n // Set custom tags for properties\n if (properties) {\n Object.entries(properties).forEach(([key, value]) => {\n clarity('set', key, String(value));\n });\n }\n }\n } catch (error) {\n logger('error', 'Failed to track Clarity event', { eventName, error });\n }\n}\n\n/**\n * Builds a tracking event name from prefix, page and event segments.\n * The prefix is expected to already include a trailing separator (e.g. 'nuvempago_lending_').\n * Page is optional — when absent the segment is omitted from the name.\n *\n * @example\n * buildEventName('nuvempago_lending_', 'dashboard', 'charge', 'view')\n * // → 'nuvempago_lending_dashboard_charge_view'\n *\n * buildEventName('nuvempago_lending_', undefined, 'charge', 'view')\n * // → 'nuvempago_lending_charge_view'\n */\nexport function buildEventName(\n prefix: string,\n page: string | undefined,\n context: string,\n event: string,\n): string {\n // Prefix already contains its trailing separator — join only the remaining segments\n const segments = [page, context, event].filter(Boolean);\n return `${prefix}${segments.join('_')}`;\n}\n\n/**\n * Builds a TrackingProperties object from a loose record, dropping entries\n * whose value is undefined so callers don't need to guard optional fields.\n */\nexport function buildTrackingProperties(\n source: Record<string, TrackingValue | undefined>,\n): TrackingProperties {\n return Object.fromEntries(\n Object.entries(source).filter(\n (entry): entry is [string, TrackingValue] => entry[1] !== undefined,\n ),\n );\n}\n\n/**\n * Track event with both Amplitude and Clarity (fail silently)\n */\nexport function trackEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n trackAmplitudeEvent(eventName, properties, log);\n trackClarityEvent(eventName, properties, log);\n}\n\n/**\n * Clarity command API shape — kept module-local to avoid polluting the\n * consumer's global Window type with a conflicting declaration.\n * https://learn.microsoft.com/en-us/clarity/setup-and-installation/clarity-api\n */\ntype ClarityCommand =\n | 'init'\n | 'event'\n | 'set'\n | 'identify'\n | 'consent'\n | 'upgrade';\n\ntype ClarityFn = {\n (command: ClarityCommand, ...args: string[]): void;\n q?: string[][];\n};\n\n/** Access window.clarity without declaring a global augmentation */\nfunction getClarity(): ClarityFn | undefined {\n return (window as unknown as Record<string, unknown>).clarity as\n | ClarityFn\n | undefined;\n}\n","import React, { createContext, useContext, useEffect, useMemo } from 'react';\nimport { SWRConfig } from 'swr';\nimport type {\n LiveStateFetcher,\n AnalyticsConfig,\n TrackingProperties,\n} from '../types';\nimport { initializeAmplitude, initializeClarity } from '../utils/analytics';\nimport { createLogger, type LoggerFn } from '../utils/logger';\n\ninterface LiveStateContextValue {\n fetcher: LiveStateFetcher;\n analytics?: AnalyticsConfig;\n onEvent?: (eventName: string, properties?: TrackingProperties) => void;\n disabled?: boolean;\n log: LoggerFn;\n}\n\nconst LiveStateContext = createContext<LiveStateContextValue | null>(null);\n\nexport interface LiveStateProviderProps {\n children: React.ReactNode;\n fetcher: LiveStateFetcher;\n analytics?: AnalyticsConfig;\n onEvent?: (eventName: string, properties?: TrackingProperties) => void;\n onLog?: LoggerFn;\n /**\n * When true, disables all analytics SDKs (Amplitude and Clarity).\n * Useful in development and test environments to suppress SDK warnings\n * and avoid polluting analytics data without needing to mock anything.\n * The `onEvent` and `onLog` callbacks still fire when disabled.\n *\n * @example\n * <LiveStateProvider disabled={process.env.NODE_ENV !== 'production'} />\n */\n disabled?: boolean;\n}\n\n/**\n * LiveStateProvider\n *\n * Root-level provider that configures fetcher function and analytics.\n * Initializes Amplitude and Clarity on mount.\n *\n * The optional `onEvent` callback is fired on every tracking event before\n * sending to Amplitude/Clarity — useful for simulators and debugging tools.\n *\n * The optional `onLog` callback receives all internal lib logs — route them\n * to your APM (Datadog, Sentry, etc.) to avoid relying on console.*.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateProvider({\n children,\n fetcher,\n analytics,\n onEvent,\n onLog,\n disabled = false,\n}: LiveStateProviderProps) {\n const log = useMemo(() => createLogger(onLog), [onLog]);\n\n useEffect(() => {\n if (disabled) return;\n\n if (analytics?.amplitudeKey) {\n initializeAmplitude(analytics.amplitudeKey, log);\n }\n\n if (analytics?.clarityProjectId) {\n initializeClarity(analytics.clarityProjectId, log);\n }\n }, [analytics, disabled, log]);\n\n const contextValue = useMemo(\n () => ({ fetcher, analytics, onEvent, disabled, log }),\n [fetcher, analytics, onEvent, disabled, log],\n );\n\n return (\n <SWRConfig value={{ provider: () => new Map() }}>\n <LiveStateContext.Provider value={contextValue}>\n {children}\n </LiveStateContext.Provider>\n </SWRConfig>\n );\n}\n\n/**\n * useLiveStateContext\n *\n * Internal hook to access LiveStateContext\n */\nexport function useLiveStateContext() {\n const context = useContext(LiveStateContext);\n\n if (!context) {\n throw new Error('useLiveState must be used within LiveStateProvider');\n }\n\n return context;\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./LiveStateProvider-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./LiveStateProvider-vcADappI.cjs`);let t=require(`react`),n=require(`swr`);n=e.s(n,1);let r=require(`react/jsx-runtime`),i=require(`@nimbus-ds/components`),a=require(`@nimbus-ds/icons`);function o(){let{fetcher:t,log:r}=e.n(),{data:i,error:a,isLoading:o,mutate:s}=(0,n.default)(`live-state`,t,{revalidateOnFocus:!1,revalidateOnReconnect:!0,dedupingInterval:6e4,shouldRetryOnError:!1,onError:e=>{r(`error`,`Failed to fetch live state`,{error:e})}});return{liveState:i??null,isLoading:o,isError:a!=null,error:a??null,refresh:async()=>{try{await s()}catch(e){r(`error`,`Failed to refresh live state`,{error:e})}}}}function s(){let{onEvent:n,disabled:r,log:i}=e.n();return(0,t.useCallback)((t,a)=>{n?.(t,a),r||e.a(t,a,i)},[n,r,i])}var c=`@tiendanube/live-state:closable`,l=c;function u(){try{let e=localStorage.getItem(l);if(!e)return{};let t=JSON.parse(e);if(typeof t!=`object`||!t)return{};let n={};for(let[e,r]of Object.entries(t))if(!(typeof r!=`object`||!r)){n[e]={};for(let[t,i]of Object.entries(r))if(typeof i==`number`)n[e][t]={count:i,closedAt:0};else if(typeof i==`object`&&i){let r=i;n[e][t]={count:typeof r.count==`number`?r.count:0,closedAt:typeof r.closedAt==`number`?r.closedAt:0}}}return n}catch{return{}}}function d(e){try{localStorage.setItem(l,JSON.stringify(e))}catch{}}function f({context:e,id:n,maxCloseTimes:r=3,expiresIn:i}){let[a,o]=(0,t.useState)(null);return(0,t.useEffect)(()=>{if(!n){o(!0);return}let t=u(),a=t[e]?.[n];if(!a){o(!0);return}let{count:s,closedAt:c}=a;if(i!=null&&c>0&&Date.now()-c>i){let r={...t};r[e]&&(delete r[e][n],d(r)),o(!0);return}o(s<r)},[e,n,r,i]),{isVisible:a,close:(0,t.useCallback)(()=>{if(o(!1),!n)return;let t=u(),r=t[e]?.[n]?.count??0;d({...t,[e]:{...t[e]??{},[n]:{count:r+1,closedAt:Date.now()}}})},[e,n])}}function p(t,n){let r=e.o(n);try{switch(t.type){case`redirect`:window.location.href=t.url;break;case`whatsapp`:{let e=t.whatsappMessage?`${t.url}?text=${encodeURIComponent(t.whatsappMessage)}`:t.url;window.open(e,`_blank`);break}case`external`:window.open(t.url,`_blank`);break;default:r(`warn`,`Unknown CTA type: ${t.type}`,{cta:t})}}catch(e){r(`error`,`Failed to handle CTA click`,{cta:t,error:e})}}function m({data:n,trackingConfig:a,onClose:o,onCtaClick:c}){let l=s(),{log:u}=e.n(),d=n.type===`alert`?`danger`:`warning`,f=(0,t.useCallback)(()=>e.i({context:n.context,campaignId:n.campaignId,group:n.group,...n.metadata,...a.properties}),[n,a.properties]),m=(0,t.useCallback)(()=>{l(e.r(a.prefix,a.page,n.context,`click`),f()),c?c(n.cta):p(n.cta,u)},[n,a,f,l,c,u]),h=(0,t.useCallback)(()=>{l(e.r(a.prefix,a.page,n.context,`close`),f()),o?.()},[o,n,a,f,l]);return(0,t.useEffect)(()=>{l(e.r(a.prefix,a.page,n.context,`view`),f())},[]),(0,r.jsx)(i.Alert,{appearance:d,title:n.title,onRemove:o?h:void 0,children:(0,r.jsxs)(i.Box,{display:`flex`,alignItems:`flex-start`,gap:`3`,flexWrap:`wrap`,flexDirection:`column`,children:[(0,r.jsx)(i.Box,{flex:`1 1 auto`,minWidth:`220px`,children:(0,r.jsx)(i.Text,{children:n.message})}),(0,r.jsx)(i.Box,{display:`flex`,justifyContent:`flex-end`,marginTop:`2`,children:(0,r.jsx)(i.Button,{appearance:`primary`,onClick:m,children:n.cta.label})})]})})}var h={blue:{background:`#0050C3`,icon:`#FFFFFF`},white:{background:`#FFFFFF`,icon:`#0059D5`}};function g({data:n,trackingConfig:o,defaultVariant:c=`blue`,onClose:l,isMobile:u,onCtaClick:d}){let f=s(),{log:m}=e.n(),g=n.variant||c,_=h[g],[v,y]=(0,t.useState)(()=>window.innerWidth<750);(0,t.useEffect)(()=>{let e=()=>y(window.innerWidth<750);return window.addEventListener(`resize`,e),()=>window.removeEventListener(`resize`,e)},[]);let b=u??v,x=(0,t.useCallback)(()=>e.i({context:n.context,campaignId:n.campaignId,group:n.group,...n.metadata,...o.properties}),[n,o.properties]),S=(0,t.useCallback)(()=>{f(e.r(o.prefix,o.page,n.context,`click`),x()),d?d(n.cta):p(n.cta,m)},[n,o,x,f,d,m]),C=(0,t.useCallback)(()=>{f(e.r(o.prefix,o.page,n.context,`close`),x()),l?.()},[l,n,o,x,f]);(0,t.useEffect)(()=>{f(e.r(o.prefix,o.page,n.context,`view`),x())},[]);let w=(0,r.jsx)(i.Link,{as:`a`,textDecoration:`none`,onClick:S,"data-testid":`live-state-cta-link`,children:(0,r.jsx)(i.Text,{color:`primary-interactive`,fontSize:`base`,children:n.cta.label})});return(0,r.jsx)(i.Card,{children:(0,r.jsxs)(i.Box,{display:`flex`,flexDirection:`row`,justifyContent:`space-between`,children:[(0,r.jsxs)(i.Box,{display:`flex`,gap:`4`,alignItems:b?`flex-start`:`center`,paddingRight:`1-5`,children:[(0,r.jsx)(i.Box,{minWidth:`32px`,children:(0,r.jsx)(`div`,{style:{width:32,height:32,borderRadius:`35%`,borderColor:g===`white`?`#E7E7E7`:`transparent`,borderWidth:1,borderStyle:`solid`,background:_.background,display:`flex`,alignItems:`center`,justifyContent:`center`},children:(0,r.jsx)(a.MoneyIcon,{style:{color:_.icon}})})}),(0,r.jsxs)(i.Box,{display:`grid`,gap:`2`,children:[(0,r.jsxs)(i.Box,{children:[(0,r.jsx)(i.Text,{fontSize:`base`,color:`neutral-textHigh`,children:n.title}),(0,r.jsx)(i.Text,{fontSize:`base`,color:`neutral-textLow`,children:n.message})]}),b&&w]})]}),(!b||l)&&(0,r.jsxs)(i.Box,{display:`flex`,flexDirection:b?`column`:`row`,alignItems:b?`flex-end`:`center`,gap:`2`,children:[!b&&w,l&&(0,r.jsx)(`button`,{type:`button`,"data-testid":`live-state-close-button`,"aria-label":`Fechar notificação`,onClick:C,style:{background:`none`,border:`none`,padding:0,cursor:`pointer`,display:`flex`,alignItems:`center`,justifyContent:`center`},children:(0,r.jsx)(i.Icon,{source:(0,r.jsx)(a.CloseIcon,{width:`18px`,height:`18px`}),color:`neutral-interactive`})})]})]})})}function _({data:t,loading:n,trackingConfig:i,allowedContexts:a,defaultVariant:o,isMobile:s,onCtaClick:c}){let{log:l}=e.n(),{isVisible:u,close:d}=f({context:t?.context??``,id:t?.campaignId,maxCloseTimes:t?.metadata?.maxCloseTimes,expiresIn:t?.metadata?.expiresIn});if(n||!t)return null;if(!t.context||!t.type||!t.title||!t.message||!t.cta?.label||!t.cta?.url||!t.cta?.type)return l(`warn`,`Invalid payload, missing required fields`,{data:t}),null;if(a&&!a.includes(t.context)||u===null||!u)return null;let p=t.closable?d:void 0;return t.type===`alert`||t.type===`warning`?(0,r.jsx)(m,{data:t,onClose:p,trackingConfig:i,onCtaClick:c}):t.type===`info`?(0,r.jsx)(g,{data:t,onClose:p,trackingConfig:i,defaultVariant:o,isMobile:s,onCtaClick:c}):(l(`warn`,`Unknown type: ${t.type}`,{data:t}),null)}exports.LIVE_STATE_STORAGE_KEY=c,exports.LiveStateAlert=m,exports.LiveStateInfo=g,exports.LiveStateProvider=e.t,exports.LiveStateRenderer=_,exports.handleCtaClick=p,exports.trackEvent=e.a,exports.useLiveState=o,exports.useTrackEvent=s;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, i as t, n, o as r, r as i, t as a } from "./LiveStateProvider-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, t as a } from "./LiveStateProvider-DeRIGqmT.js";
|
|
2
2
|
import { useCallback as o, useEffect as s, useState as c } from "react";
|
|
3
3
|
import l from "swr";
|
|
4
4
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
@@ -38,18 +38,4 @@ export declare function buildTrackingProperties(source: Record<string, TrackingV
|
|
|
38
38
|
* Track event with both Amplitude and Clarity (fail silently)
|
|
39
39
|
*/
|
|
40
40
|
export declare function trackEvent(eventName: string, properties?: TrackingProperties, log?: LoggerFn): void;
|
|
41
|
-
/**
|
|
42
|
-
* Clarity command API shape as documented in the Clarity SDK
|
|
43
|
-
* https://learn.microsoft.com/en-us/clarity/setup-and-installation/clarity-api
|
|
44
|
-
*/
|
|
45
|
-
type ClarityCommand = 'init' | 'event' | 'set' | 'identify' | 'consent' | 'upgrade';
|
|
46
|
-
declare global {
|
|
47
|
-
interface Window {
|
|
48
|
-
clarity?: {
|
|
49
|
-
(command: ClarityCommand, ...args: string[]): void;
|
|
50
|
-
q?: string[][];
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
export {};
|
|
55
41
|
//# sourceMappingURL=analytics.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../../src/utils/analytics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAClE,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAQvD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,GAAG,IAAI,CAcxE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,CAAC,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,QAAQ,GACb,OAAO,CAAC,IAAI,CAAC,CAuBf;AA2BD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,kBAAkB,EAC/B,GAAG,CAAC,EAAE,QAAQ,GACb,IAAI,CAON;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,kBAAkB,EAC/B,GAAG,CAAC,EAAE,QAAQ,GACb,IAAI,
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../../src/utils/analytics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAClE,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAQvD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,GAAG,IAAI,CAcxE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,CAAC,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,QAAQ,GACb,OAAO,CAAC,IAAI,CAAC,CAuBf;AA2BD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,kBAAkB,EAC/B,GAAG,CAAC,EAAE,QAAQ,GACb,IAAI,CAON;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,kBAAkB,EAC/B,GAAG,CAAC,EAAE,QAAQ,GACb,IAAI,CAiBN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,MAAM,CAIR;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC,GAChD,kBAAkB,CAMpB;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,kBAAkB,EAC/B,GAAG,CAAC,EAAE,QAAQ,GACb,IAAI,CAGN"}
|
package/dist/testing.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./LiveStateProvider-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./LiveStateProvider-vcADappI.cjs`);let t=require(`react`);t=e.s(t,1);let n=require(`swr`),r=require(`react/jsx-runtime`);function i(e={}){return{context:`awareness`,type:`info`,title:`Mock notification`,message:`This is a mock notification for testing.`,cta:{label:`Learn more`,url:`/mock`,type:`redirect`},...e}}var a=t.default.createContext(null);function o({children:i,liveState:o=null,isLoading:s=!1,isError:c=!1,onEvent:l}){let u=t.default.useCallback(async()=>{if(s)return new Promise(()=>{});if(c)throw Error(`Mock fetch error`);return o},[o,s,c]),d=t.default.useMemo(()=>e.o(void 0),[]),f=t.default.useMemo(()=>({fetcher:u,onEvent:l,disabled:!0,log:d}),[u,l,d]);return(0,r.jsx)(n.SWRConfig,{value:{provider:()=>new Map,dedupingInterval:0},children:(0,r.jsx)(a.Provider,{value:f,children:i})})}function s(e={}){return{liveState:e.liveState??null,isLoading:e.isLoading??!1,isError:e.isError??!1,error:e.error??null,refresh:e.refresh??(()=>Promise.resolve())}}exports.MockLiveStateProvider=o,exports.createMockLiveState=i,exports.mockUseLiveState=s,exports.useLiveStateContext=e.n;
|
|
2
2
|
//# sourceMappingURL=testing.cjs.map
|
package/dist/testing.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiendanube/live-state",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.14",
|
|
4
4
|
"description": "Generic React library for rendering live state notifications",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
"types": "./dist/testing.d.ts"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
+
"typesVersions": {
|
|
21
|
+
"*": {
|
|
22
|
+
"testing": ["./dist/testing.d.ts"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
20
25
|
"files": [
|
|
21
26
|
"dist",
|
|
22
27
|
"README.md",
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`react`);c=s(c,1);let l=require(`swr`),u=require(`@amplitude/analytics-browser`);u=s(u,1);let d=require(`react/jsx-runtime`);var f=(e,t,n)=>{let r=`[LiveState]`;e===`error`?console.error(r,t,...n?[n]:[]):console.warn(r,t,...n?[n]:[])};function p(e){return(t,n,r)=>{e?e(t,n,r):f(t,n,r)}}var m=u.createInstance();function h(e,t){let n=p(t);try{if(!e){n(`warn`,`Amplitude API key not provided`);return}m.init(e,{defaultTracking:!1})}catch(e){n(`error`,`Failed to initialize Amplitude`,{error:e})}}async function g(e,t){let n=p(t);try{if(window.clarity){n(`info`,`Clarity already initialized by client`);return}let t=e||_();if(!t){n(`warn`,`Clarity project ID not provided`);return}(await import(`@microsoft/clarity`)).default.init(t)}catch(e){n(`error`,`Failed to initialize Clarity`,{error:e})}}function _(){try{let e=document.querySelector(`script[src*="clarity.ms"]`);if(e)return e.getAttribute(`src`)?.match(/\/([a-z0-9]+)$/)?.[1];let t=window.clarity?.q?.[0]?.[1];if(typeof t==`string`)return t}catch{}}function v(e,t,n){let r=p(n);try{m.track(e,t)}catch(t){r(`error`,`Failed to track Amplitude event`,{eventName:e,error:t})}}function y(e,t,n){let r=p(n);try{window.clarity&&(window.clarity(`event`,e),t&&Object.entries(t).forEach(([e,t])=>{window.clarity?.(`set`,e,String(t))}))}catch(t){r(`error`,`Failed to track Clarity event`,{eventName:e,error:t})}}function b(e,t,n,r){return`${e}${[t,n,r].filter(Boolean).join(`_`)}`}function x(e){return Object.fromEntries(Object.entries(e).filter(e=>e[1]!==void 0))}function S(e,t,n){v(e,t,n),y(e,t,n)}var C=(0,c.createContext)(null);function w({children:e,fetcher:t,analytics:n,onEvent:r,onLog:i,disabled:a=!1}){let o=(0,c.useMemo)(()=>p(i),[i]);(0,c.useEffect)(()=>{a||(n?.amplitudeKey&&h(n.amplitudeKey,o),n?.clarityProjectId&&g(n.clarityProjectId,o))},[n,a,o]);let s=(0,c.useMemo)(()=>({fetcher:t,analytics:n,onEvent:r,disabled:a,log:o}),[t,n,r,a,o]);return(0,d.jsx)(l.SWRConfig,{value:{provider:()=>new Map},children:(0,d.jsx)(C.Provider,{value:s,children:e})})}function T(){let e=(0,c.useContext)(C);if(!e)throw Error(`useLiveState must be used within LiveStateProvider`);return e}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return w}});
|
|
2
|
-
//# sourceMappingURL=LiveStateProvider-BAkFYuZ2.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LiveStateProvider-BAkFYuZ2.cjs","names":[],"sources":["../src/utils/logger.ts","../src/utils/analytics.ts","../src/providers/LiveStateProvider.tsx"],"sourcesContent":["import type { LogLevel } from '../types';\n\n/**\n * Logger function type — matches the signature of LiveStateProviderProps.onLog.\n */\nexport type LoggerFn = (\n level: LogLevel,\n message: string,\n context?: Record<string, unknown>,\n) => void;\n\n/**\n * Default logger — falls back to console.* when no onLog is provided.\n * Uses console.warn for 'info' and 'warn', console.error for 'error'.\n */\nconst defaultLogger: LoggerFn = (level, message, context) => {\n const prefix = '[LiveState]';\n if (level === 'error') {\n console.error(prefix, message, ...(context ? [context] : []));\n } else {\n console.warn(prefix, message, ...(context ? [context] : []));\n }\n};\n\n/**\n * Creates a logger bound to an optional consumer-provided onLog callback.\n * When onLog is provided it is called exclusively — the default console.*\n * fallback does NOT run, giving the consumer full control over output.\n *\n * @example\n * const log = createLogger(onLog);\n * log('warn', 'Amplitude API key not provided');\n * log('error', 'Failed to fetch', { url: '/v1/live-state', status: 500 });\n */\nexport function createLogger(onLog?: LoggerFn): LoggerFn {\n return (level, message, context) => {\n if (onLog) {\n onLog(level, message, context);\n } else {\n defaultLogger(level, message, context);\n }\n };\n}\n","import * as amplitude from '@amplitude/analytics-browser';\nimport type { TrackingProperties, TrackingValue } from '../types';\nimport { createLogger, type LoggerFn } from './logger';\n\n/**\n * Isolated Amplitude instance for the lib — avoids conflicts with the\n * consumer app's own Amplitude instance when both coexist on the same page.\n */\nconst amplitudeInstance = amplitude.createInstance();\n\n/**\n * Initialize Amplitude (fail silently)\n */\nexport function initializeAmplitude(apiKey: string, log?: LoggerFn): void {\n const logger = createLogger(log);\n try {\n if (!apiKey) {\n logger('warn', 'Amplitude API key not provided');\n return;\n }\n\n amplitudeInstance.init(apiKey, {\n defaultTracking: false,\n });\n } catch (error) {\n logger('error', 'Failed to initialize Amplitude', { error });\n }\n}\n\n/**\n * Initialize Clarity (fail silently)\n */\nexport async function initializeClarity(\n projectId?: string,\n log?: LoggerFn,\n): Promise<void> {\n const logger = createLogger(log);\n try {\n // Check if Clarity is already loaded by the client\n if (window.clarity) {\n logger('info', 'Clarity already initialized by client');\n return;\n }\n\n // If client provided project ID, use it\n const finalProjectId = projectId || getClientClarityProjectId();\n\n if (!finalProjectId) {\n logger('warn', 'Clarity project ID not provided');\n return;\n }\n\n // Initialize Clarity with project ID\n const Clarity = (await import('@microsoft/clarity')).default;\n Clarity.init(finalProjectId);\n } catch (error) {\n logger('error', 'Failed to initialize Clarity', { error });\n }\n}\n\n/**\n * Try to extract Clarity project ID from client's installation\n */\nfunction getClientClarityProjectId(): string | undefined {\n try {\n // Check if Clarity script tag exists\n const clarityScript = document.querySelector('script[src*=\"clarity.ms\"]');\n if (clarityScript) {\n const src = clarityScript.getAttribute('src');\n const match = src?.match(/\\/([a-z0-9]+)$/);\n return match?.[1];\n }\n\n // Check global clarity config: shape is clarity.q = [['init', projectId, ...]]\n const projectId = window.clarity?.q?.[0]?.[1];\n if (typeof projectId === 'string') {\n return projectId;\n }\n } catch {\n // Fail silently\n }\n\n return undefined;\n}\n\n/**\n * Track event with Amplitude (fail silently)\n */\nexport function trackAmplitudeEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n const logger = createLogger(log);\n try {\n amplitudeInstance.track(eventName, properties);\n } catch (error) {\n logger('error', 'Failed to track Amplitude event', { eventName, error });\n }\n}\n\n/**\n * Track event with Clarity (fail silently)\n */\nexport function trackClarityEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n const logger = createLogger(log);\n try {\n if (window.clarity) {\n window.clarity('event', eventName);\n\n // Set custom tags for properties\n if (properties) {\n Object.entries(properties).forEach(([key, value]) => {\n window.clarity?.('set', key, String(value));\n });\n }\n }\n } catch (error) {\n logger('error', 'Failed to track Clarity event', { eventName, error });\n }\n}\n\n/**\n * Builds a tracking event name from prefix, page and event segments.\n * The prefix is expected to already include a trailing separator (e.g. 'nuvempago_lending_').\n * Page is optional — when absent the segment is omitted from the name.\n *\n * @example\n * buildEventName('nuvempago_lending_', 'dashboard', 'charge', 'view')\n * // → 'nuvempago_lending_dashboard_charge_view'\n *\n * buildEventName('nuvempago_lending_', undefined, 'charge', 'view')\n * // → 'nuvempago_lending_charge_view'\n */\nexport function buildEventName(\n prefix: string,\n page: string | undefined,\n context: string,\n event: string,\n): string {\n // Prefix already contains its trailing separator — join only the remaining segments\n const segments = [page, context, event].filter(Boolean);\n return `${prefix}${segments.join('_')}`;\n}\n\n/**\n * Builds a TrackingProperties object from a loose record, dropping entries\n * whose value is undefined so callers don't need to guard optional fields.\n */\nexport function buildTrackingProperties(\n source: Record<string, TrackingValue | undefined>,\n): TrackingProperties {\n return Object.fromEntries(\n Object.entries(source).filter(\n (entry): entry is [string, TrackingValue] => entry[1] !== undefined,\n ),\n );\n}\n\n/**\n * Track event with both Amplitude and Clarity (fail silently)\n */\nexport function trackEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n trackAmplitudeEvent(eventName, properties, log);\n trackClarityEvent(eventName, properties, log);\n}\n\n/**\n * Clarity command API shape as documented in the Clarity SDK\n * https://learn.microsoft.com/en-us/clarity/setup-and-installation/clarity-api\n */\ntype ClarityCommand =\n | 'init'\n | 'event'\n | 'set'\n | 'identify'\n | 'consent'\n | 'upgrade';\n\ndeclare global {\n interface Window {\n clarity?: {\n (command: ClarityCommand, ...args: string[]): void;\n q?: string[][];\n };\n }\n}\n","import React, { createContext, useContext, useEffect, useMemo } from 'react';\nimport { SWRConfig } from 'swr';\nimport type {\n LiveStateFetcher,\n AnalyticsConfig,\n TrackingProperties,\n} from '../types';\nimport { initializeAmplitude, initializeClarity } from '../utils/analytics';\nimport { createLogger, type LoggerFn } from '../utils/logger';\n\ninterface LiveStateContextValue {\n fetcher: LiveStateFetcher;\n analytics?: AnalyticsConfig;\n onEvent?: (eventName: string, properties?: TrackingProperties) => void;\n disabled?: boolean;\n log: LoggerFn;\n}\n\nconst LiveStateContext = createContext<LiveStateContextValue | null>(null);\n\nexport interface LiveStateProviderProps {\n children: React.ReactNode;\n fetcher: LiveStateFetcher;\n analytics?: AnalyticsConfig;\n onEvent?: (eventName: string, properties?: TrackingProperties) => void;\n onLog?: LoggerFn;\n /**\n * When true, disables all analytics SDKs (Amplitude and Clarity).\n * Useful in development and test environments to suppress SDK warnings\n * and avoid polluting analytics data without needing to mock anything.\n * The `onEvent` and `onLog` callbacks still fire when disabled.\n *\n * @example\n * <LiveStateProvider disabled={process.env.NODE_ENV !== 'production'} />\n */\n disabled?: boolean;\n}\n\n/**\n * LiveStateProvider\n *\n * Root-level provider that configures fetcher function and analytics.\n * Initializes Amplitude and Clarity on mount.\n *\n * The optional `onEvent` callback is fired on every tracking event before\n * sending to Amplitude/Clarity — useful for simulators and debugging tools.\n *\n * The optional `onLog` callback receives all internal lib logs — route them\n * to your APM (Datadog, Sentry, etc.) to avoid relying on console.*.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateProvider({\n children,\n fetcher,\n analytics,\n onEvent,\n onLog,\n disabled = false,\n}: LiveStateProviderProps) {\n const log = useMemo(() => createLogger(onLog), [onLog]);\n\n useEffect(() => {\n if (disabled) return;\n\n if (analytics?.amplitudeKey) {\n initializeAmplitude(analytics.amplitudeKey, log);\n }\n\n if (analytics?.clarityProjectId) {\n initializeClarity(analytics.clarityProjectId, log);\n }\n }, [analytics, disabled, log]);\n\n const contextValue = useMemo(\n () => ({ fetcher, analytics, onEvent, disabled, log }),\n [fetcher, analytics, onEvent, disabled, log],\n );\n\n return (\n <SWRConfig value={{ provider: () => new Map() }}>\n <LiveStateContext.Provider value={contextValue}>\n {children}\n </LiveStateContext.Provider>\n </SWRConfig>\n );\n}\n\n/**\n * useLiveStateContext\n *\n * Internal hook to access LiveStateContext\n */\nexport function useLiveStateContext() {\n const context = useContext(LiveStateContext);\n\n if (!context) {\n throw new Error('useLiveState must be used within LiveStateProvider');\n }\n\n return context;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LiveStateProvider-BH31kAuo.js","names":[],"sources":["../src/utils/logger.ts","../src/utils/analytics.ts","../src/providers/LiveStateProvider.tsx"],"sourcesContent":["import type { LogLevel } from '../types';\n\n/**\n * Logger function type — matches the signature of LiveStateProviderProps.onLog.\n */\nexport type LoggerFn = (\n level: LogLevel,\n message: string,\n context?: Record<string, unknown>,\n) => void;\n\n/**\n * Default logger — falls back to console.* when no onLog is provided.\n * Uses console.warn for 'info' and 'warn', console.error for 'error'.\n */\nconst defaultLogger: LoggerFn = (level, message, context) => {\n const prefix = '[LiveState]';\n if (level === 'error') {\n console.error(prefix, message, ...(context ? [context] : []));\n } else {\n console.warn(prefix, message, ...(context ? [context] : []));\n }\n};\n\n/**\n * Creates a logger bound to an optional consumer-provided onLog callback.\n * When onLog is provided it is called exclusively — the default console.*\n * fallback does NOT run, giving the consumer full control over output.\n *\n * @example\n * const log = createLogger(onLog);\n * log('warn', 'Amplitude API key not provided');\n * log('error', 'Failed to fetch', { url: '/v1/live-state', status: 500 });\n */\nexport function createLogger(onLog?: LoggerFn): LoggerFn {\n return (level, message, context) => {\n if (onLog) {\n onLog(level, message, context);\n } else {\n defaultLogger(level, message, context);\n }\n };\n}\n","import * as amplitude from '@amplitude/analytics-browser';\nimport type { TrackingProperties, TrackingValue } from '../types';\nimport { createLogger, type LoggerFn } from './logger';\n\n/**\n * Isolated Amplitude instance for the lib — avoids conflicts with the\n * consumer app's own Amplitude instance when both coexist on the same page.\n */\nconst amplitudeInstance = amplitude.createInstance();\n\n/**\n * Initialize Amplitude (fail silently)\n */\nexport function initializeAmplitude(apiKey: string, log?: LoggerFn): void {\n const logger = createLogger(log);\n try {\n if (!apiKey) {\n logger('warn', 'Amplitude API key not provided');\n return;\n }\n\n amplitudeInstance.init(apiKey, {\n defaultTracking: false,\n });\n } catch (error) {\n logger('error', 'Failed to initialize Amplitude', { error });\n }\n}\n\n/**\n * Initialize Clarity (fail silently)\n */\nexport async function initializeClarity(\n projectId?: string,\n log?: LoggerFn,\n): Promise<void> {\n const logger = createLogger(log);\n try {\n // Check if Clarity is already loaded by the client\n if (window.clarity) {\n logger('info', 'Clarity already initialized by client');\n return;\n }\n\n // If client provided project ID, use it\n const finalProjectId = projectId || getClientClarityProjectId();\n\n if (!finalProjectId) {\n logger('warn', 'Clarity project ID not provided');\n return;\n }\n\n // Initialize Clarity with project ID\n const Clarity = (await import('@microsoft/clarity')).default;\n Clarity.init(finalProjectId);\n } catch (error) {\n logger('error', 'Failed to initialize Clarity', { error });\n }\n}\n\n/**\n * Try to extract Clarity project ID from client's installation\n */\nfunction getClientClarityProjectId(): string | undefined {\n try {\n // Check if Clarity script tag exists\n const clarityScript = document.querySelector('script[src*=\"clarity.ms\"]');\n if (clarityScript) {\n const src = clarityScript.getAttribute('src');\n const match = src?.match(/\\/([a-z0-9]+)$/);\n return match?.[1];\n }\n\n // Check global clarity config: shape is clarity.q = [['init', projectId, ...]]\n const projectId = window.clarity?.q?.[0]?.[1];\n if (typeof projectId === 'string') {\n return projectId;\n }\n } catch {\n // Fail silently\n }\n\n return undefined;\n}\n\n/**\n * Track event with Amplitude (fail silently)\n */\nexport function trackAmplitudeEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n const logger = createLogger(log);\n try {\n amplitudeInstance.track(eventName, properties);\n } catch (error) {\n logger('error', 'Failed to track Amplitude event', { eventName, error });\n }\n}\n\n/**\n * Track event with Clarity (fail silently)\n */\nexport function trackClarityEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n const logger = createLogger(log);\n try {\n if (window.clarity) {\n window.clarity('event', eventName);\n\n // Set custom tags for properties\n if (properties) {\n Object.entries(properties).forEach(([key, value]) => {\n window.clarity?.('set', key, String(value));\n });\n }\n }\n } catch (error) {\n logger('error', 'Failed to track Clarity event', { eventName, error });\n }\n}\n\n/**\n * Builds a tracking event name from prefix, page and event segments.\n * The prefix is expected to already include a trailing separator (e.g. 'nuvempago_lending_').\n * Page is optional — when absent the segment is omitted from the name.\n *\n * @example\n * buildEventName('nuvempago_lending_', 'dashboard', 'charge', 'view')\n * // → 'nuvempago_lending_dashboard_charge_view'\n *\n * buildEventName('nuvempago_lending_', undefined, 'charge', 'view')\n * // → 'nuvempago_lending_charge_view'\n */\nexport function buildEventName(\n prefix: string,\n page: string | undefined,\n context: string,\n event: string,\n): string {\n // Prefix already contains its trailing separator — join only the remaining segments\n const segments = [page, context, event].filter(Boolean);\n return `${prefix}${segments.join('_')}`;\n}\n\n/**\n * Builds a TrackingProperties object from a loose record, dropping entries\n * whose value is undefined so callers don't need to guard optional fields.\n */\nexport function buildTrackingProperties(\n source: Record<string, TrackingValue | undefined>,\n): TrackingProperties {\n return Object.fromEntries(\n Object.entries(source).filter(\n (entry): entry is [string, TrackingValue] => entry[1] !== undefined,\n ),\n );\n}\n\n/**\n * Track event with both Amplitude and Clarity (fail silently)\n */\nexport function trackEvent(\n eventName: string,\n properties?: TrackingProperties,\n log?: LoggerFn,\n): void {\n trackAmplitudeEvent(eventName, properties, log);\n trackClarityEvent(eventName, properties, log);\n}\n\n/**\n * Clarity command API shape as documented in the Clarity SDK\n * https://learn.microsoft.com/en-us/clarity/setup-and-installation/clarity-api\n */\ntype ClarityCommand =\n | 'init'\n | 'event'\n | 'set'\n | 'identify'\n | 'consent'\n | 'upgrade';\n\ndeclare global {\n interface Window {\n clarity?: {\n (command: ClarityCommand, ...args: string[]): void;\n q?: string[][];\n };\n }\n}\n","import React, { createContext, useContext, useEffect, useMemo } from 'react';\nimport { SWRConfig } from 'swr';\nimport type {\n LiveStateFetcher,\n AnalyticsConfig,\n TrackingProperties,\n} from '../types';\nimport { initializeAmplitude, initializeClarity } from '../utils/analytics';\nimport { createLogger, type LoggerFn } from '../utils/logger';\n\ninterface LiveStateContextValue {\n fetcher: LiveStateFetcher;\n analytics?: AnalyticsConfig;\n onEvent?: (eventName: string, properties?: TrackingProperties) => void;\n disabled?: boolean;\n log: LoggerFn;\n}\n\nconst LiveStateContext = createContext<LiveStateContextValue | null>(null);\n\nexport interface LiveStateProviderProps {\n children: React.ReactNode;\n fetcher: LiveStateFetcher;\n analytics?: AnalyticsConfig;\n onEvent?: (eventName: string, properties?: TrackingProperties) => void;\n onLog?: LoggerFn;\n /**\n * When true, disables all analytics SDKs (Amplitude and Clarity).\n * Useful in development and test environments to suppress SDK warnings\n * and avoid polluting analytics data without needing to mock anything.\n * The `onEvent` and `onLog` callbacks still fire when disabled.\n *\n * @example\n * <LiveStateProvider disabled={process.env.NODE_ENV !== 'production'} />\n */\n disabled?: boolean;\n}\n\n/**\n * LiveStateProvider\n *\n * Root-level provider that configures fetcher function and analytics.\n * Initializes Amplitude and Clarity on mount.\n *\n * The optional `onEvent` callback is fired on every tracking event before\n * sending to Amplitude/Clarity — useful for simulators and debugging tools.\n *\n * The optional `onLog` callback receives all internal lib logs — route them\n * to your APM (Datadog, Sentry, etc.) to avoid relying on console.*.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateProvider({\n children,\n fetcher,\n analytics,\n onEvent,\n onLog,\n disabled = false,\n}: LiveStateProviderProps) {\n const log = useMemo(() => createLogger(onLog), [onLog]);\n\n useEffect(() => {\n if (disabled) return;\n\n if (analytics?.amplitudeKey) {\n initializeAmplitude(analytics.amplitudeKey, log);\n }\n\n if (analytics?.clarityProjectId) {\n initializeClarity(analytics.clarityProjectId, log);\n }\n }, [analytics, disabled, log]);\n\n const contextValue = useMemo(\n () => ({ fetcher, analytics, onEvent, disabled, log }),\n [fetcher, analytics, onEvent, disabled, log],\n );\n\n return (\n <SWRConfig value={{ provider: () => new Map() }}>\n <LiveStateContext.Provider value={contextValue}>\n {children}\n </LiveStateContext.Provider>\n </SWRConfig>\n );\n}\n\n/**\n * useLiveStateContext\n *\n * Internal hook to access LiveStateContext\n */\nexport function useLiveStateContext() {\n const context = useContext(LiveStateContext);\n\n if (!context) {\n throw new Error('useLiveState must be used within LiveStateProvider');\n }\n\n return context;\n}\n"],"mappings":";;;;;AAeA,IAAM,KAA2B,GAAO,GAAS,MAAY;CAC3D,IAAM,IAAS;AACf,CAAI,MAAU,UACZ,QAAQ,MAAM,GAAQ,GAAS,GAAI,IAAU,CAAC,EAAQ,GAAG,EAAE,CAAE,GAE7D,QAAQ,KAAK,GAAQ,GAAS,GAAI,IAAU,CAAC,EAAQ,GAAG,EAAE,CAAE;;AAchE,SAAgB,EAAa,GAA4B;AACvD,SAAQ,GAAO,GAAS,MAAY;AAClC,EAAI,IACF,EAAM,GAAO,GAAS,EAAQ,GAE9B,EAAc,GAAO,GAAS,EAAQ;;;;;AC/B5C,IAAM,IAAoB,EAAU,gBAAgB;AAKpD,SAAgB,EAAoB,GAAgB,GAAsB;CACxE,IAAM,IAAS,EAAa,EAAI;AAChC,KAAI;AACF,MAAI,CAAC,GAAQ;AACX,KAAO,QAAQ,iCAAiC;AAChD;;AAGF,IAAkB,KAAK,GAAQ,EAC7B,iBAAiB,IAClB,CAAC;UACK,GAAO;AACd,IAAO,SAAS,kCAAkC,EAAE,UAAO,CAAC;;;AAOhE,eAAsB,EACpB,GACA,GACe;CACf,IAAM,IAAS,EAAa,EAAI;AAChC,KAAI;AAEF,MAAI,OAAO,SAAS;AAClB,KAAO,QAAQ,wCAAwC;AACvD;;EAIF,IAAM,IAAiB,KAAa,GAA2B;AAE/D,MAAI,CAAC,GAAgB;AACnB,KAAO,QAAQ,kCAAkC;AACjD;;AAKF,GADiB,MAAM,OAAO,uBAAuB,QAC7C,KAAK,EAAe;UACrB,GAAO;AACd,IAAO,SAAS,gCAAgC,EAAE,UAAO,CAAC;;;AAO9D,SAAS,IAAgD;AACvD,KAAI;EAEF,IAAM,IAAgB,SAAS,cAAc,8BAA4B;AACzE,MAAI,EAGF,QAFY,EAAc,aAAa,MAAM,EAC1B,MAAM,iBAAiB,GAC3B;EAIjB,IAAM,IAAY,OAAO,SAAS,IAAI,KAAK;AAC3C,MAAI,OAAO,KAAc,SACvB,QAAO;SAEH;;AAUV,SAAgB,EACd,GACA,GACA,GACM;CACN,IAAM,IAAS,EAAa,EAAI;AAChC,KAAI;AACF,IAAkB,MAAM,GAAW,EAAW;UACvC,GAAO;AACd,IAAO,SAAS,mCAAmC;GAAE;GAAW;GAAO,CAAC;;;AAO5E,SAAgB,EACd,GACA,GACA,GACM;CACN,IAAM,IAAS,EAAa,EAAI;AAChC,KAAI;AACF,EAAI,OAAO,YACT,OAAO,QAAQ,SAAS,EAAU,EAG9B,KACF,OAAO,QAAQ,EAAW,CAAC,SAAS,CAAC,GAAK,OAAW;AACnD,UAAO,UAAU,OAAO,GAAK,OAAO,EAAM,CAAC;IAC3C;UAGC,GAAO;AACd,IAAO,SAAS,iCAAiC;GAAE;GAAW;GAAO,CAAC;;;AAgB1E,SAAgB,EACd,GACA,GACA,GACA,GACQ;AAGR,QAAO,GAAG,IADO;EAAC;EAAM;EAAS;EAAM,CAAC,OAAO,QAAQ,CAC3B,KAAK,IAAI;;AAOvC,SAAgB,EACd,GACoB;AACpB,QAAO,OAAO,YACZ,OAAO,QAAQ,EAAO,CAAC,QACpB,MAA4C,EAAM,OAAO,KAAA,EAC3D,CACF;;AAMH,SAAgB,EACd,GACA,GACA,GACM;AAEN,CADA,EAAoB,GAAW,GAAY,EAAI,EAC/C,EAAkB,GAAW,GAAY,EAAI;;;;AC1J/C,IAAM,IAAmB,EAA4C,KAAK;AAkC1E,SAAgB,EAAkB,EAChC,aACA,YACA,cACA,YACA,UACA,cAAW,MACc;CACzB,IAAM,IAAM,QAAc,EAAa,EAAM,EAAE,CAAC,EAAM,CAAC;AAEvD,SAAgB;AACV,QAEA,GAAW,gBACb,EAAoB,EAAU,cAAc,EAAI,EAG9C,GAAW,oBACb,EAAkB,EAAU,kBAAkB,EAAI;IAEnD;EAAC;EAAW;EAAU;EAAI,CAAC;CAE9B,IAAM,IAAe,SACZ;EAAE;EAAS;EAAW;EAAS;EAAU;EAAK,GACrD;EAAC;EAAS;EAAW;EAAS;EAAU;EAAI,CAC7C;AAED,QACE,kBAAC,GAAD;EAAW,OAAO,EAAE,gCAAgB,IAAI,KAAK,EAAE;YAC7C,kBAAC,EAAiB,UAAlB;GAA2B,OAAO;GAC/B;GACyB,CAAA;EAClB,CAAA;;AAShB,SAAgB,IAAsB;CACpC,IAAM,IAAU,EAAW,EAAiB;AAE5C,KAAI,CAAC,EACH,OAAU,MAAM,qDAAqD;AAGvE,QAAO"}
|