Skip to content

Conversation

@frontegg-david
Copy link
Contributor

@frontegg-david frontegg-david commented Dec 17, 2025

Summary by CodeRabbit

  • New Features

    • Auto serving mode with adaptive UI delivery, dual-payload (JSON+HTML) support, new UI widgets/components (Static Badge, Hybrid Status, Chart, Form), CDN dependency/import-map utilities, component bundler with caching, and template loader/processor.
  • Tests

    • Large expansion of end-to-end and unit test suites for UI tools, serving modes, dual-payloads, dependency resolution, bundling, and template processing.
  • Documentation

    • Updated demo/server and UI platform capability guides.
  • Chores

    • Removed legacy demo artifacts and consolidated demo projects/configs.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

Walkthrough

Removes legacy auth and UI demo apps, consolidates UI demos under apps/e2e/demo-e2e-ui with many new E2E suites, and adds UI rendering into the tool-call flow plus a large UI subsystem (serving-mode, dual-payload, response-builder), dependency/template tooling, and a bundler/file-cache with storage backends.

Changes

Cohort / File(s) Summary
Auth demo removals
apps/auth/demo-orchestrated-auth/*, apps/auth/demo-public/*, apps/auth/demo-transparent-auth/*
Deleted entire auth demo projects: project/webpack/jest configs, app source (apps, tools, resources, prompts), main bootstrap files, tsconfigs, and E2E tests.
E2E orchestrated project
Added/updated: apps/e2e/demo-e2e-orchestrated/project.json, apps/e2e/demo-e2e-orchestrated/webpack.config.js, apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts, apps/e2e/demo-e2e-orchestrated/e2e/*.e2e.test.ts
Added Nx project config for orchestrated e2e, adjusted webpack output path and Jest displayName/server path used by tests.
Consolidated UI demo & E2E tests
apps/e2e/demo-e2e-ui/*, apps/e2e/demo-e2e-ui/e2e/*.e2e.test.ts, apps/e2e/demo-e2e-ui/src/apps/widgets/*
Added unified UI demo app, many new E2E suites (discovery, html/react/markdown/mdx, platform detection, serving modes, stability/concurrency), new UI tools and React hook-based components.
Removed legacy UI demos
apps/ui/mdx-demo/*, apps/ui/react-demo/*
Deleted standalone MDX and React demo apps: configs, tests, tools, UI components, tsconfigs, webpack.
Tool call flow: UI integration
libs/sdk/src/tool/flows/call-tool.flow.ts
Added applyUI stage to CallToolFlow, introduced uiResult/uiMeta state fields, updated finalize logic to merge UI output and made UI rendering error-tolerant.
Session & initialization
libs/sdk/src/auth/session/utils/session-id.utils.ts, libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
Added in-memory session caching and updateSessionPayload helper; detectAIPlatform usage and persisting platformType into session payload during initialization.
Testing fixtures & client
libs/testing/src/client/mcp-test-client.ts, libs/testing/src/fixtures/*, libs/testing/src/fixtures/fixture-types.ts
McpTestClient now authenticates when a token is present even in publicMode; createClient gained clientInfo option; fixtures pass publicMode/clientInfo through.
UI adapters & response builder
libs/ui/src/adapters/*
New serving-mode resolver, dual-payload builder (Claude), and response-builder (buildToolResponseContent) producing widget/dual-payload/markdown/json-only outputs.
Dependency & template subsystem
libs/ui/src/dependency/*
Added CDN registry, dependency resolver, import-map utilities, import-parser, template loader (file/url/inline with ETag cache), template processor (html/markdown/mdx/react), schemas, types, and many tests.
Bundler / file-cache & storage
libs/ui/src/bundler/file-cache/*, libs/ui/src/bundler/file-cache/storage/*
New component builder with deterministic hashing, Filesystem and Redis storage backends, LRU eviction, optional SSR, import-map generation, and public builder APIs.
Public API re-exports
libs/ui/src/adapters/index.ts, libs/ui/src/bundler/index.ts, libs/ui/src/dependency/index.ts
Centralized re-exports exposing new adapters, file-cache, dependency, template, and bundler public APIs.
E2E tooling & CLI updates
libs/cli/e2e/run-e2e.sh, libs/cli/e2e/verdaccio.config.yaml
Verdaccio port/auth and .npmrc.e2e changes, include ui package in publish sequence, and anonymous publish permissions for e2e testing.
Docs updates
docs/draft/**, docs/live/**
Documentation updated to reference apps/e2e demos, added servingMode 'auto' and dual-payload docs, consolidated UI demo/test instructions.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant CallFlow as CallToolFlow
    participant Detector as PlatformDetector
    participant UIAdapter as ServingModeResolver
    participant Renderer as ResponseBuilder

    Client->>CallFlow: callTool(toolId, input, clientInfo)
    CallFlow->>Detector: detectAIPlatform(clientInfo, capabilities)
    Detector-->>CallFlow: platformType
    CallFlow->>UIAdapter: resolveServingMode(toolMeta, platformType, configuredMode)
    UIAdapter-->>CallFlow: { effectiveMode, useDualPayload, supportsUI }

    alt supportsUI && hasHtmlContent
        CallFlow->>Renderer: buildToolResponseContent(rawOutput, htmlContent, effectiveMode, useDualPayload, platformType, htmlPrefix)
        alt useDualPayload
            Renderer->>Renderer: buildDualPayload(data, html, prefix)
            Renderer-->>CallFlow: dual-payload content
        else widget/inline
            Renderer-->>CallFlow: contentCleared (widget) or markdown fallback
        end
    else
        CallFlow-->>Client: json-only response
    end

    CallFlow->>Client: finalize response (merged uiResult + _meta)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas to focus during review:

  • libs/ui/src/dependency/ (cdn registry, resolver, import-map): provider selection, peer dependency handling, integrity and HTTPS enforcement.
  • libs/ui/src/bundler/file-cache/storage/ (filesystem + redis): concurrency, TTL/eviction correctness, stats persistence and error handling.
  • libs/sdk/src/tool/flows/call-tool.flow.ts: applyUI stage, state schema changes, and finalize merging logic.
  • Template loader/processor: URL caching/ETag logic, lazy dependency loading (marked/mdx), and XSS/sanitization considerations.
  • Workspace/project cleanup: ensure removed projects are fully unreferenced in workspace configs and CI/test targets.

Possibly related PRs

Poem

🐰 I hopped through code and cleared the trail,

Demos reshuffled, tests set to sail.
Dual blocks whisper Claude's bright tune,
Caches hum, templates bloom by noon.
A little rabbit cheers: "Ship soon!" 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'chore: Add e2e tests for mcp ui and enhance ui lib' accurately summarizes the main changes, which involve adding comprehensive E2E tests and significant enhancements to the UI library infrastructure.
Docstring Coverage ✅ Passed Docstring coverage is 97.20% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-support-for-more-tools-ui

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/html-card.tool.ts (1)

42-61: The split data sourcing pattern is intentional but inconsistent with codebase conventions.

The template sources title from ctx.output and content/footer from ctx.input (lines 44-45). The inline comment at line 43 indicates this split is deliberate. However, this pattern is inconsistent with the codebase: 6 of 8 widget tools consolidate all template data to either ctx.output or ctx.input. Only mdx-interactive.tool.ts also uses split sourcing.

If this split is intentional for E2E test purposes, consider documenting why in a comment (as this tool already does). Otherwise, align the execute() method to return all display fields in output for consistency with the majority pattern.

♻️ Duplicate comments (2)
libs/ui/src/dependency/template-processor.ts (2)

359-363: Same type assertion issue in convenience functions.

The same unsafe cast of context.input to Record<string, unknown> appears in all three convenience functions. Apply the same fix as suggested for processTemplate.

Also applies to: 386-390, 418-422


432-451: Duplicate escapeHtml implementation.

This is the same escapeHtml implementation duplicated from processTemplate. Extracting to a shared utility would reduce duplication and ensure consistency.

🟡 Minor comments (7)
libs/ui/src/adapters/serving-mode.ts-256-265 (1)

256-265: Inconsistent capability check in platformSupportsWidgets.

This function hardcodes platform checks (openai, ext-apps, cursor) instead of using the PLATFORM_CAPABILITIES map. This creates a maintenance risk where the map could be updated but this function wouldn't reflect those changes.

Additionally, the function documentation mentions "platforms can read HTML from _meta['ui/html']", but according to PLATFORM_CAPABILITIES, other platforms like claude, continue, cody, and generic-mcp also have supportsWidgets: true.

Consider using the capabilities map for consistency:

 export function platformSupportsWidgets(platformType: AIPlatformType): boolean {
-  return platformType === 'openai' || platformType === 'ext-apps' || platformType === 'cursor';
+  const capabilities = PLATFORM_CAPABILITIES[platformType] || PLATFORM_CAPABILITIES.unknown;
+  return capabilities.supportsWidgets;
 }

If the intent is to check specifically for _meta['ui/html'] support (distinct from general widget support), consider renaming the function to clarify its purpose (e.g., platformSupportsMetaUIHtml).

libs/ui/src/dependency/schemas.ts-176-204 (1)

176-204: Import map scopes URLs lack HTTPS enforcement.

The imports field correctly enforces HTTPS URLs (lines 186-188), but scopes nested URLs (line 194) only use z.string().url() without the HTTPS refinement. Similarly, integrity keys (line 199) use z.string().url() without HTTPS enforcement.

Consider applying consistent HTTPS validation:

     scopes: z.record(z.string().min(1), z.record(z.string().min(1), z.string().url())).optional(),
+    // Note: scopes URLs should also enforce HTTPS for security

     integrity: z.record(z.string().url(), z.string().regex(/^sha(256|384|512)-[A-Za-z0-9+/=]+$/)).optional(),

Or apply the same refinement for consistency with the security posture of the imports field.

Committable suggestion skipped: line range outside the PR's diff.

libs/ui/src/bundler/file-cache/component-builder.ts-194-196 (1)

194-196: Unused variable: parseResult is assigned but never used.

The result of parseImports(source) is stored but not referenced anywhere in the build flow.

Either remove the unused call or utilize the parsed imports for validation/bundling:

     // Parse imports
-    const parseResult = parseImports(source);
+    // const parseResult = parseImports(source);
+    // TODO: Use parsed imports for validation or auto-detection of externals
libs/ui/src/bundler/file-cache/storage/redis.ts-192-196 (1)

192-196: Stats drift: entries counter increments on every set, even for overwrites.

If set is called for an existing key, localStats.entries is incremented without checking if the key already exists. Over time, this causes entries to diverge from the actual count.

Consider checking existence before incrementing, or relying on cleanup() to correct stats periodically.

libs/ui/src/bundler/file-cache/storage/redis.ts-161-165 (1)

161-165: Silent error suppression loses cache miss context.

When an exception occurs during get, the error is silently caught and a miss is recorded, but persistStats() is not called (unlike the success path at line 138). This creates inconsistency in stats persistence.

     } catch {
       this.localStats.misses++;
       this.updateHitRate();
+      await this.persistStats();
       return undefined;
     }
libs/ui/src/dependency/template-processor.ts-52-67 (1)

52-67: Custom helpers persist on the singleton across all calls.

When customHelpers are registered on the shared HandlebarsRenderer singleton, they remain registered for all subsequent calls. If different templates use helpers with the same name but different implementations, later registrations will overwrite earlier ones, potentially causing unexpected behavior.

Consider either:

  1. Documenting this behavior clearly
  2. Creating a new renderer instance when custom helpers differ
  3. Only registering helpers that don't already exist
   // Register any custom helpers
   if (customHelpers) {
     for (const [name, fn] of Object.entries(customHelpers)) {
+      // Note: This overwrites any previously registered helper with the same name
       handlebarsRenderer.registerHelper(name, fn as HelperFunction);
     }
   }
libs/ui/src/dependency/template-processor.ts-214-218 (1)

214-218: Unsafe type assertion for context.input.

context.input is typed as unknown but cast to Record<string, unknown> at line 215. If context.input is null, undefined, or a primitive, this could cause unexpected behavior in the Handlebars rendering.

     const renderContext: RenderContext = {
-      input: context.input as Record<string, unknown>,
+      input: (context.input && typeof context.input === 'object' ? context.input : {}) as Record<string, unknown>,
       output: context.output,
       structuredContent: context.structuredContent,
     };
🧹 Nitpick comments (46)
libs/sdk/src/tool/flows/call-tool.flow.ts (3)

26-32: Unused import: platformSupportsWidgets

The platformSupportsWidgets function is imported but not used anywhere in this file. The serving mode resolution already handles platform capability detection internally via resolveServingMode.

 import {
   resolveServingMode,
   buildToolResponseContent,
-  platformSupportsWidgets,
   type ToolResponseContent,
 } from '@frontmcp/ui/adapters';

374-380: Potential undefined access for input

The destructured input from this.state may be undefined since it's not accessed via this.state.required. While the code at lines 458-461 has a defensive check (input?.arguments), consider using this.state.required for consistency with other stages, or add an early guard.

   async applyUI() {
     this.logger.verbose('applyUI:start');
-    const { tool, rawOutput, input } = this.state;
+    const { tool, rawOutput } = this.state;
+    const { input } = this.state.required;

     // Skip if no tool or no UI config
     if (!tool || !hasUIConfig(tool.metadata)) {

Alternatively, if applyUI can legitimately run without input being set, the current defensive access is acceptable.


455-466: Potentially misleading comment about structuredContent

The comment at line 463 states structuredContent "Will be added in finalize", but the finalize stage doesn't appear to set structuredContent on the result. If this is intentional (i.e., structuredContent comes from the parsed output, not added separately), consider updating or removing the comment to avoid confusion.

           output: rawOutput,
-          structuredContent: undefined, // Will be added in finalize
+          structuredContent: undefined,
apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts (3)

1-18: Well-documented test file with clear platform support matrix.

The header documentation clearly explains the serving modes and platform support. However, the documentation mentions Gemini as "no UI support" but no tests cover Gemini behavior explicitly.

Consider adding a test case for Gemini to ensure completeness with the documented platform matrix:

test('Gemini should return JSON only for static mode tool', async ({ server }) => {
  const client = await server.createClient({
    transport: 'streamable-http',
    clientInfo: { name: 'Gemini', version: '1.0.0' },
  });

  const result = await client.tools.call('static-badge', {
    label: 'Gemini',
    value: 'Test',
  });

  expect(result).toBeSuccessful();
  const json = result.json<{ label: string; value: string }>();
  expect(json.label).toBe('Gemini');

  await client.disconnect();
});

46-52: Consider adding explicit hasToolUI() assertion for static mode.

The comment explains that hasToolUI() should be false for static mode, but the test doesn't assert this. Adding an explicit assertion would make the test more robust and self-documenting.

 expect(result).toBeSuccessful();
 // Static mode: UI is served via resource URL, not in response
 // So hasToolUI() is false, but JSON data is returned
+expect(result.hasToolUI()).toBe(false);
 const json = result.json<{ label: string; value: string; color: string }>();

275-293: Add hasToolUI() assertion for unsupported platforms in hybrid mode.

Supported platforms correctly assert hasToolUI() === true, but unsupported platforms don't assert hasToolUI() === false. This asymmetry could miss regressions where UI is accidentally rendered for unsupported platforms.

 expect(result).toBeSuccessful();
+expect(result.hasToolUI()).toBe(false);
 const json = result.json<{ serviceName: string; status: string }>();

Apply similar changes to the Continue, generic-mcp, and Unknown client tests in this block.

apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts (3)

20-23: Arbitrary sleep may cause flaky tests.

Using a fixed 100ms timeout for "warm-up" is non-deterministic. If the server isn't ready in 100ms, tests may fail intermittently.

Consider using a health check or readiness probe instead:

-  test.beforeAll(async () => {
-    await new Promise((resolve) => setTimeout(resolve, 100));
-  });
+  test.beforeAll(async ({ server }) => {
+    // Wait for server to be ready using a health check if available
+    await server.waitForReady?.();
+  });

Alternatively, if the testing framework handles server lifecycle, this hook may be unnecessary.


83-100: Consider verifying the dual-payload structure more explicitly.

The test checks that text() is defined but doesn't verify the expected dual-payload format (JSON + markdown-wrapped HTML) described in the file header. This could miss regressions where the format changes.

       expect(result).toBeSuccessful();
       const text = result.text();
       expect(text).toBeDefined();
+      // Verify dual-payload contains markdown-wrapped HTML
+      expect(text).toContain('```html');

1-11: Consider adding tests for Gemini and ext-apps platforms.

The file header mentions these platforms but tests are missing:

  • Gemini: Listed in platform capabilities but not tested
  • ext-apps: Mentioned as supporting _meta['ui/html'] but not tested

Based on learnings, platform behavior should be tested across configurations.

apps/e2e/demo-e2e-ui/e2e/discovery-and-metadata.e2e.test.ts (1)

39-57: Consider consolidating individual tool listing tests.

The test at lines 39-57 ("should have 8 UI tools registered") already validates all 8 expected tools, and the individual tests at lines 59-97 each call mcp.tools.list() again to check for one tool. This adds test execution time without additional coverage.

Consider removing the individual single-tool tests (lines 59-97) since they're already covered by the comprehensive test.

apps/e2e/demo-e2e-ui/src/apps/widgets/tools/form-ui.tsx (2)

118-118: Unused sending variable.

The sending variable is destructured from useSendMessage but never used. Consider either using it to show a loading state during form submission or removing it.

- const [sendMessage, { loading: sending }] = useSendMessage();
+ const [sendMessage] = useSendMessage();

Alternatively, use it to disable the submit button during sending:

- <Button type="submit" variant="primary" className="w-full" disabled={submitted}>
+ <Button type="submit" variant="primary" className="w-full" disabled={submitted || sending}>

174-187: Using array index as React key.

Using index as the key for form fields works when the field list is static, but will cause issues if fields are reordered or dynamically added/removed. If fields can change dynamically, use field.name as the key instead.

- {fields.map((field, index) => (
-   <div key={index} className="space-y-1">
+ {fields.map((field) => (
+   <div key={field.name} className="space-y-1">
libs/ui/src/bundler/file-cache/hash-calculator.ts (2)

258-292: Unused externals parameter in collectLocalDependencies.

The externals parameter is passed through recursive calls but never used to filter out external packages. The filtering at lines 278-281 only checks if imports start with . or /, which already excludes npm packages regardless of the externals list.

Either remove the unused parameter or implement the intended filtering:

 async function collectLocalDependencies(
   filePath: string,
   baseDir: string,
-  externals: string[],
   collected: Set<string>,
   maxDepth: number,
   currentDepth: number,
 ): Promise<void> {

And update the call site at line 196:

-  await collectLocalDependencies(absoluteEntryPath, baseDir, externals, files, maxDepth, 0);
+  await collectLocalDependencies(absoluteEntryPath, baseDir, files, maxDepth, 0);

297-326: Regex-based import parsing has known limitations.

The import extraction regexes may produce incorrect results for:

  • Imports inside comments or strings
  • Multi-line import statements
  • Template literal imports (import(`${path}`))

For a caching system, false positives are safer than false negatives (extra files in hash vs. missed dependencies). The current approach is acceptable for typical use cases but consider documenting these limitations.

apps/e2e/demo-e2e-orchestrated/e2e/orchestrated-auth.e2e.test.ts (1)

12-233: Consider adding error path test coverage to meet 95%+ coverage target.

The current test suite provides good coverage of happy paths across authentication scenarios. To meet the coding guideline requirement of 95%+ test coverage, consider adding tests for error conditions:

  • Invalid/malformed tokens
  • Expired tokens
  • Tokens missing required scopes
  • Tool call errors and error response handling
  • Resource not found scenarios
  • Invalid prompt parameters
  • Constructor validation if applicable

As per coding guidelines, **/*.test.ts files should achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines) and test all code paths including errors.

