Skip to content

Conversation

@luxass
Copy link
Member

@luxass luxass commented Jan 6, 2026

🔗 Linked issue

📚 Description

This PR is improving the wildcard api route. It removes the old search endpoint, but makes it part of the wildcard route.

This helps close the #420.

Some tests are failing, but those are fixed in #439. But to avoid reviewing the same code twice, i will merge this #442, before the #439.

Summary by CodeRabbit

  • New Features

    • Query-based text filtering, glob pattern matching, type filters, and sort/order controls for directory listings
    • Directory listings now include richer metadata (sizes, child counts) and consistent HEAD/GET header behavior
  • Refactor

    • Search functionality consolidated into the main listing flow with streaming responses and standardized listing handling
  • Tests

    • Expanded coverage for filtering, sorting, header behavior, and path normalization
  • Chores

    • Enforced directory path format validations in schemas

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

luxass added 3 commits January 6, 2026 16:54
Updated the test suite for the `v1_files` API to use a consistent response assertion style with `expect(response).toMatchResponse(...)`. This change improves readability and maintainability of the tests. Additionally, adjusted the handling of paths and added new tests for query and type filters.
@changeset-bot
Copy link

changeset-bot bot commented Jan 6, 2026

⚠️ No Changeset found

Latest commit: 2b6d01b

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Consolidates directory listing/filtering into the wildcard file route, extends parseUnicodeDirectory to accept basePath, adds DirectoryFilterOptions and applyDirectoryFiltersAndSort, introduces OpenAPI params, enables schema refinements enforcing leading/trailing slashes, removes the separate /search route and updates tests.

Changes

Cohort / File(s) Summary
Core parsing & filtering
apps/api/src/lib/files.ts
parseUnicodeDirectory(html, basePath?) signature added; new DirectoryFilterOptions interface; exported applyDirectoryFiltersAndSort(files, options) implementing query (prefix, case-insensitive), glob pattern, type filter, and sorting (directories first, by name or lastModified, asc/desc).
Wildcard route & metadata
apps/api/src/routes/v1_files/$wildcard.ts
Route refactored: path traversal validation, streaming file responses, directory HTML → JSON via parseUnicodeDirectory, apply filters/sort from query params (query, pattern, type, sort, order), unified header builders (buildDirectoryHeaders/buildFileHeaders), improved HEAD handling and Content-Length/stat headers.
OpenAPI params
apps/api/src/routes/v1_files/openapi-params.ts
New module exporting WILDCARD_PARAM, PATTERN_QUERY_PARAM, QUERY_PARAM, TYPE_QUERY_PARAM, SORT_QUERY_PARAM, ORDER_QUERY_PARAM with descriptions, enums, and examples for listing endpoints.
Router registration
apps/api/src/routes/v1_files/router.ts
Removed registration of the separate search route (registerSearchRoute deleted).
Search endpoint removed
apps/api/src/routes/v1_files/search.ts
File deleted — GET /search route and OpenAPI registration removed.
Wildcard route tests
apps/api/test/routes/v1_files/$wildcard.test.ts
Tests refactored to inline response assertions; added comprehensive tests for query/pattern/type filtering, sorting by name/lastModified (asc/desc), HEAD semantics, Content-Type inference, and normalized relative paths.
Search tests removed
apps/api/test/routes/v1_files/search.test.ts
Deleted — tests for the removed search endpoint removed.
Unit tests for parsing
apps/api/test/unit/files.test.ts
Updated expectations for name/path normalization (leading/trailing slashes) and basePath behavior; added test for leading-slash name normalization.
Schema runtime refinement
packages/schemas/src/fs.ts
Enabled .superRefine on FileEntrySchema enforcing paths start with / and directories end with /, with validation messages.
Schema tests updated
packages/schemas/test/fs.test.ts
Tests updated to expect trailing slashes on directory paths to match the enabled refinements.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Wildcard as Wildcard Route
    participant Unicode as Unicode Public
    participant Parser as parseUnicodeDirectory
    participant Filter as applyDirectoryFiltersAndSort

    Client->>Wildcard: GET /v1/files/{wildcard}?query&pattern&type&sort&order
    Wildcard->>Wildcard: Validate path (reject traversal)
    Wildcard->>Unicode: GET /Public/{wildcard}
    
    alt Unicode returns directory HTML
        Unicode-->>Wildcard: 200 HTML
        Wildcard->>Parser: parseUnicodeDirectory(html, basePath)
        Parser-->>Wildcard: Entry[] (names/paths/lastModified/type)
        Wildcard->>Filter: applyDirectoryFiltersAndSort(entries, options)
        Filter-->>Wildcard: Filtered & Sorted Entry[]
        Wildcard->>Wildcard: Build directory headers (stats, cache)
        Wildcard-->>Client: 200 JSON (entries) + headers
    else Unicode returns file stream
        Unicode-->>Wildcard: 200 stream (file) or 404/5xx
        Wildcard->>Wildcard: Infer Content-Type / disposition, stream response
        Wildcard-->>Client: 200 stream + headers (no forwarded Content-Length for streamed GET)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hopped through indexes, tidy and bright,

Merged paths and patterns into one neat sight,
Queries and globs now nibble with grace,
Trailing slashes find their rightful place,
A happy rabbit pats the sorted space.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: improve wildcard api route' directly summarizes the main change: consolidating search functionality into the wildcard API route by removing the separate search endpoint and enhancing the wildcard route capabilities.

✏️ Tip: You can configure your own custom Pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4edd29 and 2b6d01b.

📒 Files selected for processing (1)
  • apps/api/test/routes/v1_files/$wildcard.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions bot added apps: api Changes related to the API. pkg: schemas Changes related to the FS Bridge package. labels Jan 6, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

🌏 Preview Deployments

Application Status Preview URL
API ⏳ In Progress N/A
Website ⏳ In Progress N/A

Built from commit: 2b6d01bc9d398e49cacb24e8fa3e5f6f569ca211


🤖 This comment will be updated automatically when you push new commits to this PR.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

📋 OpenAPI Schema Analysis

Summary

API Endpoints

Change Type Count Details
🟢 Added 0 New API endpoints
🔴 Removed 1 ⚠️ Breaking
🟡 Modified 0 Endpoints with method changes
📊 Method Changes +0 / -1 Added/Removed HTTP methods

Schema Components

Change Type Count Details
🟢 Added 0 New schema components
🔴 Removed 1 ⚠️ Breaking
🟡 Modified 1 Changed schema definitions

Overall Status

Status Result
⚠️ Breaking Changes Yes - This PR contains breaking changes

Detailed Changes

🔴 Removed Endpoints (1) ⚠️ Breaking
Path Methods Description
/api/v1/files/search GET No description
📋 Schema Components Changes

Removed Schemas (1): ⚠️

  • FileEntry

Modified Schemas (1):

  • FileEntryList

Important

⚠️ Breaking Changes Detected

This PR contains breaking changes that require maintainer approval.
Maintainers: Add the approve-breaking-openapi label to approve these changes.


🤖 This comment is automatically updated when you push new commits.

@luxass luxass marked this pull request as ready for review January 7, 2026 06:17
Copilot AI review requested due to automatic review settings January 7, 2026 06:17
@greptile-apps
Copy link

greptile-apps bot commented Jan 7, 2026

Greptile Summary

This PR consolidates the search functionality into the wildcard API route, simplifying the API surface while adding powerful filtering and sorting capabilities.

Key Changes:

  • Removed the separate /api/v1/files/search endpoint in favor of query parameters on the wildcard route (query, pattern, type, sort, order)
  • Added new directory listing features: prefix search, glob pattern filtering, type filtering (files/directories), and sorting
  • Enhanced metadata headers for directory responses (children count, file count, directory count)
  • Enabled schema validation that enforces trailing slashes on directory paths and leading slashes on all paths
  • Refactored parseUnicodeDirectory to accept a basePath parameter for proper path construction

Architecture Impact:

  • Breaking change: clients using /api/v1/files/search?q=... must migrate to /api/v1/files?query=...
  • Directory entries now include full paths with leading slash and trailing slash for directories (e.g., /15.1.0/ucd/charts/)
  • Streamlined codebase with better separation of concerns via the new openapi-params.ts file

Confidence Score: 4/5

  • This PR is safe to merge with the understanding that some tests may require the dependent PR refactor: modify fs-bridge to work better #439 to pass.
  • Score reflects well-structured refactoring with comprehensive test coverage. The only concerns are: (1) the author notes some tests may fail until PR refactor: modify fs-bridge to work better #439 is merged, and (2) the search endpoint removal is a breaking API change that should be communicated to API consumers.
  • Pay attention to apps/api/src/routes/v1_files/$wildcard.ts - core route handler with most changes

Important Files Changed

Filename Overview
apps/api/src/lib/files.ts Enhanced parseUnicodeDirectory with basePath support and added new applyDirectoryFiltersAndSort function for filtering and sorting directory entries. Clean implementation with proper defaults.
apps/api/src/routes/v1_files/$wildcard.ts Significantly refactored wildcard route to integrate search functionality, add filtering/sorting parameters, and provide additional metadata headers. Properly handles HEAD requests for files but relies on c.json() for directory HEAD behavior.
apps/api/src/routes/v1_files/openapi-params.ts New file extracting OpenAPI parameter definitions. Clean separation of concerns with well-documented API parameters.
packages/schemas/src/fs.ts Enabled previously commented-out superRefine validation that enforces trailing slashes on directory paths and leading slashes on all paths.

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as Wildcard Route
    participant Files as files.ts
    participant Unicode as unicode.org

    Client->>API: GET /api/v1/files/{path}?query=Uni&type=files&sort=name
    API->>API: Validate path (no traversal)
    API->>Unicode: GET /Public/{path}?F=2
    Unicode-->>API: HTML directory listing
    API->>Files: parseUnicodeDirectory(html, basePath)
    Files-->>API: Entry[]
    API->>Files: applyDirectoryFiltersAndSort(entries, options)
    Files-->>API: Filtered & sorted Entry[]
    API->>API: buildDirectoryHeaders(files)
    API-->>Client: JSON response with headers
Loading

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR consolidates the wildcard API route by removing the dedicated /search endpoint and integrating its functionality into the main wildcard route. It enables directory paths to end with trailing slashes and adds comprehensive filtering, sorting, and query capabilities to directory listings.

Key Changes

  • Removed the separate /api/v1/files/search endpoint and integrated query-based prefix search into the wildcard route
  • Enhanced directory listing responses with query parameters for filtering (query, pattern, type) and sorting (sort, order)
  • Updated the schema to enforce path conventions (directory paths end with /, all paths start with /)

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/schemas/src/fs.ts Enabled path validation requiring directories to end with / and all paths to start with /
packages/schemas/test/fs.test.ts Updated tests to reflect new directory path format with trailing slashes
apps/api/src/routes/v1_files/$wildcard.ts Added query/pattern/type/sort/order parameters, integrated directory filtering logic, added helper functions for building headers
apps/api/src/routes/v1_files/openapi-params.ts New file defining OpenAPI parameter schemas for the enhanced wildcard route
apps/api/src/routes/v1_files/router.ts Removed registration of the search route
apps/api/src/routes/v1_files/search.ts Deleted - functionality moved to wildcard route
apps/api/src/lib/files.ts Added applyDirectoryFiltersAndSort function with query/pattern/type/sort/order support; updated parseUnicodeDirectory to accept basePath
apps/api/test/routes/v1_files/search.test.ts Deleted - tests migrated to wildcard test file
apps/api/test/routes/v1_files/$wildcard.test.ts Added extensive tests for new query filter, type filter, and sorting features; updated existing tests to use new matcher helpers
apps/api/test/unit/files.test.ts Updated tests for path handling with trailing slashes for directories

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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

Caution

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

⚠️ Outside diff range comments (1)
apps/api/src/lib/files.ts (1)

6-34: Update JSDoc to document the basePath parameter.

The function signature now includes a basePath parameter, but the JSDoc comment doesn't document it. The existing example and parameter documentation should be updated to reflect this change.

🔎 Proposed JSDoc update
 /**
  * Parses an HTML directory listing from Unicode.org and extracts file/directory entries.
  *
  * This function takes raw HTML content from a Unicode.org directory listing page
  * and converts it into a structured array of Entry objects containing metadata
  * about each file or subdirectory.
  *
  * @param {string} html - The raw HTML content from a Unicode.org directory listing page
+ * @param {string} basePath - Optional base path to prepend to entry paths (defaults to "")
  * @returns {Promise<Entry[]>} A promise that resolves to an array of Entry objects, each containing
  *          type, name, path, and lastModified information for files/directories
  *
  * @example
  * ```typescript
  * const html = await fetch('https://unicode.org/Public?F=2').then(r => r.text());
- * const entries = await parseUnicodeDirectory(html);
+ * const entries = await parseUnicodeDirectory(html, '/15.1.0');
  * console.log(entries); // [{ type: 'directory', name: 'UNIDATA', path: '/UNIDATA', ... }]
  * ```
  */
🤖 Fix all issues with AI agents
In @apps/api/test/routes/v1_files/$wildcard.test.ts:
- Around line 869-905: The test "should sort by lastModified ascending"
currently verifies descending order and uses identical timestamps, so update the
verification loop in this test (the block using results, prev, and the for loop)
to assert ascending order (each subsequent results[i].lastModified should be
greater than or equal to the previous) and change the mock entries passed to
generateAutoIndexHtml to have distinct timestamps (e.g., Date.now() - 2000,
Date.now() - 1000, Date.now()) so the sort can be reliably checked; also update
the inline comment to reflect "Verify sorted by lastModified ascending".
- Around line 907-943: The test "should sort by lastModified descending" uses
identical timestamps (all Date.now()) and a confusing verification loop; change
the mock entries passed to generateAutoIndexHtml so each file has distinct
lastModified values (e.g., now, now-1000, now-2000) so descending order is
testable, and simplify the verification by initializing prev to
results[0].lastModified and iterating from i=1 to results.length-1 asserting
results[i].lastModified <= prev, updating prev each iteration; keep the rest of
the test flow (mockFetch, executeRequest, results checks) unchanged.
🧹 Nitpick comments (6)
apps/api/src/lib/files.ts (2)

36-61: Consider using union types for better type safety.

The type, sort, and order fields are defined as string, which allows any string value. Consider using union types to restrict to valid values and improve type safety:

🔎 Proposed type safety improvements
 export interface DirectoryFilterOptions {
   /**
    * A string to filter file/directory names that start with this query (case-insensitive).
    */
   query?: string;

   /**
    * A glob pattern to filter file/directory names.
    */
   pattern?: string;

   /**
    * Type of entries to include: "all" (default), "files", or "directories".
    */
-  type?: string;
+  type?: "all" | "files" | "directories";

   /**
    * Field to sort by: "name" (default) or "lastModified".
    */
-  sort?: string;
+  sort?: "name" | "lastModified";

   /**
    * Sort order: "asc" (default) or "desc".
    */
-  order?: string;
+  order?: "asc" | "desc";
 }

78-79: Use the existing createLogger utility for consistency with other API routes.

The codebase provides a createLogger utility (in apps/api/src/lib/logger.ts) that wraps console logging with a tag prefix. Other routes like v1_versions already use it. Apply the same pattern here by importing createLogger and replacing the direct console.info calls with the logger instance for consistency across the API codebase.

apps/api/src/routes/v1_files/openapi-params.ts (1)

131-131: Consider removing unnecessary type assertions.

The as string[] type assertions on the enum arrays may be unnecessary. TypeScript should infer the correct readonly tuple type from the array literals.

🔎 Simplified enum definitions
   schema: {
     type: "string",
-    enum: ["all", "files", "directories"] as string[],
+    enum: ["all", "files", "directories"],
     default: "all",
   },

   // ... and similarly for other enums

   schema: {
     type: "string",
-    enum: ["name", "lastModified"] as string[],
+    enum: ["name", "lastModified"],
     default: "name",
   },

   schema: {
     type: "string",
-    enum: ["asc", "desc"] as string[],
+    enum: ["asc", "desc"],
     default: "asc",
   },

Also applies to: 163-163, 189-189

apps/api/src/routes/v1_files/$wildcard.ts (3)

220-247: HEAD route OpenAPI spec marks mutually exclusive headers as required.

The HEAD route response spec marks both file-specific headers (UCD_STAT_SIZE_HEADER) and directory-specific headers (UCD_STAT_CHILDREN_*) as required: true. However, these are mutually exclusive based on the resource type - file responses won't have children headers, and directory responses won't have size headers.

Consider setting these as required: false (matching the GET route) or adding clarifying documentation that these are conditional based on the X-UCD-Stat-Type header value.

🔎 Proposed fix
       [UCD_STAT_SIZE_HEADER]: {
         description: "The size of the file in bytes (only for files)",
         schema: {
           type: "string",
         },
-        required: true,
+        required: false,
       },
       [UCD_STAT_CHILDREN_HEADER]: {
         description: "Number of children (only for directories)",
         schema: {
           type: "string",
         },
-        required: true,
+        required: false,
       },
       [UCD_STAT_CHILDREN_FILES_HEADER]: {
         description: "Number of child files (only for directories)",
         schema: {
           type: "string",
         },
-        required: true,
+        required: false,
       },
       [UCD_STAT_CHILDREN_DIRS_HEADER]: {
         description: "Number of child directories (only for directories)",
         schema: {
           type: "string",
         },
-        required: true,
+        required: false,
       },

327-332: Consider adding a timeout to the upstream fetch.

The fetch to unicode.org doesn't specify a timeout. If the upstream server is slow or unresponsive, this could cause requests to hang indefinitely. Consider using AbortSignal.timeout() for resilience.

🔎 Example with timeout
       const response = await fetch(url, {
         method: "GET",
         headers: {
           "User-Agent": DEFAULT_USER_AGENT,
         },
+        signal: AbortSignal.timeout(30_000), // 30 second timeout
       });

You may also want to catch AbortError and return an appropriate gateway timeout response.


405-416: Inconsistent header construction between HEAD and GET file responses.

The GET file response builds headers inline (lines 405-412), while HEAD uses buildFileHeaders (line 400). This could lead to header drift over time. Consider extracting common header logic or documenting why they differ (GET streams without Content-Length for chunked transfer, HEAD requires buffering).

The current behavior is correct (GET omits Content-Length for streaming), but a brief comment explaining the intentional difference would aid maintainability.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5367375 and 04b1a80.

📒 Files selected for processing (10)
  • apps/api/src/lib/files.ts
  • apps/api/src/routes/v1_files/$wildcard.ts
  • apps/api/src/routes/v1_files/openapi-params.ts
  • apps/api/src/routes/v1_files/router.ts
  • apps/api/src/routes/v1_files/search.ts
  • apps/api/test/routes/v1_files/$wildcard.test.ts
  • apps/api/test/routes/v1_files/search.test.ts
  • apps/api/test/unit/files.test.ts
  • packages/schemas/src/fs.ts
  • packages/schemas/test/fs.test.ts
💤 Files with no reviewable changes (3)
  • apps/api/test/routes/v1_files/search.test.ts
  • apps/api/src/routes/v1_files/router.ts
  • apps/api/src/routes/v1_files/search.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{test,spec}.?(c|m)[jt]s?(x)

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.?(c|m)[jt]s?(x): Import test utilities from #test-utils/* path aliases instead of @ucdjs/test-utils in test files to avoid build step requirements and prevent cyclic dependencies
Use testdir() from vitest-testdirs to create temporary test directories in filesystem tests
Use mockStoreApi() utility from #test-utils/mock-store for MSW-based mocking of the UCD API in test files

Files:

  • packages/schemas/test/fs.test.ts
  • apps/api/test/unit/files.test.ts
  • apps/api/test/routes/v1_files/$wildcard.test.ts
apps/api/test/**/*.{test,spec}.?(c|m)[jt]s?(x)

📄 CodeRabbit inference engine (AGENTS.md)

Place API app tests in apps/api/test/routes/** for endpoint tests and apps/api/test/unit/** for unit tests with mocks

Files:

  • apps/api/test/unit/files.test.ts
  • apps/api/test/routes/v1_files/$wildcard.test.ts
apps/api/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Regenerate OpenAPI spec by running 'pnpm build:openapi' after changing API routes or Zod schemas

Files:

  • apps/api/src/lib/files.ts
  • apps/api/src/routes/v1_files/openapi-params.ts
  • apps/api/src/routes/v1_files/$wildcard.ts
apps/api/src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

apps/api/src/**/*.ts: Build API (apps/api) using Hono web framework with OpenAPI plugin (@hono/zod-openapi) and organize routes by version and type
Use Zod schemas for defining API route schemas and request/response types in the API application

Files:

  • apps/api/src/lib/files.ts
  • apps/api/src/routes/v1_files/openapi-params.ts
  • apps/api/src/routes/v1_files/$wildcard.ts
apps/api/src/routes/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Document API endpoints with OpenAPI annotations using @hono/zod-openapi decorators

Files:

  • apps/api/src/routes/v1_files/openapi-params.ts
  • apps/api/src/routes/v1_files/$wildcard.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: luxass
Repo: ucdjs/ucd PR: 6
File: packages/cli/src/cmd/codegen/fields.ts:68-82
Timestamp: 2025-05-04T11:52:22.858Z
Learning: User prefers to address the issue with Node.js API compatibility (`Dirent.parentPath` not being part of Node's API and the `recursive: true` option for `readdir` being only available in Node 21+) in a separate PR rather than the current one.
📚 Learning: 2025-06-09T05:10:32.105Z
Learnt from: luxass
Repo: ucdjs/ucd PR: 45
File: packages/ucd-store/src/download.ts:24-24
Timestamp: 2025-06-09T05:10:32.105Z
Learning: In the ucd-store package refactor, picomatch was moved from direct usage in download.ts to internal usage within the createPathFilter function in filter.ts. The pattern format is still picomatch-compatible, so JSDoc comments referencing picomatch pattern format remain correct.

Applied to files:

  • packages/schemas/test/fs.test.ts
  • apps/api/test/unit/files.test.ts
  • apps/api/test/routes/v1_files/$wildcard.test.ts
  • apps/api/src/lib/files.ts
  • apps/api/src/routes/v1_files/$wildcard.ts
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to **/*.{test,spec}.?(c|m)[jt]s?(x) : Import test utilities from #test-utils/* path aliases instead of ucdjs/test-utils in test files to avoid build step requirements and prevent cyclic dependencies

Applied to files:

  • packages/schemas/test/fs.test.ts
  • apps/api/test/unit/files.test.ts
  • apps/api/test/routes/v1_files/$wildcard.test.ts
📚 Learning: 2025-05-04T11:52:22.858Z
Learnt from: luxass
Repo: ucdjs/ucd PR: 6
File: packages/cli/src/cmd/codegen/fields.ts:68-82
Timestamp: 2025-05-04T11:52:22.858Z
Learning: User prefers to address the issue with Node.js API compatibility (`Dirent.parentPath` not being part of Node's API and the `recursive: true` option for `readdir` being only available in Node 21+) in a separate PR rather than the current one.

Applied to files:

  • packages/schemas/test/fs.test.ts
  • apps/api/test/unit/files.test.ts
  • apps/api/test/routes/v1_files/$wildcard.test.ts
  • apps/api/src/lib/files.ts
  • packages/schemas/src/fs.ts
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to apps/api/test/**/*.{test,spec}.?(c|m)[jt]s?(x) : Place API app tests in apps/api/test/routes/** for endpoint tests and apps/api/test/unit/** for unit tests with mocks

Applied to files:

  • apps/api/test/routes/v1_files/$wildcard.test.ts
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to **/*.{test,spec}.?(c|m)[jt]s?(x) : Use mockStoreApi() utility from #test-utils/mock-store for MSW-based mocking of the UCD API in test files

Applied to files:

  • apps/api/test/routes/v1_files/$wildcard.test.ts
📚 Learning: 2025-07-01T04:10:40.989Z
Learnt from: luxass
Repo: ucdjs/ucd PR: 89
File: apps/api/src/routes/v1_files.ts:42-43
Timestamp: 2025-07-01T04:10:40.989Z
Learning: The `traverse` method from the `apache-autoindex-parse` library always returns an array, even when HTTP requests fail or encounter network errors. Network failures are handled internally by the library rather than throwing exceptions, so catch blocks are primarily for parsing errors or unexpected JavaScript runtime errors.

Applied to files:

  • apps/api/src/lib/files.ts
📚 Learning: 2025-07-09T15:05:57.763Z
Learnt from: luxass
Repo: ucdjs/ucd PR: 97
File: packages/utils/src/fs-bridge/node.ts:40-50
Timestamp: 2025-07-09T15:05:57.763Z
Learning: The `parentPath` property on `fs.Dirent` was added in Node.js v20.12, contrary to previous assumptions about it not being part of the Node.js API.

Applied to files:

  • packages/schemas/src/fs.ts
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to apps/api/src/routes/**/*.ts : Document API endpoints with OpenAPI annotations using hono/zod-openapi decorators

Applied to files:

  • apps/api/src/routes/v1_files/openapi-params.ts
  • apps/api/src/routes/v1_files/$wildcard.ts
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to apps/api/src/**/*.ts : Build API (apps/api) using Hono web framework with OpenAPI plugin (hono/zod-openapi) and organize routes by version and type

Applied to files:

  • apps/api/src/routes/v1_files/openapi-params.ts
  • apps/api/src/routes/v1_files/$wildcard.ts
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to apps/api/src/**/*.{ts,tsx} : Regenerate OpenAPI spec by running 'pnpm build:openapi' after changing API routes or Zod schemas

Applied to files:

  • apps/api/src/routes/v1_files/$wildcard.ts
🧬 Code graph analysis (4)
apps/api/test/unit/files.test.ts (1)
apps/api/src/lib/files.ts (1)
  • parseUnicodeDirectory (24-34)
apps/api/test/routes/v1_files/$wildcard.test.ts (5)
apps/api/test/helpers/request.ts (3)
  • text (36-38)
  • executeRequest (15-40)
  • json (33-35)
packages/test-utils/src/msw.ts (1)
  • mockFetch (16-19)
apps/api/wrangler-types.d.ts (1)
  • env (10237-10237)
packages/env/src/constants.ts (2)
  • UCD_STAT_SIZE_HEADER (17-17)
  • UCD_STAT_TYPE_HEADER (16-16)
packages/env/src/index.ts (2)
  • UCD_STAT_SIZE_HEADER (5-5)
  • UCD_STAT_TYPE_HEADER (6-6)
apps/api/src/lib/files.ts (1)
packages/ucd-store-v2/src/core/context.ts (1)
  • basePath (78-80)
apps/api/src/routes/v1_files/$wildcard.ts (7)
apps/api/src/openapi.ts (1)
  • OPENAPI_TAGS (7-11)
apps/api/src/routes/v1_files/openapi-params.ts (6)
  • WILDCARD_PARAM (3-41)
  • PATTERN_QUERY_PARAM (43-88)
  • QUERY_PARAM (90-116)
  • TYPE_QUERY_PARAM (118-148)
  • SORT_QUERY_PARAM (150-175)
  • ORDER_QUERY_PARAM (177-202)
packages/env/src/constants.ts (5)
  • UCD_STAT_SIZE_HEADER (17-17)
  • UCD_STAT_CHILDREN_HEADER (18-18)
  • UCD_STAT_CHILDREN_FILES_HEADER (19-19)
  • UCD_STAT_CHILDREN_DIRS_HEADER (20-20)
  • UCD_STAT_TYPE_HEADER (16-16)
packages/env/src/index.ts (6)
  • UCD_STAT_SIZE_HEADER (5-5)
  • UCD_STAT_CHILDREN_HEADER (4-4)
  • UCD_STAT_CHILDREN_FILES_HEADER (3-3)
  • UCD_STAT_CHILDREN_DIRS_HEADER (2-2)
  • UCD_STAT_TYPE_HEADER (6-6)
  • DEFAULT_USER_AGENT (9-9)
apps/api/src/constants.ts (2)
  • MAX_AGE_ONE_WEEK_SECONDS (13-13)
  • HTML_EXTENSIONS (6-10)
apps/api/src/routes/v1_files/utils.ts (2)
  • isInvalidPath (4-22)
  • determineContentTypeFromExtension (32-38)
apps/api/src/lib/files.ts (2)
  • parseUnicodeDirectory (24-34)
  • applyDirectoryFiltersAndSort (70-125)
⏰ 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: Agent
  • GitHub Check: Greptile Review
🔇 Additional comments (14)
packages/schemas/test/fs.test.ts (1)

11-11: LGTM! Test updates align with schema validation changes.

The test paths have been correctly updated to include trailing slashes for directory entries, matching the new schema validation requirements enforced in packages/schemas/src/fs.ts.

Also applies to: 99-99

apps/api/test/unit/files.test.ts (2)

8-8: LGTM! Test updates correctly reflect the new parsing behavior.

The changes accurately validate that:

  • Directory paths preserve trailing slashes (e.g., /15.1.0/, /folder/)
  • Trailing slashes are trimmed from entry names only
  • The test name update correctly reflects this distinction

Also applies to: 18-18, 29-29, 37-38


40-49: Good addition! Test coverage for leading slash trimming.

This new test case properly validates that leading slashes are trimmed from directory names while the path preserves both leading and trailing slashes.

apps/api/src/lib/files.ts (2)

104-122: LGTM! Sorting implementation is well-designed.

The sorting logic correctly:

  • Prioritizes directories before files (like Windows File Explorer)
  • Supports name and lastModified sorting
  • Uses localeCompare with numeric awareness for natural sorting (e.g., "2.0.0" < "10.0.0")
  • Handles null lastModified values safely with nullish coalescing
  • Uses toSorted() for immutable sorting

70-125: Integration is properly implemented with correct parameter passing and edge case handling.

The route handler at apps/api/src/routes/v1_files/$wildcard.ts correctly passes query parameters to applyDirectoryFiltersAndSort with proper validation:

  • Pattern validation using isValidGlobPattern prevents malicious glob patterns (limits on length, segments, braces, stars, questions)
  • Query parameters are extracted via c.req.query() and passed directly to the function
  • Optional parameters default safely within the function (type="all", sort="name", order="asc")
  • OpenAPI schemas in openapi-params.ts ensure request validation

No actionable concerns.

apps/api/src/routes/v1_files/openapi-params.ts (4)

3-41: LGTM! Well-documented wildcard parameter.

The parameter definition is comprehensive with clear descriptions, format options table, and relevant examples. The pattern .* is appropriately permissive for a wildcard path parameter.


43-88: LGTM! Clear glob pattern documentation.

The parameter definition provides excellent documentation with:

  • Supported glob syntax table
  • Practical examples
  • Case-insensitive matching note

90-116: LGTM! Simple and clear query parameter.

The prefix-based search parameter is well-documented with appropriate examples.


118-148: LGTM! Consistent parameter definitions with good defaults.

All three filtering/sorting parameters (type, sort, order) follow a consistent structure with:

  • Clear enum values
  • Sensible defaults
  • Helpful examples
  • Comprehensive descriptions

Also applies to: 150-175, 177-202

packages/schemas/src/fs.ts (1)

62-78: Remove verification concern—the implementation is correct for Zod 4.

The code properly uses ctx.addIssue() with code: "custom" within superRefine(), which is the correct Zod 4 approach for custom validation. The unified error parameter introduced in Zod 4 applies to schema-level error handling (e.g., z.string({ error: "..." })), not to custom refinements. The validation logic is sound:

  • Directories must end with /
  • All paths must start with /

Minor note: Using z.ZodIssueCode.custom instead of the string "custom" is slightly more robust, but both work correctly in Zod 4.3.5.

apps/api/test/routes/v1_files/$wildcard.test.ts (1)

1-10: LGTM!

The test file is well-structured with comprehensive coverage of the wildcard route functionality. Imports correctly use the #test-utils/* path alias as per coding guidelines. The test organization follows the expected pattern for API endpoint tests.

apps/api/src/routes/v1_files/$wildcard.ts (3)

270-298: LGTM!

The header-building helper functions are well-structured, separating concerns for directory and file responses. They properly handle the different metadata headers for each resource type.


357-387: LGTM!

Directory listing handling is well-implemented with proper HTML parsing, glob pattern validation, and filtering/sorting. The applyDirectoryFiltersAndSort function gracefully handles invalid enum values with sensible defaults.


32-186: LGTM!

The OpenAPI route definition is comprehensive with detailed parameter documentation, examples, and proper response schemas. The endpoint description clearly explains the directory listing features and query parameters available for filtering and sorting.

As per coding guidelines, remember to run pnpm build:openapi after these changes to regenerate the OpenAPI spec.

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

🤖 Fix all issues with AI agents
In @apps/api/src/routes/v1_files/$wildcard.ts:
- Around line 405-416: The GET file handler streams without buffering and
therefore omits UCD_STAT_SIZE_HEADER, causing inconsistency with HEAD which uses
buildFileHeaders to include size; fix by reading the upstream
response.headers.get("Content-Length") and, if present, set
headers[UCD_STAT_SIZE_HEADER] to that value before calling
c.newResponse(response.body, 200, headers), otherwise document in the OpenAPI
spec that UCD_STAT_SIZE_HEADER is only guaranteed for HEAD requests (or choose
to buffer via the same logic as buildFileHeaders if an accurate size is required
for GET).
🧹 Nitpick comments (2)
apps/api/src/routes/v1_files/$wildcard.ts (2)

260-264: Clarify Content-Length header description.

The description "Byte length when applicable" is ambiguous, but the header is marked as required: true and is always present in the implementation. Consider updating the description to be more specific, such as "The size of the content in bytes" to match the actual behavior.


397-403: HEAD request buffers entire file for accurate size calculation.

The implementation buffers the complete file (line 398) to determine accurate Content-Length. While this ensures correctness, it may impact performance and memory usage for large files. Consider whether the upstream Content-Length header could be trusted directly, or document this as expected behavior for accuracy.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 04b1a80 and a4edd29.

📒 Files selected for processing (1)
  • apps/api/src/routes/v1_files/$wildcard.ts
🧰 Additional context used
📓 Path-based instructions (3)
apps/api/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Regenerate OpenAPI spec by running 'pnpm build:openapi' after changing API routes or Zod schemas

Files:

  • apps/api/src/routes/v1_files/$wildcard.ts
apps/api/src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

apps/api/src/**/*.ts: Build API (apps/api) using Hono web framework with OpenAPI plugin (@hono/zod-openapi) and organize routes by version and type
Use Zod schemas for defining API route schemas and request/response types in the API application

Files:

  • apps/api/src/routes/v1_files/$wildcard.ts
apps/api/src/routes/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Document API endpoints with OpenAPI annotations using @hono/zod-openapi decorators

Files:

  • apps/api/src/routes/v1_files/$wildcard.ts
🧠 Learnings (5)
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to apps/api/src/routes/**/*.ts : Document API endpoints with OpenAPI annotations using hono/zod-openapi decorators

Applied to files:

  • apps/api/src/routes/v1_files/$wildcard.ts
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to apps/api/src/**/*.ts : Build API (apps/api) using Hono web framework with OpenAPI plugin (hono/zod-openapi) and organize routes by version and type

Applied to files:

  • apps/api/src/routes/v1_files/$wildcard.ts
📚 Learning: 2025-06-09T05:10:32.105Z
Learnt from: luxass
Repo: ucdjs/ucd PR: 45
File: packages/ucd-store/src/download.ts:24-24
Timestamp: 2025-06-09T05:10:32.105Z
Learning: In the ucd-store package refactor, picomatch was moved from direct usage in download.ts to internal usage within the createPathFilter function in filter.ts. The pattern format is still picomatch-compatible, so JSDoc comments referencing picomatch pattern format remain correct.

Applied to files:

  • apps/api/src/routes/v1_files/$wildcard.ts
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to apps/api/src/**/*.{ts,tsx} : Regenerate OpenAPI spec by running 'pnpm build:openapi' after changing API routes or Zod schemas

Applied to files:

  • apps/api/src/routes/v1_files/$wildcard.ts
📚 Learning: 2025-12-27T11:08:54.630Z
Learnt from: CR
Repo: ucdjs/ucd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T11:08:54.630Z
Learning: Applies to apps/api/src/**/*.ts : Use Zod schemas for defining API route schemas and request/response types in the API application

Applied to files:

  • apps/api/src/routes/v1_files/$wildcard.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). (5)
  • GitHub Check: typecheck
  • GitHub Check: lint
  • GitHub Check: test (windows-latest)
  • GitHub Check: test (ubuntu-latest)
  • GitHub Check: test (macos-latest)
🔇 Additional comments (6)
apps/api/src/routes/v1_files/$wildcard.ts (6)

81-108: Headers correctly marked as optional.

The response headers are now properly configured with required: false for mutually exclusive headers (size for files, children counts for directories), which accurately reflects that these headers are conditionally present based on resource type.


270-278: LGTM: Clean helper for directory headers.

The helper function correctly constructs directory response headers with accurate child entry counts.


280-298: LGTM: Clean helper for file headers.

The helper function correctly constructs file response headers including content type, size metadata, and optional Content-Disposition.


311-318: LGTM: Proper path traversal protection.

Early path validation using isInvalidPath provides good security against path traversal attacks.


357-387: LGTM: Robust directory listing implementation.

The directory handling properly parses HTML, validates glob patterns with security limits, applies filtering/sorting, and constructs appropriate response headers.


32-43: Reminder: Regenerate OpenAPI specification.

After these schema and parameter changes, remember to regenerate the OpenAPI specification by running pnpm build:openapi.

As per coding guidelines: "Regenerate OpenAPI spec by running 'pnpm build:openapi' after changing API routes or Zod schemas"

@luxass luxass merged commit c593695 into main Jan 7, 2026
14 of 17 checks passed
@luxass luxass deleted the feat/improve-wildcard-api branch January 7, 2026 06:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

apps: api Changes related to the API. pkg: schemas Changes related to the FS Bridge package.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants