keystone-design-bootstrap 1.0.95 → 1.0.96

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keystone-design-bootstrap",
3
- "version": "1.0.95",
3
+ "version": "1.0.96",
4
4
  "description": "Keystone Design Bootstrap - Sections, Elements, and Theme System for customer websites",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -4,6 +4,7 @@ import posthog from 'posthog-js';
4
4
  import { PostHogProvider as PHProvider } from 'posthog-js/react';
5
5
  import { useEffect, Suspense } from 'react';
6
6
  import { usePathname, useSearchParams } from 'next/navigation';
7
+ import { error as logError } from './logging';
7
8
 
8
9
  const DEFAULT_HOST = 'https://us.i.posthog.com';
9
10
 
@@ -125,6 +126,40 @@ export function PostHogProvider({ apiKey, apiHost, accountId, accountName, envir
125
126
  });
126
127
  }, [apiKey, apiHost, accountId, accountName, environment]);
127
128
 
129
+ useEffect(() => {
130
+ const onWindowError = (event: ErrorEvent) => {
131
+ logError(
132
+ 'global-client',
133
+ 'WINDOW_ERROR',
134
+ {
135
+ message: event.message || 'unknown_error',
136
+ filename: event.filename || 'unknown_file',
137
+ lineno: event.lineno || 0,
138
+ colno: event.colno || 0,
139
+ stack: event.error instanceof Error ? event.error.stack || '' : '',
140
+ },
141
+ { shipToPostHog: true },
142
+ );
143
+ };
144
+
145
+ const onUnhandledRejection = (event: PromiseRejectionEvent) => {
146
+ const reason = event.reason;
147
+ const payload =
148
+ reason instanceof Error
149
+ ? { message: reason.message, stack: reason.stack || '' }
150
+ : { message: String(reason), stack: '' };
151
+
152
+ logError('global-client', 'UNHANDLED_REJECTION', payload, { shipToPostHog: true });
153
+ };
154
+
155
+ window.addEventListener('error', onWindowError);
156
+ window.addEventListener('unhandledrejection', onUnhandledRejection);
157
+ return () => {
158
+ window.removeEventListener('error', onWindowError);
159
+ window.removeEventListener('unhandledrejection', onUnhandledRejection);
160
+ };
161
+ }, []);
162
+
128
163
  return (
129
164
  <PHProvider client={posthog}>
130
165
  <Suspense fallback={null}>
@@ -30,3 +30,5 @@ export { GoogleTagManager } from './GoogleTagManager';
30
30
  export type { GoogleTagManagerProps } from './GoogleTagManager';
31
31
  export { captureEvent, captureCustomEvent } from './captureEvent';
32
32
  export type { KsEventName, KsEventProperties } from './captureEvent';
33
+ export { log, warn, error, CHANNEL_COLORS } from './logging';
34
+ export type { LogLevel, LogOptions } from './logging';
@@ -0,0 +1,123 @@
1
+ 'use client';
2
+
3
+ import { captureCustomEvent } from './captureEvent';
4
+
5
+ const BUILD_CONSOLE_DISABLED = process.env.NEXT_PUBLIC_LOGGING_DISABLED === '1';
6
+ const BUILD_POSTHOG_DISABLED = process.env.NEXT_PUBLIC_POSTHOG_LOGGING_DISABLED === '1';
7
+ const POSTHOG_LOG_EVENT = 'keystone_client_log';
8
+
9
+ interface LoggingWindow extends Window {
10
+ __loggingDisabled?: boolean;
11
+ __posthogLoggingDisabled?: boolean;
12
+ }
13
+
14
+ export type LogLevel = 'info' | 'warn' | 'error';
15
+
16
+ export type LogOptions = {
17
+ /** Send this log payload to PostHog (`keystone_client_log`). */
18
+ shipToPostHog?: boolean;
19
+ };
20
+
21
+ /**
22
+ * Channel → accent colour. Unregistered channels fall through to gray.
23
+ */
24
+ export const CHANNEL_COLORS: Record<string, string> = {
25
+ // Shared diagnostics
26
+ 'global-client': '#d2a8ff',
27
+
28
+ // Section pins (desktop)
29
+ 'every-channel-pin': '#9febd7',
30
+ 'hero-pin': '#6ecc8b',
31
+ 'pricing-pin': '#399587',
32
+ 'product-screens-pin': '#4fafa0',
33
+ 'social-proof-pin': '#ffbb8a',
34
+ 'value-props-pin': '#e0a733',
35
+ 'work-pin': '#f57e56',
36
+
37
+ // Section pins (mobile)
38
+ 'mobile-every-channel-pin': '#7ed9c6',
39
+ 'mobile-hero-pin': '#f0eee6',
40
+ 'mobile-pricing-pin': '#80d4ff',
41
+ 'mobile-product-screens-pin': '#3a9085',
42
+ 'mobile-social-proof-pin': '#ffd580',
43
+ 'mobile-value-props-pin': '#4fafa0',
44
+ };
45
+
46
+ function isConsoleEnabled(): boolean {
47
+ if (BUILD_CONSOLE_DISABLED) return false;
48
+ if (typeof window === 'undefined') return true;
49
+ return (window as LoggingWindow).__loggingDisabled !== true;
50
+ }
51
+
52
+ function isPostHogShippingEnabled(): boolean {
53
+ if (BUILD_POSTHOG_DISABLED) return false;
54
+ if (typeof window === 'undefined') return false;
55
+ return (window as LoggingWindow).__posthogLoggingDisabled !== true;
56
+ }
57
+
58
+ function formatDetail(detail: Record<string, unknown>): string {
59
+ return Object.entries(detail)
60
+ .map(([k, v]) => `${k}=${typeof v === 'number' ? v.toFixed(4) : String(v)}`)
61
+ .join(' ');
62
+ }
63
+
64
+ function emitConsole(level: LogLevel, channel: string, event: string, detail: Record<string, unknown>): void {
65
+ const color = CHANNEL_COLORS[channel] ?? '#aaa';
66
+ const detailStr = formatDetail(detail);
67
+ const message = `%c[${channel}] %c${event}%c ${detailStr}`;
68
+ const channelStyle = `color:${color}; font-weight:bold`;
69
+ const eventStyle = level === 'error'
70
+ ? 'color:#e94e4e; font-weight:bold'
71
+ : level === 'warn'
72
+ ? 'color:#f5a623; font-weight:bold'
73
+ : 'color:#fff; font-weight:bold';
74
+ const detailStyle = 'color:#999; font-weight:normal';
75
+
76
+ if (level === 'error') {
77
+ console.error(message, channelStyle, eventStyle, detailStyle);
78
+ return;
79
+ }
80
+ if (level === 'warn') {
81
+ console.warn(message, channelStyle, eventStyle, detailStyle);
82
+ return;
83
+ }
84
+ console.log(message, channelStyle, eventStyle, detailStyle);
85
+ }
86
+
87
+ function maybeShipToPostHog(
88
+ level: LogLevel,
89
+ channel: string,
90
+ event: string,
91
+ detail: Record<string, unknown>,
92
+ options?: LogOptions,
93
+ ): void {
94
+ const shouldShip = options?.shipToPostHog ?? level !== 'info';
95
+ if (!shouldShip || !isPostHogShippingEnabled()) return;
96
+
97
+ captureCustomEvent(POSTHOG_LOG_EVENT, {
98
+ level,
99
+ channel,
100
+ event,
101
+ detail,
102
+ });
103
+ }
104
+
105
+ function emit(level: LogLevel, channel: string, event: string, detail: Record<string, unknown>, options?: LogOptions): void {
106
+ // Keep warn/error visible even when regular logs are muted.
107
+ if (level !== 'info' || isConsoleEnabled()) {
108
+ emitConsole(level, channel, event, detail);
109
+ }
110
+ maybeShipToPostHog(level, channel, event, detail, options);
111
+ }
112
+
113
+ export function log(channel: string, event: string, detail: Record<string, unknown> = {}, options?: LogOptions): void {
114
+ emit('info', channel, event, detail, options);
115
+ }
116
+
117
+ export function warn(channel: string, event: string, detail: Record<string, unknown> = {}, options?: LogOptions): void {
118
+ emit('warn', channel, event, detail, options);
119
+ }
120
+
121
+ export function error(channel: string, event: string, detail: Record<string, unknown> = {}, options?: LogOptions): void {
122
+ emit('error', channel, event, detail, options);
123
+ }