docs/live/docs/authentication/demo-servers.mdx (1)

148-170: Consider clarifying port usage in test example.

The example uses port 4001 for the test server, while the table above documents default ports as 3003, 3005, and 3004. While this demonstrates using a custom port (which is good), a brief comment explaining why a different port is chosen could help readers understand this is intentional to avoid conflicts.

Example enhancement:

test.beforeAll(async () => {
  // Use a different port to avoid conflicts with running dev servers
  serverProcess = await TestServer.startNx('demo-e2e-public', { port: 4001 });
});
libs/ui/src/dependency/__tests__/template-loader.test.ts (2)

99-111: Consider testing error message specifics.

While the tests correctly verify that HTTP URLs are rejected and invalid URLs throw, they don't assert on the exact error type or detailed error messages. Consider adding assertions on error types or messages for more precise validation.

Example enhancement:

 it('should throw for HTTP URL', () => {
-  expect(() => validateTemplateUrl('http://cdn.example.com/widget.html')).toThrow('Template URLs must use HTTPS');
+  expect(() => validateTemplateUrl('http://cdn.example.com/widget.html'))
+    .toThrow('Template URLs must use HTTPS');
 });

 it('should throw for invalid URL', () => {
-  expect(() => validateTemplateUrl('not-a-url')).toThrow();
+  expect(() => validateTemplateUrl('not-a-url'))
+    .toThrow(/invalid|URL/i);
 });

164-201: Enhance URL cache tests with concurrent access and edge cases.

While the basic cache operations are tested, consider adding tests for:

  • Concurrent cache access (multiple simultaneous reads/writes)
  • Cache size limits or eviction policies (if implemented)
  • Clearing cache while operations are in progress
  • Memory leak prevention scenarios

Based on learnings, maintaining 95%+ test coverage requires comprehensive edge case testing.

libs/ui/src/dependency/__tests__/cdn-registry.test.ts (2)

69-121: Add tests for malformed registry entries.

Consider adding tests for error scenarios such as:

  • Registry entries with missing required fields (packageName, providers)
  • Invalid URL formats in provider configurations
  • Circular dependencies in provider fallback chains
  • Null or undefined registry entries

Based on learnings, test all code paths including errors.


159-193: Consider adding edge case tests for registry merging.

While the basic merge scenarios are covered, consider testing:

  • Merging with conflicting peer dependencies
  • Merging empty or null registries
  • Merging registries with overlapping but different providers
  • Deep merge behavior for nested provider configurations

These scenarios help achieve 95%+ test coverage.

Based on learnings, maintain 95%+ test coverage across statements, branches, functions, and lines.

apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts (2)

145-178: Add Gemini platform test for comprehensive coverage.

While OpenAI and Claude are tested, consider adding a Gemini platform test to ensure compatibility across all major AI platforms.

Based on learnings, test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable.

Example:

test('should handle Gemini client', async ({ server }) => {
  const client = await server.createClient({
    transport: 'streamable-http',
    clientInfo: { name: 'Gemini', version: '1.0.0' },
  });

  const result = await client.tools.call('html-card', {
    title: 'Test',
    content: 'Content',
  });

  expect(result).toBeSuccessful();

  await client.disconnect();
});

180-194: LGTM! Consider adding concurrent error scenarios.

The concurrent operations test is good. Optionally, consider testing concurrent calls with some failures to verify error isolation and no cross-contamination between parallel operations.

libs/ui/src/dependency/__tests__/import-map.test.ts (2)

171-182: LGTM! HTML escaping test is present.

The test correctly validates that special characters in URLs don't break the generated JSON. Consider expanding to test additional characters that could cause XSS issues: <, >, ', ", and script injection attempts.

Based on learnings, test HTML escaping for user-provided content to prevent XSS attacks.


308-346: Enhance validation tests with additional error scenarios.

While the basic validation is tested, consider adding tests for:

  • Malformed import map structure (missing required fields)
  • Invalid scope prefixes
  • Duplicate import keys
  • Mixed valid/invalid entries
  • Cross-origin security violations (if applicable)

Based on learnings, achieve and maintain 95%+ test coverage across all metrics.

libs/ui/src/dependency/__tests__/template-processor.test.ts (1)

330-370: Consider adding stress tests for edge cases.

While the basic edge cases are well-covered, consider adding tests for:

  • Very large templates (e.g., 10MB+)
  • Deeply nested data structures (e.g., 20+ levels)
  • Circular references in context data
  • Templates with thousands of Handlebars expressions
  • Unicode normalization issues

These help ensure robustness under stress conditions while maintaining 95%+ test coverage.

Based on learnings, maintain 95%+ test coverage across statements, branches, functions, and lines.

libs/ui/src/adapters/__tests__/response-builder.test.ts (1)

225-290: LGTM! Edge case handling is robust.

The circular reference test (lines 262-277) is particularly important. However, consider adding tests for XSS in the htmlContent parameter to ensure user-provided HTML is properly sanitized before being embedded in responses.

Based on learnings, test HTML escaping for user-provided content to prevent XSS attacks.

Example:

it('should sanitize XSS in htmlContent', () => {
  const result = buildToolResponseContent(
    createOptions({
      servingMode: 'inline',
      platformType: 'openai',
      htmlContent: '<script>alert("xss")</script><div>Safe</div>',
    }),
  );
  
  // Verify script tags are escaped or removed
  expect(result.content[0].text).not.toContain('<script>');
});
apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts (1)

582-628: LGTM! Concurrent operations tests validate thread safety.

The concurrent call tests ensure that multiple operations don't interfere with each other. Optionally, consider testing concurrent operations where some fail to verify error isolation.

libs/ui/src/dependency/__tests__/resolver.test.ts (1)

197-214: Consider adding platform-specific tests for Gemini and ngrok.

The factory function tests cover createClaudeResolver and createOpenAIResolver, but based on learnings, tests should cover behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable. If createGeminiResolver or similar factory functions exist, add corresponding tests.

  describe('createOpenAIResolver', () => {
    it('should create OpenAI-optimized resolver', () => {
      const resolver = createOpenAIResolver();
      const result = resolver.resolve('react');

      // OpenAI prefers esm.sh or jsdelivr
      expect(result.cdnUrl).toBeDefined();
    });
  });
+
+  // TODO: Add tests for createGeminiResolver if available
+  // describe('createGeminiResolver', () => { ... });
});
libs/ui/src/adapters/__tests__/dual-payload.test.ts (2)

145-149: Consider removing non-null assertion.

