keystone-design-bootstrap 1.0.98 → 1.0.99

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.
@@ -42,7 +42,9 @@ import { resourceFromAttributes } from "@opentelemetry/resources";
42
42
  import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
43
43
  var BUILD_CONSOLE_DISABLED = process.env.NEXT_PUBLIC_LOGGING_DISABLED === "1";
44
44
  var BUILD_POSTHOG_DISABLED = process.env.NEXT_PUBLIC_POSTHOG_LOGGING_DISABLED === "1";
45
+ var MAX_PENDING_POSTHOG_LOGS = 500;
45
46
  var otelLogger = null;
47
+ var pendingPostHogLogs = [];
46
48
  var browserConsole = globalThis == null ? void 0 : globalThis["console"];
47
49
  var CHANNEL_COLORS = {
48
50
  // Shared diagnostics
@@ -84,6 +86,12 @@ function flattenAttributes(input, prefix = "") {
84
86
  }
85
87
  return output;
86
88
  }
89
+ function enqueuePendingPostHogLog(entry) {
90
+ if (pendingPostHogLogs.length >= MAX_PENDING_POSTHOG_LOGS) {
91
+ pendingPostHogLogs.shift();
92
+ }
93
+ pendingPostHogLogs.push(entry);
94
+ }
87
95
  function isConsoleEnabled() {
88
96
  if (BUILD_CONSOLE_DISABLED) return false;
89
97
  if (typeof window === "undefined") return true;
@@ -116,7 +124,7 @@ function emitConsole(level, channel, event, detail) {
116
124
  (_d = browserConsole == null ? void 0 : browserConsole.log) == null ? void 0 : _d.call(browserConsole, message, channelStyle, eventStyle, detailStyle);
117
125
  }
118
126
  function emitToPostHog(level, channel, event, detail) {
119
- if (!otelLogger) return;
127
+ if (!otelLogger) return false;
120
128
  const severityText = level === "warn" ? "WARNING" : level === "error" ? "ERROR" : "INFO";
121
129
  const attributes = flattenAttributes(__spreadValues({ channel }, detail));
122
130
  otelLogger.emit({
@@ -124,6 +132,7 @@ function emitToPostHog(level, channel, event, detail) {
124
132
  body: event,
125
133
  attributes
126
134
  });
135
+ return true;
127
136
  }
128
137
  function emit(level, channel, event, detail, options) {
129
138
  var _a;
@@ -132,7 +141,10 @@ function emit(level, channel, event, detail, options) {
132
141
  emitConsole(level, channel, event, detail);
133
142
  }
134
143
  if (shouldShip) {
135
- emitToPostHog(level, channel, event, detail);
144
+ const shipped = emitToPostHog(level, channel, event, detail);
145
+ if (!shipped) {
146
+ enqueuePendingPostHogLog({ level, channel, event, detail });
147
+ }
136
148
  }
137
149
  }
138
150
  function warn(channel, event, detail = {}, options) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/contexts/ThemeContext.tsx","../../src/themes/index.ts","../../src/tracking/logging.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContext } from 'react';\nimport { Theme, isValidTheme } from '../themes';\nimport { warn } from '../tracking/logging';\n\ninterface ThemeContextValue {\n theme: Theme;\n}\n\nconst ThemeContext = createContext<ThemeContextValue>({ theme: 'classic' });\n\nexport function ThemeProvider({ \n theme, \n children \n}: { \n theme: Theme; \n children: React.ReactNode;\n}) {\n // Validate theme at runtime\n if (!isValidTheme(theme)) {\n warn('theme', 'INVALID_THEME_FALLBACK', { theme });\n theme = 'classic';\n }\n \n return (\n <ThemeContext.Provider value={{ theme }}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\nexport function useTheme() {\n return useContext(ThemeContext);\n}\n","/**\n * Theme Configuration\n * Single source of truth for all themes\n */\n\nexport const THEME_CONFIG = {\n classic: '', // Base files with no suffix (hero-home.tsx)\n aman: '.aman', // Aman Hotels variant files (hero-home.aman.tsx)\n barelux: '.barelux', // Bare Lux Studio variant files (hero-home.barelux.tsx)\n balance: '.balance', // Balance Aesthetics variant files (hero-home.balance.tsx)\n custom: '', // Fully custom sites — no design-system CSS loaded, all styling built in the site itself\n} as const;\n\nexport type Theme = keyof typeof THEME_CONFIG;\n\nexport function getAvailableThemes(): Theme[] {\n return Object.keys(THEME_CONFIG) as Theme[];\n}\n\nexport function getThemeSuffix(theme: Theme): string {\n return THEME_CONFIG[theme] || '';\n}\n\nexport function isValidTheme(theme: string): theme is Theme {\n return theme in THEME_CONFIG;\n}\n","'use client';\n\nimport { logs } from '@opentelemetry/api-logs';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs';\n\nconst BUILD_CONSOLE_DISABLED = process.env.NEXT_PUBLIC_LOGGING_DISABLED === '1';\nconst BUILD_POSTHOG_DISABLED = process.env.NEXT_PUBLIC_POSTHOG_LOGGING_DISABLED === '1';\ninterface LoggingWindow extends Window {\n __loggingDisabled?: boolean;\n __posthogLoggingDisabled?: boolean;\n}\n\nexport type LogLevel = 'info' | 'warn' | 'error';\n\nexport type LogOptions = {\n /** Send this log payload to PostHog Logs product. */\n shipToPostHog?: boolean;\n};\n\ntype OTelInitOptions = {\n apiKey: string;\n apiHost?: string;\n serviceName?: string;\n environment?: string;\n accountId?: number;\n accountName?: string;\n};\nlet otelProvider: LoggerProvider | null = null;\nlet otelLogger: ReturnType<typeof logs.getLogger> | null = null;\nlet otelInitFingerprint = '';\nconst browserConsole = globalThis?.['console'];\n\n/**\n * Channel → accent colour. Unregistered channels fall through to gray.\n */\nexport const CHANNEL_COLORS: Record<string, string> = {\n // Shared diagnostics\n 'global-client': '#d2a8ff',\n\n // Section pins (desktop)\n 'every-channel-pin': '#9febd7',\n 'hero-pin': '#6ecc8b',\n 'pricing-pin': '#399587',\n 'product-screens-pin': '#4fafa0',\n 'social-proof-pin': '#ffbb8a',\n 'value-props-pin': '#e0a733',\n 'work-pin': '#f57e56',\n\n // Section pins (mobile)\n 'mobile-every-channel-pin': '#7ed9c6',\n 'mobile-hero-pin': '#f0eee6',\n 'mobile-pricing-pin': '#80d4ff',\n 'mobile-product-screens-pin': '#3a9085',\n 'mobile-social-proof-pin': '#ffd580',\n 'mobile-value-props-pin': '#4fafa0',\n};\n\nfunction flattenAttributes(input: Record<string, unknown>, prefix = ''): Record<string, string | number | boolean> {\n const output: Record<string, string | number | boolean> = {};\n\n for (const [key, value] of Object.entries(input)) {\n const nextKey = prefix ? `${prefix}.${key}` : key;\n if (value === null || value === undefined) continue;\n\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n output[nextKey] = value;\n continue;\n }\n\n if (Array.isArray(value)) {\n output[nextKey] = JSON.stringify(value);\n continue;\n }\n\n if (typeof value === 'object') {\n Object.assign(output, flattenAttributes(value as Record<string, unknown>, nextKey));\n continue;\n }\n\n output[nextKey] = String(value);\n }\n\n return output;\n}\n\nfunction stripTrailingSlash(host: string): string {\n return host.replace(/\\/+$/, '');\n}\n\nexport function initializePostHogLogging(options: OTelInitOptions): void {\n if (typeof window === 'undefined') return;\n if (!options.apiKey?.trim()) return;\n\n const apiHost = stripTrailingSlash(options.apiHost?.trim() || 'https://us.i.posthog.com');\n const serviceName = options.serviceName?.trim() || 'keystone-customer-site';\n const environment = options.environment?.trim() || 'production';\n const fingerprint = `${apiHost}|${options.apiKey}|${serviceName}|${environment}|${options.accountId ?? ''}|${options.accountName ?? ''}`;\n if (otelLogger && otelInitFingerprint === fingerprint) return;\n\n if (otelProvider) {\n // Cleanly replace logger provider when config changes.\n void otelProvider.shutdown().catch(() => {});\n otelProvider = null;\n otelLogger = null;\n }\n\n const baseAttributes: Record<string, string | number | boolean> = {\n 'service.name': serviceName,\n environment,\n site_domain: window.location.hostname,\n };\n if (options.accountId !== undefined) baseAttributes.account_id = options.accountId;\n if (options.accountName) baseAttributes.account_name = options.accountName;\n\n const exporter = new OTLPLogExporter({\n url: `${apiHost}/i/v1/logs?token=${encodeURIComponent(options.apiKey)}`,\n headers: {\n // Keep request \"simple\" to avoid CORS preflight in browser.\n 'Content-Type': 'text/plain',\n },\n });\n\n otelProvider = new LoggerProvider({\n resource: resourceFromAttributes(baseAttributes),\n processors: [new BatchLogRecordProcessor(exporter)],\n });\n\n logs.setGlobalLoggerProvider(otelProvider);\n otelLogger = logs.getLogger(serviceName);\n otelInitFingerprint = fingerprint;\n}\n\nfunction isConsoleEnabled(): boolean {\n if (BUILD_CONSOLE_DISABLED) return false;\n if (typeof window === 'undefined') return true;\n return (window as LoggingWindow).__loggingDisabled !== true;\n}\n\nfunction isPostHogShippingEnabled(): boolean {\n if (BUILD_POSTHOG_DISABLED) return false;\n if (typeof window === 'undefined') return false;\n return (window as LoggingWindow).__posthogLoggingDisabled !== true;\n}\n\nfunction formatDetail(detail: Record<string, unknown>): string {\n return Object.entries(detail)\n .map(([k, v]) => `${k}=${typeof v === 'number' ? v.toFixed(4) : String(v)}`)\n .join(' ');\n}\n\nfunction emitConsole(\n level: LogLevel,\n channel: string,\n event: string,\n detail: Record<string, unknown>,\n): void {\n const color = CHANNEL_COLORS[channel] ?? '#aaa';\n const detailStr = formatDetail(detail);\n const message = `%c[${channel}] %c${event}%c ${detailStr}`;\n const channelStyle = `color:${color}; font-weight:bold`;\n const eventStyle = level === 'error'\n ? 'color:#e94e4e; font-weight:bold'\n : level === 'warn'\n ? 'color:#f5a623; font-weight:bold'\n : 'color:#fff; font-weight:bold';\n const detailStyle = 'color:#999; font-weight:normal';\n\n if (level === 'error') {\n browserConsole?.error?.(message, channelStyle, eventStyle, detailStyle);\n return;\n }\n if (level === 'warn') {\n browserConsole?.warn?.(message, channelStyle, eventStyle, detailStyle);\n return;\n }\n browserConsole?.log?.(message, channelStyle, eventStyle, detailStyle);\n}\n\nfunction emitToPostHog(level: LogLevel, channel: string, event: string, detail: Record<string, unknown>): void {\n if (!otelLogger) return;\n\n const severityText = level === 'warn' ? 'WARNING' : level === 'error' ? 'ERROR' : 'INFO';\n const attributes = flattenAttributes({ channel, ...detail });\n\n otelLogger.emit({\n severityText,\n body: event,\n attributes,\n });\n}\n\nfunction emit(level: LogLevel, channel: string, event: string, detail: Record<string, unknown>, options?: LogOptions): void {\n const shouldShip = (options?.shipToPostHog ?? level !== 'info') && isPostHogShippingEnabled();\n // Keep warn/error visible even when regular logs are muted.\n if (level !== 'info' || isConsoleEnabled()) {\n emitConsole(level, channel, event, detail);\n }\n if (shouldShip) {\n emitToPostHog(level, channel, event, detail);\n }\n}\n\nexport function log(channel: string, event: string, detail: Record<string, unknown> = {}, options?: LogOptions): void {\n emit('info', channel, event, detail, options);\n}\n\nexport function warn(channel: string, event: string, detail: Record<string, unknown> = {}, options?: LogOptions): void {\n emit('warn', channel, event, detail, options);\n}\n\nexport function error(channel: string, event: string, detail: Record<string, unknown> = {}, options?: LogOptions): void {\n emit('error', channel, event, detail, options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAEA,SAAS,eAAe,kBAAkB;;;ACGnC,IAAM,eAAe;AAAA,EAC1B,SAAS;AAAA;AAAA,EACT,MAAM;AAAA;AAAA,EACN,SAAS;AAAA;AAAA,EACT,SAAS;AAAA;AAAA,EACT,QAAQ;AAAA;AACV;AAYO,SAAS,aAAa,OAA+B;AAC1D,SAAO,SAAS;AAClB;;;ACvBA,SAAS,YAAY;AACrB,SAAS,uBAAuB;AAChC,SAAS,8BAA8B;AACvC,SAAS,yBAAyB,sBAAsB;AAExD,IAAM,yBAAyB,QAAQ,IAAI,iCAAiC;AAC5E,IAAM,yBAAyB,QAAQ,IAAI,yCAAyC;AAsBpF,IAAI,aAAuD;AAE3D,IAAM,iBAAiB,yCAAa;AAK7B,IAAM,iBAAyC;AAAA;AAAA,EAEpD,iBAAiB;AAAA;AAAA,EAGjB,qBAAqB;AAAA,EACrB,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,uBAAuB;AAAA,EACvB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,YAAY;AAAA;AAAA,EAGZ,4BAA4B;AAAA,EAC5B,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,8BAA8B;AAAA,EAC9B,2BAA2B;AAAA,EAC3B,0BAA0B;AAC5B;AAEA,SAAS,kBAAkB,OAAgC,SAAS,IAA+C;AACjH,QAAM,SAAoD,CAAC;AAE3D,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAM,UAAU,SAAS,GAAG,MAAM,IAAI,GAAG,KAAK;AAC9C,QAAI,UAAU,QAAQ,UAAU,OAAW;AAE3C,QAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AACxF,aAAO,OAAO,IAAI;AAClB;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO,OAAO,IAAI,KAAK,UAAU,KAAK;AACtC;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,OAAO,QAAQ,kBAAkB,OAAkC,OAAO,CAAC;AAClF;AAAA,IACF;AAEA,WAAO,OAAO,IAAI,OAAO,KAAK;AAAA,EAChC;AAEA,SAAO;AACT;AAiDA,SAAS,mBAA4B;AACnC,MAAI,uBAAwB,QAAO;AACnC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAQ,OAAyB,sBAAsB;AACzD;AAEA,SAAS,2BAAoC;AAC3C,MAAI,uBAAwB,QAAO;AACnC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAQ,OAAyB,6BAA6B;AAChE;AAEA,SAAS,aAAa,QAAyC;AAC7D,SAAO,OAAO,QAAQ,MAAM,EACzB,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,IAAI,OAAO,CAAC,CAAC,EAAE,EAC1E,KAAK,IAAI;AACd;AAEA,SAAS,YACP,OACA,SACA,OACA,QACM;AA7JR;AA8JE,QAAM,SAAQ,oBAAe,OAAO,MAAtB,YAA2B;AACzC,QAAM,YAAY,aAAa,MAAM;AACrC,QAAM,UAAU,MAAM,OAAO,OAAO,KAAK,MAAM,SAAS;AACxD,QAAM,eAAe,SAAS,KAAK;AACnC,QAAM,aAAa,UAAU,UACzB,oCACA,UAAU,SACR,oCACA;AACN,QAAM,cAAc;AAEpB,MAAI,UAAU,SAAS;AACrB,2DAAgB,UAAhB,wCAAwB,SAAS,cAAc,YAAY;AAC3D;AAAA,EACF;AACA,MAAI,UAAU,QAAQ;AACpB,2DAAgB,SAAhB,wCAAuB,SAAS,cAAc,YAAY;AAC1D;AAAA,EACF;AACA,yDAAgB,QAAhB,wCAAsB,SAAS,cAAc,YAAY;AAC3D;AAEA,SAAS,cAAc,OAAiB,SAAiB,OAAe,QAAuC;AAC7G,MAAI,CAAC,WAAY;AAEjB,QAAM,eAAe,UAAU,SAAS,YAAY,UAAU,UAAU,UAAU;AAClF,QAAM,aAAa,kBAAkB,iBAAE,WAAY,OAAQ;AAE3D,aAAW,KAAK;AAAA,IACd;AAAA,IACA,MAAM;AAAA,IACN;AAAA,EACF,CAAC;AACH;AAEA,SAAS,KAAK,OAAiB,SAAiB,OAAe,QAAiC,SAA4B;AAjM5H;AAkME,QAAM,eAAc,wCAAS,kBAAT,YAA0B,UAAU,WAAW,yBAAyB;AAE5F,MAAI,UAAU,UAAU,iBAAiB,GAAG;AAC1C,gBAAY,OAAO,SAAS,OAAO,MAAM;AAAA,EAC3C;AACA,MAAI,YAAY;AACd,kBAAc,OAAO,SAAS,OAAO,MAAM;AAAA,EAC7C;AACF;AAMO,SAAS,KAAK,SAAiB,OAAe,SAAkC,CAAC,GAAG,SAA4B;AACrH,OAAK,QAAQ,SAAS,OAAO,QAAQ,OAAO;AAC9C;;;AFxMA,IAAM,eAAe,cAAiC,EAAE,OAAO,UAAU,CAAC;AAEnE,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AACF,GAGG;AAED,MAAI,CAAC,aAAa,KAAK,GAAG;AACxB,SAAK,SAAS,0BAA0B,EAAE,MAAM,CAAC;AACjD,YAAQ;AAAA,EACV;AAEA,SACE,oCAAC,aAAa,UAAb,EAAsB,OAAO,EAAE,MAAM,KACnC,QACH;AAEJ;AAEO,SAAS,WAAW;AACzB,SAAO,WAAW,YAAY;AAChC;","names":[]}
1
+ {"version":3,"sources":["../../src/contexts/ThemeContext.tsx","../../src/themes/index.ts","../../src/tracking/logging.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContext } from 'react';\nimport { Theme, isValidTheme } from '../themes';\nimport { warn } from '../tracking/logging';\n\ninterface ThemeContextValue {\n theme: Theme;\n}\n\nconst ThemeContext = createContext<ThemeContextValue>({ theme: 'classic' });\n\nexport function ThemeProvider({ \n theme, \n children \n}: { \n theme: Theme; \n children: React.ReactNode;\n}) {\n // Validate theme at runtime\n if (!isValidTheme(theme)) {\n warn('theme', 'INVALID_THEME_FALLBACK', { theme });\n theme = 'classic';\n }\n \n return (\n <ThemeContext.Provider value={{ theme }}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\nexport function useTheme() {\n return useContext(ThemeContext);\n}\n","/**\n * Theme Configuration\n * Single source of truth for all themes\n */\n\nexport const THEME_CONFIG = {\n classic: '', // Base files with no suffix (hero-home.tsx)\n aman: '.aman', // Aman Hotels variant files (hero-home.aman.tsx)\n barelux: '.barelux', // Bare Lux Studio variant files (hero-home.barelux.tsx)\n balance: '.balance', // Balance Aesthetics variant files (hero-home.balance.tsx)\n custom: '', // Fully custom sites — no design-system CSS loaded, all styling built in the site itself\n} as const;\n\nexport type Theme = keyof typeof THEME_CONFIG;\n\nexport function getAvailableThemes(): Theme[] {\n return Object.keys(THEME_CONFIG) as Theme[];\n}\n\nexport function getThemeSuffix(theme: Theme): string {\n return THEME_CONFIG[theme] || '';\n}\n\nexport function isValidTheme(theme: string): theme is Theme {\n return theme in THEME_CONFIG;\n}\n","'use client';\n\nimport { logs } from '@opentelemetry/api-logs';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs';\n\nconst BUILD_CONSOLE_DISABLED = process.env.NEXT_PUBLIC_LOGGING_DISABLED === '1';\nconst BUILD_POSTHOG_DISABLED = process.env.NEXT_PUBLIC_POSTHOG_LOGGING_DISABLED === '1';\ninterface LoggingWindow extends Window {\n __loggingDisabled?: boolean;\n __posthogLoggingDisabled?: boolean;\n}\n\nexport type LogLevel = 'info' | 'warn' | 'error';\n\nexport type LogOptions = {\n /** Send this log payload to PostHog Logs product. */\n shipToPostHog?: boolean;\n};\n\ntype OTelInitOptions = {\n apiKey: string;\n apiHost?: string;\n serviceName?: string;\n environment?: string;\n accountId?: number;\n accountName?: string;\n};\n\ntype PendingShipLog = {\n level: LogLevel;\n channel: string;\n event: string;\n detail: Record<string, unknown>;\n};\n\nconst MAX_PENDING_POSTHOG_LOGS = 500;\nlet otelProvider: LoggerProvider | null = null;\nlet otelLogger: ReturnType<typeof logs.getLogger> | null = null;\nlet otelInitFingerprint = '';\nconst pendingPostHogLogs: PendingShipLog[] = [];\nconst browserConsole = globalThis?.['console'];\n\n/**\n * Channel → accent colour. Unregistered channels fall through to gray.\n */\nexport const CHANNEL_COLORS: Record<string, string> = {\n // Shared diagnostics\n 'global-client': '#d2a8ff',\n\n // Section pins (desktop)\n 'every-channel-pin': '#9febd7',\n 'hero-pin': '#6ecc8b',\n 'pricing-pin': '#399587',\n 'product-screens-pin': '#4fafa0',\n 'social-proof-pin': '#ffbb8a',\n 'value-props-pin': '#e0a733',\n 'work-pin': '#f57e56',\n\n // Section pins (mobile)\n 'mobile-every-channel-pin': '#7ed9c6',\n 'mobile-hero-pin': '#f0eee6',\n 'mobile-pricing-pin': '#80d4ff',\n 'mobile-product-screens-pin': '#3a9085',\n 'mobile-social-proof-pin': '#ffd580',\n 'mobile-value-props-pin': '#4fafa0',\n};\n\nfunction flattenAttributes(input: Record<string, unknown>, prefix = ''): Record<string, string | number | boolean> {\n const output: Record<string, string | number | boolean> = {};\n\n for (const [key, value] of Object.entries(input)) {\n const nextKey = prefix ? `${prefix}.${key}` : key;\n if (value === null || value === undefined) continue;\n\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n output[nextKey] = value;\n continue;\n }\n\n if (Array.isArray(value)) {\n output[nextKey] = JSON.stringify(value);\n continue;\n }\n\n if (typeof value === 'object') {\n Object.assign(output, flattenAttributes(value as Record<string, unknown>, nextKey));\n continue;\n }\n\n output[nextKey] = String(value);\n }\n\n return output;\n}\n\nfunction stripTrailingSlash(host: string): string {\n return host.replace(/\\/+$/, '');\n}\n\nfunction enqueuePendingPostHogLog(entry: PendingShipLog): void {\n if (pendingPostHogLogs.length >= MAX_PENDING_POSTHOG_LOGS) {\n pendingPostHogLogs.shift();\n }\n pendingPostHogLogs.push(entry);\n}\n\nfunction flushPendingPostHogLogs(): void {\n if (!otelLogger || pendingPostHogLogs.length === 0) return;\n\n while (pendingPostHogLogs.length > 0) {\n const entry = pendingPostHogLogs.shift();\n if (!entry) break;\n emitToPostHog(entry.level, entry.channel, entry.event, entry.detail);\n }\n}\n\nexport function initializePostHogLogging(options: OTelInitOptions): void {\n if (typeof window === 'undefined') return;\n if (!options.apiKey?.trim()) return;\n\n const apiHost = stripTrailingSlash(options.apiHost?.trim() || 'https://us.i.posthog.com');\n const serviceName = options.serviceName?.trim() || 'keystone-customer-site';\n const environment = options.environment?.trim() || 'production';\n const fingerprint = `${apiHost}|${options.apiKey}|${serviceName}|${environment}|${options.accountId ?? ''}|${options.accountName ?? ''}`;\n if (otelLogger && otelInitFingerprint === fingerprint) return;\n\n if (otelProvider) {\n // Cleanly replace logger provider when config changes.\n void otelProvider.shutdown().catch(() => {});\n otelProvider = null;\n otelLogger = null;\n }\n\n const baseAttributes: Record<string, string | number | boolean> = {\n 'service.name': serviceName,\n environment,\n site_domain: window.location.hostname,\n };\n if (options.accountId !== undefined) baseAttributes.account_id = options.accountId;\n if (options.accountName) baseAttributes.account_name = options.accountName;\n\n const exporter = new OTLPLogExporter({\n url: `${apiHost}/i/v1/logs?token=${encodeURIComponent(options.apiKey)}`,\n headers: {\n // Keep request \"simple\" to avoid CORS preflight in browser.\n 'Content-Type': 'text/plain',\n },\n });\n\n otelProvider = new LoggerProvider({\n resource: resourceFromAttributes(baseAttributes),\n processors: [new BatchLogRecordProcessor(exporter)],\n });\n\n logs.setGlobalLoggerProvider(otelProvider);\n otelLogger = logs.getLogger(serviceName);\n otelInitFingerprint = fingerprint;\n flushPendingPostHogLogs();\n}\n\nfunction isConsoleEnabled(): boolean {\n if (BUILD_CONSOLE_DISABLED) return false;\n if (typeof window === 'undefined') return true;\n return (window as LoggingWindow).__loggingDisabled !== true;\n}\n\nfunction isPostHogShippingEnabled(): boolean {\n if (BUILD_POSTHOG_DISABLED) return false;\n if (typeof window === 'undefined') return false;\n return (window as LoggingWindow).__posthogLoggingDisabled !== true;\n}\n\nfunction formatDetail(detail: Record<string, unknown>): string {\n return Object.entries(detail)\n .map(([k, v]) => `${k}=${typeof v === 'number' ? v.toFixed(4) : String(v)}`)\n .join(' ');\n}\n\nfunction emitConsole(\n level: LogLevel,\n channel: string,\n event: string,\n detail: Record<string, unknown>,\n): void {\n const color = CHANNEL_COLORS[channel] ?? '#aaa';\n const detailStr = formatDetail(detail);\n const message = `%c[${channel}] %c${event}%c ${detailStr}`;\n const channelStyle = `color:${color}; font-weight:bold`;\n const eventStyle = level === 'error'\n ? 'color:#e94e4e; font-weight:bold'\n : level === 'warn'\n ? 'color:#f5a623; font-weight:bold'\n : 'color:#fff; font-weight:bold';\n const detailStyle = 'color:#999; font-weight:normal';\n\n if (level === 'error') {\n browserConsole?.error?.(message, channelStyle, eventStyle, detailStyle);\n return;\n }\n if (level === 'warn') {\n browserConsole?.warn?.(message, channelStyle, eventStyle, detailStyle);\n return;\n }\n browserConsole?.log?.(message, channelStyle, eventStyle, detailStyle);\n}\n\nfunction emitToPostHog(level: LogLevel, channel: string, event: string, detail: Record<string, unknown>): boolean {\n if (!otelLogger) return false;\n\n const severityText = level === 'warn' ? 'WARNING' : level === 'error' ? 'ERROR' : 'INFO';\n const attributes = flattenAttributes({ channel, ...detail });\n\n otelLogger.emit({\n severityText,\n body: event,\n attributes,\n });\n return true;\n}\n\nfunction emit(level: LogLevel, channel: string, event: string, detail: Record<string, unknown>, options?: LogOptions): void {\n const shouldShip = (options?.shipToPostHog ?? level !== 'info') && isPostHogShippingEnabled();\n // Keep warn/error visible even when regular logs are muted.\n if (level !== 'info' || isConsoleEnabled()) {\n emitConsole(level, channel, event, detail);\n }\n if (shouldShip) {\n const shipped = emitToPostHog(level, channel, event, detail);\n if (!shipped) {\n enqueuePendingPostHogLog({ level, channel, event, detail });\n }\n }\n}\n\nexport function log(channel: string, event: string, detail: Record<string, unknown> = {}, options?: LogOptions): void {\n emit('info', channel, event, detail, options);\n}\n\nexport function warn(channel: string, event: string, detail: Record<string, unknown> = {}, options?: LogOptions): void {\n emit('warn', channel, event, detail, options);\n}\n\nexport function error(channel: string, event: string, detail: Record<string, unknown> = {}, options?: LogOptions): void {\n emit('error', channel, event, detail, options);\n}\n"],"mappings":"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","names":[]}
@@ -71,7 +71,9 @@ import { resourceFromAttributes } from "@opentelemetry/resources";
71
71
  import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
72
72
  var BUILD_CONSOLE_DISABLED = process.env.NEXT_PUBLIC_LOGGING_DISABLED === "1";
73
73
  var BUILD_POSTHOG_DISABLED = process.env.NEXT_PUBLIC_POSTHOG_LOGGING_DISABLED === "1";
74
+ var MAX_PENDING_POSTHOG_LOGS = 500;
74
75
  var otelLogger = null;
76
+ var pendingPostHogLogs = [];
75
77
  var browserConsole = globalThis == null ? void 0 : globalThis["console"];
76
78
  var CHANNEL_COLORS = {
77
79
  // Shared diagnostics
@@ -113,6 +115,12 @@ function flattenAttributes(input, prefix = "") {
113
115
  }
114
116
  return output;
115
117
  }
118
+ function enqueuePendingPostHogLog(entry) {
119
+ if (pendingPostHogLogs.length >= MAX_PENDING_POSTHOG_LOGS) {
120
+ pendingPostHogLogs.shift();
121
+ }
122
+ pendingPostHogLogs.push(entry);
123
+ }
116
124
  function isConsoleEnabled() {
117
125
  if (BUILD_CONSOLE_DISABLED) return false;
118
126
  if (typeof window === "undefined") return true;
@@ -145,7 +153,7 @@ function emitConsole(level, channel, event, detail) {
145
153
  (_d = browserConsole == null ? void 0 : browserConsole.log) == null ? void 0 : _d.call(browserConsole, message, channelStyle, eventStyle, detailStyle);
146
154
  }
147
155
  function emitToPostHog(level, channel, event, detail) {
148
- if (!otelLogger) return;
156
+ if (!otelLogger) return false;
149
157
  const severityText = level === "warn" ? "WARNING" : level === "error" ? "ERROR" : "INFO";
150
158
  const attributes = flattenAttributes(__spreadValues({ channel }, detail));
151
159
  otelLogger.emit({
@@ -153,6 +161,7 @@ function emitToPostHog(level, channel, event, detail) {
153
161
  body: event,
154
162
  attributes
155
163
  });
164
+ return true;
156
165
  }
157
166
  function emit(level, channel, event, detail, options) {
158
167
  var _a;
@@ -161,7 +170,10 @@ function emit(level, channel, event, detail, options) {
161
170
  emitConsole(level, channel, event, detail);
162
171
  }
163
172
  if (shouldShip) {
164
- emitToPostHog(level, channel, event, detail);
173
+ const shipped = emitToPostHog(level, channel, event, detail);
174
+ if (!shipped) {
175
+ enqueuePendingPostHogLog({ level, channel, event, detail });
176
+ }
165
177
  }
166
178
  }
167
179
  function warn(channel, event, detail = {}, options) {