@seed-ship/mcp-ui-solid 5.3.1 → 5.4.0

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/components/StreamingUIRenderer.cjs +106 -90
  3. package/dist/components/StreamingUIRenderer.cjs.map +1 -1
  4. package/dist/components/StreamingUIRenderer.d.ts +7 -0
  5. package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
  6. package/dist/components/StreamingUIRenderer.js +107 -91
  7. package/dist/components/StreamingUIRenderer.js.map +1 -1
  8. package/dist/components/UIResourceRenderer.cjs +101 -82
  9. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  10. package/dist/components/UIResourceRenderer.d.ts +23 -0
  11. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  12. package/dist/components/UIResourceRenderer.js +102 -83
  13. package/dist/components/UIResourceRenderer.js.map +1 -1
  14. package/dist/index.cjs +7 -0
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +3 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +7 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/utils/logger.cjs +26 -4
  22. package/dist/utils/logger.cjs.map +1 -1
  23. package/dist/utils/logger.d.ts +30 -3
  24. package/dist/utils/logger.d.ts.map +1 -1
  25. package/dist/utils/logger.js +27 -5
  26. package/dist/utils/logger.js.map +1 -1
  27. package/dist/utils/perf.cjs +34 -0
  28. package/dist/utils/perf.cjs.map +1 -0
  29. package/dist/utils/perf.d.ts +19 -0
  30. package/dist/utils/perf.d.ts.map +1 -0
  31. package/dist/utils/perf.js +34 -0
  32. package/dist/utils/perf.js.map +1 -0
  33. package/package.json +1 -1
  34. package/src/components/StreamingUIRenderer.tsx +54 -2
  35. package/src/components/UIResourceRenderer.errorMode.test.tsx +95 -0
  36. package/src/components/UIResourceRenderer.tsx +72 -4
  37. package/src/index.ts +7 -0
  38. package/src/utils/logger.test.ts +130 -0
  39. package/src/utils/logger.ts +60 -7
  40. package/src/utils/perf.test.ts +59 -0
  41. package/src/utils/perf.ts +50 -0
  42. package/tsconfig.tsbuildinfo +1 -1