Line 148 uses match![1] which violates the coding guideline to avoid non-null assertions. Although the preceding expect(match).toBeTruthy() provides a logical guard, consider restructuring:

      // Extract HTML from result
      const match = result.content[1].text.match(/```html\n([\s\S]*?)\n```/);
      expect(match).toBeTruthy();
-     expect(match![1]).toBe(html);
+     if (match) {
+       expect(match[1]).toBe(html);
+     }

As per coding guidelines, avoid non-null assertions (!) - use proper error handling instead.


76-97: Consider adding XSS-focused test for user-provided data.

The test suite thoroughly covers data handling, but per learnings, tests should verify HTML escaping for user-provided content to prevent XSS attacks. Consider adding a test that explicitly verifies malicious content in the data object is safely serialized:

it('should safely serialize data containing potential XSS vectors', () => {
  const result = buildDualPayload({
    data: { 
      userInput: '<script>alert("xss")</script>',
      nested: { html: '<img onerror="alert(1)" src="x">' }
    },
    html: '<html></html>',
  });

  // Data is JSON-stringified, so HTML is not executed
  const parsed = JSON.parse(result.content[0].text);
  expect(parsed.userInput).toBe('<script>alert("xss")</script>');
});

Based on learnings: Test HTML escaping for user-provided content to prevent XSS attacks.

libs/ui/src/bundler/file-cache/storage/filesystem.ts (2)

100-102: Use specific error classes instead of generic Error.

Per coding guidelines, specific error classes with MCP error codes should be used instead of generic errors.

-      throw new Error(`Failed to initialize cache directory: ${error}`);
+      throw new CacheInitializationError(`Failed to initialize cache directory: ${error}`);

Also applies to: 248-250, 393-395


134-136: Fire-and-forget write may silently lose metadata updates.

The async write for access metadata is not awaited and errors are swallowed. While this avoids blocking reads, it means lastAccessedAt and accessCount updates may be lost without any indication.

Consider at minimum logging the error for debugging:

       // Write back updated metadata (async, don't await)
-      this.writeEntry(filePath, entry).catch(() => {});
+      this.writeEntry(filePath, entry).catch((err) => {
+        console.debug(`Failed to update cache metadata for ${key}: ${err}`);
+      });
libs/ui/src/bundler/file-cache/component-builder.ts (1)

462-473: Factory function accepts unknown then casts - acceptable pattern but could be typed.

