@seed-ship/mcp-ui-solid 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.8] - 2025-11-16
9
+
10
+ ### Fixed
11
+ - **CRITICAL SSR FIX**: Added `isServer` guards to all browser APIs in `GenerativeUIErrorBoundary.tsx`
12
+ - Previous versions crashed on Railway SSR with: `Error: Client-only API called on the server side`
13
+ - Browser APIs used without guards: `performance.now()`, `navigator.userAgent`, `window.innerWidth/height`
14
+ - These APIs don't exist in Node.js SSR environment, causing immediate crashes
15
+ - Solution: Wrapped all browser API calls with `isServer` conditionals
16
+ - Affected locations:
17
+ - Line 114: `createSignal(isServer ? 0 : performance.now())`
18
+ - Line 118: `const renderEndTime = isServer ? 0 : performance.now()`
19
+ - Line 130: `userAgent: isServer ? 'server' : navigator.userAgent`
20
+ - Lines 131-133: `viewport: isServer ? { width: 0, height: 0 } : { width: window.innerWidth, height: window.innerHeight }`
21
+ - Line 203: `const renderStart = isServer ? 0 : performance.now()` (withPerformanceMonitoring)
22
+ - Line 212: `if (!isServer && typeof window !== 'undefined')` (requestAnimationFrame guard)
23
+ - Line 242: `const mountTime = isServer ? 0 : performance.now()` (useComponentTelemetry)
24
+ - Line 252: `const lifetime = isServer ? 0 : performance.now() - mountTime`
25
+
26
+ ### Technical Details
27
+ - `isServer` is a compile-time constant from `solid-js/web`
28
+ - On server: `isServer = true`, browser APIs return safe defaults (0, 'server', empty viewport)
29
+ - On client: `isServer = false`, real browser APIs are used
30
+ - No runtime overhead: dead code elimination removes unused branches
31
+ - Fully compatible with SolidStart SSR on Railway and other Node.js platforms
32
+
33
+ ### Migration Notes
34
+ - No breaking changes for consumers
35
+ - Drop-in replacement for v1.0.7
36
+ - Fixes production SSR crashes on Railway and similar Node.js SSR platforms
37
+ - Telemetry data will show default values on server-side renders (expected behavior)
38
+
8
39
  ## [1.0.7] - 2025-11-16
9
40
 
10
41
  ### Fixed
@@ -36,10 +36,10 @@ function DefaultErrorFallback(props) {
36
36
  }
37
37
  const GenerativeUIErrorBoundary = (props) => {
38
38
  const [retryKey, setRetryKey] = solidJs.createSignal(0);
39
- const [renderStartTime] = solidJs.createSignal(performance.now());
39
+ const [renderStartTime] = solidJs.createSignal(web.isServer ? 0 : performance.now());
40
40
  const handleError = (error) => {
41
41
  var _a;
42
- const renderEndTime = performance.now();
42
+ const renderEndTime = web.isServer ? 0 : performance.now();
43
43
  const renderDuration = renderEndTime - renderStartTime();
44
44
  const errorContext = {
45
45
  componentId: props.componentId,
@@ -49,8 +49,11 @@ const GenerativeUIErrorBoundary = (props) => {
49
49
  renderDuration,
50
50
  retryCount: retryKey(),
51
51
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
52
- userAgent: navigator.userAgent,
53
- viewport: {
52
+ userAgent: web.isServer ? "server" : navigator.userAgent,
53
+ viewport: web.isServer ? {
54
+ width: 0,
55
+ height: 0
56
+ } : {
54
57
  width: window.innerWidth,
55
58
  height: window.innerHeight
56
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GenerativeUIErrorBoundary.cjs","sources":["../../src/components/GenerativeUIErrorBoundary.tsx"],"sourcesContent":["/**\n * Generative UI Error Boundary with Telemetry\n * Phase 0: Error isolation + structured logging\n *\n * Features:\n * - Component-level error isolation\n * - Structured logging with context\n * - Performance timing\n * - Retry mechanism\n * - User-friendly fallback UI\n */\n\nimport { Component, ErrorBoundary, createSignal, Show } from 'solid-js'\nimport { createLogger } from '../utils/logger'\nimport type { RendererError } from '../types'\n\nconst logger = createLogger('generative-ui')\n\n/**\n * Props for GenerativeUIErrorBoundary\n */\nexport interface GenerativeUIErrorBoundaryProps {\n /**\n * Component identifier for telemetry\n */\n componentId: string\n\n /**\n * Component type for context\n */\n componentType: string\n\n /**\n * Error callback\n */\n onError?: (error: RendererError) => void\n\n /**\n * Allow retry on error\n */\n allowRetry?: boolean\n\n /**\n * Child components to wrap\n */\n children: any\n\n /**\n * Custom fallback UI (optional)\n */\n fallback?: (error: Error, retry?: () => void) => any\n}\n\n/**\n * Default fallback UI for errors\n */\nfunction DefaultErrorFallback(props: {\n error: Error\n componentId: string\n componentType: string\n allowRetry?: boolean\n onRetry?: () => void\n}) {\n return (\n <div class=\"w-full h-full bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4\">\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-shrink-0\">\n <svg\n class=\"w-5 h-5 text-yellow-600 dark:text-yellow-400\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-sm font-medium text-yellow-900 dark:text-yellow-100\">\n Component Failed to Render\n </p>\n <p class=\"text-xs text-yellow-700 dark:text-yellow-300 mt-1\">\n Type: {props.componentType} | ID: {props.componentId.slice(0, 8)}...\n </p>\n <Show when={import.meta.env.DEV}>\n <p class=\"text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono\">\n {props.error.message}\n </p>\n </Show>\n <Show when={props.allowRetry}>\n <button\n onClick={props.onRetry}\n class=\"mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline\"\n >\n Retry Rendering\n </button>\n </Show>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Generative UI Error Boundary Component\n */\nexport const GenerativeUIErrorBoundary: Component<GenerativeUIErrorBoundaryProps> = (props) => {\n const [retryKey, setRetryKey] = createSignal(0)\n const [renderStartTime] = createSignal(performance.now())\n\n // Handle error with telemetry\n const handleError = (error: Error) => {\n const renderEndTime = performance.now()\n const renderDuration = renderEndTime - renderStartTime()\n\n // Structure error context\n const errorContext = {\n componentId: props.componentId,\n componentType: props.componentType,\n errorMessage: error.message,\n errorStack: error.stack,\n renderDuration,\n retryCount: retryKey(),\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n viewport: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n }\n\n // Log to structured logger\n logger.error(`Component render failed: ${props.componentType}`, errorContext)\n\n // Call error callback\n props.onError?.({\n type: 'render',\n message: error.message,\n componentId: props.componentId,\n details: errorContext,\n })\n\n // In production, send to monitoring service\n if (import.meta.env.PROD) {\n // Future: Send to Sentry or other APM\n // Sentry.captureException(error, { contexts: { component: errorContext } })\n }\n }\n\n // Retry mechanism\n const handleRetry = () => {\n const newRetryCount = retryKey() + 1\n logger.info(`Retrying component render: ${props.componentType}`, {\n componentId: props.componentId,\n retryCount: newRetryCount,\n })\n setRetryKey(newRetryCount)\n }\n\n return (\n <ErrorBoundary\n fallback={(error) => {\n handleError(error)\n\n // Use custom fallback if provided\n if (props.fallback) {\n return props.fallback(error, props.allowRetry ? handleRetry : undefined)\n }\n\n // Default fallback\n return (\n <DefaultErrorFallback\n error={error}\n componentId={props.componentId}\n componentType={props.componentType}\n allowRetry={props.allowRetry}\n onRetry={handleRetry}\n />\n )\n }}\n >\n {/* Key prop for forcing remount on retry */}\n {(() => {\n const _ = retryKey() // Access signal to track changes\n return <>{props.children}</>\n })()}\n </ErrorBoundary>\n )\n}\n\n/**\n * Performance monitoring wrapper\n * Logs render times for performance analysis\n */\nexport function withPerformanceMonitoring<P extends { componentId: string; componentType: string }>(\n WrappedComponent: Component<P>\n) {\n return (props: P) => {\n const renderStart = performance.now()\n\n // Log render start\n logger.debug(`Component render start: ${props.componentType}`, {\n componentId: props.componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Measure on mount completion\n if (typeof window !== 'undefined') {\n requestAnimationFrame(() => {\n const renderEnd = performance.now()\n const duration = renderEnd - renderStart\n\n logger.info(`Component rendered: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n timestamp: new Date().toISOString(),\n })\n\n // Warn if render is slow (>50ms target)\n if (duration > 50) {\n logger.warn(`Slow component render: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n threshold: 50,\n })\n }\n })\n }\n\n return <WrappedComponent {...props} />\n }\n}\n\n/**\n * Hook to track component lifecycle events\n */\nexport function useComponentTelemetry(componentId: string, componentType: string) {\n const mountTime = performance.now()\n\n // Log mount\n logger.debug(`Component mounted: ${componentType}`, {\n componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Return cleanup function for unmount\n return () => {\n const lifetime = performance.now() - mountTime\n logger.debug(`Component unmounted: ${componentType}`, {\n componentId,\n lifetime,\n timestamp: new Date().toISOString(),\n })\n }\n}\n"],"names":["logger","createLogger","DefaultErrorFallback","props","_el$","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_el$6","_el$7","_el$0","_el$8","_el$1","_$insert","componentType","componentId","slice","_$createComponent","Show","when","import","children","_el$10","_tmpl$","error","message","allowRetry","_el$11","_tmpl$2","_$addEventListener","onRetry","GenerativeUIErrorBoundary","retryKey","setRetryKey","createSignal","renderStartTime","performance","now","handleError","renderEndTime","renderDuration","errorContext","errorMessage","errorStack","stack","retryCount","timestamp","Date","toISOString","userAgent","navigator","viewport","width","window","innerWidth","height","innerHeight","onError","type","details","handleRetry","newRetryCount","info","ErrorBoundary","fallback","undefined","_$memo","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"GenerativeUIErrorBoundary.cjs","sources":["../../src/components/GenerativeUIErrorBoundary.tsx"],"sourcesContent":["/**\n * Generative UI Error Boundary with Telemetry\n * Phase 0: Error isolation + structured logging\n *\n * Features:\n * - Component-level error isolation\n * - Structured logging with context\n * - Performance timing\n * - Retry mechanism\n * - User-friendly fallback UI\n */\n\nimport { Component, ErrorBoundary, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport { createLogger } from '../utils/logger'\nimport type { RendererError } from '../types'\n\nconst logger = createLogger('generative-ui')\n\n/**\n * Props for GenerativeUIErrorBoundary\n */\nexport interface GenerativeUIErrorBoundaryProps {\n /**\n * Component identifier for telemetry\n */\n componentId: string\n\n /**\n * Component type for context\n */\n componentType: string\n\n /**\n * Error callback\n */\n onError?: (error: RendererError) => void\n\n /**\n * Allow retry on error\n */\n allowRetry?: boolean\n\n /**\n * Child components to wrap\n */\n children: any\n\n /**\n * Custom fallback UI (optional)\n */\n fallback?: (error: Error, retry?: () => void) => any\n}\n\n/**\n * Default fallback UI for errors\n */\nfunction DefaultErrorFallback(props: {\n error: Error\n componentId: string\n componentType: string\n allowRetry?: boolean\n onRetry?: () => void\n}) {\n return (\n <div class=\"w-full h-full bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4\">\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-shrink-0\">\n <svg\n class=\"w-5 h-5 text-yellow-600 dark:text-yellow-400\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-sm font-medium text-yellow-900 dark:text-yellow-100\">\n Component Failed to Render\n </p>\n <p class=\"text-xs text-yellow-700 dark:text-yellow-300 mt-1\">\n Type: {props.componentType} | ID: {props.componentId.slice(0, 8)}...\n </p>\n <Show when={import.meta.env.DEV}>\n <p class=\"text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono\">\n {props.error.message}\n </p>\n </Show>\n <Show when={props.allowRetry}>\n <button\n onClick={props.onRetry}\n class=\"mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline\"\n >\n Retry Rendering\n </button>\n </Show>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Generative UI Error Boundary Component\n */\nexport const GenerativeUIErrorBoundary: Component<GenerativeUIErrorBoundaryProps> = (props) => {\n const [retryKey, setRetryKey] = createSignal(0)\n const [renderStartTime] = createSignal(isServer ? 0 : performance.now())\n\n // Handle error with telemetry\n const handleError = (error: Error) => {\n const renderEndTime = isServer ? 0 : performance.now()\n const renderDuration = renderEndTime - renderStartTime()\n\n // Structure error context\n const errorContext = {\n componentId: props.componentId,\n componentType: props.componentType,\n errorMessage: error.message,\n errorStack: error.stack,\n renderDuration,\n retryCount: retryKey(),\n timestamp: new Date().toISOString(),\n userAgent: isServer ? 'server' : navigator.userAgent,\n viewport: isServer\n ? { width: 0, height: 0 }\n : { width: window.innerWidth, height: window.innerHeight },\n }\n\n // Log to structured logger\n logger.error(`Component render failed: ${props.componentType}`, errorContext)\n\n // Call error callback\n props.onError?.({\n type: 'render',\n message: error.message,\n componentId: props.componentId,\n details: errorContext,\n })\n\n // In production, send to monitoring service\n if (import.meta.env.PROD) {\n // Future: Send to Sentry or other APM\n // Sentry.captureException(error, { contexts: { component: errorContext } })\n }\n }\n\n // Retry mechanism\n const handleRetry = () => {\n const newRetryCount = retryKey() + 1\n logger.info(`Retrying component render: ${props.componentType}`, {\n componentId: props.componentId,\n retryCount: newRetryCount,\n })\n setRetryKey(newRetryCount)\n }\n\n return (\n <ErrorBoundary\n fallback={(error) => {\n handleError(error)\n\n // Use custom fallback if provided\n if (props.fallback) {\n return props.fallback(error, props.allowRetry ? handleRetry : undefined)\n }\n\n // Default fallback\n return (\n <DefaultErrorFallback\n error={error}\n componentId={props.componentId}\n componentType={props.componentType}\n allowRetry={props.allowRetry}\n onRetry={handleRetry}\n />\n )\n }}\n >\n {/* Key prop for forcing remount on retry */}\n {(() => {\n const _ = retryKey() // Access signal to track changes\n return <>{props.children}</>\n })()}\n </ErrorBoundary>\n )\n}\n\n/**\n * Performance monitoring wrapper\n * Logs render times for performance analysis\n */\nexport function withPerformanceMonitoring<P extends { componentId: string; componentType: string }>(\n WrappedComponent: Component<P>\n) {\n return (props: P) => {\n const renderStart = isServer ? 0 : performance.now()\n\n // Log render start\n logger.debug(`Component render start: ${props.componentType}`, {\n componentId: props.componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Measure on mount completion (client-side only)\n if (!isServer && typeof window !== 'undefined') {\n requestAnimationFrame(() => {\n const renderEnd = performance.now()\n const duration = renderEnd - renderStart\n\n logger.info(`Component rendered: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n timestamp: new Date().toISOString(),\n })\n\n // Warn if render is slow (>50ms target)\n if (duration > 50) {\n logger.warn(`Slow component render: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n threshold: 50,\n })\n }\n })\n }\n\n return <WrappedComponent {...props} />\n }\n}\n\n/**\n * Hook to track component lifecycle events\n */\nexport function useComponentTelemetry(componentId: string, componentType: string) {\n const mountTime = isServer ? 0 : performance.now()\n\n // Log mount\n logger.debug(`Component mounted: ${componentType}`, {\n componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Return cleanup function for unmount\n return () => {\n const lifetime = isServer ? 0 : performance.now() - mountTime\n logger.debug(`Component unmounted: ${componentType}`, {\n componentId,\n lifetime,\n timestamp: new Date().toISOString(),\n })\n }\n}\n"],"names":["logger","createLogger","DefaultErrorFallback","props","_el$","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_el$6","_el$7","_el$0","_el$8","_el$1","_$insert","componentType","componentId","slice","_$createComponent","Show","when","import","children","_el$10","_tmpl$","error","message","allowRetry","_el$11","_tmpl$2","_$addEventListener","onRetry","GenerativeUIErrorBoundary","retryKey","setRetryKey","createSignal","renderStartTime","isServer","performance","now","handleError","renderEndTime","renderDuration","errorContext","errorMessage","errorStack","stack","retryCount","timestamp","Date","toISOString","userAgent","navigator","viewport","width","height","window","innerWidth","innerHeight","onError","type","details","handleRetry","newRetryCount","info","ErrorBoundary","fallback","undefined","_$memo","_$delegateEvents"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"GenerativeUIErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/components/GenerativeUIErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAqC,MAAM,UAAU,CAAA;AAEvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAI7C;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,aAAa,EAAE,MAAM,CAAA;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAExC;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IAEpB;;OAEG;IACH,QAAQ,EAAE,GAAG,CAAA;IAEb;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,KAAK,GAAG,CAAA;CACrD;AAwDD;;GAEG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,CAAC,8BAA8B,CAkF/E,CAAA;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,EAChG,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAC,IAEtB,OAAO,CAAC,oCAkCjB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,cAkB/E"}
1
+ {"version":3,"file":"GenerativeUIErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/components/GenerativeUIErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAqC,MAAM,UAAU,CAAA;AAGvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAI7C;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,aAAa,EAAE,MAAM,CAAA;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAExC;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IAEpB;;OAEG;IACH,QAAQ,EAAE,GAAG,CAAA;IAEb;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,KAAK,GAAG,CAAA;CACrD;AAwDD;;GAEG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,CAAC,8BAA8B,CAiF/E,CAAA;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,EAChG,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAC,IAEtB,OAAO,CAAC,oCAkCjB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,cAkB/E"}
@@ -1,4 +1,4 @@
1
- import { delegateEvents, createComponent, memo, template, insert, addEventListener } from "solid-js/web";
1
+ import { delegateEvents, isServer, createComponent, memo, template, insert, addEventListener } from "solid-js/web";
2
2
  import { createSignal, ErrorBoundary, Show } from "solid-js";
3
3
  import { createLogger } from "../utils/logger.js";
4
4
  var _tmpl$ = /* @__PURE__ */ template(`<p class="text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono">`), _tmpl$2 = /* @__PURE__ */ template(`<button class="mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline">Retry Rendering`), _tmpl$3 = /* @__PURE__ */ template(`<div class="w-full h-full bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4"><div class="flex items-start gap-3"><div class=flex-shrink-0><svg class="w-5 h-5 text-yellow-600 dark:text-yellow-400"fill=none stroke=currentColor viewBox="0 0 24 24"><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg></div><div class="flex-1 min-w-0"><p class="text-sm font-medium text-yellow-900 dark:text-yellow-100">Component Failed to Render</p><p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">Type: <!> | ID: <!>...`);
@@ -34,10 +34,10 @@ function DefaultErrorFallback(props) {
34
34
  }
35
35
  const GenerativeUIErrorBoundary = (props) => {
36
36
  const [retryKey, setRetryKey] = createSignal(0);
37
- const [renderStartTime] = createSignal(performance.now());
37
+ const [renderStartTime] = createSignal(isServer ? 0 : performance.now());
38
38
  const handleError = (error) => {
39
39
  var _a;
40
- const renderEndTime = performance.now();
40
+ const renderEndTime = isServer ? 0 : performance.now();
41
41
  const renderDuration = renderEndTime - renderStartTime();
42
42
  const errorContext = {
43
43
  componentId: props.componentId,
@@ -47,8 +47,11 @@ const GenerativeUIErrorBoundary = (props) => {
47
47
  renderDuration,
48
48
  retryCount: retryKey(),
49
49
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
50
- userAgent: navigator.userAgent,
51
- viewport: {
50
+ userAgent: isServer ? "server" : navigator.userAgent,
51
+ viewport: isServer ? {
52
+ width: 0,
53
+ height: 0
54
+ } : {
52
55
  width: window.innerWidth,
53
56
  height: window.innerHeight
54
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GenerativeUIErrorBoundary.js","sources":["../../src/components/GenerativeUIErrorBoundary.tsx"],"sourcesContent":["/**\n * Generative UI Error Boundary with Telemetry\n * Phase 0: Error isolation + structured logging\n *\n * Features:\n * - Component-level error isolation\n * - Structured logging with context\n * - Performance timing\n * - Retry mechanism\n * - User-friendly fallback UI\n */\n\nimport { Component, ErrorBoundary, createSignal, Show } from 'solid-js'\nimport { createLogger } from '../utils/logger'\nimport type { RendererError } from '../types'\n\nconst logger = createLogger('generative-ui')\n\n/**\n * Props for GenerativeUIErrorBoundary\n */\nexport interface GenerativeUIErrorBoundaryProps {\n /**\n * Component identifier for telemetry\n */\n componentId: string\n\n /**\n * Component type for context\n */\n componentType: string\n\n /**\n * Error callback\n */\n onError?: (error: RendererError) => void\n\n /**\n * Allow retry on error\n */\n allowRetry?: boolean\n\n /**\n * Child components to wrap\n */\n children: any\n\n /**\n * Custom fallback UI (optional)\n */\n fallback?: (error: Error, retry?: () => void) => any\n}\n\n/**\n * Default fallback UI for errors\n */\nfunction DefaultErrorFallback(props: {\n error: Error\n componentId: string\n componentType: string\n allowRetry?: boolean\n onRetry?: () => void\n}) {\n return (\n <div class=\"w-full h-full bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4\">\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-shrink-0\">\n <svg\n class=\"w-5 h-5 text-yellow-600 dark:text-yellow-400\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-sm font-medium text-yellow-900 dark:text-yellow-100\">\n Component Failed to Render\n </p>\n <p class=\"text-xs text-yellow-700 dark:text-yellow-300 mt-1\">\n Type: {props.componentType} | ID: {props.componentId.slice(0, 8)}...\n </p>\n <Show when={import.meta.env.DEV}>\n <p class=\"text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono\">\n {props.error.message}\n </p>\n </Show>\n <Show when={props.allowRetry}>\n <button\n onClick={props.onRetry}\n class=\"mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline\"\n >\n Retry Rendering\n </button>\n </Show>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Generative UI Error Boundary Component\n */\nexport const GenerativeUIErrorBoundary: Component<GenerativeUIErrorBoundaryProps> = (props) => {\n const [retryKey, setRetryKey] = createSignal(0)\n const [renderStartTime] = createSignal(performance.now())\n\n // Handle error with telemetry\n const handleError = (error: Error) => {\n const renderEndTime = performance.now()\n const renderDuration = renderEndTime - renderStartTime()\n\n // Structure error context\n const errorContext = {\n componentId: props.componentId,\n componentType: props.componentType,\n errorMessage: error.message,\n errorStack: error.stack,\n renderDuration,\n retryCount: retryKey(),\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n viewport: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n }\n\n // Log to structured logger\n logger.error(`Component render failed: ${props.componentType}`, errorContext)\n\n // Call error callback\n props.onError?.({\n type: 'render',\n message: error.message,\n componentId: props.componentId,\n details: errorContext,\n })\n\n // In production, send to monitoring service\n if (import.meta.env.PROD) {\n // Future: Send to Sentry or other APM\n // Sentry.captureException(error, { contexts: { component: errorContext } })\n }\n }\n\n // Retry mechanism\n const handleRetry = () => {\n const newRetryCount = retryKey() + 1\n logger.info(`Retrying component render: ${props.componentType}`, {\n componentId: props.componentId,\n retryCount: newRetryCount,\n })\n setRetryKey(newRetryCount)\n }\n\n return (\n <ErrorBoundary\n fallback={(error) => {\n handleError(error)\n\n // Use custom fallback if provided\n if (props.fallback) {\n return props.fallback(error, props.allowRetry ? handleRetry : undefined)\n }\n\n // Default fallback\n return (\n <DefaultErrorFallback\n error={error}\n componentId={props.componentId}\n componentType={props.componentType}\n allowRetry={props.allowRetry}\n onRetry={handleRetry}\n />\n )\n }}\n >\n {/* Key prop for forcing remount on retry */}\n {(() => {\n const _ = retryKey() // Access signal to track changes\n return <>{props.children}</>\n })()}\n </ErrorBoundary>\n )\n}\n\n/**\n * Performance monitoring wrapper\n * Logs render times for performance analysis\n */\nexport function withPerformanceMonitoring<P extends { componentId: string; componentType: string }>(\n WrappedComponent: Component<P>\n) {\n return (props: P) => {\n const renderStart = performance.now()\n\n // Log render start\n logger.debug(`Component render start: ${props.componentType}`, {\n componentId: props.componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Measure on mount completion\n if (typeof window !== 'undefined') {\n requestAnimationFrame(() => {\n const renderEnd = performance.now()\n const duration = renderEnd - renderStart\n\n logger.info(`Component rendered: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n timestamp: new Date().toISOString(),\n })\n\n // Warn if render is slow (>50ms target)\n if (duration > 50) {\n logger.warn(`Slow component render: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n threshold: 50,\n })\n }\n })\n }\n\n return <WrappedComponent {...props} />\n }\n}\n\n/**\n * Hook to track component lifecycle events\n */\nexport function useComponentTelemetry(componentId: string, componentType: string) {\n const mountTime = performance.now()\n\n // Log mount\n logger.debug(`Component mounted: ${componentType}`, {\n componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Return cleanup function for unmount\n return () => {\n const lifetime = performance.now() - mountTime\n logger.debug(`Component unmounted: ${componentType}`, {\n componentId,\n lifetime,\n timestamp: new Date().toISOString(),\n })\n }\n}\n"],"names":["logger","createLogger","DefaultErrorFallback","props","_el$","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_el$6","_el$7","_el$0","_el$8","_el$1","_$insert","componentType","componentId","slice","_$createComponent","Show","when","import","children","_el$10","_tmpl$","error","message","allowRetry","_el$11","_tmpl$2","_$addEventListener","onRetry","GenerativeUIErrorBoundary","retryKey","setRetryKey","createSignal","renderStartTime","performance","now","handleError","renderEndTime","renderDuration","errorContext","errorMessage","errorStack","stack","retryCount","timestamp","Date","toISOString","userAgent","navigator","viewport","width","window","innerWidth","height","innerHeight","onError","type","details","handleRetry","newRetryCount","info","ErrorBoundary","fallback","undefined","_$memo","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"GenerativeUIErrorBoundary.js","sources":["../../src/components/GenerativeUIErrorBoundary.tsx"],"sourcesContent":["/**\n * Generative UI Error Boundary with Telemetry\n * Phase 0: Error isolation + structured logging\n *\n * Features:\n * - Component-level error isolation\n * - Structured logging with context\n * - Performance timing\n * - Retry mechanism\n * - User-friendly fallback UI\n */\n\nimport { Component, ErrorBoundary, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport { createLogger } from '../utils/logger'\nimport type { RendererError } from '../types'\n\nconst logger = createLogger('generative-ui')\n\n/**\n * Props for GenerativeUIErrorBoundary\n */\nexport interface GenerativeUIErrorBoundaryProps {\n /**\n * Component identifier for telemetry\n */\n componentId: string\n\n /**\n * Component type for context\n */\n componentType: string\n\n /**\n * Error callback\n */\n onError?: (error: RendererError) => void\n\n /**\n * Allow retry on error\n */\n allowRetry?: boolean\n\n /**\n * Child components to wrap\n */\n children: any\n\n /**\n * Custom fallback UI (optional)\n */\n fallback?: (error: Error, retry?: () => void) => any\n}\n\n/**\n * Default fallback UI for errors\n */\nfunction DefaultErrorFallback(props: {\n error: Error\n componentId: string\n componentType: string\n allowRetry?: boolean\n onRetry?: () => void\n}) {\n return (\n <div class=\"w-full h-full bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4\">\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-shrink-0\">\n <svg\n class=\"w-5 h-5 text-yellow-600 dark:text-yellow-400\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"text-sm font-medium text-yellow-900 dark:text-yellow-100\">\n Component Failed to Render\n </p>\n <p class=\"text-xs text-yellow-700 dark:text-yellow-300 mt-1\">\n Type: {props.componentType} | ID: {props.componentId.slice(0, 8)}...\n </p>\n <Show when={import.meta.env.DEV}>\n <p class=\"text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono\">\n {props.error.message}\n </p>\n </Show>\n <Show when={props.allowRetry}>\n <button\n onClick={props.onRetry}\n class=\"mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline\"\n >\n Retry Rendering\n </button>\n </Show>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Generative UI Error Boundary Component\n */\nexport const GenerativeUIErrorBoundary: Component<GenerativeUIErrorBoundaryProps> = (props) => {\n const [retryKey, setRetryKey] = createSignal(0)\n const [renderStartTime] = createSignal(isServer ? 0 : performance.now())\n\n // Handle error with telemetry\n const handleError = (error: Error) => {\n const renderEndTime = isServer ? 0 : performance.now()\n const renderDuration = renderEndTime - renderStartTime()\n\n // Structure error context\n const errorContext = {\n componentId: props.componentId,\n componentType: props.componentType,\n errorMessage: error.message,\n errorStack: error.stack,\n renderDuration,\n retryCount: retryKey(),\n timestamp: new Date().toISOString(),\n userAgent: isServer ? 'server' : navigator.userAgent,\n viewport: isServer\n ? { width: 0, height: 0 }\n : { width: window.innerWidth, height: window.innerHeight },\n }\n\n // Log to structured logger\n logger.error(`Component render failed: ${props.componentType}`, errorContext)\n\n // Call error callback\n props.onError?.({\n type: 'render',\n message: error.message,\n componentId: props.componentId,\n details: errorContext,\n })\n\n // In production, send to monitoring service\n if (import.meta.env.PROD) {\n // Future: Send to Sentry or other APM\n // Sentry.captureException(error, { contexts: { component: errorContext } })\n }\n }\n\n // Retry mechanism\n const handleRetry = () => {\n const newRetryCount = retryKey() + 1\n logger.info(`Retrying component render: ${props.componentType}`, {\n componentId: props.componentId,\n retryCount: newRetryCount,\n })\n setRetryKey(newRetryCount)\n }\n\n return (\n <ErrorBoundary\n fallback={(error) => {\n handleError(error)\n\n // Use custom fallback if provided\n if (props.fallback) {\n return props.fallback(error, props.allowRetry ? handleRetry : undefined)\n }\n\n // Default fallback\n return (\n <DefaultErrorFallback\n error={error}\n componentId={props.componentId}\n componentType={props.componentType}\n allowRetry={props.allowRetry}\n onRetry={handleRetry}\n />\n )\n }}\n >\n {/* Key prop for forcing remount on retry */}\n {(() => {\n const _ = retryKey() // Access signal to track changes\n return <>{props.children}</>\n })()}\n </ErrorBoundary>\n )\n}\n\n/**\n * Performance monitoring wrapper\n * Logs render times for performance analysis\n */\nexport function withPerformanceMonitoring<P extends { componentId: string; componentType: string }>(\n WrappedComponent: Component<P>\n) {\n return (props: P) => {\n const renderStart = isServer ? 0 : performance.now()\n\n // Log render start\n logger.debug(`Component render start: ${props.componentType}`, {\n componentId: props.componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Measure on mount completion (client-side only)\n if (!isServer && typeof window !== 'undefined') {\n requestAnimationFrame(() => {\n const renderEnd = performance.now()\n const duration = renderEnd - renderStart\n\n logger.info(`Component rendered: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n timestamp: new Date().toISOString(),\n })\n\n // Warn if render is slow (>50ms target)\n if (duration > 50) {\n logger.warn(`Slow component render: ${props.componentType}`, {\n componentId: props.componentId,\n renderDuration: duration,\n threshold: 50,\n })\n }\n })\n }\n\n return <WrappedComponent {...props} />\n }\n}\n\n/**\n * Hook to track component lifecycle events\n */\nexport function useComponentTelemetry(componentId: string, componentType: string) {\n const mountTime = isServer ? 0 : performance.now()\n\n // Log mount\n logger.debug(`Component mounted: ${componentType}`, {\n componentId,\n timestamp: new Date().toISOString(),\n })\n\n // Return cleanup function for unmount\n return () => {\n const lifetime = isServer ? 0 : performance.now() - mountTime\n logger.debug(`Component unmounted: ${componentType}`, {\n componentId,\n lifetime,\n timestamp: new Date().toISOString(),\n })\n }\n}\n"],"names":["logger","createLogger","DefaultErrorFallback","props","_el$","_tmpl$3","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_el$6","_el$7","_el$0","_el$8","_el$1","_$insert","componentType","componentId","slice","_$createComponent","Show","when","import","children","_el$10","_tmpl$","error","message","allowRetry","_el$11","_tmpl$2","_$addEventListener","onRetry","GenerativeUIErrorBoundary","retryKey","setRetryKey","createSignal","renderStartTime","isServer","performance","now","handleError","renderEndTime","renderDuration","errorContext","errorMessage","errorStack","stack","retryCount","timestamp","Date","toISOString","userAgent","navigator","viewport","width","height","window","innerWidth","innerHeight","onError","type","details","handleRetry","newRetryCount","info","ErrorBoundary","fallback","undefined","_$memo","_$delegateEvents"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-ship/mcp-ui-solid",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "SolidJS components for rendering MCP-generated UI resources",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",