package/dist/index.cjs CHANGED
@@ -37,6 +37,8 @@ const serverCapabilitiesStore = require("./stores/server-capabilities-store.cjs"
37
37
  const GhostText = require("./components/GhostText.cjs");
38
38
  const AutocompleteDropdown = require("./components/AutocompleteDropdown.cjs");
39
39
  const AutocompleteFormField = require("./components/AutocompleteFormField.cjs");
40
+ const logger = require("./utils/logger.cjs");
41
+ const perf = require("./utils/perf.cjs");
40
42
  const useStreamingUI = require("./hooks/useStreamingUI.cjs");
41
43
  const useAction = require("./hooks/useAction.cjs");
42
44
  const useConditionalField = require("./hooks/useConditionalField.cjs");
@@ -93,6 +95,11 @@ exports.GhostText = GhostText.GhostText;
93
95
  exports.GhostTextInput = GhostText.GhostTextInput;
94
96
  exports.AutocompleteDropdown = AutocompleteDropdown.AutocompleteDropdown;
95
97
  exports.AutocompleteFormField = AutocompleteFormField.AutocompleteFormField;
98
+ exports.isDebugEnabled = logger.isDebugEnabled;
99
+ exports.setDebugMode = logger.setDebugMode;
100
+ exports.PERF_PREFIX = perf.PERF_PREFIX;
101
+ exports.markRenderEnd = perf.markRenderEnd;
102
+ exports.markRenderStart = perf.markRenderStart;
96
103
  exports.useStreamingUI = useStreamingUI.useStreamingUI;
97
104
  exports.useAction = useAction.useAction;
98
105
  exports.useToolAction = useAction.useToolAction;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.cts CHANGED
@@ -53,6 +53,9 @@ export { GhostText, GhostTextInput } from './components/GhostText';
53
53
  export { AutocompleteDropdown } from './components/AutocompleteDropdown';
54
54
  export { AutocompleteFormField } from './components/AutocompleteFormField';
55
55
  export type { UIResourceRendererProps, StreamingUIRendererProps, GenerativeUIErrorBoundaryProps, } from './components';
56
+ export type { ValidationErrorMode } from './components/UIResourceRenderer';
57
+ export { setDebugMode, isDebugEnabled } from './utils/logger';
58
+ export { markRenderStart, markRenderEnd, PERF_PREFIX } from './utils/perf';
56
59
  export type { DraggableGridItemProps } from './components/DraggableGridItem';
57
60
  export type { ResizeHandleProps as ResizeHandleComponentProps } from './components/ResizeHandle';
58
61
  export type { EditableUIResourceRendererProps } from './components/EditableUIResourceRenderer';
package/dist/index.d.ts CHANGED
@@ -53,6 +53,9 @@ export { GhostText, GhostTextInput } from './components/GhostText';
53
53
  export { AutocompleteDropdown } from './components/AutocompleteDropdown';
54
54
  export { AutocompleteFormField } from './components/AutocompleteFormField';
55
55
  export type { UIResourceRendererProps, StreamingUIRendererProps, GenerativeUIErrorBoundaryProps, } from './components';
56
+ export type { ValidationErrorMode } from './components/UIResourceRenderer';
57
+ export { setDebugMode, isDebugEnabled } from './utils/logger';
58
+ export { markRenderStart, markRenderEnd, PERF_PREFIX } from './utils/perf';
56
59
  export type { DraggableGridItemProps } from './components/DraggableGridItem';
57
60
  export type { ResizeHandleProps as ResizeHandleComponentProps } from './components/ResizeHandle';
58
61
  export type { EditableUIResourceRendererProps } from './components/EditableUIResourceRenderer';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -35,6 +35,8 @@ import { ServerCapabilitiesContext, ServerCapabilitiesProvider, createServerCapa
35
35
  import { GhostText, GhostTextInput } from "./components/GhostText.js";
36
36
  import { AutocompleteDropdown } from "./components/AutocompleteDropdown.js";
37
37
  import { AutocompleteFormField } from "./components/AutocompleteFormField.js";
38
+ import { isDebugEnabled, setDebugMode } from "./utils/logger.js";
39
+ import { PERF_PREFIX, markRenderEnd, markRenderStart } from "./utils/perf.js";
38
40
  import { useStreamingUI } from "./hooks/useStreamingUI.js";
39
41
  import { useAction, useToolAction } from "./hooks/useAction.js";
40
42
  import { evaluateCondition, useConditionalField } from "./hooks/useConditionalField.js";
@@ -80,6 +82,7 @@ export {
80
82
  GhostTextInput,
81
83
  MCPActionContext,
82
84
  MCPActionProvider,
85
+ PERF_PREFIX,
83
86
  PromptReplacedError,
84
87
  ResizeHandle,
85
88
  ScratchpadPanel,
@@ -107,7 +110,11 @@ export {
107
110
  elicitationToPromptConfig,
108
111
  evaluateCondition,
109
112
  getIframeSandbox,
113
+ isDebugEnabled,
114
+ markRenderEnd,
115
+ markRenderStart,
110
116
  mergeScratchpadSections,
117
+ setDebugMode,
111
118
  setServerCapabilities,
112
119
  useAction,
113
120
  useAutocomplete,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,6 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const isDev = typeof process !== "undefined" && process.env.NODE_ENV !== "production";
3
+ let debugOverride = null;
4
+ function readEnvFlag() {
5
+ if (typeof process !== "undefined" && process.env) {
6
+ if (process.env.MCP_UI_DEBUG === "true") return true;
7
+ if (process.env.NODE_ENV !== "production") return true;
8
+ }
9
+ if (typeof globalThis !== "undefined" && globalThis.__MCP_UI_DEBUG__ === true) {
10
+ return true;
11
+ }
12
+ return false;
13
+ }
14
+ function isDebugActive() {
15
+ if (debugOverride !== null) return debugOverride;
16
+ return readEnvFlag();
17
+ }
18
+ function setDebugMode(enabled) {
19
+ debugOverride = enabled;
20
+ }
21
+ function isDebugEnabled() {
22
+ return isDebugActive();
23
+ }
4
24
  function formatLogMessage(feature, message, context) {
5
25
  const contextStr = context ? ` ${JSON.stringify(context)}` : "";
6
26
  return `[@seed-ship/mcp-ui-solid:${feature}] ${message}${contextStr}`;
@@ -8,12 +28,12 @@ function formatLogMessage(feature, message, context) {
8
28
  function createLogger(feature) {
9
29
  return {
10
30
  info(message, context) {
11
- if (isDev) {
31
+ if (isDebugActive()) {
12
32
  console.info(formatLogMessage(feature, message, context));
13
33
  }
14
34
  },
15
35
  warn(message, context) {
16
- if (isDev) {
36
+ if (isDebugActive()) {
17
37
  console.warn(formatLogMessage(feature, message, context));
18
38
  }
19
39
  },
@@ -21,11 +41,13 @@ function createLogger(feature) {
21
41
  console.error(formatLogMessage(feature, message, context));
22
42
  },
23
43
  debug(message, context) {
24
- if (isDev) {
44
+ if (isDebugActive()) {
25
45
  console.debug(formatLogMessage(feature, message, context));
26
46
  }
27
47
  }
28
48
  };
29
49
  }
30
50
  exports.createLogger = createLogger;
51
+ exports.isDebugEnabled = isDebugEnabled;
52
+ exports.setDebugMode = setDebugMode;
31
53
  //# sourceMappingURL=logger.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.cjs","sources":["../../src/utils/logger.ts"],"sourcesContent":["/**\n * Simple internal logger utility\n *\n * Provides basic logging functionality for the package.\n * Consumers can disable logging by setting NODE_ENV=production\n * or by implementing their own logging solution.\n */\n\nconst isDev = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'\n\nexport interface Logger {\n info(message: string, context?: Record<string, unknown>): void\n warn(message: string, context?: Record<string, unknown>): void\n error(message: string, context?: Record<string, unknown>): void\n debug(message: string, context?: Record<string, unknown>): void\n}\n\nfunction formatLogMessage(\n feature: string,\n message: string,\n context?: Record<string, unknown>\n): string {\n const contextStr = context ? ` ${JSON.stringify(context)}` : ''\n return `[@seed-ship/mcp-ui-solid:${feature}] ${message}${contextStr}`\n}\n\n/**\n * Creates a feature-scoped logger\n *\n * @param feature - Feature name for log prefixing\n * @returns Logger instance\n *\n * @example\n * ```typescript\n * const logger = createLogger('my-component')\n * logger.info('Component mounted', { componentId: '123' })\n * ```\n */\nexport function createLogger(feature: string): Logger {\n return {\n info(message: string, context?: Record<string, unknown>) {\n if (isDev) {\n console.info(formatLogMessage(feature, message, context))\n }\n },\n\n warn(message: string, context?: Record<string, unknown>) {\n if (isDev) {\n console.warn(formatLogMessage(feature, message, context))\n }\n },\n\n error(message: string, context?: Record<string, unknown>) {\n // Always log errors, even in production\n console.error(formatLogMessage(feature, message, context))\n },\n\n debug(message: string, context?: Record<string, unknown>) {\n if (isDev) {\n console.debug(formatLogMessage(feature, message, context))\n }\n },\n }\n}\n\n/**\n * No-op logger for testing or when logging is disabled\n */\nexport const noopLogger: Logger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n debug: () => {},\n}\n"],"names":[],"mappings":";;AAQA,MAAM,QAAQ,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;AASzE,SAAS,iBACP,SACA,SACA,SACQ;AACR,QAAM,aAAa,UAAU,IAAI,KAAK,UAAU,OAAO,CAAC,KAAK;AAC7D,SAAO,4BAA4B,OAAO,KAAK,OAAO,GAAG,UAAU;AACrE;AAcO,SAAS,aAAa,SAAyB;AACpD,SAAO;AAAA,IACL,KAAK,SAAiB,SAAmC;AACvD,UAAI,OAAO;AACT,gBAAQ,KAAK,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,IAEA,KAAK,SAAiB,SAAmC;AACvD,UAAI,OAAO;AACT,gBAAQ,KAAK,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,IAEA,MAAM,SAAiB,SAAmC;AAExD,cAAQ,MAAM,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,IAC3D;AAAA,IAEA,MAAM,SAAiB,SAAmC;AACxD,UAAI,OAAO;AACT,gBAAQ,MAAM,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EAAA;AAEJ;;"}
1
+ {"version":3,"file":"logger.cjs","sources":["../../src/utils/logger.ts"],"sourcesContent":["/**\n * Simple internal logger utility\n *\n * Logging is enabled when EITHER:\n * 1. `process.env.NODE_ENV !== 'production'` (dev build), OR\n * 2. `process.env.MCP_UI_DEBUG === 'true'` (server-side opt-in for prod), OR\n * 3. `globalThis.__MCP_UI_DEBUG__ === true` (browser-side runtime toggle), OR\n * 4. `setDebugMode(true)` has been called from app code.\n *\n * `error` always logs regardless of mode.\n *\n * @see setDebugMode, isDebugEnabled — runtime controls (v5.4.0)\n */\n\ndeclare global {\n // Browser-side runtime flag — settable from devtools console:\n // `globalThis.__MCP_UI_DEBUG__ = true`\n // eslint-disable-next-line no-var\n var __MCP_UI_DEBUG__: boolean | undefined\n}\n\nlet debugOverride: boolean | null = null\n\nfunction readEnvFlag(): boolean {\n if (typeof process !== 'undefined' && process.env) {\n if (process.env.MCP_UI_DEBUG === 'true') return true\n if (process.env.NODE_ENV !== 'production') return true\n }\n if (typeof globalThis !== 'undefined' && globalThis.__MCP_UI_DEBUG__ === true) {\n return true\n }\n return false\n}\n\nfunction isDebugActive(): boolean {\n if (debugOverride !== null) return debugOverride\n return readEnvFlag()\n}\n\n/**\n * Programmatically enable/disable verbose logging at runtime.\n *\n * Pass `null` to clear the override and fall back to env-based detection.\n *\n * @example\n * ```ts\n * import { setDebugMode } from '@seed-ship/mcp-ui-solid'\n * setDebugMode(true) // turn on verbose logs\n * setDebugMode(false) // turn off (overrides NODE_ENV=development)\n * setDebugMode(null) // restore env-based behavior\n * ```\n */\nexport function setDebugMode(enabled: boolean | null): void {\n debugOverride = enabled\n}\n\n/**\n * Whether verbose logging is currently active (env + override combined).\n */\nexport function isDebugEnabled(): boolean {\n return isDebugActive()\n}\n\nexport interface Logger {\n info(message: string, context?: Record<string, unknown>): void\n warn(message: string, context?: Record<string, unknown>): void\n error(message: string, context?: Record<string, unknown>): void\n debug(message: string, context?: Record<string, unknown>): void\n}\n\nfunction formatLogMessage(\n feature: string,\n message: string,\n context?: Record<string, unknown>\n): string {\n const contextStr = context ? ` ${JSON.stringify(context)}` : ''\n return `[@seed-ship/mcp-ui-solid:${feature}] ${message}${contextStr}`\n}\n\n/**\n * Creates a feature-scoped logger\n *\n * @param feature - Feature name for log prefixing\n * @returns Logger instance\n *\n * @example\n * ```typescript\n * const logger = createLogger('my-component')\n * logger.info('Component mounted', { componentId: '123' })\n * ```\n */\nexport function createLogger(feature: string): Logger {\n return {\n info(message: string, context?: Record<string, unknown>) {\n if (isDebugActive()) {\n console.info(formatLogMessage(feature, message, context))\n }\n },\n\n warn(message: string, context?: Record<string, unknown>) {\n if (isDebugActive()) {\n console.warn(formatLogMessage(feature, message, context))\n }\n },\n\n error(message: string, context?: Record<string, unknown>) {\n // Always log errors, even in production\n console.error(formatLogMessage(feature, message, context))\n },\n\n debug(message: string, context?: Record<string, unknown>) {\n if (isDebugActive()) {\n console.debug(formatLogMessage(feature, message, context))\n }\n },\n }\n}\n\n/**\n * No-op logger for testing or when logging is disabled\n */\nexport const noopLogger: Logger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n debug: () => {},\n}\n"],"names":[],"mappings":";;AAqBA,IAAI,gBAAgC;AAEpC,SAAS,cAAuB;AAC9B,MAAI,OAAO,YAAY,eAAe,QAAQ,KAAK;AACjD,QAAI,QAAQ,IAAI,iBAAiB,OAAQ,QAAO;AAChD,QAAI,QAAQ,IAAI,aAAa,aAAc,QAAO;AAAA,EACpD;AACA,MAAI,OAAO,eAAe,eAAe,WAAW,qBAAqB,MAAM;AAC7E,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,gBAAyB;AAChC,MAAI,kBAAkB,KAAM,QAAO;AACnC,SAAO,YAAA;AACT;AAeO,SAAS,aAAa,SAA+B;AAC1D,kBAAgB;AAClB;AAKO,SAAS,iBAA0B;AACxC,SAAO,cAAA;AACT;AASA,SAAS,iBACP,SACA,SACA,SACQ;AACR,QAAM,aAAa,UAAU,IAAI,KAAK,UAAU,OAAO,CAAC,KAAK;AAC7D,SAAO,4BAA4B,OAAO,KAAK,OAAO,GAAG,UAAU;AACrE;AAcO,SAAS,aAAa,SAAyB;AACpD,SAAO;AAAA,IACL,KAAK,SAAiB,SAAmC;AACvD,UAAI,iBAAiB;AACnB,gBAAQ,KAAK,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,IAEA,KAAK,SAAiB,SAAmC;AACvD,UAAI,iBAAiB;AACnB,gBAAQ,KAAK,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,IAEA,MAAM,SAAiB,SAAmC;AAExD,cAAQ,MAAM,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,IAC3D;AAAA,IAEA,MAAM,SAAiB,SAAmC;AACxD,UAAI,iBAAiB;AACnB,gBAAQ,MAAM,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EAAA;AAEJ;;;;"}
@@ -1,10 +1,37 @@
1
1
  /**
2
2
  * Simple internal logger utility
3
3
  *
4
- * Provides basic logging functionality for the package.
5
- * Consumers can disable logging by setting NODE_ENV=production
6
- * or by implementing their own logging solution.
4
+ * Logging is enabled when EITHER:
5
+ * 1. `process.env.NODE_ENV !== 'production'` (dev build), OR
6
+ * 2. `process.env.MCP_UI_DEBUG === 'true'` (server-side opt-in for prod), OR
7
+ * 3. `globalThis.__MCP_UI_DEBUG__ === true` (browser-side runtime toggle), OR
8
+ * 4. `setDebugMode(true)` has been called from app code.
9
+ *
10
+ * `error` always logs regardless of mode.
11
+ *
12
+ * @see setDebugMode, isDebugEnabled — runtime controls (v5.4.0)
13
+ */
14
+ declare global {
15
+ var __MCP_UI_DEBUG__: boolean | undefined;
16
+ }
17
+ /**
18
+ * Programmatically enable/disable verbose logging at runtime.
19
+ *
20
+ * Pass `null` to clear the override and fall back to env-based detection.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { setDebugMode } from '@seed-ship/mcp-ui-solid'
25
+ * setDebugMode(true) // turn on verbose logs
26
+ * setDebugMode(false) // turn off (overrides NODE_ENV=development)
27
+ * setDebugMode(null) // restore env-based behavior
28
+ * ```
29
+ */
30
+ export declare function setDebugMode(enabled: boolean | null): void;
31
+ /**
32
+ * Whether verbose logging is currently active (env + override combined).
7
33
  */
34
+ export declare function isDebugEnabled(): boolean;
8
35
  export interface Logger {
9
36
  info(message: string, context?: Record<string, unknown>): void;
10
37
  warn(message: string, context?: Record<string, unknown>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAC9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAC9D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAC/D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAChE;AAWD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAyBpD;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,MAKxB,CAAA"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,CAAC,MAAM,CAAC;IAIb,IAAI,gBAAgB,EAAE,OAAO,GAAG,SAAS,CAAA;CAC1C;AAoBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAE1D;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAC9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAC9D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAC/D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAChE;AAWD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAyBpD;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,MAKxB,CAAA"}
@@ -1,4 +1,24 @@
1
- const isDev = typeof process !== "undefined" && process.env.NODE_ENV !== "production";
1
+ let debugOverride = null;
2
+ function readEnvFlag() {
3
+ if (typeof process !== "undefined" && process.env) {
4
+ if (process.env.MCP_UI_DEBUG === "true") return true;
5
+ if (process.env.NODE_ENV !== "production") return true;
6
+ }
7
+ if (typeof globalThis !== "undefined" && globalThis.__MCP_UI_DEBUG__ === true) {
8
+ return true;
9
+ }
10
+ return false;
11
+ }
12
+ function isDebugActive() {
13
+ if (debugOverride !== null) return debugOverride;
14
+ return readEnvFlag();
15
+ }
16
+ function setDebugMode(enabled) {
17
+ debugOverride = enabled;
18
+ }
19
+ function isDebugEnabled() {
20
+ return isDebugActive();
21
+ }
2
22
  function formatLogMessage(feature, message, context) {
3
23
  const contextStr = context ? ` ${JSON.stringify(context)}` : "";
4
24
  return `[@seed-ship/mcp-ui-solid:${feature}] ${message}${contextStr}`;
@@ -6,12 +26,12 @@ function formatLogMessage(feature, message, context) {
6
26
  function createLogger(feature) {
7
27
  return {
8
28
  info(message, context) {
9
- if (isDev) {
29
+ if (isDebugActive()) {
10
30
  console.info(formatLogMessage(feature, message, context));
11
31
  }
12
32
  },
13
33
  warn(message, context) {
14
- if (isDev) {
34
+ if (isDebugActive()) {
15
35
  console.warn(formatLogMessage(feature, message, context));
16
36
  }
17
37
  },
@@ -19,13 +39,15 @@ function createLogger(feature) {
19
39
  console.error(formatLogMessage(feature, message, context));
20
40
  },
21
41
  debug(message, context) {
22
- if (isDev) {
42
+ if (isDebugActive()) {
23
43
  console.debug(formatLogMessage(feature, message, context));
24
44
  }
25
45
  }
26
46
  };
27
47
  }
28
48
  export {
29
- createLogger
49
+ createLogger,
50
+ isDebugEnabled,
51
+ setDebugMode
30
52
  };
31
53
  //# sourceMappingURL=logger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sources":["../../src/utils/logger.ts"],"sourcesContent":["/**\n * Simple internal logger utility\n *\n * Provides basic logging functionality for the package.\n * Consumers can disable logging by setting NODE_ENV=production\n * or by implementing their own logging solution.\n */\n\nconst isDev = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'\n\nexport interface Logger {\n info(message: string, context?: Record<string, unknown>): void\n warn(message: string, context?: Record<string, unknown>): void\n error(message: string, context?: Record<string, unknown>): void\n debug(message: string, context?: Record<string, unknown>): void\n}\n\nfunction formatLogMessage(\n feature: string,\n message: string,\n context?: Record<string, unknown>\n): string {\n const contextStr = context ? ` ${JSON.stringify(context)}` : ''\n return `[@seed-ship/mcp-ui-solid:${feature}] ${message}${contextStr}`\n}\n\n/**\n * Creates a feature-scoped logger\n *\n * @param feature - Feature name for log prefixing\n * @returns Logger instance\n *\n * @example\n * ```typescript\n * const logger = createLogger('my-component')\n * logger.info('Component mounted', { componentId: '123' })\n * ```\n */\nexport function createLogger(feature: string): Logger {\n return {\n info(message: string, context?: Record<string, unknown>) {\n if (isDev) {\n console.info(formatLogMessage(feature, message, context))\n }\n },\n\n warn(message: string, context?: Record<string, unknown>) {\n if (isDev) {\n console.warn(formatLogMessage(feature, message, context))\n }\n },\n\n error(message: string, context?: Record<string, unknown>) {\n // Always log errors, even in production\n console.error(formatLogMessage(feature, message, context))\n },\n\n debug(message: string, context?: Record<string, unknown>) {\n if (isDev) {\n console.debug(formatLogMessage(feature, message, context))\n }\n },\n }\n}\n\n/**\n * No-op logger for testing or when logging is disabled\n */\nexport const noopLogger: Logger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n debug: () => {},\n}\n"],"names":[],"mappings":"AAQA,MAAM,QAAQ,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;AASzE,SAAS,iBACP,SACA,SACA,SACQ;AACR,QAAM,aAAa,UAAU,IAAI,KAAK,UAAU,OAAO,CAAC,KAAK;AAC7D,SAAO,4BAA4B,OAAO,KAAK,OAAO,GAAG,UAAU;AACrE;AAcO,SAAS,aAAa,SAAyB;AACpD,SAAO;AAAA,IACL,KAAK,SAAiB,SAAmC;AACvD,UAAI,OAAO;AACT,gBAAQ,KAAK,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,IAEA,KAAK,SAAiB,SAAmC;AACvD,UAAI,OAAO;AACT,gBAAQ,KAAK,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,IAEA,MAAM,SAAiB,SAAmC;AAExD,cAAQ,MAAM,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,IAC3D;AAAA,IAEA,MAAM,SAAiB,SAAmC;AACxD,UAAI,OAAO;AACT,gBAAQ,MAAM,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"logger.js","sources":["../../src/utils/logger.ts"],"sourcesContent":["/**\n * Simple internal logger utility\n *\n * Logging is enabled when EITHER:\n * 1. `process.env.NODE_ENV !== 'production'` (dev build), OR\n * 2. `process.env.MCP_UI_DEBUG === 'true'` (server-side opt-in for prod), OR\n * 3. `globalThis.__MCP_UI_DEBUG__ === true` (browser-side runtime toggle), OR\n * 4. `setDebugMode(true)` has been called from app code.\n *\n * `error` always logs regardless of mode.\n *\n * @see setDebugMode, isDebugEnabled — runtime controls (v5.4.0)\n */\n\ndeclare global {\n // Browser-side runtime flag — settable from devtools console:\n // `globalThis.__MCP_UI_DEBUG__ = true`\n // eslint-disable-next-line no-var\n var __MCP_UI_DEBUG__: boolean | undefined\n}\n\nlet debugOverride: boolean | null = null\n\nfunction readEnvFlag(): boolean {\n if (typeof process !== 'undefined' && process.env) {\n if (process.env.MCP_UI_DEBUG === 'true') return true\n if (process.env.NODE_ENV !== 'production') return true\n }\n if (typeof globalThis !== 'undefined' && globalThis.__MCP_UI_DEBUG__ === true) {\n return true\n }\n return false\n}\n\nfunction isDebugActive(): boolean {\n if (debugOverride !== null) return debugOverride\n return readEnvFlag()\n}\n\n/**\n * Programmatically enable/disable verbose logging at runtime.\n *\n * Pass `null` to clear the override and fall back to env-based detection.\n *\n * @example\n * ```ts\n * import { setDebugMode } from '@seed-ship/mcp-ui-solid'\n * setDebugMode(true) // turn on verbose logs\n * setDebugMode(false) // turn off (overrides NODE_ENV=development)\n * setDebugMode(null) // restore env-based behavior\n * ```\n */\nexport function setDebugMode(enabled: boolean | null): void {\n debugOverride = enabled\n}\n\n/**\n * Whether verbose logging is currently active (env + override combined).\n */\nexport function isDebugEnabled(): boolean {\n return isDebugActive()\n}\n\nexport interface Logger {\n info(message: string, context?: Record<string, unknown>): void\n warn(message: string, context?: Record<string, unknown>): void\n error(message: string, context?: Record<string, unknown>): void\n debug(message: string, context?: Record<string, unknown>): void\n}\n\nfunction formatLogMessage(\n feature: string,\n message: string,\n context?: Record<string, unknown>\n): string {\n const contextStr = context ? ` ${JSON.stringify(context)}` : ''\n return `[@seed-ship/mcp-ui-solid:${feature}] ${message}${contextStr}`\n}\n\n/**\n * Creates a feature-scoped logger\n *\n * @param feature - Feature name for log prefixing\n * @returns Logger instance\n *\n * @example\n * ```typescript\n * const logger = createLogger('my-component')\n * logger.info('Component mounted', { componentId: '123' })\n * ```\n */\nexport function createLogger(feature: string): Logger {\n return {\n info(message: string, context?: Record<string, unknown>) {\n if (isDebugActive()) {\n console.info(formatLogMessage(feature, message, context))\n }\n },\n\n warn(message: string, context?: Record<string, unknown>) {\n if (isDebugActive()) {\n console.warn(formatLogMessage(feature, message, context))\n }\n },\n\n error(message: string, context?: Record<string, unknown>) {\n // Always log errors, even in production\n console.error(formatLogMessage(feature, message, context))\n },\n\n debug(message: string, context?: Record<string, unknown>) {\n if (isDebugActive()) {\n console.debug(formatLogMessage(feature, message, context))\n }\n },\n }\n}\n\n/**\n * No-op logger for testing or when logging is disabled\n */\nexport const noopLogger: Logger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n debug: () => {},\n}\n"],"names":[],"mappings":"AAqBA,IAAI,gBAAgC;AAEpC,SAAS,cAAuB;AAC9B,MAAI,OAAO,YAAY,eAAe,QAAQ,KAAK;AACjD,QAAI,QAAQ,IAAI,iBAAiB,OAAQ,QAAO;AAChD,QAAI,QAAQ,IAAI,aAAa,aAAc,QAAO;AAAA,EACpD;AACA,MAAI,OAAO,eAAe,eAAe,WAAW,qBAAqB,MAAM;AAC7E,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,gBAAyB;AAChC,MAAI,kBAAkB,KAAM,QAAO;AACnC,SAAO,YAAA;AACT;AAeO,SAAS,aAAa,SAA+B;AAC1D,kBAAgB;AAClB;AAKO,SAAS,iBAA0B;AACxC,SAAO,cAAA;AACT;AASA,SAAS,iBACP,SACA,SACA,SACQ;AACR,QAAM,aAAa,UAAU,IAAI,KAAK,UAAU,OAAO,CAAC,KAAK;AAC7D,SAAO,4BAA4B,OAAO,KAAK,OAAO,GAAG,UAAU;AACrE;AAcO,SAAS,aAAa,SAAyB;AACpD,SAAO;AAAA,IACL,KAAK,SAAiB,SAAmC;AACvD,UAAI,iBAAiB;AACnB,gBAAQ,KAAK,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,IAEA,KAAK,SAAiB,SAAmC;AACvD,UAAI,iBAAiB;AACnB,gBAAQ,KAAK,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,IAEA,MAAM,SAAiB,SAAmC;AAExD,cAAQ,MAAM,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,IAC3D;AAAA,IAEA,MAAM,SAAiB,SAAmC;AACxD,UAAI,iBAAiB;AACnB,gBAAQ,MAAM,iBAAiB,SAAS,SAAS,OAAO,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const PERF_PREFIX = "mcp-ui:component:";
4
+ function hasPerf() {
5
+ return typeof performance !== "undefined" && typeof performance.mark === "function";
6
+ }
7
+ function markRenderStart(componentId) {
8
+ if (!hasPerf()) return;
9
+ try {
10
+ performance.mark(`${PERF_PREFIX}${componentId}:render-start`);
11
+ } catch {
12
+ }
13
+ }
14
+ function markRenderEnd(componentId) {
15
+ if (!hasPerf()) return;
16
+ try {
17
+ performance.mark(`${PERF_PREFIX}${componentId}:render-end`);
18
+ if (typeof performance.measure === "function") {
19
+ try {
20
+ performance.measure(
21
+ `${PERF_PREFIX}${componentId}:render`,
22
+ `${PERF_PREFIX}${componentId}:render-start`,
23
+ `${PERF_PREFIX}${componentId}:render-end`
24
+ );
25
+ } catch {
26
+ }
27
+ }
28
+ } catch {
29
+ }
30
+ }
31
+ exports.PERF_PREFIX = PERF_PREFIX;
32
+ exports.markRenderEnd = markRenderEnd;
33
+ exports.markRenderStart = markRenderStart;
34
+ //# sourceMappingURL=perf.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf.cjs","sources":["../../src/utils/perf.ts"],"sourcesContent":["/**\n * Performance markers for component renders (v5.4.0)\n *\n * Emits `performance.mark()` entries that show up automatically in Chrome\n * DevTools \"Performance\" panel under user timings. Consumers can also\n * query them via `performance.getEntriesByName(...)` for custom tracing.\n *\n * Naming convention :\n * `mcp-ui:component:<id>:render-start`\n * `mcp-ui:component:<id>:render-end`\n * `mcp-ui:component:<id>:render` (a `measure` between the two)\n *\n * Always-on: marks are cheap (sub-microsecond) and only matter when a\n * profiler is recording. SSR-safe (`performance` is guarded).\n */\n\nexport const PERF_PREFIX = 'mcp-ui:component:'\n\nfunction hasPerf(): boolean {\n return typeof performance !== 'undefined' && typeof performance.mark === 'function'\n}\n\nexport function markRenderStart(componentId: string): void {\n if (!hasPerf()) return\n try {\n performance.mark(`${PERF_PREFIX}${componentId}:render-start`)\n } catch {\n // Ignore — performance.mark can throw on malformed names; not worth crashing the render.\n }\n}\n\nexport function markRenderEnd(componentId: string): void {\n if (!hasPerf()) return\n try {\n performance.mark(`${PERF_PREFIX}${componentId}:render-end`)\n if (typeof performance.measure === 'function') {\n try {\n performance.measure(\n `${PERF_PREFIX}${componentId}:render`,\n `${PERF_PREFIX}${componentId}:render-start`,\n `${PERF_PREFIX}${componentId}:render-end`\n )\n } catch {\n // Start mark may be missing if the render path was short-circuited — ignore.\n }\n }\n } catch {\n // Ignore.\n }\n}\n"],"names":[],"mappings":";;AAgBO,MAAM,cAAc;AAE3B,SAAS,UAAmB;AAC1B,SAAO,OAAO,gBAAgB,eAAe,OAAO,YAAY,SAAS;AAC3E;AAEO,SAAS,gBAAgB,aAA2B;AACzD,MAAI,CAAC,UAAW;AAChB,MAAI;AACF,gBAAY,KAAK,GAAG,WAAW,GAAG,WAAW,eAAe;AAAA,EAC9D,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,cAAc,aAA2B;AACvD,MAAI,CAAC,UAAW;AAChB,MAAI;AACF,gBAAY,KAAK,GAAG,WAAW,GAAG,WAAW,aAAa;AAC1D,QAAI,OAAO,YAAY,YAAY,YAAY;AAC7C,UAAI;AACF,oBAAY;AAAA,UACV,GAAG,WAAW,GAAG,WAAW;AAAA,UAC5B,GAAG,WAAW,GAAG,WAAW;AAAA,UAC5B,GAAG,WAAW,GAAG,WAAW;AAAA,QAAA;AAAA,MAEhC,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACF;;;;"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Performance markers for component renders (v5.4.0)
3
+ *
4
+ * Emits `performance.mark()` entries that show up automatically in Chrome
5
+ * DevTools "Performance" panel under user timings. Consumers can also
6
+ * query them via `performance.getEntriesByName(...)` for custom tracing.
7
+ *
8
+ * Naming convention :
9
+ * `mcp-ui:component:<id>:render-start`
10
+ * `mcp-ui:component:<id>:render-end`
11
+ * `mcp-ui:component:<id>:render` (a `measure` between the two)
12
+ *
13
+ * Always-on: marks are cheap (sub-microsecond) and only matter when a
14
+ * profiler is recording. SSR-safe (`performance` is guarded).
15
+ */
16
+ export declare const PERF_PREFIX = "mcp-ui:component:";
17
+ export declare function markRenderStart(componentId: string): void;
18
+ export declare function markRenderEnd(componentId: string): void;
19
+ //# sourceMappingURL=perf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf.d.ts","sourceRoot":"","sources":["../../src/utils/perf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,WAAW,sBAAsB,CAAA;AAM9C,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAOzD;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAkBvD"}
@@ -0,0 +1,34 @@
1
+ const PERF_PREFIX = "mcp-ui:component:";
2
+ function hasPerf() {
3
+ return typeof performance !== "undefined" && typeof performance.mark === "function";
4
+ }
5
+ function markRenderStart(componentId) {
6
+ if (!hasPerf()) return;
7
+ try {
8
+ performance.mark(`${PERF_PREFIX}${componentId}:render-start`);
9
+ } catch {
10
+ }
11
+ }
12
+ function markRenderEnd(componentId) {
13
+ if (!hasPerf()) return;
14
+ try {
15
+ performance.mark(`${PERF_PREFIX}${componentId}:render-end`);
16
+ if (typeof performance.measure === "function") {
17
+ try {
18
+ performance.measure(
19
+ `${PERF_PREFIX}${componentId}:render`,
20
+ `${PERF_PREFIX}${componentId}:render-start`,
21
+ `${PERF_PREFIX}${componentId}:render-end`
22
+ );
23
+ } catch {
24
+ }
25
+ }
26
+ } catch {
27
+ }
28
+ }
29
+ export {
30
+ PERF_PREFIX,
31
+ markRenderEnd,
32
+ markRenderStart
33
+ };
34
+ //# sourceMappingURL=perf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf.js","sources":["../../src/utils/perf.ts"],"sourcesContent":["/**\n * Performance markers for component renders (v5.4.0)\n *\n * Emits `performance.mark()` entries that show up automatically in Chrome\n * DevTools \"Performance\" panel under user timings. Consumers can also\n * query them via `performance.getEntriesByName(...)` for custom tracing.\n *\n * Naming convention :\n * `mcp-ui:component:<id>:render-start`\n * `mcp-ui:component:<id>:render-end`\n * `mcp-ui:component:<id>:render` (a `measure` between the two)\n *\n * Always-on: marks are cheap (sub-microsecond) and only matter when a\n * profiler is recording. SSR-safe (`performance` is guarded).\n */\n\nexport const PERF_PREFIX = 'mcp-ui:component:'\n\nfunction hasPerf(): boolean {\n return typeof performance !== 'undefined' && typeof performance.mark === 'function'\n}\n\nexport function markRenderStart(componentId: string): void {\n if (!hasPerf()) return\n try {\n performance.mark(`${PERF_PREFIX}${componentId}:render-start`)\n } catch {\n // Ignore — performance.mark can throw on malformed names; not worth crashing the render.\n }\n}\n\nexport function markRenderEnd(componentId: string): void {\n if (!hasPerf()) return\n try {\n performance.mark(`${PERF_PREFIX}${componentId}:render-end`)\n if (typeof performance.measure === 'function') {\n try {\n performance.measure(\n `${PERF_PREFIX}${componentId}:render`,\n `${PERF_PREFIX}${componentId}:render-start`,\n `${PERF_PREFIX}${componentId}:render-end`\n )\n } catch {\n // Start mark may be missing if the render path was short-circuited — ignore.\n }\n }\n } catch {\n // Ignore.\n }\n}\n"],"names":[],"mappings":"AAgBO,MAAM,cAAc;AAE3B,SAAS,UAAmB;AAC1B,SAAO,OAAO,gBAAgB,eAAe,OAAO,YAAY,SAAS;AAC3E;AAEO,SAAS,gBAAgB,aAA2B;AACzD,MAAI,CAAC,UAAW;AAChB,MAAI;AACF,gBAAY,KAAK,GAAG,WAAW,GAAG,WAAW,eAAe;AAAA,EAC9D,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,cAAc,aAA2B;AACvD,MAAI,CAAC,UAAW;AAChB,MAAI;AACF,gBAAY,KAAK,GAAG,WAAW,GAAG,WAAW,aAAa;AAC1D,QAAI,OAAO,YAAY,YAAY,YAAY;AAC7C,UAAI;AACF,oBAAY;AAAA,UACV,GAAG,WAAW,GAAG,WAAW;AAAA,UAC5B,GAAG,WAAW,GAAG,WAAW;AAAA,UAC5B,GAAG,WAAW,GAAG,WAAW;AAAA,QAAA;AAAA,MAEhC,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACF;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-ship/mcp-ui-solid",
3
- "version": "5.3.1",
3
+ "version": "5.4.0",
4
4
  "description": "SolidJS components for rendering MCP-generated UI resources",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,12 +26,20 @@ import { useStreamingUI, type UseStreamingUIOptions } from '../hooks/useStreamin
26
26
  import type { UIComponent, RendererError } from '../types'
27
27
  import { validateComponent } from '../services/validation'
28
28
  import { GenerativeUIErrorBoundary } from './GenerativeUIErrorBoundary'
29
+ import { markRenderStart, markRenderEnd } from '../utils/perf'
30
+ import type { ValidationErrorMode } from './UIResourceRenderer'
29
31
 
30
32
  export interface StreamingUIRendererProps extends UseStreamingUIOptions {
31
33
  class?: string
32
34
  showProgress?: boolean
33
35
  showMetadata?: boolean
34
36
  onRenderError?: (error: RendererError) => void
37
+ /**
38
+ * How to react when a streamed component fails `validateComponent()`
39
+ * (v5.4.0). Defaults to `'block'` (full red error card — pre-v5.4.0
40
+ * behavior). See `ValidationErrorMode` in `UIResourceRenderer`.
41
+ */
42
+ errorMode?: ValidationErrorMode
35
43
  }
36
44
 
37
45
  /**
@@ -41,7 +49,12 @@ export interface StreamingUIRendererProps extends UseStreamingUIOptions {
41
49
  function StreamingComponentRenderer(props: {
42
50
  component: UIComponent
43
51
  onError?: (error: RendererError) => void
52
+ errorMode?: ValidationErrorMode
44
53
  }) {
54
+ // Performance marks (v5.4.0) — see utils/perf.ts
55
+ markRenderStart(props.component.id)
56
+ onMount(() => markRenderEnd(props.component.id))
57
+
45
58
  // Validate component before rendering
46
59
  const validation = validateComponent(props.component)
47
60
  if (!validation.valid) {
@@ -52,11 +65,46 @@ function StreamingComponentRenderer(props: {
52
65
  details: validation.errors,
53
66
  })
54
67
 
68
+ const mode: ValidationErrorMode = props.errorMode ?? 'block'
69
+ const firstError = validation.errors?.[0]?.message || 'Unknown validation error'
70
+
71
+ if (mode === 'silent') {
72
+ return null
73
+ }
74
+
75
+ if (mode === 'inline-warn') {
76
+ return (
77
+ <div
78
+ class="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 text-xs text-yellow-800 dark:text-yellow-200"
79
+ role="alert"
80
+ aria-label="Component validation warning"
81
+ title={firstError}
82
+ >
83
+ <svg
84
+ xmlns="http://www.w3.org/2000/svg"
85
+ class="w-3.5 h-3.5"
86
+ viewBox="0 0 24 24"
87
+ fill="none"
88
+ stroke="currentColor"
89
+ stroke-width="2"
90
+ stroke-linecap="round"
91
+ stroke-linejoin="round"
92
+ aria-hidden="true"
93
+ >
94
+ <path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
95
+ <line x1="12" y1="9" x2="12" y2="13" />
96
+ <line x1="12" y1="17" x2="12.01" y2="17" />
97
+ </svg>
98
+ <span>Invalid {props.component.type}</span>
99
+ </div>
100
+ )
101
+ }
102
+
55
103
  return (
56
104
  <div class="w-full bg-error-subtle border border-border-error rounded-lg p-4">
57
105
  <p class="text-sm font-medium text-error-primary">Validation Error</p>
58
106
  <p class="text-xs text-text-secondary mt-1">
59
- {validation.errors?.[0]?.message || 'Unknown validation error'}
107
+ {firstError}
60
108
  </p>
61
109
  </div>
62
110
  )
@@ -214,7 +262,11 @@ export function StreamingUIRenderer(props: StreamingUIRendererProps) {
214
262
  `}
215
263
  style={`grid-column-start: ${component.position.colStart}; grid-column-end: ${component.position.colStart + component.position.colSpan}`}
216
264
  >
217
- <StreamingComponentRenderer component={component} onError={props.onRenderError} />
265
+ <StreamingComponentRenderer
266
+ component={component}
267
+ onError={props.onRenderError}
268
+ errorMode={props.errorMode}
269
+ />
218
270
  </div>
219
271
  )
220
272
  }}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Tests for `errorMode` prop on `<UIResourceRenderer>` — v5.4.0 (B.3)
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach } from 'vitest'
6
+ import { render, cleanup } from '@solidjs/testing-library'
7
+ import { UIResourceRenderer } from './UIResourceRenderer'
8
+ import type { UIComponent, RendererError } from '../types'
9
+
10
+ // A component that fails `validateComponent()` — colStart=99 violates the 1-12 grid range
11
+ const invalidComponent: UIComponent = {
12
+ id: 'broken-1',
13
+ type: 'metric',
14
+ position: { colStart: 99, colSpan: 1 },
15
+ params: { value: 42 },
16
+ }
17
+
18
+ const validComponent: UIComponent = {
19
+ id: 'ok-1',
20
+ type: 'metric',
21
+ position: { colStart: 1, colSpan: 6 },
22
+ params: { title: 'OK', value: 42 },
23
+ }
24
+
25
+ describe('<UIResourceRenderer errorMode> — v5.4.0', () => {
26
+ beforeEach(() => {
27
+ cleanup()
28
+ })
29
+
30
+ it("default (no prop) = 'block': renders the red Validation Error card", () => {
31
+ const { getByText } = render(() => <UIResourceRenderer content={invalidComponent} />)
32
+ expect(getByText('Validation Error')).toBeTruthy()
33
+ })
34
+
35
+ it("errorMode='block' explicitly: same as default", () => {
36
+ const { getByText } = render(() => (
37
+ <UIResourceRenderer content={invalidComponent} errorMode="block" />
38
+ ))
39
+ expect(getByText('Validation Error')).toBeTruthy()
40
+ })
41
+
42
+ it("errorMode='inline-warn': renders compact yellow chip, no big red card", () => {
43
+ const { container, queryByText } = render(() => (
44
+ <UIResourceRenderer content={invalidComponent} errorMode="inline-warn" />
45
+ ))
46
+ expect(queryByText('Validation Error')).toBeNull()
47
+
48
+ const chip = container.querySelector('[role="alert"][aria-label="Component validation warning"]')
49
+ expect(chip).toBeTruthy()
50
+ expect(chip!.textContent).toContain('Invalid metric')
51
+ // tooltip carries the error message
52
+ expect(chip!.getAttribute('title')).toBeTruthy()
53
+ })
54
+
55
+ it("errorMode='silent': renders nothing in the slot, no error UI", () => {
56
+ const { container, queryByText, queryByRole } = render(() => (
57
+ <UIResourceRenderer content={invalidComponent} errorMode="silent" />
58
+ ))
59
+ expect(queryByText('Validation Error')).toBeNull()
60
+ expect(queryByRole('alert')).toBeNull()
61
+ // The slot wrapper is still in the DOM (grid layout) but has no error UI inside
62
+ expect(container.querySelector('[role="alert"]')).toBeNull()
63
+ })
64
+
65
+ it("onError still fires for ALL three modes (consumer can always log)", () => {
66
+ const errors: RendererError[] = []
67
+ const onError = (e: RendererError) => errors.push(e)
68
+
69
+ cleanup()
70
+ render(() => (
71
+ <UIResourceRenderer content={invalidComponent} errorMode="block" onError={onError} />
72
+ ))
73
+ cleanup()
74
+ render(() => (
75
+ <UIResourceRenderer content={invalidComponent} errorMode="inline-warn" onError={onError} />
76
+ ))
77
+ cleanup()
78
+ render(() => (
79
+ <UIResourceRenderer content={invalidComponent} errorMode="silent" onError={onError} />
80
+ ))
81
+
82
+ expect(errors.length).toBe(3)
83
+ expect(errors.every((e) => e.type === 'validation')).toBe(true)
84
+ expect(errors.every((e) => e.componentId === 'broken-1')).toBe(true)
85
+ })
86
+
87
+ it("valid components render normally regardless of errorMode", () => {
88
+ const { queryByText } = render(() => (
89
+ <UIResourceRenderer content={validComponent} errorMode="inline-warn" />
90
+ ))
91
+ // No error UI for a valid component
92
+ expect(queryByText('Validation Error')).toBeNull()
93
+ expect(queryByText('Invalid metric')).toBeNull()
94
+ })
95
+ })