@tiendanube/live-state 1.0.0-beta.14 → 1.0.0-beta.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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`),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,onNavigate:a,mockData:o,disabled:s=!1}){let u=(0,c.useMemo)(()=>p(i),[i]);(0,c.useEffect)(()=>{s||(n?.amplitudeKey&&h(n.amplitudeKey,u),n?.clarityProjectId&&g(n.clarityProjectId,u))},[n,s,u]);let f=(0,c.useMemo)(()=>({fetcher:t,analytics:n,onEvent:r,onNavigate:a,mockData:o,disabled:s,log:u}),[t,n,r,a,o,s,u]);return(0,d.jsx)(l.SWRConfig,{value:{provider:()=>new Map},children:(0,d.jsx)(w.Provider,{value:f,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-ByK7lK2v.cjs.map
@@ -1 +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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"}
1
+ {"version":3,"file":"LiveStateProvider-ByK7lK2v.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 { createContext, useContext, useEffect, useMemo } from 'react';\nimport { SWRConfig } from 'swr';\nimport type {\n LiveStateFetcher,\n AnalyticsConfig,\n TrackingProperties,\n LiveStateResponse,\n CtaConfig,\n LiveStateProviderProps,\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 onNavigate?: (cta: CtaConfig) => void;\n mockData?: LiveStateResponse | null;\n disabled?: boolean;\n log: LoggerFn;\n}\n\nconst LiveStateContext = createContext<LiveStateContextValue | null>(null);\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 onNavigate,\n mockData,\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, onNavigate, mockData, disabled, log }),\n [fetcher, analytics, onEvent, onNavigate, mockData, 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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"}
@@ -96,32 +96,36 @@ function v() {
96
96
  //#endregion
97
97
  //#region src/providers/LiveStateProvider.tsx
98
98
  var y = e(null);
99
- function b({ children: e, fetcher: t, analytics: a, onEvent: s, onLog: l, disabled: f = !1 }) {
100
- let p = r(() => c(l), [l]);
99
+ function b({ children: e, fetcher: t, analytics: a, onEvent: s, onLog: l, onNavigate: f, mockData: p, disabled: m = !1 }) {
100
+ let h = r(() => c(l), [l]);
101
101
  n(() => {
102
- f || (a?.amplitudeKey && u(a.amplitudeKey, p), a?.clarityProjectId && d(a.clarityProjectId, p));
102
+ m || (a?.amplitudeKey && u(a.amplitudeKey, h), a?.clarityProjectId && d(a.clarityProjectId, h));
103
103
  }, [
104
104
  a,
105
- f,
106
- p
105
+ m,
106
+ h
107
107
  ]);
108
- let m = r(() => ({
108
+ let g = r(() => ({
109
109
  fetcher: t,
110
110
  analytics: a,
111
111
  onEvent: s,
112
- disabled: f,
113
- log: p
112
+ onNavigate: f,
113
+ mockData: p,
114
+ disabled: m,
115
+ log: h
114
116
  }), [
115
117
  t,
116
118
  a,
117
119
  s,
118
120
  f,
119
- p
121
+ p,
122
+ m,
123
+ h
120
124
  ]);
121
125
  return /* @__PURE__ */ o(i, {
122
126
  value: { provider: () => /* @__PURE__ */ new Map() },
123
127
  children: /* @__PURE__ */ o(y.Provider, {
124
- value: m,
128
+ value: g,
125
129
  children: e
126
130
  })
127
131
  });
@@ -134,4 +138,4 @@ function x() {
134
138
  //#endregion
135
139
  export { _ as a, g as i, x as n, c as o, h as r, b as t };
136
140
 
137
- //# sourceMappingURL=LiveStateProvider-DeRIGqmT.js.map
141
+ //# sourceMappingURL=LiveStateProvider-V3rVH59O.js.map
@@ -1 +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":"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"}
1
+ {"version":3,"file":"LiveStateProvider-V3rVH59O.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 { createContext, useContext, useEffect, useMemo } from 'react';\nimport { SWRConfig } from 'swr';\nimport type {\n LiveStateFetcher,\n AnalyticsConfig,\n TrackingProperties,\n LiveStateResponse,\n CtaConfig,\n LiveStateProviderProps,\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 onNavigate?: (cta: CtaConfig) => void;\n mockData?: LiveStateResponse | null;\n disabled?: boolean;\n log: LoggerFn;\n}\n\nconst LiveStateContext = createContext<LiveStateContextValue | null>(null);\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 onNavigate,\n mockData,\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, onNavigate, mockData, disabled, log }),\n [fetcher, analytics, onEvent, onNavigate, mockData, 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-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;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./LiveStateProvider-ByK7lK2v.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,mockData:r,log:i}=e.n(),a=r!==void 0,{data:o,error:s,isLoading:c,mutate:l}=(0,n.default)(a?null:`live-state`,t,{revalidateOnFocus:!1,revalidateOnReconnect:!0,dedupingInterval:6e4,shouldRetryOnError:!1,onError:e=>{i(`error`,`Failed to fetch live state`,{error:e})}}),u=async()=>{if(!a)try{await l()}catch(e){i(`error`,`Failed to refresh live state`,{error:e})}};return a?{liveState:r,isLoading:!1,isError:!1,error:null,refresh:u}:{liveState:o??null,isLoading:c,isError:s!=null,error:s??null,refresh:u}}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})}}var m=new Set([`strong`,`em`,`b`,`i`,`br`,`span`]),h=/<(script|style|iframe|object|embed|form|input|button|textarea|select|link|meta|head|body|html|svg|math)[\s\S]*?<\/\1\s*>|<(script|style|iframe|object|embed|form|input|button|textarea|select|link|meta|head|body|html|svg|math)[^>]*\/?>/gi;function g(e){return e.replace(h,``).replace(/<([a-zA-Z][a-zA-Z0-9]*)\s[^>]*>/g,`<$1>`).replace(/<\/?([a-zA-Z][a-zA-Z0-9]*)[^>]*>/g,(e,t)=>m.has(t.toLowerCase())?e:``)}function _(e){return/<[a-zA-Z][^>]*>/.test(e)}function v({content:e,...t}){if(_(e)){let n=g(e);return(0,r.jsx)(i.Text,{...t,children:(0,r.jsx)(`span`,{dangerouslySetInnerHTML:{__html:n}})})}return(0,r.jsx)(i.Text,{...t,children:e})}function y({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)(v,{content: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 b={blue:{background:`#0050C3`,icon:`#FFFFFF`},white:{background:`#FFFFFF`,icon:`#0059D5`}};function x({data:n,trackingConfig:o,defaultVariant:c=`blue`,onClose:l,isMobile:u,onCtaClick:d}){let f=s(),{log:m}=e.n(),h=n.variant||c,g=b[h],[_,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 x=u??_,S=(0,t.useCallback)(()=>e.i({context:n.context,campaignId:n.campaignId,group:n.group,...n.metadata,...o.properties}),[n,o.properties]),C=(0,t.useCallback)(()=>{f(e.r(o.prefix,o.page,n.context,`click`),S()),d?d(n.cta):p(n.cta,m)},[n,o,S,f,d,m]),w=(0,t.useCallback)(()=>{f(e.r(o.prefix,o.page,n.context,`close`),S()),l?.()},[l,n,o,S,f]);(0,t.useEffect)(()=>{f(e.r(o.prefix,o.page,n.context,`view`),S())},[]);let T=(0,r.jsx)(i.Link,{as:`a`,textDecoration:`none`,onClick:C,"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:x?`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:h===`white`?`#E7E7E7`:`transparent`,borderWidth:1,borderStyle:`solid`,background:g.background,display:`flex`,alignItems:`center`,justifyContent:`center`},children:(0,r.jsx)(a.MoneyIcon,{style:{color:g.icon}})})}),(0,r.jsxs)(i.Box,{display:`grid`,gap:`2`,children:[(0,r.jsxs)(i.Box,{children:[(0,r.jsx)(v,{content:n.title,fontSize:`base`,color:`neutral-textHigh`}),(0,r.jsx)(v,{content:n.message,fontSize:`base`,color:`neutral-textLow`})]}),x&&T]})]}),(!x||l)&&(0,r.jsxs)(i.Box,{display:`flex`,flexDirection:x?`column`:`row`,alignItems:x?`flex-end`:`center`,gap:`2`,children:[!x&&T,l&&(0,r.jsx)(`button`,{type:`button`,"data-testid":`live-state-close-button`,"aria-label":`Fechar notificação`,onClick:w,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 S({data:t,loading:n,trackingConfig:i,allowedContexts:a,defaultVariant:o,isMobile:s,onCtaClick:c}){let{log:l,onNavigate:u}=e.n(),d=c??u,{isVisible:p,close:m}=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)||p===null||!p)return null;let h=t.closable?m:void 0;return t.type===`alert`||t.type===`warning`?(0,r.jsx)(y,{data:t,onClose:h,trackingConfig:i,onCtaClick:d}):t.type===`info`?(0,r.jsx)(x,{data:t,onClose:h,trackingConfig:i,defaultVariant:o,isMobile:s,onCtaClick:d}):(l(`warn`,`Unknown type: ${t.type}`,{data:t}),null)}exports.LIVE_STATE_STORAGE_KEY=c,exports.LiveStateAlert=y,exports.LiveStateInfo=x,exports.LiveStateProvider=e.t,exports.LiveStateRenderer=S,exports.handleCtaClick=p,exports.trackEvent=e.a,exports.useLiveState=o,exports.useTrackEvent=s;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/hooks/useLiveState.ts","../src/hooks/useTrackEvent.ts","../src/utils/closable-storage.ts","../src/hooks/useClosable.ts","../src/utils/cta.ts","../src/components/LiveStateAlert.tsx","../src/components/LiveStateInfo.tsx","../src/components/LiveStateRenderer.tsx"],"sourcesContent":["import useSWR from 'swr';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport type { LiveStateResponse } from '../types';\n\ninterface UseLiveStateReturn {\n /** Live state data */\n liveState: LiveStateResponse | null;\n\n /** True while the first fetch is in progress */\n isLoading: boolean;\n\n /**\n * True when the last fetch attempt resulted in an error.\n * Use this to implement fallbacks or custom error logging.\n * Note: the lib already logs a warning to the console on every error.\n */\n isError: boolean;\n\n /**\n * The raw error from the last failed fetch, or null.\n * Pair with `isError` for more specific error handling.\n */\n error: Error | null;\n\n /** Manually trigger a re-fetch */\n refresh: () => Promise<void>;\n}\n\n/**\n * useLiveState hook\n *\n * Fetches and manages live state data using SWR with automatic caching,\n * deduplication, and revalidation.\n *\n * Must be used within LiveStateProvider. See examples/ directory for usage.\n */\nexport function useLiveState(): UseLiveStateReturn {\n const { fetcher, log } = useLiveStateContext();\n\n const { data, error, isLoading, mutate } = useSWR<LiveStateResponse | null>(\n 'live-state',\n fetcher,\n {\n revalidateOnFocus: false,\n revalidateOnReconnect: true,\n dedupingInterval: 60000, // 1 minute\n shouldRetryOnError: false, // Fail silently\n onError: err => {\n log('error', 'Failed to fetch live state', { error: err });\n },\n },\n );\n\n const refresh = async () => {\n try {\n await mutate();\n } catch (err) {\n log('error', 'Failed to refresh live state', { error: err });\n }\n };\n\n return {\n liveState: data ?? null,\n isLoading,\n isError: error != null,\n error: error ?? null,\n refresh,\n };\n}\n","import { useCallback } from 'react';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { trackEvent } from '../utils/analytics';\nimport type { TrackingProperties } from '../types';\n\n/**\n * useTrackEvent\n *\n * Returns a `track` function that fires both the internal analytics\n * (Amplitude + Clarity) and the optional `onEvent` callback provided\n * to LiveStateProvider — useful for simulators and debugging tools.\n */\nexport function useTrackEvent() {\n const { onEvent, disabled, log } = useLiveStateContext();\n\n const track = useCallback(\n (eventName: string, properties?: TrackingProperties) => {\n // onEvent callback fires even when disabled — it is an observability\n // hook, not an analytics SDK, so the caller opts in deliberately.\n onEvent?.(eventName, properties);\n\n // Skip SDK calls when analytics are disabled (e.g. dev/test environments)\n if (!disabled) {\n trackEvent(eventName, properties, log);\n }\n },\n [onEvent, disabled, log],\n );\n\n return track;\n}\n","export const LIVE_STATE_STORAGE_KEY = '@tiendanube/live-state:closable';\n\n/** @internal */\nconst STORAGE_KEY = LIVE_STATE_STORAGE_KEY;\n\nexport interface ClosableState {\n count: number;\n closedAt: number;\n}\n\nexport type ClosableStates = Record<string, Record<string, ClosableState>>;\n\n/**\n * Reads closable states from localStorage.\n * Normalises legacy format (plain number) to the current shape ({ count, closedAt }).\n * Returns an empty object on any error (fail-silent).\n */\nexport function getClosableStates(): ClosableStates {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return {};\n\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== 'object' || parsed === null) return {};\n\n const normalized: ClosableStates = {};\n\n for (const [ctx, ctxState] of Object.entries(\n parsed as Record<string, unknown>,\n )) {\n if (typeof ctxState !== 'object' || ctxState === null) continue;\n\n normalized[ctx] = {};\n\n for (const [id, entry] of Object.entries(\n ctxState as Record<string, unknown>,\n )) {\n if (typeof entry === 'number') {\n // legacy format: plain close count\n normalized[ctx][id] = { count: entry, closedAt: 0 };\n } else if (typeof entry === 'object' && entry !== null) {\n const candidate = entry as Partial<ClosableState>;\n normalized[ctx][id] = {\n count: typeof candidate.count === 'number' ? candidate.count : 0,\n closedAt:\n typeof candidate.closedAt === 'number' ? candidate.closedAt : 0,\n };\n }\n }\n }\n\n return normalized;\n } catch {\n return {};\n }\n}\n\n/**\n * Persists closable states to localStorage.\n * Fails silently if localStorage is unavailable or full.\n */\nexport function setClosableStates(states: ClosableStates): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(states));\n } catch {\n // localStorage unavailable or quota exceeded — application continues normally\n }\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport {\n getClosableStates,\n setClosableStates,\n} from '../utils/closable-storage';\n\nexport interface UseClosableOptions {\n /** Notification context (e.g. 'awareness', 'charge') */\n context: string;\n /** Unique notification ID (campaignId). If absent, always visible — no close tracking. */\n id: string | undefined;\n /** Maximum number of times the user can close before it is hidden permanently */\n maxCloseTimes?: number;\n /** Milliseconds after which the close counter resets (optional) */\n expiresIn?: number;\n}\n\nexport interface UseClosableReturn {\n /** Whether the notification should be shown. null = initial check in progress. */\n isVisible: boolean | null;\n /** Call this to record a close action */\n close: () => void;\n}\n\n/**\n * Manages the close state of a notification using localStorage.\n *\n * After the user closes a notification `maxCloseTimes` times, it is hidden\n * permanently. Optionally, the counter resets after `expiresIn` milliseconds.\n *\n * Fails silently — errors in localStorage never break the application.\n *\n * @example\n * ```tsx\n * const { isVisible, close } = useClosable({\n * context: 'awareness',\n * id: 'awareness-2026-q1',\n * maxCloseTimes: 3,\n * expiresIn: 86_400_000, // 1 day\n * });\n *\n * if (!isVisible) return null;\n * return <Notification onClose={close} />;\n * ```\n */\nexport function useClosable({\n context,\n id,\n maxCloseTimes = 3,\n expiresIn,\n}: UseClosableOptions): UseClosableReturn {\n const [isVisible, setIsVisible] = useState<boolean | null>(null);\n\n useEffect(() => {\n // No ID means no close tracking — always show\n if (!id) {\n setIsVisible(true);\n return;\n }\n\n const states = getClosableStates();\n const entry = states[context]?.[id];\n\n if (!entry) {\n setIsVisible(true);\n return;\n }\n\n const { count, closedAt } = entry;\n\n // Check expiry — reset counter and show if expired\n if (expiresIn != null && closedAt > 0) {\n const elapsed = Date.now() - closedAt;\n if (elapsed > expiresIn) {\n const updated = { ...states };\n if (updated[context]) {\n delete updated[context][id];\n setClosableStates(updated);\n }\n setIsVisible(true);\n return;\n }\n }\n\n setIsVisible(count < maxCloseTimes);\n }, [context, id, maxCloseTimes, expiresIn]);\n\n const close = useCallback(() => {\n // Always hide visually\n setIsVisible(false);\n\n // Only persist to localStorage if there is an id to track\n if (!id) return;\n\n const states = getClosableStates();\n const currentCount = states[context]?.[id]?.count ?? 0;\n\n const updated: typeof states = {\n ...states,\n [context]: {\n ...(states[context] ?? {}),\n [id]: {\n count: currentCount + 1,\n closedAt: Date.now(),\n },\n },\n };\n\n setClosableStates(updated);\n }, [context, id]);\n\n return { isVisible, close };\n}\n","import type { CtaConfig } from '../types';\nimport { createLogger, type LoggerFn } from './logger';\n\n/**\n * Handle CTA click based on type\n *\n * @param cta - CTA configuration from LiveStateResponse\n * @param log - Optional logger (defaults to console.warn)\n */\nexport function handleCtaClick(cta: CtaConfig, log?: LoggerFn): void {\n const logger = createLogger(log);\n try {\n switch (cta.type) {\n case 'redirect':\n window.location.href = cta.url;\n break;\n\n case 'whatsapp': {\n const whatsappUrl = cta.whatsappMessage\n ? `${cta.url}?text=${encodeURIComponent(cta.whatsappMessage)}`\n : cta.url;\n window.open(whatsappUrl, '_blank');\n break;\n }\n\n case 'external':\n window.open(cta.url, '_blank');\n break;\n\n default:\n logger('warn', `Unknown CTA type: ${(cta as CtaConfig).type}`, { cta });\n }\n } catch (error) {\n logger('error', 'Failed to handle CTA click', { cta, error });\n }\n}\n","import { useCallback, useEffect } from 'react';\nimport { Alert, Box, Button, Text } from '@nimbus-ds/components';\nimport type { LiveStateResponse, TrackingConfig } from '../types';\nimport { buildTrackingProperties, buildEventName } from '../utils/analytics';\nimport { handleCtaClick } from '../utils/cta';\nimport { useTrackEvent } from '../hooks/useTrackEvent';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\n\nexport interface LiveStateAlertProps {\n data: LiveStateResponse;\n trackingConfig: TrackingConfig;\n /** When provided, shows a close button and calls this on dismiss */\n onClose?: () => void;\n /**\n * Override CTA click behaviour.\n * When provided, replaces default navigation so the caller controls what\n * happens on click (e.g. prevent navigation in a simulator, use a router).\n */\n onCtaClick?: (cta: import('../types').CtaConfig) => void;\n}\n\n/**\n * LiveStateAlert\n *\n * Renders an Alert component (Nimbus) for alert/warning types.\n * Automatically tracks view, click, and close events.\n * Shows a close button when onClose is provided.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateAlert({\n data,\n trackingConfig,\n onClose,\n onCtaClick,\n}: LiveStateAlertProps) {\n const track = useTrackEvent();\n const { log } = useLiveStateContext();\n const appearance = data.type === 'alert' ? 'danger' : 'warning';\n\n const buildEventProperties = useCallback(\n () =>\n buildTrackingProperties({\n context: data.context,\n campaignId: data.campaignId,\n group: data.group,\n ...data.metadata,\n ...trackingConfig.properties,\n }),\n [data, trackingConfig.properties],\n );\n\n const handleClick = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'click',\n );\n track(eventName, buildEventProperties());\n if (onCtaClick) {\n onCtaClick(data.cta);\n } else {\n handleCtaClick(data.cta, log);\n }\n }, [data, trackingConfig, buildEventProperties, track, onCtaClick, log]);\n\n const handleClose = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'close',\n );\n track(eventName, buildEventProperties());\n onClose?.();\n }, [onClose, data, trackingConfig, buildEventProperties, track]);\n\n useEffect(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'view',\n );\n track(eventName, buildEventProperties());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <Alert\n appearance={appearance}\n title={data.title}\n onRemove={onClose ? handleClose : undefined}>\n <Box\n display=\"flex\"\n alignItems=\"flex-start\"\n gap=\"3\"\n flexWrap=\"wrap\"\n flexDirection=\"column\">\n <Box flex=\"1 1 auto\" minWidth=\"220px\">\n <Text>{data.message}</Text>\n </Box>\n\n <Box display=\"flex\" justifyContent=\"flex-end\" marginTop=\"2\">\n <Button appearance=\"primary\" onClick={handleClick}>\n {data.cta.label}\n </Button>\n </Box>\n </Box>\n </Alert>\n );\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { Card, Box, Text, Link, Icon } from '@nimbus-ds/components';\nimport { MoneyIcon, CloseIcon } from '@nimbus-ds/icons';\nimport type { LiveStateResponse, TrackingConfig } from '../types';\nimport { buildTrackingProperties, buildEventName } from '../utils/analytics';\nimport { handleCtaClick } from '../utils/cta';\nimport { useTrackEvent } from '../hooks/useTrackEvent';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\n\nexport interface LiveStateInfoProps {\n data: LiveStateResponse;\n trackingConfig: TrackingConfig;\n defaultVariant?: 'blue' | 'white';\n /** When provided, shows a close button and calls this on dismiss */\n onClose?: () => void;\n /**\n * Override mobile layout detection.\n * When provided, bypasses `window.innerWidth < 750` so simulators and\n * testing tools can force a specific layout without resizing the window.\n */\n isMobile?: boolean;\n /**\n * Override CTA click behaviour.\n * When provided, replaces default navigation so the caller controls what\n * happens on click (e.g. prevent navigation in a simulator, use a router).\n */\n onCtaClick?: (cta: import('../types').CtaConfig) => void;\n}\n\nconst ICON_COLOR_MAP = {\n blue: { background: '#0050C3', icon: '#FFFFFF' },\n white: { background: '#FFFFFF', icon: '#0059D5' },\n} as const;\n\n/**\n * LiveStateInfo\n *\n * Renders a Card component (Nimbus) for info type.\n * Uses MoneyIcon with colored circle background, Text components,\n * and a Link-style CTA — matching the LendingBanner design reference.\n * Automatically tracks view, click, and close events.\n * Shows a close button when onClose is provided.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateInfo({\n data,\n trackingConfig,\n defaultVariant = 'blue',\n onClose,\n isMobile: isMobileProp,\n onCtaClick,\n}: LiveStateInfoProps) {\n const track = useTrackEvent();\n const { log } = useLiveStateContext();\n const variant = data.variant || defaultVariant;\n const iconConfig = ICON_COLOR_MAP[variant];\n\n const [isMobileWindow, setIsMobileWindow] = useState(\n () => window.innerWidth < 750,\n );\n\n useEffect(() => {\n const handler = () => setIsMobileWindow(window.innerWidth < 750);\n window.addEventListener('resize', handler);\n return () => window.removeEventListener('resize', handler);\n }, []);\n\n // Prop overrides window detection — allows simulators/tests to force layout\n const isMobile = isMobileProp ?? isMobileWindow;\n\n const buildEventProperties = useCallback(\n () =>\n buildTrackingProperties({\n context: data.context,\n campaignId: data.campaignId,\n group: data.group,\n ...data.metadata,\n ...trackingConfig.properties,\n }),\n [data, trackingConfig.properties],\n );\n\n const handleClick = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'click',\n );\n track(eventName, buildEventProperties());\n if (onCtaClick) {\n onCtaClick(data.cta);\n } else {\n handleCtaClick(data.cta, log);\n }\n }, [data, trackingConfig, buildEventProperties, track, onCtaClick, log]);\n\n const handleClose = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'close',\n );\n track(eventName, buildEventProperties());\n onClose?.();\n }, [onClose, data, trackingConfig, buildEventProperties, track]);\n\n useEffect(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'view',\n );\n track(eventName, buildEventProperties());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const ctaLink = (\n <Link\n as=\"a\"\n textDecoration=\"none\"\n onClick={handleClick}\n data-testid=\"live-state-cta-link\">\n <Text color=\"primary-interactive\" fontSize=\"base\">\n {data.cta.label}\n </Text>\n </Link>\n );\n\n return (\n <Card>\n <Box display=\"flex\" flexDirection=\"row\" justifyContent=\"space-between\">\n {/* Left: icon + text (+ cta on mobile) */}\n <Box\n display=\"flex\"\n gap=\"4\"\n alignItems={isMobile ? 'flex-start' : 'center'}\n paddingRight=\"1-5\">\n <Box minWidth=\"32px\">\n <div\n style={{\n width: 32,\n height: 32,\n borderRadius: '35%',\n borderColor: variant === 'white' ? '#E7E7E7' : 'transparent',\n borderWidth: 1,\n borderStyle: 'solid',\n background: iconConfig.background,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n <MoneyIcon style={{ color: iconConfig.icon }} />\n </div>\n </Box>\n\n <Box display=\"grid\" gap=\"2\">\n <Box>\n <Text fontSize=\"base\" color=\"neutral-textHigh\">\n {data.title}\n </Text>\n <Text fontSize=\"base\" color=\"neutral-textLow\">\n {data.message}\n </Text>\n </Box>\n\n {/* CTA shown below text on mobile */}\n {isMobile && ctaLink}\n </Box>\n </Box>\n\n {/* Right: cta (desktop only) + close button */}\n {(!isMobile || onClose) && (\n <Box\n display=\"flex\"\n flexDirection={isMobile ? 'column' : 'row'}\n alignItems={isMobile ? 'flex-end' : 'center'}\n gap=\"2\">\n {/* CTA shown on the right side on desktop */}\n {!isMobile && ctaLink}\n\n {onClose && (\n <button\n type=\"button\"\n data-testid=\"live-state-close-button\"\n aria-label=\"Fechar notificação\"\n onClick={handleClose}\n style={{\n background: 'none',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n <Icon\n source={<CloseIcon width=\"18px\" height=\"18px\" />}\n color=\"neutral-interactive\"\n />\n </button>\n )}\n </Box>\n )}\n </Box>\n </Card>\n );\n}\n","import type { LiveStateRendererProps } from '../types';\nimport { useClosable } from '../hooks/useClosable';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { LiveStateAlert } from './LiveStateAlert';\nimport { LiveStateInfo } from './LiveStateInfo';\n\n/**\n * LiveStateRenderer\n *\n * Main component that validates payload, filters by context, and renders\n * the appropriate visual component (Alert or Info) based on type.\n * Handles closable notifications via useClosable when closable is configured.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateRenderer({\n data,\n loading,\n trackingConfig,\n allowedContexts,\n defaultVariant,\n isMobile,\n onCtaClick,\n}: LiveStateRendererProps) {\n const { log } = useLiveStateContext();\n const { isVisible, close } = useClosable({\n context: data?.context ?? '',\n id: data?.campaignId,\n maxCloseTimes: data?.metadata?.maxCloseTimes,\n expiresIn: data?.metadata?.expiresIn,\n });\n\n // Don't render while loading or no data\n if (loading || !data) {\n return null;\n }\n\n // Validate required fields\n if (\n !data.context ||\n !data.type ||\n !data.title ||\n !data.message ||\n !data.cta?.label ||\n !data.cta?.url ||\n !data.cta?.type\n ) {\n log('warn', 'Invalid payload, missing required fields', { data });\n return null;\n }\n\n // Filter by allowed contexts\n if (allowedContexts && !allowedContexts.includes(data.context)) {\n return null;\n }\n\n // Awaiting localStorage check\n if (isVisible === null) {\n return null;\n }\n\n // Hidden — user reached maxCloseTimes\n if (!isVisible) {\n return null;\n }\n\n // Pass onClose whenever closable is true.\n // campaignId is optional — useClosable handles id=undefined gracefully\n // (always visible, no localStorage tracking).\n const onClose = data.closable ? close : undefined;\n\n // Render appropriate component based on type\n if (data.type === 'alert' || data.type === 'warning') {\n return (\n <LiveStateAlert\n data={data}\n onClose={onClose}\n trackingConfig={trackingConfig}\n onCtaClick={onCtaClick}\n />\n );\n }\n\n if (data.type === 'info') {\n return (\n <LiveStateInfo\n data={data}\n onClose={onClose}\n trackingConfig={trackingConfig}\n defaultVariant={defaultVariant}\n isMobile={isMobile}\n onCtaClick={onCtaClick}\n />\n );\n }\n\n log('warn', `Unknown type: ${data.type}`, { data });\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/hooks/useLiveState.ts","../src/hooks/useTrackEvent.ts","../src/utils/closable-storage.ts","../src/hooks/useClosable.ts","../src/utils/cta.ts","../src/utils/sanitize.ts","../src/components/RichText.tsx","../src/components/LiveStateAlert.tsx","../src/components/LiveStateInfo.tsx","../src/components/LiveStateRenderer.tsx"],"sourcesContent":["import useSWR from 'swr';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport type { LiveStateResponse } from '../types';\n\ninterface UseLiveStateReturn {\n /** Live state data */\n liveState: LiveStateResponse | null;\n\n /** True while the first fetch is in progress */\n isLoading: boolean;\n\n /**\n * True when the last fetch attempt resulted in an error.\n * Use this to implement fallbacks or custom error logging.\n * Note: the lib already logs a warning to the console on every error.\n */\n isError: boolean;\n\n /**\n * The raw error from the last failed fetch, or null.\n * Pair with `isError` for more specific error handling.\n */\n error: Error | null;\n\n /** Manually trigger a re-fetch */\n refresh: () => Promise<void>;\n}\n\n/**\n * useLiveState hook\n *\n * Fetches and manages live state data using SWR with automatic caching,\n * deduplication, and revalidation.\n *\n * Must be used within LiveStateProvider. See examples/ directory for usage.\n */\nexport function useLiveState(): UseLiveStateReturn {\n const { fetcher, mockData, log } = useLiveStateContext();\n\n // When mockData is provided, bypass SWR entirely — the fetcher is never called.\n // This enables local development and Storybook without a real backend.\n const isMocked = mockData !== undefined;\n\n const { data, error, isLoading, mutate } = useSWR<LiveStateResponse | null>(\n isMocked ? null : 'live-state', // null key disables SWR when mocked\n fetcher,\n {\n revalidateOnFocus: false,\n revalidateOnReconnect: true,\n dedupingInterval: 60000, // 1 minute\n shouldRetryOnError: false, // Fail silently\n onError: err => {\n log('error', 'Failed to fetch live state', { error: err });\n },\n },\n );\n\n const refresh = async () => {\n if (isMocked) return; // no-op when mocked\n try {\n await mutate();\n } catch (err) {\n log('error', 'Failed to refresh live state', { error: err });\n }\n };\n\n if (isMocked) {\n return {\n liveState: mockData,\n isLoading: false,\n isError: false,\n error: null,\n refresh,\n };\n }\n\n return {\n liveState: data ?? null,\n isLoading,\n isError: error != null,\n error: error ?? null,\n refresh,\n };\n}\n","import { useCallback } from 'react';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { trackEvent } from '../utils/analytics';\nimport type { TrackingProperties } from '../types';\n\n/**\n * useTrackEvent\n *\n * Returns a `track` function that fires both the internal analytics\n * (Amplitude + Clarity) and the optional `onEvent` callback provided\n * to LiveStateProvider — useful for simulators and debugging tools.\n */\nexport function useTrackEvent() {\n const { onEvent, disabled, log } = useLiveStateContext();\n\n const track = useCallback(\n (eventName: string, properties?: TrackingProperties) => {\n // onEvent callback fires even when disabled — it is an observability\n // hook, not an analytics SDK, so the caller opts in deliberately.\n onEvent?.(eventName, properties);\n\n // Skip SDK calls when analytics are disabled (e.g. dev/test environments)\n if (!disabled) {\n trackEvent(eventName, properties, log);\n }\n },\n [onEvent, disabled, log],\n );\n\n return track;\n}\n","export const LIVE_STATE_STORAGE_KEY = '@tiendanube/live-state:closable';\n\n/** @internal */\nconst STORAGE_KEY = LIVE_STATE_STORAGE_KEY;\n\nexport interface ClosableState {\n count: number;\n closedAt: number;\n}\n\nexport type ClosableStates = Record<string, Record<string, ClosableState>>;\n\n/**\n * Reads closable states from localStorage.\n * Normalises legacy format (plain number) to the current shape ({ count, closedAt }).\n * Returns an empty object on any error (fail-silent).\n */\nexport function getClosableStates(): ClosableStates {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return {};\n\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== 'object' || parsed === null) return {};\n\n const normalized: ClosableStates = {};\n\n for (const [ctx, ctxState] of Object.entries(\n parsed as Record<string, unknown>,\n )) {\n if (typeof ctxState !== 'object' || ctxState === null) continue;\n\n normalized[ctx] = {};\n\n for (const [id, entry] of Object.entries(\n ctxState as Record<string, unknown>,\n )) {\n if (typeof entry === 'number') {\n // legacy format: plain close count\n normalized[ctx][id] = { count: entry, closedAt: 0 };\n } else if (typeof entry === 'object' && entry !== null) {\n const candidate = entry as Partial<ClosableState>;\n normalized[ctx][id] = {\n count: typeof candidate.count === 'number' ? candidate.count : 0,\n closedAt:\n typeof candidate.closedAt === 'number' ? candidate.closedAt : 0,\n };\n }\n }\n }\n\n return normalized;\n } catch {\n return {};\n }\n}\n\n/**\n * Persists closable states to localStorage.\n * Fails silently if localStorage is unavailable or full.\n */\nexport function setClosableStates(states: ClosableStates): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(states));\n } catch {\n // localStorage unavailable or quota exceeded — application continues normally\n }\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport {\n getClosableStates,\n setClosableStates,\n} from '../utils/closable-storage';\n\nexport interface UseClosableOptions {\n /** Notification context (e.g. 'awareness', 'charge') */\n context: string;\n /** Unique notification ID (campaignId). If absent, always visible — no close tracking. */\n id: string | undefined;\n /** Maximum number of times the user can close before it is hidden permanently */\n maxCloseTimes?: number;\n /** Milliseconds after which the close counter resets (optional) */\n expiresIn?: number;\n}\n\nexport interface UseClosableReturn {\n /** Whether the notification should be shown. null = initial check in progress. */\n isVisible: boolean | null;\n /** Call this to record a close action */\n close: () => void;\n}\n\n/**\n * Manages the close state of a notification using localStorage.\n *\n * After the user closes a notification `maxCloseTimes` times, it is hidden\n * permanently. Optionally, the counter resets after `expiresIn` milliseconds.\n *\n * Fails silently — errors in localStorage never break the application.\n *\n * @example\n * ```tsx\n * const { isVisible, close } = useClosable({\n * context: 'awareness',\n * id: 'awareness-2026-q1',\n * maxCloseTimes: 3,\n * expiresIn: 86_400_000, // 1 day\n * });\n *\n * if (!isVisible) return null;\n * return <Notification onClose={close} />;\n * ```\n */\nexport function useClosable({\n context,\n id,\n maxCloseTimes = 3,\n expiresIn,\n}: UseClosableOptions): UseClosableReturn {\n const [isVisible, setIsVisible] = useState<boolean | null>(null);\n\n useEffect(() => {\n // No ID means no close tracking — always show\n if (!id) {\n setIsVisible(true);\n return;\n }\n\n const states = getClosableStates();\n const entry = states[context]?.[id];\n\n if (!entry) {\n setIsVisible(true);\n return;\n }\n\n const { count, closedAt } = entry;\n\n // Check expiry — reset counter and show if expired\n if (expiresIn != null && closedAt > 0) {\n const elapsed = Date.now() - closedAt;\n if (elapsed > expiresIn) {\n const updated = { ...states };\n if (updated[context]) {\n delete updated[context][id];\n setClosableStates(updated);\n }\n setIsVisible(true);\n return;\n }\n }\n\n setIsVisible(count < maxCloseTimes);\n }, [context, id, maxCloseTimes, expiresIn]);\n\n const close = useCallback(() => {\n // Always hide visually\n setIsVisible(false);\n\n // Only persist to localStorage if there is an id to track\n if (!id) return;\n\n const states = getClosableStates();\n const currentCount = states[context]?.[id]?.count ?? 0;\n\n const updated: typeof states = {\n ...states,\n [context]: {\n ...(states[context] ?? {}),\n [id]: {\n count: currentCount + 1,\n closedAt: Date.now(),\n },\n },\n };\n\n setClosableStates(updated);\n }, [context, id]);\n\n return { isVisible, close };\n}\n","import type { CtaConfig } from '../types';\nimport { createLogger, type LoggerFn } from './logger';\n\n/**\n * Handle CTA click based on type\n *\n * @param cta - CTA configuration from LiveStateResponse\n * @param log - Optional logger (defaults to console.warn)\n */\nexport function handleCtaClick(cta: CtaConfig, log?: LoggerFn): void {\n const logger = createLogger(log);\n try {\n switch (cta.type) {\n case 'redirect':\n window.location.href = cta.url;\n break;\n\n case 'whatsapp': {\n const whatsappUrl = cta.whatsappMessage\n ? `${cta.url}?text=${encodeURIComponent(cta.whatsappMessage)}`\n : cta.url;\n window.open(whatsappUrl, '_blank');\n break;\n }\n\n case 'external':\n window.open(cta.url, '_blank');\n break;\n\n default:\n logger('warn', `Unknown CTA type: ${(cta as CtaConfig).type}`, { cta });\n }\n } catch (error) {\n logger('error', 'Failed to handle CTA click', { cta, error });\n }\n}\n","/**\n * Minimal HTML sanitiser for notification content.\n *\n * Allows only a strict allowlist of inline formatting tags that are expected\n * in notification messages (strong, em, b, i, br, span). All other tags and\n * attributes are stripped, preventing XSS even if the backend is compromised.\n *\n * This intentionally avoids a runtime dependency on DOMPurify or similar\n * libraries — the allowlist is narrow enough to be maintained safely here.\n */\n\n/** Tags that are safe to render inside notification text */\nconst ALLOWED_TAGS = new Set(['strong', 'em', 'b', 'i', 'br', 'span']);\n\n/**\n * Tags whose entire content (open tag + inner text + close tag) must be\n * removed, not just the surrounding tags. This prevents script injection\n * even if the backend is compromised.\n */\nconst BLOCK_TAGS_RE =\n /<(script|style|iframe|object|embed|form|input|button|textarea|select|link|meta|head|body|html|svg|math)[\\s\\S]*?<\\/\\1\\s*>|<(script|style|iframe|object|embed|form|input|button|textarea|select|link|meta|head|body|html|svg|math)[^>]*\\/?>/gi;\n\n/**\n * Strips all HTML tags except those in ALLOWED_TAGS.\n * Attributes are removed from all allowed tags to prevent event-handler\n * injection (e.g. `<span onclick=\"...\">`).\n * Dangerous tags (script, style, iframe, …) have their entire content removed.\n *\n * Returns the sanitised HTML string, safe to pass to `dangerouslySetInnerHTML`.\n */\nexport function sanitizeHtml(input: string): string {\n // Step 1: remove dangerous tags and their inner content entirely\n const withoutDangerous = input.replace(BLOCK_TAGS_RE, '');\n\n // Step 2: remove all attributes from every remaining tag\n const withoutAttributes = withoutDangerous.replace(\n /<([a-zA-Z][a-zA-Z0-9]*)\\s[^>]*>/g,\n '<$1>',\n );\n\n // Step 3: strip any remaining tag that is not in the allowlist\n return withoutAttributes.replace(\n /<\\/?([a-zA-Z][a-zA-Z0-9]*)[^>]*>/g,\n (match, tag: string) => (ALLOWED_TAGS.has(tag.toLowerCase()) ? match : ''),\n );\n}\n\n/**\n * Returns true when the string contains at least one HTML tag,\n * indicating it should be rendered as rich text.\n */\nexport function containsHtml(input: string): boolean {\n return /<[a-zA-Z][^>]*>/.test(input);\n}\n","import { Text } from '@nimbus-ds/components';\nimport type { ComponentProps } from 'react';\nimport { sanitizeHtml, containsHtml } from '../utils/sanitize';\n\ntype TextProps = ComponentProps<typeof Text>;\n\ninterface RichTextProps extends Omit<TextProps, 'children'> {\n content: string;\n}\n\n/**\n * RichText\n *\n * Renders a text string that may contain inline HTML formatting (e.g. <strong>,\n * <em>, <br>). When HTML is detected, the content is sanitised through a strict\n * allowlist before being injected via dangerouslySetInnerHTML. Plain strings\n * are rendered via the Nimbus Text component without any DOM injection.\n *\n * Allowed tags: strong, em, b, i, br, span. All attributes are stripped.\n */\nexport function RichText({ content, ...textProps }: RichTextProps) {\n if (containsHtml(content)) {\n const safe = sanitizeHtml(content);\n return (\n <Text {...textProps}>\n {/* eslint-disable-next-line react/no-danger */}\n <span dangerouslySetInnerHTML={{ __html: safe }} />\n </Text>\n );\n }\n\n return <Text {...textProps}>{content}</Text>;\n}\n","import { useCallback, useEffect } from 'react';\nimport { Alert, Box, Button } from '@nimbus-ds/components';\nimport type { LiveStateResponse, TrackingConfig } from '../types';\nimport { buildTrackingProperties, buildEventName } from '../utils/analytics';\nimport { handleCtaClick } from '../utils/cta';\nimport { useTrackEvent } from '../hooks/useTrackEvent';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { RichText } from './RichText';\n\nexport interface LiveStateAlertProps {\n data: LiveStateResponse;\n trackingConfig: TrackingConfig;\n /** When provided, shows a close button and calls this on dismiss */\n onClose?: () => void;\n /**\n * Override CTA click behaviour.\n * When provided, replaces default navigation so the caller controls what\n * happens on click (e.g. prevent navigation in a simulator, use a router).\n */\n onCtaClick?: (cta: import('../types').CtaConfig) => void;\n}\n\n/**\n * LiveStateAlert\n *\n * Renders an Alert component (Nimbus) for alert/warning types.\n * Automatically tracks view, click, and close events.\n * Shows a close button when onClose is provided.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateAlert({\n data,\n trackingConfig,\n onClose,\n onCtaClick,\n}: LiveStateAlertProps) {\n const track = useTrackEvent();\n const { log } = useLiveStateContext();\n const appearance = data.type === 'alert' ? 'danger' : 'warning';\n\n const buildEventProperties = useCallback(\n () =>\n buildTrackingProperties({\n context: data.context,\n campaignId: data.campaignId,\n group: data.group,\n ...data.metadata,\n ...trackingConfig.properties,\n }),\n [data, trackingConfig.properties],\n );\n\n const handleClick = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'click',\n );\n track(eventName, buildEventProperties());\n if (onCtaClick) {\n onCtaClick(data.cta);\n } else {\n handleCtaClick(data.cta, log);\n }\n }, [data, trackingConfig, buildEventProperties, track, onCtaClick, log]);\n\n const handleClose = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'close',\n );\n track(eventName, buildEventProperties());\n onClose?.();\n }, [onClose, data, trackingConfig, buildEventProperties, track]);\n\n useEffect(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'view',\n );\n track(eventName, buildEventProperties());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <Alert\n appearance={appearance}\n title={data.title}\n onRemove={onClose ? handleClose : undefined}>\n <Box\n display=\"flex\"\n alignItems=\"flex-start\"\n gap=\"3\"\n flexWrap=\"wrap\"\n flexDirection=\"column\">\n <Box flex=\"1 1 auto\" minWidth=\"220px\">\n <RichText content={data.message} />\n </Box>\n\n <Box display=\"flex\" justifyContent=\"flex-end\" marginTop=\"2\">\n <Button appearance=\"primary\" onClick={handleClick}>\n {data.cta.label}\n </Button>\n </Box>\n </Box>\n </Alert>\n );\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { Card, Box, Text, Link, Icon } from '@nimbus-ds/components';\nimport { MoneyIcon, CloseIcon } from '@nimbus-ds/icons';\nimport type { LiveStateResponse, TrackingConfig } from '../types';\nimport { buildTrackingProperties, buildEventName } from '../utils/analytics';\nimport { handleCtaClick } from '../utils/cta';\nimport { useTrackEvent } from '../hooks/useTrackEvent';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { RichText } from './RichText';\n\nexport interface LiveStateInfoProps {\n data: LiveStateResponse;\n trackingConfig: TrackingConfig;\n defaultVariant?: 'blue' | 'white';\n /** When provided, shows a close button and calls this on dismiss */\n onClose?: () => void;\n /**\n * Override mobile layout detection.\n * When provided, bypasses `window.innerWidth < 750` so simulators and\n * testing tools can force a specific layout without resizing the window.\n */\n isMobile?: boolean;\n /**\n * Override CTA click behaviour.\n * When provided, replaces default navigation so the caller controls what\n * happens on click (e.g. prevent navigation in a simulator, use a router).\n */\n onCtaClick?: (cta: import('../types').CtaConfig) => void;\n}\n\nconst ICON_COLOR_MAP = {\n blue: { background: '#0050C3', icon: '#FFFFFF' },\n white: { background: '#FFFFFF', icon: '#0059D5' },\n} as const;\n\n/**\n * LiveStateInfo\n *\n * Renders a Card component (Nimbus) for info type.\n * Uses MoneyIcon with colored circle background, Text components,\n * and a Link-style CTA — matching the LendingBanner design reference.\n * Automatically tracks view, click, and close events.\n * Shows a close button when onClose is provided.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateInfo({\n data,\n trackingConfig,\n defaultVariant = 'blue',\n onClose,\n isMobile: isMobileProp,\n onCtaClick,\n}: LiveStateInfoProps) {\n const track = useTrackEvent();\n const { log } = useLiveStateContext();\n const variant = data.variant || defaultVariant;\n const iconConfig = ICON_COLOR_MAP[variant];\n\n const [isMobileWindow, setIsMobileWindow] = useState(\n () => window.innerWidth < 750,\n );\n\n useEffect(() => {\n const handler = () => setIsMobileWindow(window.innerWidth < 750);\n window.addEventListener('resize', handler);\n return () => window.removeEventListener('resize', handler);\n }, []);\n\n // Prop overrides window detection — allows simulators/tests to force layout\n const isMobile = isMobileProp ?? isMobileWindow;\n\n const buildEventProperties = useCallback(\n () =>\n buildTrackingProperties({\n context: data.context,\n campaignId: data.campaignId,\n group: data.group,\n ...data.metadata,\n ...trackingConfig.properties,\n }),\n [data, trackingConfig.properties],\n );\n\n const handleClick = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'click',\n );\n track(eventName, buildEventProperties());\n if (onCtaClick) {\n onCtaClick(data.cta);\n } else {\n handleCtaClick(data.cta, log);\n }\n }, [data, trackingConfig, buildEventProperties, track, onCtaClick, log]);\n\n const handleClose = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'close',\n );\n track(eventName, buildEventProperties());\n onClose?.();\n }, [onClose, data, trackingConfig, buildEventProperties, track]);\n\n useEffect(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'view',\n );\n track(eventName, buildEventProperties());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const ctaLink = (\n <Link\n as=\"a\"\n textDecoration=\"none\"\n onClick={handleClick}\n data-testid=\"live-state-cta-link\">\n <Text color=\"primary-interactive\" fontSize=\"base\">\n {data.cta.label}\n </Text>\n </Link>\n );\n\n return (\n <Card>\n <Box display=\"flex\" flexDirection=\"row\" justifyContent=\"space-between\">\n {/* Left: icon + text (+ cta on mobile) */}\n <Box\n display=\"flex\"\n gap=\"4\"\n alignItems={isMobile ? 'flex-start' : 'center'}\n paddingRight=\"1-5\">\n <Box minWidth=\"32px\">\n <div\n style={{\n width: 32,\n height: 32,\n borderRadius: '35%',\n borderColor: variant === 'white' ? '#E7E7E7' : 'transparent',\n borderWidth: 1,\n borderStyle: 'solid',\n background: iconConfig.background,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n <MoneyIcon style={{ color: iconConfig.icon }} />\n </div>\n </Box>\n\n <Box display=\"grid\" gap=\"2\">\n <Box>\n <RichText content={data.title} fontSize=\"base\" color=\"neutral-textHigh\" />\n <RichText content={data.message} fontSize=\"base\" color=\"neutral-textLow\" />\n </Box>\n\n {/* CTA shown below text on mobile */}\n {isMobile && ctaLink}\n </Box>\n </Box>\n\n {/* Right: cta (desktop only) + close button */}\n {(!isMobile || onClose) && (\n <Box\n display=\"flex\"\n flexDirection={isMobile ? 'column' : 'row'}\n alignItems={isMobile ? 'flex-end' : 'center'}\n gap=\"2\">\n {/* CTA shown on the right side on desktop */}\n {!isMobile && ctaLink}\n\n {onClose && (\n <button\n type=\"button\"\n data-testid=\"live-state-close-button\"\n aria-label=\"Fechar notificação\"\n onClick={handleClose}\n style={{\n background: 'none',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n <Icon\n source={<CloseIcon width=\"18px\" height=\"18px\" />}\n color=\"neutral-interactive\"\n />\n </button>\n )}\n </Box>\n )}\n </Box>\n </Card>\n );\n}\n","import type { LiveStateRendererProps } from '../types';\nimport { useClosable } from '../hooks/useClosable';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { LiveStateAlert } from './LiveStateAlert';\nimport { LiveStateInfo } from './LiveStateInfo';\n\n/**\n * LiveStateRenderer\n *\n * Main component that validates payload, filters by context, and renders\n * the appropriate visual component (Alert or Info) based on type.\n * Handles closable notifications via useClosable when closable is configured.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateRenderer({\n data,\n loading,\n trackingConfig,\n allowedContexts,\n defaultVariant,\n isMobile,\n onCtaClick,\n}: LiveStateRendererProps) {\n const { log, onNavigate } = useLiveStateContext();\n\n // Priority: onCtaClick (per-renderer) > onNavigate (provider) > default handler\n const resolvedCtaClick = onCtaClick ?? onNavigate;\n const { isVisible, close } = useClosable({\n context: data?.context ?? '',\n id: data?.campaignId,\n maxCloseTimes: data?.metadata?.maxCloseTimes,\n expiresIn: data?.metadata?.expiresIn,\n });\n\n // Don't render while loading or no data\n if (loading || !data) {\n return null;\n }\n\n // Validate required fields\n if (\n !data.context ||\n !data.type ||\n !data.title ||\n !data.message ||\n !data.cta?.label ||\n !data.cta?.url ||\n !data.cta?.type\n ) {\n log('warn', 'Invalid payload, missing required fields', { data });\n return null;\n }\n\n // Filter by allowed contexts\n if (allowedContexts && !allowedContexts.includes(data.context)) {\n return null;\n }\n\n // Awaiting localStorage check\n if (isVisible === null) {\n return null;\n }\n\n // Hidden — user reached maxCloseTimes\n if (!isVisible) {\n return null;\n }\n\n // Pass onClose whenever closable is true.\n // campaignId is optional — useClosable handles id=undefined gracefully\n // (always visible, no localStorage tracking).\n const onClose = data.closable ? close : undefined;\n\n // Render appropriate component based on type\n if (data.type === 'alert' || data.type === 'warning') {\n return (\n <LiveStateAlert\n data={data}\n onClose={onClose}\n trackingConfig={trackingConfig}\n onCtaClick={resolvedCtaClick}\n />\n );\n }\n\n if (data.type === 'info') {\n return (\n <LiveStateInfo\n data={data}\n onClose={onClose}\n trackingConfig={trackingConfig}\n defaultVariant={defaultVariant}\n isMobile={isMobile}\n onCtaClick={resolvedCtaClick}\n />\n );\n }\n\n log('warn', `Unknown type: ${data.type}`, { data });\n return null;\n}\n"],"mappings":"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"}
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-DeRIGqmT.js";
1
+ import { a as e, i as t, n, o as r, r as i, t as a } from "./LiveStateProvider-V3rVH59O.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";
@@ -6,28 +6,34 @@ import { Alert as f, Box as p, Button as m, Card as h, Icon as g, Link as _, Tex
6
6
  import { CloseIcon as y, MoneyIcon as b } from "@nimbus-ds/icons";
7
7
  //#region src/hooks/useLiveState.ts
8
8
  function x() {
9
- let { fetcher: e, log: t } = n(), { data: r, error: i, isLoading: a, mutate: o } = l("live-state", e, {
9
+ let { fetcher: e, mockData: t, log: r } = n(), i = t !== void 0, { data: a, error: o, isLoading: s, mutate: c } = l(i ? null : "live-state", e, {
10
10
  revalidateOnFocus: !1,
11
11
  revalidateOnReconnect: !0,
12
12
  dedupingInterval: 6e4,
13
13
  shouldRetryOnError: !1,
14
14
  onError: (e) => {
15
- t("error", "Failed to fetch live state", { error: e });
15
+ r("error", "Failed to fetch live state", { error: e });
16
16
  }
17
- });
18
- return {
19
- liveState: r ?? null,
20
- isLoading: a,
21
- isError: i != null,
22
- error: i ?? null,
23
- refresh: async () => {
24
- try {
25
- await o();
26
- } catch (e) {
27
- t("error", "Failed to refresh live state", { error: e });
28
- }
17
+ }), u = async () => {
18
+ if (!i) try {
19
+ await c();
20
+ } catch (e) {
21
+ r("error", "Failed to refresh live state", { error: e });
29
22
  }
30
23
  };
24
+ return i ? {
25
+ liveState: t,
26
+ isLoading: !1,
27
+ isError: !1,
28
+ error: null,
29
+ refresh: u
30
+ } : {
31
+ liveState: a ?? null,
32
+ isLoading: s,
33
+ isError: o != null,
34
+ error: o ?? null,
35
+ refresh: u
36
+ };
31
37
  }
32
38
  //#endregion
33
39
  //#region src/hooks/useTrackEvent.ts
@@ -146,15 +152,46 @@ function O(e, t) {
146
152
  }
147
153
  }
148
154
  //#endregion
155
+ //#region src/utils/sanitize.ts
156
+ var k = new Set([
157
+ "strong",
158
+ "em",
159
+ "b",
160
+ "i",
161
+ "br",
162
+ "span"
163
+ ]), A = /<(script|style|iframe|object|embed|form|input|button|textarea|select|link|meta|head|body|html|svg|math)[\s\S]*?<\/\1\s*>|<(script|style|iframe|object|embed|form|input|button|textarea|select|link|meta|head|body|html|svg|math)[^>]*\/?>/gi;
164
+ function j(e) {
165
+ return e.replace(A, "").replace(/<([a-zA-Z][a-zA-Z0-9]*)\s[^>]*>/g, "<$1>").replace(/<\/?([a-zA-Z][a-zA-Z0-9]*)[^>]*>/g, (e, t) => k.has(t.toLowerCase()) ? e : "");
166
+ }
167
+ function M(e) {
168
+ return /<[a-zA-Z][^>]*>/.test(e);
169
+ }
170
+ //#endregion
171
+ //#region src/components/RichText.tsx
172
+ function N({ content: e, ...t }) {
173
+ if (M(e)) {
174
+ let n = j(e);
175
+ return /* @__PURE__ */ u(v, {
176
+ ...t,
177
+ children: /* @__PURE__ */ u("span", { dangerouslySetInnerHTML: { __html: n } })
178
+ });
179
+ }
180
+ return /* @__PURE__ */ u(v, {
181
+ ...t,
182
+ children: e
183
+ });
184
+ }
185
+ //#endregion
149
186
  //#region src/components/LiveStateAlert.tsx
150
- function k({ data: e, trackingConfig: r, onClose: a, onCtaClick: c }) {
187
+ function P({ data: e, trackingConfig: r, onClose: a, onCtaClick: c }) {
151
188
  let l = S(), { log: h } = n(), g = e.type === "alert" ? "danger" : "warning", _ = o(() => t({
152
189
  context: e.context,
153
190
  campaignId: e.campaignId,
154
191
  group: e.group,
155
192
  ...e.metadata,
156
193
  ...r.properties
157
- }), [e, r.properties]), y = o(() => {
194
+ }), [e, r.properties]), v = o(() => {
158
195
  l(i(r.prefix, r.page, e.context, "click"), _()), c ? c(e.cta) : O(e.cta, h);
159
196
  }, [
160
197
  e,
@@ -163,7 +200,7 @@ function k({ data: e, trackingConfig: r, onClose: a, onCtaClick: c }) {
163
200
  l,
164
201
  c,
165
202
  h
166
- ]), b = o(() => {
203
+ ]), y = o(() => {
167
204
  l(i(r.prefix, r.page, e.context, "close"), _()), a?.();
168
205
  }, [
169
206
  a,
@@ -177,7 +214,7 @@ function k({ data: e, trackingConfig: r, onClose: a, onCtaClick: c }) {
177
214
  }, []), /* @__PURE__ */ u(f, {
178
215
  appearance: g,
179
216
  title: e.title,
180
- onRemove: a ? b : void 0,
217
+ onRemove: a ? y : void 0,
181
218
  children: /* @__PURE__ */ d(p, {
182
219
  display: "flex",
183
220
  alignItems: "flex-start",
@@ -187,14 +224,14 @@ function k({ data: e, trackingConfig: r, onClose: a, onCtaClick: c }) {
187
224
  children: [/* @__PURE__ */ u(p, {
188
225
  flex: "1 1 auto",
189
226
  minWidth: "220px",
190
- children: /* @__PURE__ */ u(v, { children: e.message })
227
+ children: /* @__PURE__ */ u(N, { content: e.message })
191
228
  }), /* @__PURE__ */ u(p, {
192
229
  display: "flex",
193
230
  justifyContent: "flex-end",
194
231
  marginTop: "2",
195
232
  children: /* @__PURE__ */ u(m, {
196
233
  appearance: "primary",
197
- onClick: y,
234
+ onClick: v,
198
235
  children: e.cta.label
199
236
  })
200
237
  })]
@@ -203,7 +240,7 @@ function k({ data: e, trackingConfig: r, onClose: a, onCtaClick: c }) {
203
240
  }
204
241
  //#endregion
205
242
  //#region src/components/LiveStateInfo.tsx
206
- var A = {
243
+ var F = {
207
244
  blue: {
208
245
  background: "#0050C3",
209
246
  icon: "#FFFFFF"
@@ -213,43 +250,43 @@ var A = {
213
250
  icon: "#0059D5"
214
251
  }
215
252
  };
216
- function j({ data: e, trackingConfig: r, defaultVariant: a = "blue", onClose: l, isMobile: f, onCtaClick: m }) {
217
- let x = S(), { log: C } = n(), w = e.variant || a, T = A[w], [E, D] = c(() => window.innerWidth < 750);
253
+ function I({ data: e, trackingConfig: r, defaultVariant: a = "blue", onClose: l, isMobile: f, onCtaClick: m }) {
254
+ let x = S(), { log: C } = n(), w = e.variant || a, T = F[w], [E, D] = c(() => window.innerWidth < 750);
218
255
  s(() => {
219
256
  let e = () => D(window.innerWidth < 750);
220
257
  return window.addEventListener("resize", e), () => window.removeEventListener("resize", e);
221
258
  }, []);
222
- let k = f ?? E, j = o(() => t({
259
+ let k = f ?? E, A = o(() => t({
223
260
  context: e.context,
224
261
  campaignId: e.campaignId,
225
262
  group: e.group,
226
263
  ...e.metadata,
227
264
  ...r.properties
228
- }), [e, r.properties]), M = o(() => {
229
- x(i(r.prefix, r.page, e.context, "click"), j()), m ? m(e.cta) : O(e.cta, C);
265
+ }), [e, r.properties]), j = o(() => {
266
+ x(i(r.prefix, r.page, e.context, "click"), A()), m ? m(e.cta) : O(e.cta, C);
230
267
  }, [
231
268
  e,
232
269
  r,
233
- j,
270
+ A,
234
271
  x,
235
272
  m,
236
273
  C
237
- ]), N = o(() => {
238
- x(i(r.prefix, r.page, e.context, "close"), j()), l?.();
274
+ ]), M = o(() => {
275
+ x(i(r.prefix, r.page, e.context, "close"), A()), l?.();
239
276
  }, [
240
277
  l,
241
278
  e,
242
279
  r,
243
- j,
280
+ A,
244
281
  x
245
282
  ]);
246
283
  s(() => {
247
- x(i(r.prefix, r.page, e.context, "view"), j());
284
+ x(i(r.prefix, r.page, e.context, "view"), A());
248
285
  }, []);
249
286
  let P = /* @__PURE__ */ u(_, {
250
287
  as: "a",
251
288
  textDecoration: "none",
252
- onClick: M,
289
+ onClick: j,
253
290
  "data-testid": "live-state-cta-link",
254
291
  children: /* @__PURE__ */ u(v, {
255
292
  color: "primary-interactive",
@@ -286,14 +323,14 @@ function j({ data: e, trackingConfig: r, defaultVariant: a = "blue", onClose: l,
286
323
  }), /* @__PURE__ */ d(p, {
287
324
  display: "grid",
288
325
  gap: "2",
289
- children: [/* @__PURE__ */ d(p, { children: [/* @__PURE__ */ u(v, {
326
+ children: [/* @__PURE__ */ d(p, { children: [/* @__PURE__ */ u(N, {
327
+ content: e.title,
290
328
  fontSize: "base",
291
- color: "neutral-textHigh",
292
- children: e.title
293
- }), /* @__PURE__ */ u(v, {
329
+ color: "neutral-textHigh"
330
+ }), /* @__PURE__ */ u(N, {
331
+ content: e.message,
294
332
  fontSize: "base",
295
- color: "neutral-textLow",
296
- children: e.message
333
+ color: "neutral-textLow"
297
334
  })] }), k && P]
298
335
  })]
299
336
  }), (!k || l) && /* @__PURE__ */ d(p, {
@@ -305,7 +342,7 @@ function j({ data: e, trackingConfig: r, defaultVariant: a = "blue", onClose: l,
305
342
  type: "button",
306
343
  "data-testid": "live-state-close-button",
307
344
  "aria-label": "Fechar notificação",
308
- onClick: N,
345
+ onClick: M,
309
346
  style: {
310
347
  background: "none",
311
348
  border: "none",
@@ -328,8 +365,8 @@ function j({ data: e, trackingConfig: r, defaultVariant: a = "blue", onClose: l,
328
365
  }
329
366
  //#endregion
330
367
  //#region src/components/LiveStateRenderer.tsx
331
- function M({ data: e, loading: t, trackingConfig: r, allowedContexts: i, defaultVariant: a, isMobile: o, onCtaClick: s }) {
332
- let { log: c } = n(), { isVisible: l, close: d } = D({
368
+ function L({ data: e, loading: t, trackingConfig: r, allowedContexts: i, defaultVariant: a, isMobile: o, onCtaClick: s }) {
369
+ let { log: c, onNavigate: l } = n(), d = s ?? l, { isVisible: f, close: p } = D({
333
370
  context: e?.context ?? "",
334
371
  id: e?.campaignId,
335
372
  maxCloseTimes: e?.metadata?.maxCloseTimes,
@@ -337,23 +374,23 @@ function M({ data: e, loading: t, trackingConfig: r, allowedContexts: i, default
337
374
  });
338
375
  if (t || !e) return null;
339
376
  if (!e.context || !e.type || !e.title || !e.message || !e.cta?.label || !e.cta?.url || !e.cta?.type) return c("warn", "Invalid payload, missing required fields", { data: e }), null;
340
- if (i && !i.includes(e.context) || l === null || !l) return null;
341
- let f = e.closable ? d : void 0;
342
- return e.type === "alert" || e.type === "warning" ? /* @__PURE__ */ u(k, {
377
+ if (i && !i.includes(e.context) || f === null || !f) return null;
378
+ let m = e.closable ? p : void 0;
379
+ return e.type === "alert" || e.type === "warning" ? /* @__PURE__ */ u(P, {
343
380
  data: e,
344
- onClose: f,
381
+ onClose: m,
345
382
  trackingConfig: r,
346
- onCtaClick: s
347
- }) : e.type === "info" ? /* @__PURE__ */ u(j, {
383
+ onCtaClick: d
384
+ }) : e.type === "info" ? /* @__PURE__ */ u(I, {
348
385
  data: e,
349
- onClose: f,
386
+ onClose: m,
350
387
  trackingConfig: r,
351
388
  defaultVariant: a,
352
389
  isMobile: o,
353
- onCtaClick: s
390
+ onCtaClick: d
354
391
  }) : (c("warn", `Unknown type: ${e.type}`, { data: e }), null);
355
392
  }
356
393
  //#endregion
357
- export { C as LIVE_STATE_STORAGE_KEY, k as LiveStateAlert, j as LiveStateInfo, a as LiveStateProvider, M as LiveStateRenderer, O as handleCtaClick, e as trackEvent, x as useLiveState, S as useTrackEvent };
394
+ export { C as LIVE_STATE_STORAGE_KEY, P as LiveStateAlert, I as LiveStateInfo, a as LiveStateProvider, L as LiveStateRenderer, O as handleCtaClick, e as trackEvent, x as useLiveState, S as useTrackEvent };
358
395
 
359
396
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/hooks/useLiveState.ts","../src/hooks/useTrackEvent.ts","../src/utils/closable-storage.ts","../src/hooks/useClosable.ts","../src/utils/cta.ts","../src/components/LiveStateAlert.tsx","../src/components/LiveStateInfo.tsx","../src/components/LiveStateRenderer.tsx"],"sourcesContent":["import useSWR from 'swr';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport type { LiveStateResponse } from '../types';\n\ninterface UseLiveStateReturn {\n /** Live state data */\n liveState: LiveStateResponse | null;\n\n /** True while the first fetch is in progress */\n isLoading: boolean;\n\n /**\n * True when the last fetch attempt resulted in an error.\n * Use this to implement fallbacks or custom error logging.\n * Note: the lib already logs a warning to the console on every error.\n */\n isError: boolean;\n\n /**\n * The raw error from the last failed fetch, or null.\n * Pair with `isError` for more specific error handling.\n */\n error: Error | null;\n\n /** Manually trigger a re-fetch */\n refresh: () => Promise<void>;\n}\n\n/**\n * useLiveState hook\n *\n * Fetches and manages live state data using SWR with automatic caching,\n * deduplication, and revalidation.\n *\n * Must be used within LiveStateProvider. See examples/ directory for usage.\n */\nexport function useLiveState(): UseLiveStateReturn {\n const { fetcher, log } = useLiveStateContext();\n\n const { data, error, isLoading, mutate } = useSWR<LiveStateResponse | null>(\n 'live-state',\n fetcher,\n {\n revalidateOnFocus: false,\n revalidateOnReconnect: true,\n dedupingInterval: 60000, // 1 minute\n shouldRetryOnError: false, // Fail silently\n onError: err => {\n log('error', 'Failed to fetch live state', { error: err });\n },\n },\n );\n\n const refresh = async () => {\n try {\n await mutate();\n } catch (err) {\n log('error', 'Failed to refresh live state', { error: err });\n }\n };\n\n return {\n liveState: data ?? null,\n isLoading,\n isError: error != null,\n error: error ?? null,\n refresh,\n };\n}\n","import { useCallback } from 'react';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { trackEvent } from '../utils/analytics';\nimport type { TrackingProperties } from '../types';\n\n/**\n * useTrackEvent\n *\n * Returns a `track` function that fires both the internal analytics\n * (Amplitude + Clarity) and the optional `onEvent` callback provided\n * to LiveStateProvider — useful for simulators and debugging tools.\n */\nexport function useTrackEvent() {\n const { onEvent, disabled, log } = useLiveStateContext();\n\n const track = useCallback(\n (eventName: string, properties?: TrackingProperties) => {\n // onEvent callback fires even when disabled — it is an observability\n // hook, not an analytics SDK, so the caller opts in deliberately.\n onEvent?.(eventName, properties);\n\n // Skip SDK calls when analytics are disabled (e.g. dev/test environments)\n if (!disabled) {\n trackEvent(eventName, properties, log);\n }\n },\n [onEvent, disabled, log],\n );\n\n return track;\n}\n","export const LIVE_STATE_STORAGE_KEY = '@tiendanube/live-state:closable';\n\n/** @internal */\nconst STORAGE_KEY = LIVE_STATE_STORAGE_KEY;\n\nexport interface ClosableState {\n count: number;\n closedAt: number;\n}\n\nexport type ClosableStates = Record<string, Record<string, ClosableState>>;\n\n/**\n * Reads closable states from localStorage.\n * Normalises legacy format (plain number) to the current shape ({ count, closedAt }).\n * Returns an empty object on any error (fail-silent).\n */\nexport function getClosableStates(): ClosableStates {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return {};\n\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== 'object' || parsed === null) return {};\n\n const normalized: ClosableStates = {};\n\n for (const [ctx, ctxState] of Object.entries(\n parsed as Record<string, unknown>,\n )) {\n if (typeof ctxState !== 'object' || ctxState === null) continue;\n\n normalized[ctx] = {};\n\n for (const [id, entry] of Object.entries(\n ctxState as Record<string, unknown>,\n )) {\n if (typeof entry === 'number') {\n // legacy format: plain close count\n normalized[ctx][id] = { count: entry, closedAt: 0 };\n } else if (typeof entry === 'object' && entry !== null) {\n const candidate = entry as Partial<ClosableState>;\n normalized[ctx][id] = {\n count: typeof candidate.count === 'number' ? candidate.count : 0,\n closedAt:\n typeof candidate.closedAt === 'number' ? candidate.closedAt : 0,\n };\n }\n }\n }\n\n return normalized;\n } catch {\n return {};\n }\n}\n\n/**\n * Persists closable states to localStorage.\n * Fails silently if localStorage is unavailable or full.\n */\nexport function setClosableStates(states: ClosableStates): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(states));\n } catch {\n // localStorage unavailable or quota exceeded — application continues normally\n }\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport {\n getClosableStates,\n setClosableStates,\n} from '../utils/closable-storage';\n\nexport interface UseClosableOptions {\n /** Notification context (e.g. 'awareness', 'charge') */\n context: string;\n /** Unique notification ID (campaignId). If absent, always visible — no close tracking. */\n id: string | undefined;\n /** Maximum number of times the user can close before it is hidden permanently */\n maxCloseTimes?: number;\n /** Milliseconds after which the close counter resets (optional) */\n expiresIn?: number;\n}\n\nexport interface UseClosableReturn {\n /** Whether the notification should be shown. null = initial check in progress. */\n isVisible: boolean | null;\n /** Call this to record a close action */\n close: () => void;\n}\n\n/**\n * Manages the close state of a notification using localStorage.\n *\n * After the user closes a notification `maxCloseTimes` times, it is hidden\n * permanently. Optionally, the counter resets after `expiresIn` milliseconds.\n *\n * Fails silently — errors in localStorage never break the application.\n *\n * @example\n * ```tsx\n * const { isVisible, close } = useClosable({\n * context: 'awareness',\n * id: 'awareness-2026-q1',\n * maxCloseTimes: 3,\n * expiresIn: 86_400_000, // 1 day\n * });\n *\n * if (!isVisible) return null;\n * return <Notification onClose={close} />;\n * ```\n */\nexport function useClosable({\n context,\n id,\n maxCloseTimes = 3,\n expiresIn,\n}: UseClosableOptions): UseClosableReturn {\n const [isVisible, setIsVisible] = useState<boolean | null>(null);\n\n useEffect(() => {\n // No ID means no close tracking — always show\n if (!id) {\n setIsVisible(true);\n return;\n }\n\n const states = getClosableStates();\n const entry = states[context]?.[id];\n\n if (!entry) {\n setIsVisible(true);\n return;\n }\n\n const { count, closedAt } = entry;\n\n // Check expiry — reset counter and show if expired\n if (expiresIn != null && closedAt > 0) {\n const elapsed = Date.now() - closedAt;\n if (elapsed > expiresIn) {\n const updated = { ...states };\n if (updated[context]) {\n delete updated[context][id];\n setClosableStates(updated);\n }\n setIsVisible(true);\n return;\n }\n }\n\n setIsVisible(count < maxCloseTimes);\n }, [context, id, maxCloseTimes, expiresIn]);\n\n const close = useCallback(() => {\n // Always hide visually\n setIsVisible(false);\n\n // Only persist to localStorage if there is an id to track\n if (!id) return;\n\n const states = getClosableStates();\n const currentCount = states[context]?.[id]?.count ?? 0;\n\n const updated: typeof states = {\n ...states,\n [context]: {\n ...(states[context] ?? {}),\n [id]: {\n count: currentCount + 1,\n closedAt: Date.now(),\n },\n },\n };\n\n setClosableStates(updated);\n }, [context, id]);\n\n return { isVisible, close };\n}\n","import type { CtaConfig } from '../types';\nimport { createLogger, type LoggerFn } from './logger';\n\n/**\n * Handle CTA click based on type\n *\n * @param cta - CTA configuration from LiveStateResponse\n * @param log - Optional logger (defaults to console.warn)\n */\nexport function handleCtaClick(cta: CtaConfig, log?: LoggerFn): void {\n const logger = createLogger(log);\n try {\n switch (cta.type) {\n case 'redirect':\n window.location.href = cta.url;\n break;\n\n case 'whatsapp': {\n const whatsappUrl = cta.whatsappMessage\n ? `${cta.url}?text=${encodeURIComponent(cta.whatsappMessage)}`\n : cta.url;\n window.open(whatsappUrl, '_blank');\n break;\n }\n\n case 'external':\n window.open(cta.url, '_blank');\n break;\n\n default:\n logger('warn', `Unknown CTA type: ${(cta as CtaConfig).type}`, { cta });\n }\n } catch (error) {\n logger('error', 'Failed to handle CTA click', { cta, error });\n }\n}\n","import { useCallback, useEffect } from 'react';\nimport { Alert, Box, Button, Text } from '@nimbus-ds/components';\nimport type { LiveStateResponse, TrackingConfig } from '../types';\nimport { buildTrackingProperties, buildEventName } from '../utils/analytics';\nimport { handleCtaClick } from '../utils/cta';\nimport { useTrackEvent } from '../hooks/useTrackEvent';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\n\nexport interface LiveStateAlertProps {\n data: LiveStateResponse;\n trackingConfig: TrackingConfig;\n /** When provided, shows a close button and calls this on dismiss */\n onClose?: () => void;\n /**\n * Override CTA click behaviour.\n * When provided, replaces default navigation so the caller controls what\n * happens on click (e.g. prevent navigation in a simulator, use a router).\n */\n onCtaClick?: (cta: import('../types').CtaConfig) => void;\n}\n\n/**\n * LiveStateAlert\n *\n * Renders an Alert component (Nimbus) for alert/warning types.\n * Automatically tracks view, click, and close events.\n * Shows a close button when onClose is provided.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateAlert({\n data,\n trackingConfig,\n onClose,\n onCtaClick,\n}: LiveStateAlertProps) {\n const track = useTrackEvent();\n const { log } = useLiveStateContext();\n const appearance = data.type === 'alert' ? 'danger' : 'warning';\n\n const buildEventProperties = useCallback(\n () =>\n buildTrackingProperties({\n context: data.context,\n campaignId: data.campaignId,\n group: data.group,\n ...data.metadata,\n ...trackingConfig.properties,\n }),\n [data, trackingConfig.properties],\n );\n\n const handleClick = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'click',\n );\n track(eventName, buildEventProperties());\n if (onCtaClick) {\n onCtaClick(data.cta);\n } else {\n handleCtaClick(data.cta, log);\n }\n }, [data, trackingConfig, buildEventProperties, track, onCtaClick, log]);\n\n const handleClose = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'close',\n );\n track(eventName, buildEventProperties());\n onClose?.();\n }, [onClose, data, trackingConfig, buildEventProperties, track]);\n\n useEffect(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'view',\n );\n track(eventName, buildEventProperties());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <Alert\n appearance={appearance}\n title={data.title}\n onRemove={onClose ? handleClose : undefined}>\n <Box\n display=\"flex\"\n alignItems=\"flex-start\"\n gap=\"3\"\n flexWrap=\"wrap\"\n flexDirection=\"column\">\n <Box flex=\"1 1 auto\" minWidth=\"220px\">\n <Text>{data.message}</Text>\n </Box>\n\n <Box display=\"flex\" justifyContent=\"flex-end\" marginTop=\"2\">\n <Button appearance=\"primary\" onClick={handleClick}>\n {data.cta.label}\n </Button>\n </Box>\n </Box>\n </Alert>\n );\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { Card, Box, Text, Link, Icon } from '@nimbus-ds/components';\nimport { MoneyIcon, CloseIcon } from '@nimbus-ds/icons';\nimport type { LiveStateResponse, TrackingConfig } from '../types';\nimport { buildTrackingProperties, buildEventName } from '../utils/analytics';\nimport { handleCtaClick } from '../utils/cta';\nimport { useTrackEvent } from '../hooks/useTrackEvent';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\n\nexport interface LiveStateInfoProps {\n data: LiveStateResponse;\n trackingConfig: TrackingConfig;\n defaultVariant?: 'blue' | 'white';\n /** When provided, shows a close button and calls this on dismiss */\n onClose?: () => void;\n /**\n * Override mobile layout detection.\n * When provided, bypasses `window.innerWidth < 750` so simulators and\n * testing tools can force a specific layout without resizing the window.\n */\n isMobile?: boolean;\n /**\n * Override CTA click behaviour.\n * When provided, replaces default navigation so the caller controls what\n * happens on click (e.g. prevent navigation in a simulator, use a router).\n */\n onCtaClick?: (cta: import('../types').CtaConfig) => void;\n}\n\nconst ICON_COLOR_MAP = {\n blue: { background: '#0050C3', icon: '#FFFFFF' },\n white: { background: '#FFFFFF', icon: '#0059D5' },\n} as const;\n\n/**\n * LiveStateInfo\n *\n * Renders a Card component (Nimbus) for info type.\n * Uses MoneyIcon with colored circle background, Text components,\n * and a Link-style CTA — matching the LendingBanner design reference.\n * Automatically tracks view, click, and close events.\n * Shows a close button when onClose is provided.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateInfo({\n data,\n trackingConfig,\n defaultVariant = 'blue',\n onClose,\n isMobile: isMobileProp,\n onCtaClick,\n}: LiveStateInfoProps) {\n const track = useTrackEvent();\n const { log } = useLiveStateContext();\n const variant = data.variant || defaultVariant;\n const iconConfig = ICON_COLOR_MAP[variant];\n\n const [isMobileWindow, setIsMobileWindow] = useState(\n () => window.innerWidth < 750,\n );\n\n useEffect(() => {\n const handler = () => setIsMobileWindow(window.innerWidth < 750);\n window.addEventListener('resize', handler);\n return () => window.removeEventListener('resize', handler);\n }, []);\n\n // Prop overrides window detection — allows simulators/tests to force layout\n const isMobile = isMobileProp ?? isMobileWindow;\n\n const buildEventProperties = useCallback(\n () =>\n buildTrackingProperties({\n context: data.context,\n campaignId: data.campaignId,\n group: data.group,\n ...data.metadata,\n ...trackingConfig.properties,\n }),\n [data, trackingConfig.properties],\n );\n\n const handleClick = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'click',\n );\n track(eventName, buildEventProperties());\n if (onCtaClick) {\n onCtaClick(data.cta);\n } else {\n handleCtaClick(data.cta, log);\n }\n }, [data, trackingConfig, buildEventProperties, track, onCtaClick, log]);\n\n const handleClose = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'close',\n );\n track(eventName, buildEventProperties());\n onClose?.();\n }, [onClose, data, trackingConfig, buildEventProperties, track]);\n\n useEffect(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'view',\n );\n track(eventName, buildEventProperties());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const ctaLink = (\n <Link\n as=\"a\"\n textDecoration=\"none\"\n onClick={handleClick}\n data-testid=\"live-state-cta-link\">\n <Text color=\"primary-interactive\" fontSize=\"base\">\n {data.cta.label}\n </Text>\n </Link>\n );\n\n return (\n <Card>\n <Box display=\"flex\" flexDirection=\"row\" justifyContent=\"space-between\">\n {/* Left: icon + text (+ cta on mobile) */}\n <Box\n display=\"flex\"\n gap=\"4\"\n alignItems={isMobile ? 'flex-start' : 'center'}\n paddingRight=\"1-5\">\n <Box minWidth=\"32px\">\n <div\n style={{\n width: 32,\n height: 32,\n borderRadius: '35%',\n borderColor: variant === 'white' ? '#E7E7E7' : 'transparent',\n borderWidth: 1,\n borderStyle: 'solid',\n background: iconConfig.background,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n <MoneyIcon style={{ color: iconConfig.icon }} />\n </div>\n </Box>\n\n <Box display=\"grid\" gap=\"2\">\n <Box>\n <Text fontSize=\"base\" color=\"neutral-textHigh\">\n {data.title}\n </Text>\n <Text fontSize=\"base\" color=\"neutral-textLow\">\n {data.message}\n </Text>\n </Box>\n\n {/* CTA shown below text on mobile */}\n {isMobile && ctaLink}\n </Box>\n </Box>\n\n {/* Right: cta (desktop only) + close button */}\n {(!isMobile || onClose) && (\n <Box\n display=\"flex\"\n flexDirection={isMobile ? 'column' : 'row'}\n alignItems={isMobile ? 'flex-end' : 'center'}\n gap=\"2\">\n {/* CTA shown on the right side on desktop */}\n {!isMobile && ctaLink}\n\n {onClose && (\n <button\n type=\"button\"\n data-testid=\"live-state-close-button\"\n aria-label=\"Fechar notificação\"\n onClick={handleClose}\n style={{\n background: 'none',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n <Icon\n source={<CloseIcon width=\"18px\" height=\"18px\" />}\n color=\"neutral-interactive\"\n />\n </button>\n )}\n </Box>\n )}\n </Box>\n </Card>\n );\n}\n","import type { LiveStateRendererProps } from '../types';\nimport { useClosable } from '../hooks/useClosable';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { LiveStateAlert } from './LiveStateAlert';\nimport { LiveStateInfo } from './LiveStateInfo';\n\n/**\n * LiveStateRenderer\n *\n * Main component that validates payload, filters by context, and renders\n * the appropriate visual component (Alert or Info) based on type.\n * Handles closable notifications via useClosable when closable is configured.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateRenderer({\n data,\n loading,\n trackingConfig,\n allowedContexts,\n defaultVariant,\n isMobile,\n onCtaClick,\n}: LiveStateRendererProps) {\n const { log } = useLiveStateContext();\n const { isVisible, close } = useClosable({\n context: data?.context ?? '',\n id: data?.campaignId,\n maxCloseTimes: data?.metadata?.maxCloseTimes,\n expiresIn: data?.metadata?.expiresIn,\n });\n\n // Don't render while loading or no data\n if (loading || !data) {\n return null;\n }\n\n // Validate required fields\n if (\n !data.context ||\n !data.type ||\n !data.title ||\n !data.message ||\n !data.cta?.label ||\n !data.cta?.url ||\n !data.cta?.type\n ) {\n log('warn', 'Invalid payload, missing required fields', { data });\n return null;\n }\n\n // Filter by allowed contexts\n if (allowedContexts && !allowedContexts.includes(data.context)) {\n return null;\n }\n\n // Awaiting localStorage check\n if (isVisible === null) {\n return null;\n }\n\n // Hidden — user reached maxCloseTimes\n if (!isVisible) {\n return null;\n }\n\n // Pass onClose whenever closable is true.\n // campaignId is optional — useClosable handles id=undefined gracefully\n // (always visible, no localStorage tracking).\n const onClose = data.closable ? close : undefined;\n\n // Render appropriate component based on type\n if (data.type === 'alert' || data.type === 'warning') {\n return (\n <LiveStateAlert\n data={data}\n onClose={onClose}\n trackingConfig={trackingConfig}\n onCtaClick={onCtaClick}\n />\n );\n }\n\n if (data.type === 'info') {\n return (\n <LiveStateInfo\n data={data}\n onClose={onClose}\n trackingConfig={trackingConfig}\n defaultVariant={defaultVariant}\n isMobile={isMobile}\n onCtaClick={onCtaClick}\n />\n );\n }\n\n log('warn', `Unknown type: ${data.type}`, { data });\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/hooks/useLiveState.ts","../src/hooks/useTrackEvent.ts","../src/utils/closable-storage.ts","../src/hooks/useClosable.ts","../src/utils/cta.ts","../src/utils/sanitize.ts","../src/components/RichText.tsx","../src/components/LiveStateAlert.tsx","../src/components/LiveStateInfo.tsx","../src/components/LiveStateRenderer.tsx"],"sourcesContent":["import useSWR from 'swr';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport type { LiveStateResponse } from '../types';\n\ninterface UseLiveStateReturn {\n /** Live state data */\n liveState: LiveStateResponse | null;\n\n /** True while the first fetch is in progress */\n isLoading: boolean;\n\n /**\n * True when the last fetch attempt resulted in an error.\n * Use this to implement fallbacks or custom error logging.\n * Note: the lib already logs a warning to the console on every error.\n */\n isError: boolean;\n\n /**\n * The raw error from the last failed fetch, or null.\n * Pair with `isError` for more specific error handling.\n */\n error: Error | null;\n\n /** Manually trigger a re-fetch */\n refresh: () => Promise<void>;\n}\n\n/**\n * useLiveState hook\n *\n * Fetches and manages live state data using SWR with automatic caching,\n * deduplication, and revalidation.\n *\n * Must be used within LiveStateProvider. See examples/ directory for usage.\n */\nexport function useLiveState(): UseLiveStateReturn {\n const { fetcher, mockData, log } = useLiveStateContext();\n\n // When mockData is provided, bypass SWR entirely — the fetcher is never called.\n // This enables local development and Storybook without a real backend.\n const isMocked = mockData !== undefined;\n\n const { data, error, isLoading, mutate } = useSWR<LiveStateResponse | null>(\n isMocked ? null : 'live-state', // null key disables SWR when mocked\n fetcher,\n {\n revalidateOnFocus: false,\n revalidateOnReconnect: true,\n dedupingInterval: 60000, // 1 minute\n shouldRetryOnError: false, // Fail silently\n onError: err => {\n log('error', 'Failed to fetch live state', { error: err });\n },\n },\n );\n\n const refresh = async () => {\n if (isMocked) return; // no-op when mocked\n try {\n await mutate();\n } catch (err) {\n log('error', 'Failed to refresh live state', { error: err });\n }\n };\n\n if (isMocked) {\n return {\n liveState: mockData,\n isLoading: false,\n isError: false,\n error: null,\n refresh,\n };\n }\n\n return {\n liveState: data ?? null,\n isLoading,\n isError: error != null,\n error: error ?? null,\n refresh,\n };\n}\n","import { useCallback } from 'react';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { trackEvent } from '../utils/analytics';\nimport type { TrackingProperties } from '../types';\n\n/**\n * useTrackEvent\n *\n * Returns a `track` function that fires both the internal analytics\n * (Amplitude + Clarity) and the optional `onEvent` callback provided\n * to LiveStateProvider — useful for simulators and debugging tools.\n */\nexport function useTrackEvent() {\n const { onEvent, disabled, log } = useLiveStateContext();\n\n const track = useCallback(\n (eventName: string, properties?: TrackingProperties) => {\n // onEvent callback fires even when disabled — it is an observability\n // hook, not an analytics SDK, so the caller opts in deliberately.\n onEvent?.(eventName, properties);\n\n // Skip SDK calls when analytics are disabled (e.g. dev/test environments)\n if (!disabled) {\n trackEvent(eventName, properties, log);\n }\n },\n [onEvent, disabled, log],\n );\n\n return track;\n}\n","export const LIVE_STATE_STORAGE_KEY = '@tiendanube/live-state:closable';\n\n/** @internal */\nconst STORAGE_KEY = LIVE_STATE_STORAGE_KEY;\n\nexport interface ClosableState {\n count: number;\n closedAt: number;\n}\n\nexport type ClosableStates = Record<string, Record<string, ClosableState>>;\n\n/**\n * Reads closable states from localStorage.\n * Normalises legacy format (plain number) to the current shape ({ count, closedAt }).\n * Returns an empty object on any error (fail-silent).\n */\nexport function getClosableStates(): ClosableStates {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return {};\n\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== 'object' || parsed === null) return {};\n\n const normalized: ClosableStates = {};\n\n for (const [ctx, ctxState] of Object.entries(\n parsed as Record<string, unknown>,\n )) {\n if (typeof ctxState !== 'object' || ctxState === null) continue;\n\n normalized[ctx] = {};\n\n for (const [id, entry] of Object.entries(\n ctxState as Record<string, unknown>,\n )) {\n if (typeof entry === 'number') {\n // legacy format: plain close count\n normalized[ctx][id] = { count: entry, closedAt: 0 };\n } else if (typeof entry === 'object' && entry !== null) {\n const candidate = entry as Partial<ClosableState>;\n normalized[ctx][id] = {\n count: typeof candidate.count === 'number' ? candidate.count : 0,\n closedAt:\n typeof candidate.closedAt === 'number' ? candidate.closedAt : 0,\n };\n }\n }\n }\n\n return normalized;\n } catch {\n return {};\n }\n}\n\n/**\n * Persists closable states to localStorage.\n * Fails silently if localStorage is unavailable or full.\n */\nexport function setClosableStates(states: ClosableStates): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(states));\n } catch {\n // localStorage unavailable or quota exceeded — application continues normally\n }\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport {\n getClosableStates,\n setClosableStates,\n} from '../utils/closable-storage';\n\nexport interface UseClosableOptions {\n /** Notification context (e.g. 'awareness', 'charge') */\n context: string;\n /** Unique notification ID (campaignId). If absent, always visible — no close tracking. */\n id: string | undefined;\n /** Maximum number of times the user can close before it is hidden permanently */\n maxCloseTimes?: number;\n /** Milliseconds after which the close counter resets (optional) */\n expiresIn?: number;\n}\n\nexport interface UseClosableReturn {\n /** Whether the notification should be shown. null = initial check in progress. */\n isVisible: boolean | null;\n /** Call this to record a close action */\n close: () => void;\n}\n\n/**\n * Manages the close state of a notification using localStorage.\n *\n * After the user closes a notification `maxCloseTimes` times, it is hidden\n * permanently. Optionally, the counter resets after `expiresIn` milliseconds.\n *\n * Fails silently — errors in localStorage never break the application.\n *\n * @example\n * ```tsx\n * const { isVisible, close } = useClosable({\n * context: 'awareness',\n * id: 'awareness-2026-q1',\n * maxCloseTimes: 3,\n * expiresIn: 86_400_000, // 1 day\n * });\n *\n * if (!isVisible) return null;\n * return <Notification onClose={close} />;\n * ```\n */\nexport function useClosable({\n context,\n id,\n maxCloseTimes = 3,\n expiresIn,\n}: UseClosableOptions): UseClosableReturn {\n const [isVisible, setIsVisible] = useState<boolean | null>(null);\n\n useEffect(() => {\n // No ID means no close tracking — always show\n if (!id) {\n setIsVisible(true);\n return;\n }\n\n const states = getClosableStates();\n const entry = states[context]?.[id];\n\n if (!entry) {\n setIsVisible(true);\n return;\n }\n\n const { count, closedAt } = entry;\n\n // Check expiry — reset counter and show if expired\n if (expiresIn != null && closedAt > 0) {\n const elapsed = Date.now() - closedAt;\n if (elapsed > expiresIn) {\n const updated = { ...states };\n if (updated[context]) {\n delete updated[context][id];\n setClosableStates(updated);\n }\n setIsVisible(true);\n return;\n }\n }\n\n setIsVisible(count < maxCloseTimes);\n }, [context, id, maxCloseTimes, expiresIn]);\n\n const close = useCallback(() => {\n // Always hide visually\n setIsVisible(false);\n\n // Only persist to localStorage if there is an id to track\n if (!id) return;\n\n const states = getClosableStates();\n const currentCount = states[context]?.[id]?.count ?? 0;\n\n const updated: typeof states = {\n ...states,\n [context]: {\n ...(states[context] ?? {}),\n [id]: {\n count: currentCount + 1,\n closedAt: Date.now(),\n },\n },\n };\n\n setClosableStates(updated);\n }, [context, id]);\n\n return { isVisible, close };\n}\n","import type { CtaConfig } from '../types';\nimport { createLogger, type LoggerFn } from './logger';\n\n/**\n * Handle CTA click based on type\n *\n * @param cta - CTA configuration from LiveStateResponse\n * @param log - Optional logger (defaults to console.warn)\n */\nexport function handleCtaClick(cta: CtaConfig, log?: LoggerFn): void {\n const logger = createLogger(log);\n try {\n switch (cta.type) {\n case 'redirect':\n window.location.href = cta.url;\n break;\n\n case 'whatsapp': {\n const whatsappUrl = cta.whatsappMessage\n ? `${cta.url}?text=${encodeURIComponent(cta.whatsappMessage)}`\n : cta.url;\n window.open(whatsappUrl, '_blank');\n break;\n }\n\n case 'external':\n window.open(cta.url, '_blank');\n break;\n\n default:\n logger('warn', `Unknown CTA type: ${(cta as CtaConfig).type}`, { cta });\n }\n } catch (error) {\n logger('error', 'Failed to handle CTA click', { cta, error });\n }\n}\n","/**\n * Minimal HTML sanitiser for notification content.\n *\n * Allows only a strict allowlist of inline formatting tags that are expected\n * in notification messages (strong, em, b, i, br, span). All other tags and\n * attributes are stripped, preventing XSS even if the backend is compromised.\n *\n * This intentionally avoids a runtime dependency on DOMPurify or similar\n * libraries — the allowlist is narrow enough to be maintained safely here.\n */\n\n/** Tags that are safe to render inside notification text */\nconst ALLOWED_TAGS = new Set(['strong', 'em', 'b', 'i', 'br', 'span']);\n\n/**\n * Tags whose entire content (open tag + inner text + close tag) must be\n * removed, not just the surrounding tags. This prevents script injection\n * even if the backend is compromised.\n */\nconst BLOCK_TAGS_RE =\n /<(script|style|iframe|object|embed|form|input|button|textarea|select|link|meta|head|body|html|svg|math)[\\s\\S]*?<\\/\\1\\s*>|<(script|style|iframe|object|embed|form|input|button|textarea|select|link|meta|head|body|html|svg|math)[^>]*\\/?>/gi;\n\n/**\n * Strips all HTML tags except those in ALLOWED_TAGS.\n * Attributes are removed from all allowed tags to prevent event-handler\n * injection (e.g. `<span onclick=\"...\">`).\n * Dangerous tags (script, style, iframe, …) have their entire content removed.\n *\n * Returns the sanitised HTML string, safe to pass to `dangerouslySetInnerHTML`.\n */\nexport function sanitizeHtml(input: string): string {\n // Step 1: remove dangerous tags and their inner content entirely\n const withoutDangerous = input.replace(BLOCK_TAGS_RE, '');\n\n // Step 2: remove all attributes from every remaining tag\n const withoutAttributes = withoutDangerous.replace(\n /<([a-zA-Z][a-zA-Z0-9]*)\\s[^>]*>/g,\n '<$1>',\n );\n\n // Step 3: strip any remaining tag that is not in the allowlist\n return withoutAttributes.replace(\n /<\\/?([a-zA-Z][a-zA-Z0-9]*)[^>]*>/g,\n (match, tag: string) => (ALLOWED_TAGS.has(tag.toLowerCase()) ? match : ''),\n );\n}\n\n/**\n * Returns true when the string contains at least one HTML tag,\n * indicating it should be rendered as rich text.\n */\nexport function containsHtml(input: string): boolean {\n return /<[a-zA-Z][^>]*>/.test(input);\n}\n","import { Text } from '@nimbus-ds/components';\nimport type { ComponentProps } from 'react';\nimport { sanitizeHtml, containsHtml } from '../utils/sanitize';\n\ntype TextProps = ComponentProps<typeof Text>;\n\ninterface RichTextProps extends Omit<TextProps, 'children'> {\n content: string;\n}\n\n/**\n * RichText\n *\n * Renders a text string that may contain inline HTML formatting (e.g. <strong>,\n * <em>, <br>). When HTML is detected, the content is sanitised through a strict\n * allowlist before being injected via dangerouslySetInnerHTML. Plain strings\n * are rendered via the Nimbus Text component without any DOM injection.\n *\n * Allowed tags: strong, em, b, i, br, span. All attributes are stripped.\n */\nexport function RichText({ content, ...textProps }: RichTextProps) {\n if (containsHtml(content)) {\n const safe = sanitizeHtml(content);\n return (\n <Text {...textProps}>\n {/* eslint-disable-next-line react/no-danger */}\n <span dangerouslySetInnerHTML={{ __html: safe }} />\n </Text>\n );\n }\n\n return <Text {...textProps}>{content}</Text>;\n}\n","import { useCallback, useEffect } from 'react';\nimport { Alert, Box, Button } from '@nimbus-ds/components';\nimport type { LiveStateResponse, TrackingConfig } from '../types';\nimport { buildTrackingProperties, buildEventName } from '../utils/analytics';\nimport { handleCtaClick } from '../utils/cta';\nimport { useTrackEvent } from '../hooks/useTrackEvent';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { RichText } from './RichText';\n\nexport interface LiveStateAlertProps {\n data: LiveStateResponse;\n trackingConfig: TrackingConfig;\n /** When provided, shows a close button and calls this on dismiss */\n onClose?: () => void;\n /**\n * Override CTA click behaviour.\n * When provided, replaces default navigation so the caller controls what\n * happens on click (e.g. prevent navigation in a simulator, use a router).\n */\n onCtaClick?: (cta: import('../types').CtaConfig) => void;\n}\n\n/**\n * LiveStateAlert\n *\n * Renders an Alert component (Nimbus) for alert/warning types.\n * Automatically tracks view, click, and close events.\n * Shows a close button when onClose is provided.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateAlert({\n data,\n trackingConfig,\n onClose,\n onCtaClick,\n}: LiveStateAlertProps) {\n const track = useTrackEvent();\n const { log } = useLiveStateContext();\n const appearance = data.type === 'alert' ? 'danger' : 'warning';\n\n const buildEventProperties = useCallback(\n () =>\n buildTrackingProperties({\n context: data.context,\n campaignId: data.campaignId,\n group: data.group,\n ...data.metadata,\n ...trackingConfig.properties,\n }),\n [data, trackingConfig.properties],\n );\n\n const handleClick = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'click',\n );\n track(eventName, buildEventProperties());\n if (onCtaClick) {\n onCtaClick(data.cta);\n } else {\n handleCtaClick(data.cta, log);\n }\n }, [data, trackingConfig, buildEventProperties, track, onCtaClick, log]);\n\n const handleClose = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'close',\n );\n track(eventName, buildEventProperties());\n onClose?.();\n }, [onClose, data, trackingConfig, buildEventProperties, track]);\n\n useEffect(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'view',\n );\n track(eventName, buildEventProperties());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <Alert\n appearance={appearance}\n title={data.title}\n onRemove={onClose ? handleClose : undefined}>\n <Box\n display=\"flex\"\n alignItems=\"flex-start\"\n gap=\"3\"\n flexWrap=\"wrap\"\n flexDirection=\"column\">\n <Box flex=\"1 1 auto\" minWidth=\"220px\">\n <RichText content={data.message} />\n </Box>\n\n <Box display=\"flex\" justifyContent=\"flex-end\" marginTop=\"2\">\n <Button appearance=\"primary\" onClick={handleClick}>\n {data.cta.label}\n </Button>\n </Box>\n </Box>\n </Alert>\n );\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { Card, Box, Text, Link, Icon } from '@nimbus-ds/components';\nimport { MoneyIcon, CloseIcon } from '@nimbus-ds/icons';\nimport type { LiveStateResponse, TrackingConfig } from '../types';\nimport { buildTrackingProperties, buildEventName } from '../utils/analytics';\nimport { handleCtaClick } from '../utils/cta';\nimport { useTrackEvent } from '../hooks/useTrackEvent';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { RichText } from './RichText';\n\nexport interface LiveStateInfoProps {\n data: LiveStateResponse;\n trackingConfig: TrackingConfig;\n defaultVariant?: 'blue' | 'white';\n /** When provided, shows a close button and calls this on dismiss */\n onClose?: () => void;\n /**\n * Override mobile layout detection.\n * When provided, bypasses `window.innerWidth < 750` so simulators and\n * testing tools can force a specific layout without resizing the window.\n */\n isMobile?: boolean;\n /**\n * Override CTA click behaviour.\n * When provided, replaces default navigation so the caller controls what\n * happens on click (e.g. prevent navigation in a simulator, use a router).\n */\n onCtaClick?: (cta: import('../types').CtaConfig) => void;\n}\n\nconst ICON_COLOR_MAP = {\n blue: { background: '#0050C3', icon: '#FFFFFF' },\n white: { background: '#FFFFFF', icon: '#0059D5' },\n} as const;\n\n/**\n * LiveStateInfo\n *\n * Renders a Card component (Nimbus) for info type.\n * Uses MoneyIcon with colored circle background, Text components,\n * and a Link-style CTA — matching the LendingBanner design reference.\n * Automatically tracks view, click, and close events.\n * Shows a close button when onClose is provided.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateInfo({\n data,\n trackingConfig,\n defaultVariant = 'blue',\n onClose,\n isMobile: isMobileProp,\n onCtaClick,\n}: LiveStateInfoProps) {\n const track = useTrackEvent();\n const { log } = useLiveStateContext();\n const variant = data.variant || defaultVariant;\n const iconConfig = ICON_COLOR_MAP[variant];\n\n const [isMobileWindow, setIsMobileWindow] = useState(\n () => window.innerWidth < 750,\n );\n\n useEffect(() => {\n const handler = () => setIsMobileWindow(window.innerWidth < 750);\n window.addEventListener('resize', handler);\n return () => window.removeEventListener('resize', handler);\n }, []);\n\n // Prop overrides window detection — allows simulators/tests to force layout\n const isMobile = isMobileProp ?? isMobileWindow;\n\n const buildEventProperties = useCallback(\n () =>\n buildTrackingProperties({\n context: data.context,\n campaignId: data.campaignId,\n group: data.group,\n ...data.metadata,\n ...trackingConfig.properties,\n }),\n [data, trackingConfig.properties],\n );\n\n const handleClick = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'click',\n );\n track(eventName, buildEventProperties());\n if (onCtaClick) {\n onCtaClick(data.cta);\n } else {\n handleCtaClick(data.cta, log);\n }\n }, [data, trackingConfig, buildEventProperties, track, onCtaClick, log]);\n\n const handleClose = useCallback(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'close',\n );\n track(eventName, buildEventProperties());\n onClose?.();\n }, [onClose, data, trackingConfig, buildEventProperties, track]);\n\n useEffect(() => {\n const eventName = buildEventName(\n trackingConfig.prefix,\n trackingConfig.page,\n data.context,\n 'view',\n );\n track(eventName, buildEventProperties());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const ctaLink = (\n <Link\n as=\"a\"\n textDecoration=\"none\"\n onClick={handleClick}\n data-testid=\"live-state-cta-link\">\n <Text color=\"primary-interactive\" fontSize=\"base\">\n {data.cta.label}\n </Text>\n </Link>\n );\n\n return (\n <Card>\n <Box display=\"flex\" flexDirection=\"row\" justifyContent=\"space-between\">\n {/* Left: icon + text (+ cta on mobile) */}\n <Box\n display=\"flex\"\n gap=\"4\"\n alignItems={isMobile ? 'flex-start' : 'center'}\n paddingRight=\"1-5\">\n <Box minWidth=\"32px\">\n <div\n style={{\n width: 32,\n height: 32,\n borderRadius: '35%',\n borderColor: variant === 'white' ? '#E7E7E7' : 'transparent',\n borderWidth: 1,\n borderStyle: 'solid',\n background: iconConfig.background,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n <MoneyIcon style={{ color: iconConfig.icon }} />\n </div>\n </Box>\n\n <Box display=\"grid\" gap=\"2\">\n <Box>\n <RichText content={data.title} fontSize=\"base\" color=\"neutral-textHigh\" />\n <RichText content={data.message} fontSize=\"base\" color=\"neutral-textLow\" />\n </Box>\n\n {/* CTA shown below text on mobile */}\n {isMobile && ctaLink}\n </Box>\n </Box>\n\n {/* Right: cta (desktop only) + close button */}\n {(!isMobile || onClose) && (\n <Box\n display=\"flex\"\n flexDirection={isMobile ? 'column' : 'row'}\n alignItems={isMobile ? 'flex-end' : 'center'}\n gap=\"2\">\n {/* CTA shown on the right side on desktop */}\n {!isMobile && ctaLink}\n\n {onClose && (\n <button\n type=\"button\"\n data-testid=\"live-state-close-button\"\n aria-label=\"Fechar notificação\"\n onClick={handleClose}\n style={{\n background: 'none',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n <Icon\n source={<CloseIcon width=\"18px\" height=\"18px\" />}\n color=\"neutral-interactive\"\n />\n </button>\n )}\n </Box>\n )}\n </Box>\n </Card>\n );\n}\n","import type { LiveStateRendererProps } from '../types';\nimport { useClosable } from '../hooks/useClosable';\nimport { useLiveStateContext } from '../providers/LiveStateProvider';\nimport { LiveStateAlert } from './LiveStateAlert';\nimport { LiveStateInfo } from './LiveStateInfo';\n\n/**\n * LiveStateRenderer\n *\n * Main component that validates payload, filters by context, and renders\n * the appropriate visual component (Alert or Info) based on type.\n * Handles closable notifications via useClosable when closable is configured.\n *\n * See examples/ directory for usage examples.\n */\nexport function LiveStateRenderer({\n data,\n loading,\n trackingConfig,\n allowedContexts,\n defaultVariant,\n isMobile,\n onCtaClick,\n}: LiveStateRendererProps) {\n const { log, onNavigate } = useLiveStateContext();\n\n // Priority: onCtaClick (per-renderer) > onNavigate (provider) > default handler\n const resolvedCtaClick = onCtaClick ?? onNavigate;\n const { isVisible, close } = useClosable({\n context: data?.context ?? '',\n id: data?.campaignId,\n maxCloseTimes: data?.metadata?.maxCloseTimes,\n expiresIn: data?.metadata?.expiresIn,\n });\n\n // Don't render while loading or no data\n if (loading || !data) {\n return null;\n }\n\n // Validate required fields\n if (\n !data.context ||\n !data.type ||\n !data.title ||\n !data.message ||\n !data.cta?.label ||\n !data.cta?.url ||\n !data.cta?.type\n ) {\n log('warn', 'Invalid payload, missing required fields', { data });\n return null;\n }\n\n // Filter by allowed contexts\n if (allowedContexts && !allowedContexts.includes(data.context)) {\n return null;\n }\n\n // Awaiting localStorage check\n if (isVisible === null) {\n return null;\n }\n\n // Hidden — user reached maxCloseTimes\n if (!isVisible) {\n return null;\n }\n\n // Pass onClose whenever closable is true.\n // campaignId is optional — useClosable handles id=undefined gracefully\n // (always visible, no localStorage tracking).\n const onClose = data.closable ? close : undefined;\n\n // Render appropriate component based on type\n if (data.type === 'alert' || data.type === 'warning') {\n return (\n <LiveStateAlert\n data={data}\n onClose={onClose}\n trackingConfig={trackingConfig}\n onCtaClick={resolvedCtaClick}\n />\n );\n }\n\n if (data.type === 'info') {\n return (\n <LiveStateInfo\n data={data}\n onClose={onClose}\n trackingConfig={trackingConfig}\n defaultVariant={defaultVariant}\n isMobile={isMobile}\n onCtaClick={resolvedCtaClick}\n />\n );\n }\n\n log('warn', `Unknown type: ${data.type}`, { data });\n return null;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"LiveStateAlert.d.ts","sourceRoot":"","sources":["../../../src/components/LiveStateAlert.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAMlE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,cAAc,EAAE,cAAc,CAAC;IAC/B,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;CAC1D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,cAAc,EACd,OAAO,EACP,UAAU,GACX,EAAE,mBAAmB,2CA6ErB"}
1
+ {"version":3,"file":"LiveStateAlert.d.ts","sourceRoot":"","sources":["../../../src/components/LiveStateAlert.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAOlE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,cAAc,EAAE,cAAc,CAAC;IAC/B,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;CAC1D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,cAAc,EACd,OAAO,EACP,UAAU,GACX,EAAE,mBAAmB,2CA6ErB"}
@@ -1 +1 @@
1
- {"version":3,"file":"LiveStateInfo.d.ts","sourceRoot":"","sources":["../../../src/components/LiveStateInfo.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAMlE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,iBAAiB,CAAC;IACxB,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;CAC1D;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,cAAc,EACd,cAAuB,EACvB,OAAO,EACP,QAAQ,EAAE,YAAY,EACtB,UAAU,GACX,EAAE,kBAAkB,2CA8JpB"}
1
+ {"version":3,"file":"LiveStateInfo.d.ts","sourceRoot":"","sources":["../../../src/components/LiveStateInfo.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAOlE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,iBAAiB,CAAC;IACxB,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;CAC1D;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,cAAc,EACd,cAAuB,EACvB,OAAO,EACP,QAAQ,EAAE,YAAY,EACtB,UAAU,GACX,EAAE,kBAAkB,2CA0JpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"LiveStateRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/LiveStateRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAMvD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,OAAO,EACP,cAAc,EACd,eAAe,EACf,cAAc,EACd,QAAQ,EACR,UAAU,GACX,EAAE,sBAAsB,kDA2ExB"}
1
+ {"version":3,"file":"LiveStateRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/LiveStateRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAMvD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,OAAO,EACP,cAAc,EACd,eAAe,EACf,cAAc,EACd,QAAQ,EACR,UAAU,GACX,EAAE,sBAAsB,kDA8ExB"}
@@ -0,0 +1,19 @@
1
+ import { Text } from '@nimbus-ds/components';
2
+ import { ComponentProps } from 'react';
3
+ type TextProps = ComponentProps<typeof Text>;
4
+ interface RichTextProps extends Omit<TextProps, 'children'> {
5
+ content: string;
6
+ }
7
+ /**
8
+ * RichText
9
+ *
10
+ * Renders a text string that may contain inline HTML formatting (e.g. <strong>,
11
+ * <em>, <br>). When HTML is detected, the content is sanitised through a strict
12
+ * allowlist before being injected via dangerouslySetInnerHTML. Plain strings
13
+ * are rendered via the Nimbus Text component without any DOM injection.
14
+ *
15
+ * Allowed tags: strong, em, b, i, br, span. All attributes are stripped.
16
+ */
17
+ export declare function RichText({ content, ...textProps }: RichTextProps): import("react/jsx-runtime").JSX.Element;
18
+ export {};
19
+ //# sourceMappingURL=RichText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RichText.d.ts","sourceRoot":"","sources":["../../../src/components/RichText.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAG5C,KAAK,SAAS,GAAG,cAAc,CAAC,OAAO,IAAI,CAAC,CAAC;AAE7C,UAAU,aAAc,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,EAAE,aAAa,2CAYhE"}
@@ -1 +1 @@
1
- {"version":3,"file":"useLiveState.d.ts","sourceRoot":"","sources":["../../../src/hooks/useLiveState.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,UAAU,kBAAkB;IAC1B,sBAAsB;IACtB,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAEpC,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB,kCAAkC;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,kBAAkB,CAgCjD"}
1
+ {"version":3,"file":"useLiveState.d.ts","sourceRoot":"","sources":["../../../src/hooks/useLiveState.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,UAAU,kBAAkB;IAC1B,sBAAsB;IACtB,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAEpC,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB,kCAAkC;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,kBAAkB,CA+CjD"}
@@ -8,4 +8,5 @@ export { LiveStateInfo } from './components/LiveStateInfo';
8
8
  export { handleCtaClick } from './utils/cta';
9
9
  export { trackEvent } from './utils/analytics';
10
10
  export { LIVE_STATE_STORAGE_KEY } from './utils/closable-storage';
11
+ export type { LoggerFn } from './utils/logger';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,QAAQ,EACR,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAGlE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,QAAQ,EACR,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAGlE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,30 +1,14 @@
1
- import { default as React } from 'react';
2
- import { LiveStateFetcher, AnalyticsConfig, TrackingProperties } from '../types';
1
+ import { LiveStateFetcher, AnalyticsConfig, TrackingProperties, LiveStateResponse, CtaConfig, LiveStateProviderProps } from '../types';
3
2
  import { LoggerFn } from '../utils/logger';
4
3
  interface LiveStateContextValue {
5
4
  fetcher: LiveStateFetcher;
6
5
  analytics?: AnalyticsConfig;
7
6
  onEvent?: (eventName: string, properties?: TrackingProperties) => void;
7
+ onNavigate?: (cta: CtaConfig) => void;
8
+ mockData?: LiveStateResponse | null;
8
9
  disabled?: boolean;
9
10
  log: LoggerFn;
10
11
  }
11
- export interface LiveStateProviderProps {
12
- children: React.ReactNode;
13
- fetcher: LiveStateFetcher;
14
- analytics?: AnalyticsConfig;
15
- onEvent?: (eventName: string, properties?: TrackingProperties) => void;
16
- onLog?: LoggerFn;
17
- /**
18
- * When true, disables all analytics SDKs (Amplitude and Clarity).
19
- * Useful in development and test environments to suppress SDK warnings
20
- * and avoid polluting analytics data without needing to mock anything.
21
- * The `onEvent` and `onLog` callbacks still fire when disabled.
22
- *
23
- * @example
24
- * <LiveStateProvider disabled={process.env.NODE_ENV !== 'production'} />
25
- */
26
- disabled?: boolean;
27
- }
28
12
  /**
29
13
  * LiveStateProvider
30
14
  *
@@ -39,7 +23,7 @@ export interface LiveStateProviderProps {
39
23
  *
40
24
  * See examples/ directory for usage examples.
41
25
  */
42
- export declare function LiveStateProvider({ children, fetcher, analytics, onEvent, onLog, disabled, }: LiveStateProviderProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function LiveStateProvider({ children, fetcher, analytics, onEvent, onLog, onNavigate, mockData, disabled, }: LiveStateProviderProps): import("react/jsx-runtime").JSX.Element;
43
27
  /**
44
28
  * useLiveStateContext
45
29
  *
@@ -1 +1 @@
1
- {"version":3,"file":"LiveStateProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/LiveStateProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwD,MAAM,OAAO,CAAC;AAE7E,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE9D,UAAU,qBAAqB;IAC7B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,QAAQ,CAAC;CACf;AAID,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACvE,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,OAAO,EACP,SAAS,EACT,OAAO,EACP,KAAK,EACL,QAAgB,GACjB,EAAE,sBAAsB,2CA2BxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,0BAQlC"}
1
+ {"version":3,"file":"LiveStateProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/LiveStateProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,SAAS,EACT,sBAAsB,EACvB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE9D,UAAU,qBAAqB;IAC7B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACvE,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,QAAQ,CAAC;CACf;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,OAAO,EACP,SAAS,EACT,OAAO,EACP,KAAK,EACL,UAAU,EACV,QAAQ,EACR,QAAgB,GACjB,EAAE,sBAAsB,2CA2BxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,0BAQlC"}
@@ -167,10 +167,59 @@ export interface LiveStateProviderProps {
167
167
  * and avoid polluting analytics data without needing to mock anything.
168
168
  * The `onEvent` and `onLog` callbacks still fire when disabled.
169
169
  *
170
+ * Note: this prop does NOT affect the fetcher — data fetching always runs
171
+ * regardless of this flag.
172
+ *
170
173
  * @example
171
174
  * <LiveStateProvider disabled={process.env.NODE_ENV !== 'production'} />
172
175
  */
173
176
  disabled?: boolean;
177
+ /**
178
+ * Injects mock data directly, bypassing the fetcher entirely.
179
+ * Useful in local development and Storybook to test any notification scenario
180
+ * without a real backend, without deploying, and without toggling `disabled`.
181
+ *
182
+ * Pass `null` to simulate the "no notification" state.
183
+ * When `mockData` is provided the fetcher is never called.
184
+ *
185
+ * @example
186
+ * // Vite dev environment
187
+ * <LiveStateProvider
188
+ * fetcher={LENDING_FETCHER}
189
+ * disabled={import.meta.env.PROD === false}
190
+ * mockData={import.meta.env.PROD ? undefined : {
191
+ * context: 'awareness',
192
+ * type: 'info',
193
+ * title: 'Test notification',
194
+ * message: 'Testing locally.',
195
+ * cta: { label: 'Click me', url: '/admin/lending#/apply', type: 'redirect' },
196
+ * }}
197
+ * />
198
+ */
199
+ mockData?: LiveStateResponse | null;
200
+ /**
201
+ * Custom navigation handler. When provided, replaces the default
202
+ * `window.location.href` / `window.open` behaviour for all CTAs under
203
+ * this provider. Useful for iframe-based environments (e.g. Nexo) that
204
+ * require navigation to be dispatched through a message-passing SDK.
205
+ *
206
+ * Priority: `onCtaClick` (per-renderer) > `onNavigate` (provider) > default handler.
207
+ *
208
+ * Receives the full `CtaConfig` so the consumer can branch on `cta.type`.
209
+ *
210
+ * @example
211
+ * // Nexo integration
212
+ * import { goTo, openInDefaultBrowser } from '@tiendanube/nexo';
213
+ * import nexo from 'App/nexoClient';
214
+ *
215
+ * <LiveStateProvider
216
+ * onNavigate={(cta) => {
217
+ * if (cta.type === 'redirect') goTo(nexo, cta.url);
218
+ * if (cta.type === 'external') openInDefaultBrowser(nexo, cta.url);
219
+ * }}
220
+ * />
221
+ */
222
+ onNavigate?: (cta: CtaConfig) => void;
174
223
  }
175
224
  /**
176
225
  * LiveStateRenderer props
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Minimal HTML sanitiser for notification content.
3
+ *
4
+ * Allows only a strict allowlist of inline formatting tags that are expected
5
+ * in notification messages (strong, em, b, i, br, span). All other tags and
6
+ * attributes are stripped, preventing XSS even if the backend is compromised.
7
+ *
8
+ * This intentionally avoids a runtime dependency on DOMPurify or similar
9
+ * libraries — the allowlist is narrow enough to be maintained safely here.
10
+ */
11
+ /**
12
+ * Strips all HTML tags except those in ALLOWED_TAGS.
13
+ * Attributes are removed from all allowed tags to prevent event-handler
14
+ * injection (e.g. `<span onclick="...">`).
15
+ * Dangerous tags (script, style, iframe, …) have their entire content removed.
16
+ *
17
+ * Returns the sanitised HTML string, safe to pass to `dangerouslySetInnerHTML`.
18
+ */
19
+ export declare function sanitizeHtml(input: string): string;
20
+ /**
21
+ * Returns true when the string contains at least one HTML tag,
22
+ * indicating it should be rendered as rich text.
23
+ */
24
+ export declare function containsHtml(input: string): boolean;
25
+ //# sourceMappingURL=sanitize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../../src/utils/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAaH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAelD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD"}
package/dist/testing.cjs CHANGED
@@ -1,2 +1,2 @@
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;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./LiveStateProvider-ByK7lK2v.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
@@ -1,4 +1,4 @@
1
- import { n as e, o as t } from "./LiveStateProvider-DeRIGqmT.js";
1
+ import { n as e, o as t } from "./LiveStateProvider-V3rVH59O.js";
2
2
  import n from "react";
3
3
  import { SWRConfig as r } from "swr";
4
4
  import { jsx as i } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiendanube/live-state",
3
- "version": "1.0.0-beta.14",
3
+ "version": "1.0.0-beta.17",
4
4
  "description": "Generic React library for rendering live state notifications",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -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(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