The redisClient: unknown parameter is immediately cast to RedisClient. While this works, consider accepting RedisClient directly for better type safety at call sites.

 export async function createRedisBuilder(
-  redisClient: unknown,
+  redisClient: import('./storage/redis.js').RedisClient,
   keyPrefix = 'frontmcp:ui:build:',
 ): Promise<ComponentBuilder> {
   const { RedisStorage } = await import('./storage/redis.js');
   const storage = new RedisStorage({
-    client: redisClient as import('./storage/redis.js').RedisClient,
+    client: redisClient,
     keyPrefix,
   });
apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts (1)

239-284: Consider adding XSS injection test cases.

Based on learnings, tests should verify HTML escaping for user-provided content to prevent XSS attacks. The Unicode and special characters tests are good but don't specifically test malicious payloads.

Add test cases for XSS vectors:

test('should escape HTML/script injection in title', async ({ mcp }) => {
  const result = await mcp.tools.call('markdown-report', {
    title: '<script>alert("xss")</script>',
    summary: 'Test summary',
    findings: [{ title: 'F', description: 'D', severity: 'low' }],
  });

  expect(result).toBeSuccessful();
  const json = result.json<{ markdown: string }>();
  expect(json.markdown).not.toContain('<script>');
});

test('should escape HTML in finding descriptions', async ({ mcp }) => {
  const result = await mcp.tools.call('markdown-report', {
    title: 'Report',
    summary: 'Summary',
    findings: [{ 
      title: 'Finding', 
      description: '<img src=x onerror=alert(1)>', 
      severity: 'low' 
    }],
  });

  expect(result).toBeSuccessful();
  const json = result.json<{ markdown: string }>();
  expect(json.markdown).not.toContain('onerror=');
});

Based on learnings about testing HTML escaping for user-provided content.

libs/ui/src/dependency/import-parser.ts (2)

249-251: Fragile skip logic for combined imports.

The logic to skip combined imports by checking if afterMatch.startsWith(',') may produce incorrect results. The calculation match.index + match[0].length - specifier.length - 2 is complex and assumes a specific structure. Consider using the deduplication mechanism (seenStatements) instead, or add a comment explaining the expected string position.


283-285: Consider documenting the magic number for lookback window.

The 50 character lookback for detecting if a side-effect import is part of another pattern is reasonable but arbitrary. A brief comment explaining why 50 characters is sufficient would improve maintainability.

-    // Skip if this is part of another import pattern (has 'from' before it)
-    const beforeMatch = source.slice(Math.max(0, match.index - 50), match.index);
+    // Skip if this is part of another import pattern (has 'from' before it)
+    // 50 chars lookback is sufficient to capture 'from' keyword in typical import statements
+    const beforeMatch = source.slice(Math.max(0, match.index - 50), match.index);
libs/ui/src/bundler/file-cache/storage/interface.ts (1)

178-184: Handle potential JSON.stringify errors in calculateManifestSize.

JSON.stringify can throw if the manifest contains circular references or BigInt values. Consider adding error handling for robustness.

 export function calculateManifestSize(manifest: ComponentBuildManifest): number {
-  // Approximate size by serializing to JSON
-  return Buffer.byteLength(JSON.stringify(manifest), 'utf8');
+  // Approximate size by serializing to JSON
+  try {
+    return Buffer.byteLength(JSON.stringify(manifest), 'utf8');
+  } catch {
+    // Fallback estimate if serialization fails (e.g., circular refs)
+    return 0;
+  }
 }
libs/ui/src/adapters/dual-payload.ts (1)

148-163: Duplicate safeStringify implementation exists in utils.

This local safeStringify function duplicates the one in libs/ui/src/utils/safe-stringify.ts, which has a more robust implementation with circular reference handling via WeakSet. Consider importing from the existing utility instead.

+import { safeStringify } from '../utils/safe-stringify';
+
 // ... in buildDualPayload function ...
-  const dataBlock: TextContent = {
-    type: 'text',
-    text: safeStringify(data),
-  };
+  const dataBlock: TextContent = {
+    type: 'text',
+    text: data === undefined || data === null ? '{}' : safeStringify(data),
+  };

Note: The existing utility doesn't handle undefined/null'{}' conversion, so you may need to add that wrapper logic or extend the utility.

libs/ui/src/bundler/file-cache/storage/redis.ts (1)

243-262: clear() also deletes stats key, then immediately persists empty stats.

The keys pattern ${keyPrefix}* includes the stats key (e.g., frontmcp:ui:build::__stats__). Deleting it, then calling persistStats() at line 261 recreates it. This is wasteful but not incorrect. Consider filtering out the stats key before deletion for clarity:

     const pattern = `${this.options.keyPrefix}*`;
     const keys = await this.options.client.keys(pattern);
+    const dataKeys = keys.filter((k) => !k.endsWith(STATS_KEY_SUFFIX));

-    if (keys.length > 0) {
-      await this.options.client.del(keys);
+    if (dataKeys.length > 0) {
+      await this.options.client.del(dataKeys);
     }
libs/ui/src/dependency/cdn-registry.ts (1)

704-760: Topological sort handles cycles correctly.

The algorithm uses a remaining set and breaks cycles by arbitrarily picking the next package when no progress is made (lines 748-756). The guard against infinite loops is sound.

Minor: Line 713 uses non-null assertion queue.shift()!. This is safe here since the while condition guarantees queue.length > 0, but per coding guidelines, consider:

-    const packageName = queue.shift()!;
+    const packageName = queue.shift();
+    if (!packageName) continue;
libs/ui/src/dependency/template-loader.ts (1)

440-453: needsRefetch logic may be too aggressive.

The function returns true (needs refetch) when !cached.etag. However, many CDNs don't return ETags. This means templates from such CDNs will always be re-fetched, defeating the cache.

Consider adding a TTL-based fallback:

// If we have an ETag, we can validate with conditional request
// Otherwise, check if within a reasonable TTL (e.g., 5 minutes)
if (cached.etag) return false;

const fetchedAt = new Date(cached.fetchedAt).getTime();
const ttl = 5 * 60 * 1000; // 5 minutes
return Date.now() - fetchedAt > ttl;
libs/ui/src/dependency/resolver.ts (2)

103-107: Hardcoded provider 'cloudflare' for overrides may be misleading.

When using an override, the provider is always set to 'cloudflare' regardless of the actual URL source. This could cause confusion in logging or debugging.

Consider accepting the provider as part of the override, or using a neutral value like 'custom':

     if (override) {
-      return this.createResolvedDependency(packageName, override, 'cloudflare');
+      return this.createResolvedDependency(packageName, override, 'custom' as CDNProvider);
     }

27-27: Unused import: getPackageName.

getPackageName is imported from ./import-parser but never used in this file.

-import { parseImports, getPackageName } from './import-parser';
+import { parseImports } from './import-parser';
libs/ui/src/dependency/types.ts (1)

692-718: Potential edge case in file-path detection.

The file extension check at line 711 could misclassify inline template strings that happen to end with file extensions (e.g., "Use component.tsx" would be detected as file-path). While this is likely rare, consider documenting this heuristic behavior or adding a length/content check to reduce false positives.

     // Check for file path patterns
     if (
       template.startsWith('./') ||
       template.startsWith('../') ||
       template.startsWith('/') ||
       // Windows-style paths
       /^[A-Za-z]:\\/.test(template) ||
-      // File extensions
-      /\.(tsx?|jsx?|mdx?)$/i.test(template)
+      // File extensions (only if looks like a path - no spaces, reasonable length)
+      (/\.(tsx?|jsx?|mdx?)$/i.test(template) && !template.includes(' ') && template.length < 256)
     ) {
       return 'file-path';
     }
libs/ui/src/dependency/template-processor.ts (2)

107-116: Potentially unnecessary eslint-disable comment.

Line 110 disables @typescript-eslint/no-require-imports, but line 111 uses await import() (dynamic import), not require(). The eslint-disable comment may be unnecessary unless there's a different linting rule being triggered.

   try {
     // Use a variable to prevent TypeScript from checking the module at compile time
     const moduleName = 'marked';
-    // eslint-disable-next-line @typescript-eslint/no-require-imports
     markedModule = (await import(/* webpackIgnore: true */ moduleName)) as unknown as MarkedModule;
     return markedModule;
   } catch {

246-275: Use the shared escapeHtml utility from handlebars helpers instead of inline implementations.

Per coding guidelines, always use escapeHtml() utility for user-provided content. The inline implementation at lines 248-255 is duplicated again at lines 433-439. Import and use the shared utility from ../handlebars/helpers:

-import { HandlebarsRenderer, containsHandlebars, type RenderContext, type HelperFunction } from '../handlebars';
+import { HandlebarsRenderer, containsHandlebars, type RenderContext, type HelperFunction, escapeHtml } from '../handlebars';

 // In both templateContext.helpers objects (lines ~250 and ~432):
 helpers: {
-  escapeHtml: (str: string) => {
-    return str
-      .replace(/&/g, '&amp;')
-      .replace(/</g, '&lt;')
-      .replace(/>/g, '&gt;')
-      .replace(/"/g, '&quot;')
-      .replace(/'/g, '&#39;');
-  },
+  escapeHtml,
   // ... other helpers
 }

The shared utility also adds null/undefined handling, making it more robust than the inline versions.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (7)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/static-badge.tool.ts (1)

11-62: LGTM: Schemas and template properly implement security measures.

The input and output schemas use .strict() as required, and the template correctly applies escapeHtml to all user-provided content (label and value). The previous XSS vulnerability has been resolved.

apps/e2e/demo-e2e-ui/src/apps/widgets/tools/hybrid-status.tool.ts (1)

11-86: LGTM: Schemas and template properly implement security measures.

The input and output schemas use .strict() as required, and the template correctly applies escapeHtml to all user-provided string content (serviceName, status, and lastCheck). The uptime field is numeric and safely rendered. The previous XSS vulnerability has been resolved.

libs/ui/src/dependency/__tests__/template-loader.test.ts (1)

203-290: Test coverage for needsRefetch is comprehensive.

Tests cover non-URL sources, inline sources, URL not in cache, URL with ETag, URL within TTL, and TTL expiration scenarios. The 6-minute offset for TTL expiration test (line 275) correctly exceeds the 5-minute TTL.

As noted in a previous review, consider adding tests for:

  • Invalid ETag formats in cache
  • Network errors during fetch operations

Based on learnings, test all code paths including errors.

libs/ui/src/bundler/file-cache/storage/filesystem.ts (3)

354-358: Key sanitization may cause cache collisions.

Different keys like "foo/bar", "foo:bar", and "foo_bar" all map to the same filename "foo_bar.json". This could cause cache overwrites and incorrect data retrieval.


380-405: Stats inconsistency between loadStats and set methods.

loadStats uses fileStat.size (actual file bytes on disk), while set uses calculateManifestSize(manifest) (logical manifest size). This causes totalSize to be inaccurate after restart.


452-455: Bug: evictLRU cannot recover the original cache key from the sanitized filename.

The getFilePath method sanitizes keys, but evictLRU attempts to reverse this by stripping only the extension. This passes the sanitized key to delete(), which then re-sanitizes it.

libs/ui/src/dependency/import-map.ts (1)

218-221: Package names in UMD shim are not escaped, risking code injection.

This issue was flagged in a past review. If dep.packageName contains a single quote, the generated JavaScript will be syntactically invalid or exploitable.

   const entries = depsWithGlobals.map((dep) => {
     const global = dep.global!;
-    return `'${dep.packageName}': { default: window.${global}, ...window.${global} }`;
+    const safePkgName = dep.packageName.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
+    return `'${safePkgName}': { default: window.${global}, ...window.${global} }`;
   });

Additionally, global should be validated as a valid JavaScript identifier.

🧹 Nitpick comments (18)
libs/ui/src/adapters/dual-payload.ts (2)

150-162: Consider escaping all backtick sequences, not just triple backticks.

The current implementation only escapes exactly three consecutive backticks. Markdown allows longer fence delimiters (e.g., ), and sequences like html` could still break parsing if the HTML contains four backticks.

A more robust approach would escape all consecutive backtick sequences:

 function escapeCodeFence(html: string): string {
-  // If HTML contains triple backticks, we need to escape or use longer fence
-  // This is rare in practice, but we handle it for safety
-  if (html.includes('```')) {
-    // Replace triple backticks with HTML entity equivalent
-    return html.replace(/```/g, '&#96;&#96;&#96;');
-  }
-  return html;
+  // Escape all backtick sequences to prevent breaking any markdown fence
+  return html.replace(/`+/g, (match) => '&#96;'.repeat(match.length));
 }

223-225: Non-greedy regex may fail on multi-line HTML with escaped backticks.

The regex /```html\n([\s\S]*?)\n```/ uses non-greedy matching, which will stop at the first \n\`` occurrence. If HTML contains escaped backticks (````) that were converted from triple backticks, this works correctly. However, if for any reason the escaped content contains a literal newline followed by triple backticks, the regex will capture prematurely.

This is an edge case given the escaping in buildDualPayload, but worth noting for robustness.

libs/ui/src/adapters/__tests__/dual-payload.test.ts (1)

144-154: Add a test for backticks not at fence boundaries.

The test verifies triple backticks within a <pre> block are escaped. Consider adding a case for backticks that appear without the triple pattern (e.g., inline code with single/double backticks) to ensure they're preserved correctly.

it('should preserve single and double backticks unchanged', () => {
  const htmlWithBackticks = '<code>`inline`</code><pre>``template``</pre>';
  const result = buildDualPayload({
    data: {},
    html: htmlWithBackticks,
  });

  // Single and double backticks should remain unchanged
  const match = result.content[1].text.match(/```html\n([\s\S]*?)\n```/);
  expect(match?.[1]).toContain('`inline`');
  expect(match?.[1]).toContain('``template``');
});
apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts (1)

519-525: Redundant assertion can be simplified.

Line 525 re-asserts result2 as successful, but this was already verified on line 520. Consider removing the redundant assertion or replacing the comment with a more meaningful check.

       // Both should have correct platform behavior
       expect(result1.hasToolUI()).toBe(true);
-      // Claude uses dual-payload, so check it succeeded
-      expect(result2).toBeSuccessful();
+      // Claude uses dual-payload with markdown-wrapped HTML
+      expect(result2.text()).toContain('```html');
apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts (1)

163-177: Non-deterministic test data may cause flaky tests.

Using Math.random() to generate test values can lead to inconsistent test behavior and makes failures harder to reproduce. Consider using deterministic values.

       test('should handle many data points', async ({ mcp }) => {
         const data = Array.from({ length: 50 }, (_, i) => ({
           label: `Point ${i}`,
-          value: Math.floor(Math.random() * 1000),
+          value: (i + 1) * 20, // Deterministic values for reproducibility
         }));

         const result = await mcp.tools.call('react-chart', {
           data,
           title: 'Large Chart',
         });

         expect(result).toBeSuccessful();
         const json = result.json<{ data: Array<{ label: string; value: number }> }>();
         expect(json.data).toHaveLength(50);
       });
libs/ui/src/adapters/serving-mode.ts (1)

134-147: Consider using sentinel values for non-widget platform defaultMode.

For gemini and unknown platforms where supportsWidgets: false, setting defaultMode: 'inline' creates a logical inconsistency. While the code correctly checks supportsWidgets first (line 187), this could confuse maintainers.

Consider one of these approaches:

Option 1: Use a type-safe sentinel for platforms without widget support:

+type PlatformDefaultMode = Exclude<WidgetServingMode, 'auto'> | 'none';
+
 interface PlatformUICapabilities {
-  defaultMode: Exclude<WidgetServingMode, 'auto'>;
+  defaultMode: PlatformDefaultMode;
 }

 // Then for gemini/unknown:
   gemini: {
     supportsWidgets: false,
     useDualPayload: false,
     supportedModes: [],
-    defaultMode: 'inline', // Not used since supportsWidgets is false
+    defaultMode: 'none',
   },

Option 2: Make defaultMode optional and undefined for non-widget platforms:

 interface PlatformUICapabilities {
-  defaultMode: Exclude<WidgetServingMode, 'auto'>;
+  defaultMode?: Exclude<WidgetServingMode, 'auto'>;
 }
apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts (1)

34-510: Consider extracting common test patterns to reduce duplication.

Each test follows the same pattern: create client, call tool, validate, disconnect. While the current structure is clear and ensures test independence, you could reduce duplication with a helper function for the repeated client lifecycle.

Optional helper pattern:

async function testToolCall<T>(
  server: ServerFixture,
  clientInfo: { name: string; version: string },
  toolName: string,
  args: unknown,
  assertions: (result: ToolResult, json: T) => void | Promise<void>
) {
  const client = await server.createClient({
    transport: 'streamable-http',
    clientInfo,
  });

  const result = await client.tools.call(toolName, args);
  expect(result).toBeSuccessful();
  
  const json = result.json<T>();
  await assertions(result, json);
  
  await client.disconnect();
}

// Usage:
test('OpenAI should return JSON data for static badge', async ({ server }) => {
  await testToolCall(
    server,
    { name: 'ChatGPT', version: '1.0.0' },
    'static-badge',
    { label: 'Status', value: 'Active', color: 'green' },
    (result, json: { label: string; value: string; color: string }) => {
      expect(result.hasToolUI()).toBe(false);
      expect(json.label).toBe('Status');
      expect(json.value).toBe('Active');
      expect(json.color).toBe('green');
    }
  );
});
libs/ui/src/dependency/__tests__/template-loader.test.ts (1)

99-111: Consider adding test for FTP/other protocol rejection.

The tests verify HTTPS acceptance and HTTP rejection, but could benefit from testing other protocol schemes (e.g., ftp://, file://) to ensure comprehensive validation coverage.

it('should throw for file:// URL', () => {
  expect(() => validateTemplateUrl('file:///path/to/file.html')).toThrow('Template URLs must use HTTPS');
});
libs/ui/src/dependency/__tests__/resolver.test.ts (1)

132-165: Consider testing empty externals list.

The resolveFromSource tests verify filtering by externals, but don't test behavior when externals list is empty.

it('should return empty array when externals list is empty', () => {
  const resolver = new DependencyResolver({ platform: 'claude' });
  const source = `import React from 'react';`;
  const results = resolver.resolveFromSource(source, []);
  expect(results.length).toBe(0);
});
libs/ui/src/dependency/import-parser.ts (1)

368-371: extractExternalPackages duplicates parsing work.

This "faster version" comment is misleading - it simply calls parseImports and extracts one field. If performance is a concern for package-name-only extraction, a dedicated lightweight implementation would be warranted.

Consider renaming or updating the JSDoc to clarify this is a convenience wrapper rather than a performance optimization:

 /**
- * Extract only external package names from source.
- *
- * This is a faster version when you only need package names.
+ * Convenience function to extract only external package names from source.
  *
  * @param source - Source code to parse
  * @returns Array of unique external package names
  */
libs/ui/src/bundler/file-cache/storage/interface.ts (2)

181-189: Returning 0 on error silently hides size calculation failures.

When JSON.stringify fails (e.g., circular references, BigInt), returning 0 can cause incorrect cache eviction decisions since the entry appears to have no size. Consider logging a warning or returning an estimate based on a fallback strategy.

 export function calculateManifestSize(manifest: ComponentBuildManifest): number {
   // Approximate size by serializing to JSON
   try {
     return Buffer.byteLength(JSON.stringify(manifest), 'utf8');
-  } catch {
+  } catch (error) {
     // Fallback for circular references or BigInt values
+    console.warn('Failed to calculate manifest size:', error);
     return 0;
   }
 }

50-116: Two BuildCacheStorage interfaces with the same name exist in the public API—consolidate to avoid confusion for SDK consumers.

The dependency/types.ts BuildCacheStorage (6 methods: get, set, has, delete, clear, getStats) and bundler/file-cache/storage/interface.ts BuildCacheStorage (adds: type, initialize, cleanup, close) are both exported publicly but only the latter is actively used in implementations and throughout the codebase. This creates inconsistency for SDK consumers who may inadvertently import the incomplete version.

Make dependency/types.ts BuildCacheStorage extend the fuller interface from storage/interface.ts, or re-export from there. This maintains backward compatibility for existing imports while eliminating duplication and ensuring all consumers access the complete API.

libs/ui/src/bundler/file-cache/hash-calculator.ts (1)

48-55: Silent failure in hashFile could hide file access issues.

Returning undefined without logging when a file cannot be read may make debugging difficult when hash mismatches occur due to permission issues or missing files.

 export async function hashFile(filePath: string): Promise<string | undefined> {
   try {
     const content = await readFile(filePath);
     return sha256Buffer(content);
-  } catch {
+  } catch (error) {
+    // File may not exist or be inaccessible - caller handles undefined
     return undefined;
   }
 }
libs/ui/src/dependency/template-loader.ts (1)

35-49: Module-level cache state may cause issues in serverless environments.

The urlCache Map persists across requests in long-running processes but may behave unexpectedly in serverless environments where module state can be shared across invocations. Consider documenting this behavior or providing a cache instance injection option.

libs/ui/src/dependency/import-map.ts (1)

244-246: escapeHtmlAttr should also escape < and > for defense in depth.

While < and > are less critical in attribute values, escaping them provides defense against edge cases and potential context-switching attacks.

 function escapeHtmlAttr(str: string): string {
-  return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;');
+  return str
+    .replace(/&/g, '&amp;')
+    .replace(/"/g, '&quot;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;');
 }
libs/ui/src/bundler/file-cache/component-builder.ts (1)

231-235: Warning logged for failed external resolution could be noisy.

In non-strict mode with many unresolved packages, this could flood logs. Consider using a debug-level log or batching warnings.

       } catch (error) {
         // Log warning but continue - package will be bundled instead
-        console.warn(`Failed to resolve external "${pkg}": ${error}`);
+        if (process.env['DEBUG']) {
+          console.warn(`Failed to resolve external "${pkg}": ${error}`);
+        }
       }
libs/ui/src/dependency/schemas.ts (2)

19-83: Add JSDoc examples for CDN schemas.

Per coding guidelines, components and schemas should include JSDoc examples showing basic usage patterns. Consider adding example tags to demonstrate how to use these schemas.

Example JSDoc for cdnDependencySchema:

 /**
  * Schema for validating CDN dependency configuration.
+ *
+ * @example
+ * ```typescript
+ * const dependency = cdnDependencySchema.parse({
+ *   url: 'https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js',
+ *   integrity: 'sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We',
+ *   global: 'React',
+ *   esm: false
+ * });
+ * ```
  */
 export const cdnDependencySchema = z

Based on learnings, all components should have JSDoc examples with example tags showing basic usage and options patterns.


413-476: Consider using Zod's built-in SafeParseReturnType instead of manual SafeParseResult.

Zod 4 provides SafeParseReturnType<Input, Output> which already defines the discriminated union you're manually typing at line 416. Using Zod's built-in type eliminates redundancy and ensures consistency with the library's type system.

-/**
- * Safe parse result type.
- */
-export type SafeParseResult<T> = { success: true; data: T } | { success: false; error: z.ZodError };
-
 /**
  * Safely validate a CDN dependency configuration.
  *
  * @param data - Data to validate
  * @returns Safe parse result with success flag
  */
-export function safeParseCDNDependency(data: unknown): SafeParseResult<CDNDependencyOutput> {
-  return cdnDependencySchema.safeParse(data) as SafeParseResult<CDNDependencyOutput>;
+export function safeParseCDNDependency(data: unknown) {
+  return cdnDependencySchema.safeParse(data);
 }

The type assertions (as SafeParseResult<T>) are also unnecessary since Zod's .safeParse() already returns the correct type. Removing these simplifies the code and leverages Zod's type inference.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bc93de and 9d1d5f0.

📒 Files selected for processing (31)
  • apps/e2e/demo-e2e-ui/e2e/discovery-and-metadata.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/form-ui.tsx (1 hunks)
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/hybrid-status.tool.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/react-chart.tool.ts (2 hunks)
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/static-badge.tool.ts (1 hunks)
  • docs/live/docs/authentication/demo-servers.mdx (6 hunks)
  • libs/sdk/src/tool/flows/call-tool.flow.ts (5 hunks)
  • libs/ui/project.json (2 hunks)
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts (1 hunks)
  • libs/ui/src/adapters/__tests__/response-builder.test.ts (1 hunks)
  • libs/ui/src/adapters/dual-payload.ts (1 hunks)
  • libs/ui/src/adapters/response-builder.ts (1 hunks)
  • libs/ui/src/adapters/serving-mode.ts (1 hunks)
  • libs/ui/src/bundler/file-cache/component-builder.ts (1 hunks)
  • libs/ui/src/bundler/file-cache/hash-calculator.ts (1 hunks)
  • libs/ui/src/bundler/file-cache/storage/filesystem.ts (1 hunks)
  • libs/ui/src/bundler/file-cache/storage/interface.ts (1 hunks)
  • libs/ui/src/bundler/file-cache/storage/redis.ts (1 hunks)
  • libs/ui/src/dependency/__tests__/resolver.test.ts (1 hunks)
  • libs/ui/src/dependency/__tests__/template-loader.test.ts (1 hunks)
  • libs/ui/src/dependency/cdn-registry.ts (1 hunks)
  • libs/ui/src/dependency/import-map.ts (1 hunks)
  • libs/ui/src/dependency/import-parser.ts (1 hunks)
  • libs/ui/src/dependency/resolver.ts (1 hunks)
  • libs/ui/src/dependency/schemas.ts (1 hunks)
  • libs/ui/src/dependency/template-loader.ts (1 hunks)
  • libs/ui/src/dependency/template-processor.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • docs/live/docs/authentication/demo-servers.mdx
  • libs/ui/src/adapters/tests/response-builder.test.ts
  • libs/ui/src/adapters/response-builder.ts
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/form-ui.tsx
  • apps/e2e/demo-e2e-ui/e2e/discovery-and-metadata.e2e.test.ts
  • libs/ui/src/bundler/file-cache/storage/redis.ts
🧰 Additional context used
📓 Path-based instructions (6)
libs/ui/src/**/*.ts

📄 CodeRabbit inference engine (libs/ui/CLAUDE.md)

libs/ui/src/**/*.ts: Always use escapeHtml() utility for all user-provided content to prevent XSS vulnerabilities
Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Files:

  • libs/ui/src/bundler/file-cache/storage/interface.ts
  • libs/ui/src/dependency/__tests__/resolver.test.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • libs/ui/src/bundler/file-cache/storage/filesystem.ts
  • libs/ui/src/adapters/serving-mode.ts
  • libs/ui/src/bundler/file-cache/hash-calculator.ts
  • libs/ui/src/dependency/cdn-registry.ts
  • libs/ui/src/dependency/import-parser.ts
  • libs/ui/src/adapters/dual-payload.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
  • libs/ui/src/dependency/import-map.ts
  • libs/ui/src/bundler/file-cache/component-builder.ts
  • libs/ui/src/dependency/template-loader.ts
  • libs/ui/src/dependency/template-processor.ts
  • libs/ui/src/dependency/resolver.ts
  • libs/ui/src/dependency/schemas.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • libs/ui/src/bundler/file-cache/storage/interface.ts
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/hybrid-status.tool.ts
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/react-chart.tool.ts
  • libs/ui/src/dependency/__tests__/resolver.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/static-badge.tool.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • libs/ui/src/bundler/file-cache/storage/filesystem.ts
  • libs/ui/src/adapters/serving-mode.ts
  • libs/ui/src/bundler/file-cache/hash-calculator.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • libs/ui/src/dependency/cdn-registry.ts
  • libs/ui/src/dependency/import-parser.ts
  • libs/ui/src/adapters/dual-payload.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
  • libs/ui/src/dependency/import-map.ts
  • libs/ui/src/bundler/file-cache/component-builder.ts
  • libs/ui/src/dependency/template-loader.ts
  • libs/ui/src/dependency/template-processor.ts
  • libs/ui/src/dependency/resolver.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/ui/src/dependency/schemas.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/ui/src/bundler/file-cache/storage/interface.ts
  • libs/ui/project.json
  • libs/ui/src/dependency/__tests__/resolver.test.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • libs/ui/src/bundler/file-cache/storage/filesystem.ts
  • libs/ui/src/adapters/serving-mode.ts
  • libs/ui/src/bundler/file-cache/hash-calculator.ts
  • libs/ui/src/dependency/cdn-registry.ts
  • libs/ui/src/dependency/import-parser.ts
  • libs/ui/src/adapters/dual-payload.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
  • libs/ui/src/dependency/import-map.ts
  • libs/ui/src/bundler/file-cache/component-builder.ts
  • libs/ui/src/dependency/template-loader.ts
  • libs/ui/src/dependency/template-processor.ts
  • libs/ui/src/dependency/resolver.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/ui/src/dependency/schemas.ts
libs/ui/src/**/*.test.ts

📄 CodeRabbit inference engine (libs/ui/CLAUDE.md)

libs/ui/src/**/*.test.ts: Maintain 95%+ test coverage across statements, branches, functions, and lines
Test HTML escaping for user-provided content to prevent XSS attacks
Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Files:

  • libs/ui/src/dependency/__tests__/resolver.test.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.ts: Achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines)
Test all code paths including errors, constructor validation, and error class instanceof checks

Files:

  • libs/ui/src/dependency/__tests__/resolver.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
libs/{sdk,adapters,plugins,cli}/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters,plugins,cli}/src/**/*.ts: Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of unknown for execute() and read() methods
Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities in adapters
Use changeScope instead of scope for change event properties to avoid confusion with the Scope class
Validate hooks match their entry type and fail fast with InvalidHookFlowError for unsupported flows
Don't mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/sdk/src/tool/flows/call-tool.flow.ts
🧠 Learnings (22)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Add JSDoc examples with example tags showing basic usage and options patterns for all components
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Always use `escapeHtml()` utility for all user-provided content to prevent XSS vulnerabilities

Applied to files:

  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/hybrid-status.tool.ts
  • libs/ui/project.json
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/static-badge.tool.ts
  • libs/ui/src/dependency/import-map.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test HTML escaping for user-provided content to prevent XSS attacks

Applied to files:

  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/hybrid-status.tool.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/static-badge.tool.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
  • libs/ui/src/dependency/import-map.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/*/src/index.ts : Use barrel exports (index.ts) to export all public API surface without legacy exports or aliases

Applied to files:

  • libs/ui/project.json
  • libs/ui/src/dependency/import-parser.ts
  • libs/ui/src/dependency/import-map.ts
  • libs/ui/src/bundler/file-cache/component-builder.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/** : Organize components into schema.ts (definitions) and implementation .ts files with matching names

Applied to files:

  • libs/ui/project.json
  • libs/ui/src/dependency/import-parser.ts
  • libs/ui/src/bundler/file-cache/component-builder.ts
  • libs/ui/src/dependency/schemas.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.test.ts : Write validation tests covering invalid variant/options, unknown properties, and valid option acceptance

Applied to files:

  • libs/ui/project.json
  • libs/ui/src/dependency/__tests__/resolver.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Applied to files:

  • libs/ui/project.json
  • libs/ui/src/dependency/__tests__/resolver.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • libs/ui/src/adapters/serving-mode.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Applied to files:

  • libs/ui/project.json
  • libs/ui/src/dependency/cdn-registry.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
  • libs/ui/src/dependency/import-map.ts
  • libs/ui/src/bundler/file-cache/component-builder.ts
  • libs/ui/src/dependency/template-loader.ts
  • libs/ui/src/dependency/resolver.ts
  • libs/ui/src/dependency/schemas.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Use pure HTML string generation without React/Vue/JSX - components return HTML strings only

Applied to files:

  • libs/ui/project.json
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/static-badge.tool.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/*/src/common/records/**/*.ts : Centralize record types in common/records and import from there instead of from module-specific files

Applied to files:

  • libs/ui/project.json
  • libs/ui/src/dependency/import-parser.ts
  • libs/ui/src/dependency/resolver.ts
  • libs/ui/src/dependency/schemas.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Every component must have a Zod schema with `.strict()` mode to reject unknown properties

Applied to files:

  • libs/ui/project.json
  • apps/e2e/demo-e2e-ui/src/apps/widgets/tools/react-chart.tool.ts
  • libs/ui/src/dependency/schemas.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Maintain 95%+ test coverage across statements, branches, functions, and lines

Applied to files:

  • libs/ui/project.json
  • libs/ui/src/dependency/__tests__/resolver.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.test.ts : Test all code paths including errors, constructor validation, and error class `instanceof` checks

Applied to files:

  • libs/ui/src/dependency/__tests__/resolver.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
  • libs/ui/src/adapters/__tests__/dual-payload.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • libs/ui/src/dependency/__tests__/template-loader.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Add JSDoc examples with example tags showing basic usage and options patterns for all components

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements

Applied to files:

  • libs/ui/src/bundler/file-cache/storage/filesystem.ts
  • libs/ui/src/dependency/import-map.ts
  • libs/ui/src/dependency/schemas.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Use `getCapabilities()` for dynamic capability exposure instead of hardcoding capabilities in adapters

Applied to files:

  • libs/ui/src/adapters/serving-mode.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.ts : Avoid non-null assertions (`!`) - use proper error handling and throw specific errors instead

Applied to files:

  • libs/ui/src/dependency/resolver.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Don't mutate rawInput in flows - use state.set() for managing flow state instead

Applied to files:

  • libs/sdk/src/tool/flows/call-tool.flow.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Use FrontMCP's TypeScript-first schema validation framework philosophy - all types should align with MCP protocol definitions

Applied to files:

  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/ui/src/dependency/schemas.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Use HTMX schema with strict mode including get, post, put, delete, target, swap, and trigger properties

Applied to files:

  • libs/ui/src/dependency/schemas.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Use consistent enum naming for variants ('primary', 'secondary', 'outline', 'ghost', 'danger', 'success') and sizes ('xs', 'sm', 'md', 'lg', 'xl')

Applied to files:

  • libs/ui/src/dependency/schemas.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Use Zod schema validation for all component inputs as a core validation strategy

Applied to files:

  • libs/ui/src/dependency/schemas.ts
🧬 Code graph analysis (17)
libs/ui/src/bundler/file-cache/storage/interface.ts (1)
libs/ui/src/dependency/types.ts (3)
  • BuildCacheStorage (418-461)
  • ComponentBuildManifest (327-408)
  • CacheStats (466-491)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/hybrid-status.tool.ts (3)
libs/cli/src/templates/3rd-party-integration/src/tools/example.list.ts (3)
  • inputSchema (12-17)
  • Input (19-19)
  • Output (41-41)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/react-chart.tool.ts (1)
  • Tool (39-66)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/static-badge.tool.ts (1)
  • Tool (31-72)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/react-chart.tool.ts (2)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/hybrid-status.tool.ts (1)
  • Tool (34-104)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/static-badge.tool.ts (1)
  • Tool (31-72)
libs/ui/src/dependency/__tests__/resolver.test.ts (1)
libs/ui/src/dependency/resolver.ts (8)
  • DependencyResolver (80-299)
  • DependencyResolutionError (37-42)
  • createResolver (312-320)
  • createClaudeResolver (327-329)
  • createOpenAIResolver (336-338)
  • resolveDependencies (350-357)
  • generateImportMapForPackages (368-372)
  • NoProviderError (47-52)
apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts (2)
scripts/bump-synchronized-versions.mjs (1)
  • result (130-130)
libs/sdk/src/common/interfaces/tool.interface.ts (1)
  • platform (120-133)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/static-badge.tool.ts (3)
libs/cli/src/templates/3rd-party-integration/src/tools/example.list.ts (3)
  • inputSchema (12-17)
  • Input (19-19)
  • Output (41-41)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/hybrid-status.tool.ts (1)
  • Tool (34-104)
apps/e2e/demo-e2e-ui/src/apps/widgets/tools/react-chart.tool.ts (1)
  • Tool (39-66)
libs/ui/src/adapters/__tests__/dual-payload.test.ts (1)
libs/ui/src/adapters/dual-payload.ts (5)
  • buildDualPayload (122-144)
  • DEFAULT_HTML_PREFIX (73-73)
  • isDualPayload (172-202)
  • parseDualPayload (208-232)
  • DualPayloadResult (57-64)
libs/ui/src/bundler/file-cache/hash-calculator.ts (1)
libs/ui/src/dependency/types.ts (2)
  • CDNDependency (57-113)
  • FileBundleOptions (142-191)
apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts (3)
scripts/bump-synchronized-versions.mjs (1)
  • result (130-130)
libs/testing/src/http-mock/http-mock.ts (1)
  • text (545-551)
libs/sdk/src/common/interfaces/tool.interface.ts (1)
  • platform (120-133)
libs/ui/src/dependency/cdn-registry.ts (1)
libs/ui/src/dependency/types.ts (4)
  • CDNRegistry (548-548)
  • CDNPlatformType (36-36)
  • CDNProvider (25-25)
  • CDNRegistryEntry (500-543)
libs/ui/src/dependency/import-parser.ts (1)
libs/ui/src/dependency/types.ts (2)
  • ParsedImportResult (655-675)
  • ParsedImport (600-650)
libs/ui/src/adapters/dual-payload.ts (1)
libs/ui/src/utils/safe-stringify.ts (1)
  • safeStringify (30-47)
libs/ui/src/dependency/__tests__/template-loader.test.ts (2)
libs/ui/src/dependency/template-loader.ts (9)
  • detectTemplateSource (73-86)
  • isFileBasedTemplate (91-93)
  • validateTemplateUrl (106-112)
  • detectFormatFromUrl (120-123)
  • resolveFilePath (322-324)
  • clearUrlCache (47-49)
  • getUrlCache (40-42)
  • invalidateUrlCache (486-488)
  • needsRefetch (453-478)
libs/ui/src/dependency/types.ts (1)
  • ResolvedTemplate (780-825)
libs/ui/src/dependency/import-map.ts (1)
libs/ui/src/dependency/types.ts (3)
  • ResolvedDependency (286-321)
  • ImportMap (259-277)
  • CDNDependency (57-113)
libs/ui/src/dependency/template-loader.ts (2)
libs/ui/src/dependency/types.ts (7)
  • UrlFetchResult (754-774)
  • TemplateSource (746-749)
  • detectTemplateMode (692-726)
  • TemplateMode (684-684)
  • TemplateFormat (740-740)
  • detectFormatFromPath (927-944)
  • ResolvedTemplate (780-825)
libs/ui/src/bundler/file-cache/hash-calculator.ts (1)
  • sha256 (28-30)
libs/ui/src/dependency/template-processor.ts (2)
libs/ui/src/dependency/types.ts (5)
  • TemplateSource (746-749)
  • ResolvedTemplate (780-825)
  • TemplateProcessingOptions (830-887)
  • ProcessedTemplate (892-919)
  • TemplateFormat (740-740)
libs/ui/src/handlebars/index.ts (1)
  • HandlebarsRenderer (146-358)
libs/sdk/src/tool/flows/call-tool.flow.ts (4)
libs/ui/src/adapters/response-builder.ts (2)
  • ToolResponseContent (66-87)
  • buildToolResponseContent (125-209)
libs/ui/src/index.ts (3)
  • ToolResponseContent (79-79)
  • resolveServingMode (71-71)
  • buildToolResponseContent (80-80)
libs/ui/src/adapters/index.ts (3)
  • ToolResponseContent (54-54)
  • resolveServingMode (43-43)
  • buildToolResponseContent (56-56)
libs/ui/src/adapters/serving-mode.ts (1)
  • resolveServingMode (182-225)
🪛 ast-grep (0.40.0)
libs/ui/src/dependency/import-parser.ts

[warning] 200-200: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(IMPORT_PATTERNS.defaultAndNamed.source, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 217-217: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(IMPORT_PATTERNS.defaultAndNamespace.source, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 233-233: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(IMPORT_PATTERNS.named.source, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 250-250: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(IMPORT_PATTERNS.default.source, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 269-269: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(IMPORT_PATTERNS.namespace.source, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 284-284: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(IMPORT_PATTERNS.sideEffect.source, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 303-303: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(IMPORT_PATTERNS.dynamic.source, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 317-317: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(IMPORT_PATTERNS.reExport.source, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 331-331: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(IMPORT_PATTERNS.reExportAll.source, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Build Libraries

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (4)
libs/ui/src/dependency/__tests__/import-parser.test.ts (3)

1-296: Add input variation and error handling tests to reach 95%+ coverage.

The test suite lacks coverage for input edge cases and error paths. Per coding guidelines, tests must cover all code paths including errors and maintain 95%+ coverage across all metrics.

Add tests for:

  1. Mixed quote styles:
it('should handle mixed quote styles', () => {
  const source = `
    import { foo } from "react";
    import { bar } from 'lodash';
  `;
  const result = parseImports(source);
  expect(result.externalPackages).toContain('react');
  expect(result.externalPackages).toContain('lodash');
});
  1. Whitespace variations:
it('should handle extra whitespace in imports', () => {
  const source = `import  {  useState  ,  useEffect  }  from  'react'  ;`;
  const result = parseImports(source);
  expect(result.imports[0].namedImports).toContain('useState');
  expect(result.imports[0].namedImports).toContain('useEffect');
});
  1. Special characters in specifiers:
it('should handle special characters in package names', () => {
  const source = `import { foo } from '@scope/pkg-name.v2';`;
  const result = parseImports(source);
  expect(result.externalPackages).toContain('@scope/pkg-name.v2');
});
  1. Malformed imports (if implementation has error handling):
it('should handle malformed import syntax gracefully', () => {
  const source = `import { from 'react'`;
  expect(() => parseImports(source)).not.toThrow(); // or .toThrow() if validation exists
});

Based on learnings, maintain 95%+ test coverage across statements, branches, functions, and lines.


112-118: Enhance line/column edge case testing.

The current test only verifies line > 0 and column >= 0. To achieve 95%+ coverage and test boundary conditions, add tests for:

  • First-line import (column should be 0)
  • Imports after multiple newlines (verify exact line numbers)
  • Multiple imports with increasing line numbers
it('should report correct line and column for first-line import', () => {
  const source = `import React from 'react';`;
  const result = parseImports(source);
  
  expect(result.imports[0].line).toBe(1);
  expect(result.imports[0].column).toBe(0);
});

it('should report correct line numbers after newlines', () => {
  const source = `

import React from 'react';

import { Chart } from 'chart.js';`;
  const result = parseImports(source);
  
  expect(result.imports[0].line).toBe(3);
  expect(result.imports[1].line).toBe(5);
});

Based on learnings, maintain 95%+ test coverage across all metrics.


213-226: Add explicit duplicate import statement test.

While this test verifies package-level deduplication, it doesn't test duplicate import statement filtering. The implementation likely uses deduplication logic (e.g., seenStatements Set) that should be verified.

Add a test case:

it('should deduplicate identical import statements', () => {
  const source = `
    import React from 'react';
    import React from 'react';
    import { useState } from 'react';
  `;
  const result = parseImports(source);
  
  // Should only have 2 unique import statements, not 3
  expect(result.imports.length).toBe(2);
  expect(result.externalPackages.length).toBe(1);
});

Based on learnings, maintain 95%+ test coverage across all metrics.

libs/ui/src/bundler/file-cache/storage/redis.ts (1)

178-204: Critical: Same serialization bug as get() method.

Line 197 has the identical serialization bug: when json: false, String(entry) produces "[object Object]", corrupting the cache entry.

Apply this diff:

-   const serialized = this.options.json ? JSON.stringify(entry) : String(entry);
+   const serialized = JSON.stringify(entry);

    await this.options.client.setex(redisKey, effectiveTtl, serialized);
🧹 Nitpick comments (2)
libs/ui/src/bundler/file-cache/storage/filesystem.ts (2)

177-182: Consider the trade-offs of fire-and-forget metadata updates.

The fire-and-forget pattern improves read performance but silent failures could lead to slightly stale LRU data, potentially affecting eviction accuracy. This is generally acceptable for cache metadata, but consider whether:

  • Adding structured logging (instead of console.debug) would help monitor failure rates in production
  • Critical LRU accuracy scenarios might warrant awaiting the write

155-155: Consider using async file operations consistently.

The synchronous existsSync() works correctly but is inconsistent with the async/await pattern used throughout. Consider using the async access() method from fs/promises for better consistency.

Example:

try {
  await access(filePath);
} catch {
  this.stats.misses++;
  this.updateHitRate();
  return undefined;
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d1d5f0 and 826c225.

📒 Files selected for processing (3)
  • libs/ui/src/bundler/file-cache/storage/filesystem.ts (1 hunks)
  • libs/ui/src/bundler/file-cache/storage/redis.ts (1 hunks)
  • libs/ui/src/dependency/__tests__/import-parser.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
libs/ui/src/**/*.ts

📄 CodeRabbit inference engine (libs/ui/CLAUDE.md)

libs/ui/src/**/*.ts: Always use escapeHtml() utility for all user-provided content to prevent XSS vulnerabilities
Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Files:

  • libs/ui/src/bundler/file-cache/storage/filesystem.ts
  • libs/ui/src/bundler/file-cache/storage/redis.ts
  • libs/ui/src/dependency/__tests__/import-parser.test.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • libs/ui/src/bundler/file-cache/storage/filesystem.ts
  • libs/ui/src/bundler/file-cache/storage/redis.ts
  • libs/ui/src/dependency/__tests__/import-parser.test.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/ui/src/bundler/file-cache/storage/filesystem.ts
  • libs/ui/src/bundler/file-cache/storage/redis.ts
  • libs/ui/src/dependency/__tests__/import-parser.test.ts
libs/ui/src/**/*.test.ts

📄 CodeRabbit inference engine (libs/ui/CLAUDE.md)

libs/ui/src/**/*.test.ts: Maintain 95%+ test coverage across statements, branches, functions, and lines
Test HTML escaping for user-provided content to prevent XSS attacks
Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Files:

  • libs/ui/src/dependency/__tests__/import-parser.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.ts: Achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines)
Test all code paths including errors, constructor validation, and error class instanceof checks

Files:

  • libs/ui/src/dependency/__tests__/import-parser.test.ts
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements

Applied to files:

  • libs/ui/src/bundler/file-cache/storage/filesystem.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Maintain 95%+ test coverage across statements, branches, functions, and lines

Applied to files:

  • libs/ui/src/dependency/__tests__/import-parser.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Applied to files:

  • libs/ui/src/dependency/__tests__/import-parser.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.test.ts : Write validation tests covering invalid variant/options, unknown properties, and valid option acceptance

Applied to files:

  • libs/ui/src/dependency/__tests__/import-parser.test.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.test.ts : Test all code paths including errors, constructor validation, and error class `instanceof` checks

Applied to files:

  • libs/ui/src/dependency/__tests__/import-parser.test.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.test.ts : Achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines)

Applied to files:

  • libs/ui/src/dependency/__tests__/import-parser.test.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/*/src/index.ts : Use barrel exports (index.ts) to export all public API surface without legacy exports or aliases

Applied to files:

  • libs/ui/src/dependency/__tests__/import-parser.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test HTML escaping for user-provided content to prevent XSS attacks

Applied to files:

  • libs/ui/src/dependency/__tests__/import-parser.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
🔇 Additional comments (14)
libs/ui/src/bundler/file-cache/storage/filesystem.ts (5)

23-57: LGTM! Well-structured error classes.

The error hierarchy is properly implemented with specific error types, optional cause tracking, and correct use of the override keyword for TypeScript strict mode compliance.


356-360: Good fix! Hash-based approach resolves collision issue.

The SHA-256 hash correctly addresses the previous review concern about cache key collisions. Different keys now always produce different filenames, eliminating the sanitization collision problem.


383-409: Good fix! Stats consistency restored.

The loadStats() method now correctly reads and parses each cache entry to extract entry.metadata.size, ensuring consistency with the set() method's use of calculateManifestSize(). This addresses the previous review concern about totalSize inaccuracy after restart.


430-476: Good fix! Eviction now uses original cache keys.

The evictLRU() method correctly reads entry.metadata.key from the parsed cache entry and passes it to delete(), eliminating the previous bug where it tried to reverse-engineer the key from the filename. The special handling for corrupted files is also appropriate.


1-492: Excellent implementation! All previous review concerns addressed.

The FilesystemStorage implementation is well-structured with:

  • ✅ All three previous review issues successfully resolved (hash-based filenames, consistent stats, metadata-based eviction)
  • ✅ Proper TypeScript strict mode compliance (no any, no !)
  • ✅ Specific error classes with cause tracking
  • ✅ Comprehensive error handling throughout
  • ✅ Clean separation of concerns
  • ✅ Good documentation

The optional refactoring suggestions are minor style improvements and don't block merging.

libs/ui/src/dependency/__tests__/import-parser.test.ts (2)

142-197: Good coverage of re-export patterns and combined imports.

The tests for re-exports (named, namespace/all, mixed) and combined import forms (default+named, default+namespace) directly address the previous review feedback and improve branch coverage.


273-294: Helper function branches now tested.

Direct unit tests for getPackageName now cover scoped packages, subpaths, and edge cases, addressing the previous review's concern about untested helper branches.

libs/ui/src/bundler/file-cache/storage/redis.ts (7)

17-28: LGTM! Well-designed compatibility interface.

The RedisClient interface provides good compatibility with both ioredis and redis packages. The use of unknown for return types is appropriate for flexible Redis client implementations.


33-51: LGTM! Clean interface design.

The RedisStorageOptions interface appropriately extends StorageOptions with Redis-specific configuration. The required client property ensures proper dependency injection.


209-215: LGTM! Clean existence check.

The has() method correctly uses the Redis exists command and returns a boolean.


220-246: LGTM! Previous type coercion bug has been fixed.

Line 230 now correctly always uses JSON.parse(data) to parse the CacheEntry, fixing the bug identified in previous reviews. The stats tracking logic is sound.


251-270: LGTM! Correct cache clearing implementation.

The clear() method appropriately uses keys() to find all entries (acceptable performance trade-off for a destructive clear operation) and properly resets statistics.


284-316: LGTM! Previous type coercion bug has been fixed.

Line 304 now correctly always uses JSON.parse(data) to parse the CacheEntry, fixing the bug identified in previous reviews. The cleanup logic properly recalculates statistics and handles corrupted entries gracefully.


351-389: LGTM! Helper methods are well-implemented.

The private helper methods correctly handle stats persistence (always using JSON) and provide clean utility functions. The stats are correctly stored separately from cache entries.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
libs/ui/src/dependency/template-loader.ts (1)

289-313: Path traversal check should use path.relative for robustness.

The current implementation on line 297 uses startsWith(normalizedBase + sep) which was flagged in a previous review. While using sep helps, this approach doesn't handle all edge cases on Windows (case sensitivity, normalized paths, junction points).

Additionally, line 304 uses a type assertion as NodeJS.ErrnoException which violates coding guidelines.

Apply this diff to use the recommended path.relative approach:

+import { resolve as resolvePath, isAbsolute, sep, relative } from 'path';
   // Prevent path traversal attacks - ensure resolved path stays within base directory
   const normalizedBase = resolvePath(basePath);
-  if (!absolutePath.startsWith(normalizedBase + sep) && absolutePath !== normalizedBase) {
+  const relativePath = relative(normalizedBase, absolutePath);
+  if (relativePath.startsWith('..') || isAbsolute(relativePath)) {
     throw new Error(`Template path escapes base directory: ${filePath}`);
   }

Also fix the error type assertion:

   } catch (error) {
-    const err = error as NodeJS.ErrnoException;
-    if (err.code === 'ENOENT') {
+    if (error && typeof error === 'object' && 'code' in error) {
+      const err = error as NodeJS.ErrnoException;
+      if (err.code === 'ENOENT') {
-      throw new Error(`Template file not found: ${absolutePath}`);
-    }
-    if (err.code === 'EACCES') {
+        throw new Error(`Template file not found: ${absolutePath}`);
+      }
+      if (err.code === 'EACCES') {
-      throw new Error(`Permission denied reading template file: ${absolutePath}`);
+        throw new Error(`Permission denied reading template file: ${absolutePath}`);
+      }
+      throw new Error(`Failed to read template file: ${absolutePath} - ${err.message}`);
     }
-    throw new Error(`Failed to read template file: ${absolutePath} - ${err.message}`);
+    throw new Error(`Failed to read template file: ${absolutePath}`);
   }

As per coding guidelines, avoid type assertions and use proper type guards.

apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts (1)

126-162: Tests now include appropriate field validation.

The Continue and Cody tests validate both label and value fields (lines 139-140 and 158-159). Since these tests don't pass a color parameter in the input, omitting color validation is correct.

🧹 Nitpick comments (5)
libs/ui/src/dependency/resolver.ts (1)

84-94: Extract duplicated platform default expression.

The expression options.platform ?? 'unknown' is evaluated twice on lines 86 and 87. Consider extracting it to improve readability:

  constructor(options: DependencyResolverOptions = {}) {
+   const platform = options.platform ?? 'unknown';
    this.options = {
-     platform: options.platform ?? 'unknown',
-     preferredProviders: options.preferredProviders ?? CDN_PROVIDER_PRIORITY[options.platform ?? 'unknown'],
+     platform,
+     preferredProviders: options.preferredProviders ?? CDN_PROVIDER_PRIORITY[platform],
      customRegistry: options.customRegistry ?? {},
      strictMode: options.strictMode ?? true,
      requireIntegrity: options.requireIntegrity ?? false,
    };

    this.registry = mergeRegistries(this.options.customRegistry);
  }
apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts (1)

522-531: Consider asserting specific behavior for empty points array.

The test uses toBeDefined() which doesn't verify whether the operation succeeded or failed. Since the comment indicates uncertainty about schema validation, consider verifying the actual behavior and updating the assertion to be more specific.

       test('should reject empty points array', async ({ mcp }) => {
         const result = await mcp.tools.call('mdx-interactive', {
           topic: 'Empty Points',
           points: [],
         });
 
-        // Empty array might succeed or fail depending on schema
-        // Just verify it handles the case
-        expect(result).toBeDefined();
+        // Verify the expected behavior - update if schema allows empty arrays
+        expect(result).toBeError();
       });
apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts (3)

209-237: Consider verifying content preservation for boundary tests.

The long title/summary/description tests verify success but don't confirm the content is preserved in the output. For thoroughness, consider adding assertions that the long content appears in the result.

       test('should handle very long title', async ({ mcp }) => {
+        const longTitle = 'T'.repeat(300);
         const result = await mcp.tools.call('markdown-report', {
-          title: 'T'.repeat(300),
+          title: longTitle,
           summary: 'Summary',
           findings: [{ title: 'F', description: 'D', severity: 'low' }],
         });
 
         expect(result).toBeSuccessful();
+        const json = result.json<{ title: string }>();
+        expect(json.title).toBe(longTitle);
       });

339-406: Consider verifying specific error messages or codes.

Error handling tests verify that errors occur but don't check specific error messages or MCP error codes. For more robust validation, consider asserting on error content.

       test('should reject missing title', async ({ mcp }) => {
         const result = await mcp.tools.call('markdown-report', {
           summary: 'Summary without title',
           findings: [{ title: 'F', description: 'D', severity: 'low' }],
         });

         expect(result).toBeError();
+        // Optionally verify error specifics:
+        // expect(result.error()).toContain('title');
       });

689-750: Consider verifying data integrity in concurrent tests.

The concurrent tests verify all calls succeed but don't confirm each result corresponds to its input. Under concurrency bugs, results could be swapped or corrupted.

     test('should handle concurrent report calls', async ({ mcp }) => {
       const results = await Promise.all(
         Array.from({ length: 5 }, (_, i) =>
           mcp.tools.call('markdown-report', {
             title: `Report ${i}`,
             summary: `Summary ${i}`,
             findings: [{ title: `F${i}`, description: `D${i}`, severity: 'low' }],
           }),
         ),
       );

       expect(results).toHaveLength(5);
-      results.forEach((result) => {
+      results.forEach((result, i) => {
         expect(result).toBeSuccessful();
+        const json = result.json<{ title: string }>();
+        expect(json.title).toBe(`Report ${i}`);
       });
     });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 826c225 and 466ace6.

📒 Files selected for processing (10)
  • apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts (1 hunks)
  • apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts (1 hunks)
  • libs/ui/src/bundler/file-cache/storage/redis.ts (1 hunks)
  • libs/ui/src/dependency/resolver.ts (1 hunks)
  • libs/ui/src/dependency/template-loader.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/e2e/demo-e2e-ui/e2e/react-tools.e2e.test.ts
  • libs/ui/src/bundler/file-cache/storage/redis.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts
  • libs/ui/src/dependency/template-loader.ts
  • apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts
  • libs/ui/src/dependency/resolver.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.ts: Achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines)
Test all code paths including errors, constructor validation, and error class instanceof checks

Files:

  • apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
libs/ui/src/**/*.ts

📄 CodeRabbit inference engine (libs/ui/CLAUDE.md)

libs/ui/src/**/*.ts: Always use escapeHtml() utility for all user-provided content to prevent XSS vulnerabilities
Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Files:

  • libs/ui/src/dependency/template-loader.ts
  • libs/ui/src/dependency/resolver.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/ui/src/dependency/template-loader.ts
  • libs/ui/src/dependency/resolver.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Maintain 95%+ test coverage across statements, branches, functions, and lines

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.test.ts : Write validation tests covering invalid variant/options, unknown properties, and valid option acceptance

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.test.ts : Test all code paths including errors, constructor validation, and error class `instanceof` checks

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test HTML escaping for user-provided content to prevent XSS attacks

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Applied to files:

  • libs/ui/src/dependency/template-loader.ts
  • libs/ui/src/dependency/resolver.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.test.ts : Achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines)

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Add JSDoc examples with example tags showing basic usage and options patterns for all components

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.ts : Avoid non-null assertions (`!`) - use proper error handling and throw specific errors instead

Applied to files:

  • libs/ui/src/dependency/resolver.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/*/src/common/records/**/*.ts : Centralize record types in common/records and import from there instead of from module-specific files

Applied to files:

  • libs/ui/src/dependency/resolver.ts
🧬 Code graph analysis (3)
libs/ui/src/dependency/template-loader.ts (2)
libs/ui/src/dependency/types.ts (7)
  • UrlFetchResult (755-775)
  • TemplateSource (747-750)
  • detectTemplateMode (693-727)
  • TemplateMode (685-685)
  • TemplateFormat (741-741)
  • detectFormatFromPath (928-945)
  • ResolvedTemplate (781-826)
libs/ui/src/bundler/file-cache/hash-calculator.ts (1)
  • sha256 (28-30)
libs/ui/src/dependency/resolver.ts (4)
libs/ui/src/dependency/types.ts (5)
  • CDNPlatformType (37-37)
  • CDNRegistry (549-549)
  • ResolvedDependency (287-322)
  • ImportMap (260-278)
  • CDNProvider (26-26)
libs/ui/src/dependency/cdn-registry.ts (5)
  • CDN_PROVIDER_PRIORITY (530-538)
  • mergeRegistries (663-668)
  • lookupPackage (551-556)
  • resolveAllDependencies (704-761)
  • getPackagePeerDependencies (679-694)
libs/ui/src/dependency/import-parser.ts (1)
  • parseImports (186-358)
libs/ui/src/dependency/import-map.ts (1)
  • createImportMap (38-54)
apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts (1)
libs/sdk/src/common/interfaces/tool.interface.ts (1)
  • platform (120-133)
🔇 Additional comments (38)
libs/ui/src/dependency/template-loader.ts (4)

35-49: LGTM: URL cache implementation is clean.

The in-memory Map-based cache with accessor functions for testing/debugging is appropriate for this use case.


73-93: LGTM: Source detection logic is correct.

The delegation to detectTemplateMode and proper mapping to discriminated union types is well-structured.


385-437: LGTM: Main resolver logic is well-structured.

The resolveTemplate function properly handles all template source types with appropriate format detection and content hashing. The discriminated union pattern makes the code type-safe and maintainable.


453-488: LGTM: Cache invalidation logic is sound.

The needsRefetch function properly handles both ETag-based validation (preferred) and TTL-based fallback (for CDNs without ETag support). The 5-minute TTL provides a good balance between freshness and performance.

libs/ui/src/dependency/resolver.ts (9)

1-52: Past review comments have been properly addressed.

The previous issues have been resolved:

  • Non-strict mode now correctly returns null instead of throwing (line 121)
  • generateImportMap now delegates to createImportMap (line 206)
  • The 'custom' provider is now part of the CDNProvider type union

However, the coding guidelines state: "Use specific error classes with MCP error codes instead of generic errors." The current error classes (DependencyResolutionError, NoProviderError) are specific but do not include MCP error codes. Please verify whether MCP error codes should be integrated into these errors.


104-141: LGTM!

The resolve method correctly handles all cases:

  • Explicit overrides bypass the registry
  • Non-strict mode returns null to signal bundling (properly addressed from previous review)
  • Provider selection follows platform priority with integrity checks
  • Appropriate errors are thrown when resolution fails in strict mode

150-173: LGTM!

The resolveMany method correctly:

  • Resolves all dependencies including peers using resolveAllDependencies
  • Filters out null results for packages that should be bundled
  • Handles errors appropriately based on strict/non-strict mode

186-197: LGTM!

The resolveFromSource method correctly parses imports and filters to only resolve packages explicitly listed in the externals parameter, giving developers precise control over CDN vs bundled dependencies.


205-259: LGTM!

The utility methods are well-implemented:

  • generateImportMap correctly delegates to createImportMap (addressed from previous review)
  • canResolve properly handles the nullable return from resolve
  • getUrl uses appropriate silent failure for a convenience getter
  • Accessor methods provide clean access to internal state

264-279: LGTM!

The createResolvedDependency method uses a sensible fallback chain for version resolution (explicit → URL extraction → 'latest') and conservative defaults for ESM support.


284-298: LGTM!

The version extraction regex patterns handle common CDN URL formats adequately. While some edge cases may not be covered (e.g., versions without trailing slashes), the method is a fallback mechanism with additional safeguards ('latest' default), making this implementation appropriate.


312-338: LGTM!

The factory functions provide clean, type-safe shortcuts for creating resolvers. Platform-specific factories (createClaudeResolver, createOpenAIResolver) reduce boilerplate while maintaining type safety through Omit<DependencyResolverOptions, 'platform'>.


350-372: LGTM!

The convenience functions (resolveDependencies, generateImportMapForPackages) provide ergonomic one-liners for common workflows, properly delegating to the resolver class methods.

apps/e2e/demo-e2e-ui/e2e/serving-modes.e2e.test.ts (3)

1-56: Well-structured test file with comprehensive platform coverage.

The test organization is clear, with good separation of static/hybrid modes and supported/unsupported platforms. The OpenAI static mode test (lines 34-56) properly validates all three fields (label, value, color) and includes proper client cleanup.


380-407: Good status variant coverage with correct mappings.

The status variant tests (lines 380-407) properly verify the status-to-color and status-to-isHealthy mappings. Using as const for the statuses array ensures type safety.


474-512: Good coverage of default value behavior.

The default value tests properly verify fallback behavior when optional parameters are omitted, which is important for API contract validation.

apps/e2e/demo-e2e-ui/e2e/mdx-tools.e2e.test.ts (4)

1-60: Comprehensive MDX doc tool test coverage.

The basic functionality tests properly validate sectionCount and title fields in the JSON response. Good use of structured assertions.


135-215: Thorough edge case testing for MDX documents.

The boundary tests cover important scenarios including single/many sections, long content (5000 chars), long headings (200 chars), and empty content. This helps ensure robustness of the tool.


266-300: Good error handling test coverage.

The error tests validate required field enforcement for both the document level (title, sections) and section level (heading, content). This aligns with the coding guidelines to test all code paths including errors.


535-594: Good concurrency test coverage.

The concurrent tests properly exercise parallel execution of both MDX tools and verify all operations complete successfully. The mixed tool call test is particularly valuable for validating tool isolation.

apps/e2e/demo-e2e-ui/e2e/html-tools.e2e.test.ts (4)

1-73: Well-structured HTML table tool tests.

The basic functionality tests properly validate rowCount and columnCount in the JSON response, covering tables with and without titles. Good foundation for the test suite.


202-260: Good XSS prevention test coverage.

The XSS tests properly validate that script tags are escaped in the output (line 211) and that the tool handles various malicious inputs gracefully. The comments clarifying that XSS prevention occurs at template render time help document the security boundary. Based on learnings, this addresses the requirement to test HTML escaping for user-provided content.


396-440: Appropriate handling of special characters in card tool.

The tests correctly verify that the JSON output contains raw input values, with the understanding that XSS prevention occurs at template render time. The assertion at line 406 that json.title equals the raw script tag is correct for the JSON layer.


262-298: Comprehensive error handling for html-table.

The tests cover both missing required fields and invalid types, which is important for schema validation. This aligns with the coding guidelines to test all code paths including errors.

apps/e2e/demo-e2e-ui/e2e/ui.e2e.test.ts (4)

1-19: Good organization as a smoke test integration file.

The file properly documents its role as the main integration test with references to detailed test files. The smoke test approach is appropriate for validating all tool types work together.


21-111: Appropriate smoke test coverage for all 8 tools.

Each smoke test validates a key field from the JSON response, providing quick verification that all tools are functioning. The minimal input approach is correct for smoke testing.


113-136: Good discovery test coverage.

The tests verify all 8 tools are registered and that resources and prompts are properly exposed, ensuring the MCP server is fully configured.


153-186: Appropriate quick platform tests.

The OpenAI test validates UI presence while the Claude test verifies successful handling. Detailed platform-specific behavior is covered in platform-detection.e2e.test.ts. Based on learnings, this provides basic platform configuration testing.

apps/e2e/demo-e2e-ui/e2e/platform-detection.e2e.test.ts (5)

1-75: Comprehensive OpenAI platform tests.

The OpenAI tests properly validate UI presence across client variants (ChatGPT, OpenAI) and concurrent tool calls. Good use of hasToolUI() assertions. Based on learnings, this provides thorough platform configuration testing.


77-107: Thorough Claude dual-payload validation.

The test properly validates the dual-payload structure expected from Claude, checking both the JSON data block and the markdown-wrapped HTML block. The content type assertions (lines 98, 101) and HTML fence check (line 103) ensure the correct format is returned.


285-321: Good Gemini platform handling.

The tests correctly validate that Gemini clients receive JSON-only responses without UI metadata, matching the expected platform behavior. The hasToolUI() === false assertion is appropriate.


430-462: Valuable concurrent cross-platform data integrity test.

This test validates that data integrity is maintained when multiple platforms make concurrent calls, with each client receiving the correct maxValue based on its index. This is important for verifying session isolation under load.


503-558: Good session isolation testing.

The session isolation tests verify that separate clients maintain independent sessions and that rapid client creation/disconnection is handled correctly. This helps ensure the server properly manages client lifecycle.

apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts (5)

19-77: LGTM!

Basic functionality tests are well-structured with proper type annotations and clear assertions covering finding count, title propagation, and markdown content generation.


79-164: LGTM!

Severity level tests comprehensively cover all severity values (low, medium, high), mixed distributions, and properly count occurrences in the markdown output using regex matching.


240-286: LGTM!

Unicode and special character tests provide good internationalization coverage including Japanese, Russian, Hebrew (RTL), emoji, and markdown formatting characters. The test at line 284 properly verifies title preservation.


409-687: LGTM!

Markdown List Tool tests provide comprehensive coverage including ordered/unordered variants, completion state tracking, edge cases (empty/single/many items), Unicode handling, and proper validation of default values for optional fields.


12-16: Remove the platform configuration suggestion — it doesn't apply to E2E tests for stateless tools.

The learning about platform testing applies to libs/ui/src/**/*.test.ts (UI component tests), not E2E tests. The markdown-report and markdown-list tools are stateless data generators with no platform-dependent behavior. All E2E tests in the codebase consistently use only publicMode: true without platform configurations.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts (1)

288-354: XSS tests still don't verify actual sanitization in the output.

While the tests now parse JSON and verify output structure (findingCount, markdown defined), they still don't verify that XSS payloads are actually escaped or sanitized in the markdown content. Based on learnings, tests should verify HTML escaping for user-provided content.

The comment at lines 289-292 states markdown tools preserve content as-is, but if that's the design decision, these tests should verify that downstream rendering handles sanitization, or the comment should clarify the security boundary.

Add assertions to verify XSS content is escaped or sanitized:

      test('should safely handle script tags in title', async ({ mcp }) => {
        const result = await mcp.tools.call('markdown-report', {
          title: '<script>alert("xss")</script>',
          summary: 'Summary',
          findings: [{ title: 'Finding', description: 'Desc', severity: 'low' }],
        });

        expect(result).toBeSuccessful();
        const json = result.json<{ title: string; markdown: string; findingCount: number }>();
-       // Title is preserved in output (sanitization is at render layer)
-       expect(json.title).toBe('<script>alert("xss")</script>');
-       // Verify output structure is valid
+       // Verify XSS content is escaped in markdown output
+       expect(json.markdown).not.toContain('<script>');
+       expect(json.markdown).toContain('&lt;script&gt;');
        expect(json.findingCount).toBe(1);
-       expect(json.markdown).toBeDefined();
      });

      test('should safely handle script tags in findings', async ({ mcp }) => {
        const result = await mcp.tools.call('markdown-report', {
          title: 'XSS Test Report',
          summary: 'Testing XSS prevention',
          findings: [
            {
              title: '<img src=x onerror=alert(1)>',
              description: '<script>document.cookie</script>',
              severity: 'high',
            },
          ],
        });

        expect(result).toBeSuccessful();
        const json = result.json<{ markdown: string; findingCount: number }>();
-       // Verify tool processed the input correctly
+       // Verify XSS payloads are sanitized in output
+       expect(json.markdown).not.toMatch(/<script|onerror=/i);
+       expect(json.markdown).toContain('&lt;');
        expect(json.findingCount).toBe(1);
-       expect(json.markdown).toBeDefined();
      });

      test('should safely handle javascript protocol in links', async ({ mcp }) => {
        const result = await mcp.tools.call('markdown-report', {
          title: 'Link Injection Test',
          summary: '[Click me](javascript:alert(1))',
          findings: [{ title: 'Finding', description: 'Desc', severity: 'low' }],
        });

        expect(result).toBeSuccessful();
        const json = result.json<{ markdown: string }>();
-       expect(json.markdown).toBeDefined();
+       // Verify javascript: protocol is removed or neutralized
+       expect(json.markdown).not.toContain('javascript:');
      });

      test('should safely handle event handlers in content', async ({ mcp }) => {
        const result = await mcp.tools.call('markdown-report', {
          title: '<div onmouseover="alert(1)">Hover</div>',
          summary: '<img onload="alert(1)" src="valid.jpg">',
          findings: [{ title: 'F', description: 'D', severity: 'low' }],
        });

        expect(result).toBeSuccessful();
        const json = result.json<{ title: string; markdown: string }>();
-       expect(json.title).toBeDefined();
-       expect(json.markdown).toBeDefined();
+       // Verify event handlers are stripped or escaped
+       expect(json.markdown).not.toMatch(/onmouseover|onload/i);
+       expect(json.markdown).toContain('&lt;');
      });

Based on learnings, test HTML escaping for user-provided content to prevent XSS attacks.

🧹 Nitpick comments (1)
libs/ui/src/dependency/template-loader.ts (1)

289-303: Path traversal protection could be more robust.

While using path.sep is an improvement, the most reliable cross-platform approach is to use path.relative() to verify the resolved path doesn't escape the base directory. The past review comment suggests this approach but was marked as addressed.

Consider this more robust implementation:

   // Resolve to absolute path
   const absolutePath = isAbsolute(filePath) ? filePath : resolvePath(basePath, filePath);

   // Prevent path traversal attacks - ensure resolved path stays within base directory
   const normalizedBase = resolvePath(basePath);
-  if (!absolutePath.startsWith(normalizedBase + sep) && absolutePath !== normalizedBase) {
-    throw new Error(`Template path escapes base directory: ${filePath}`);
-  }
+  const relative = require('path').relative(normalizedBase, absolutePath);
+  if (relative.startsWith('..') || require('path').isAbsolute(relative)) {
+    throw new Error(`Template path escapes base directory: ${filePath}`);
+  }

This approach works reliably across platforms by checking if the relative path attempts to go up directories or is absolute.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 466ace6 and b9c4973.

📒 Files selected for processing (2)
  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts (1 hunks)
  • libs/ui/src/dependency/template-loader.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
  • libs/ui/src/dependency/template-loader.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.ts: Achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines)
Test all code paths including errors, constructor validation, and error class instanceof checks

Files:

  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
libs/ui/src/**/*.ts

📄 CodeRabbit inference engine (libs/ui/CLAUDE.md)

libs/ui/src/**/*.ts: Always use escapeHtml() utility for all user-provided content to prevent XSS vulnerabilities
Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Files:

  • libs/ui/src/dependency/template-loader.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/ui/src/dependency/template-loader.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test HTML escaping for user-provided content to prevent XSS attacks

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Maintain 95%+ test coverage across statements, branches, functions, and lines

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.test.ts : Write validation tests covering invalid variant/options, unknown properties, and valid option acceptance

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.test.ts : Achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines)

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.test.ts : Test all code paths including errors, constructor validation, and error class `instanceof` checks

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Always use `escapeHtml()` utility for all user-provided content to prevent XSS vulnerabilities

Applied to files:

  • apps/e2e/demo-e2e-ui/e2e/markdown-tools.e2e.test.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Applied to files:

  • libs/ui/src/dependency/template-loader.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.ts : Avoid non-null assertions (`!`) - use proper error handling and throw specific errors instead

Applied to files:

  • libs/ui/src/dependency/template-loader.ts
🧬 Code graph analysis (1)
libs/ui/src/dependency/template-loader.ts (2)
libs/ui/src/dependency/types.ts (6)
  • UrlFetchResult (755-775)
  • TemplateSource (747-750)
  • TemplateMode (685-685)
  • TemplateFormat (741-741)
  • detectFormatFromPath (928-945)
  • ResolvedTemplate (781-826)
libs/ui/src/bundler/file-cache/hash-calculator.ts (1)
  • sha256 (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
🔇 Additional comments (15)
libs/ui/src/dependency/template-loader.ts (15)

1-26: LGTM!

The imports and module documentation are well-structured. Proper use of Node.js promises API and clear package documentation.


27-49: LGTM!

The URL cache implementation is clean with proper accessor functions for testing. The in-memory Map approach is appropriate for template caching.


51-86: LGTM!

The template source detection logic is clean and well-documented with helpful examples. Good use of discriminated unions for type safety.


88-93: LGTM!

Simple utility function with clear purpose.


106-112: LGTM!

The HTTPS-only validation is a good security practice for template URLs.


114-123: LGTM!

Clean delegation to the format detection utility.


125-145: LGTM!

The options interface is well-documented with sensible defaults.


162-223: LGTM! Error handling properly fixed.

The error handling at lines 196 and 218 now correctly uses type guards (error instanceof Error && error.name === 'AbortError') instead of type assertions, addressing the previous review concern. The ETag-based caching logic is well-implemented with proper timeout handling.


225-248: LGTM!

Clean response processing with proper error handling and cache management. Good use of nullish coalescing for optional headers.


257-269: LGTM!

The options interface is well-documented with appropriate defaults.


315-324: LGTM!

Clean utility function for path resolution.


333-356: LGTM!

Well-documented options interface with sensible defaults for the main resolver.


385-437: LGTM!

The main template resolver is well-structured with clear handling for each source type (inline, file, URL). The use of format detection and override options provides good flexibility. Content hashing for cache validation is a solid approach.


439-478: LGTM!

Smart cache invalidation logic with ETag-based validation and TTL fallback for CDNs without ETag support. The 5-minute TTL provides a good balance between freshness and performance.


480-488: LGTM!

Simple and effective cache invalidation utility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants