Skip to content

Conversation

@ahmadnasriya
Copy link
Member

@ahmadnasriya ahmadnasriya commented Nov 6, 2025

Summary

This PR integrates Cachify (@nasriya/cachify) into HyperCloud (@nasriya/hypercloud) to handle caching for static routes.
The new system allows static content to update instantly on file changes without restarting the server.


✨ Key Features

  • Lazy-loading static files
    Static files are loaded into memory only on their first request.
  • In-memory caching
    Subsequent requests are served directly from memory for faster responses.
  • Real-time cache invalidation
    Uses Overwatch to detect file modifications and refresh cached content instantly.
  • HTTP cache optimization
    Automatically handles ETag, Last-Modified, and 304 Not Modified responses via HyperCloud.
  • No server restarts needed
    Static content updates are reflected immediately — ideal for static sites and CMS-like workflows.

🧩 Implementation Details

  • Added Cachify middleware to static route handling.
  • Integrated Overwatch to monitor static directories for changes.
  • Updated HyperCloud’s cache manager to leverage in-memory caching.
  • Enhanced HTTP response logic to respect browser caching headers.

✅ Benefits

  • Zero-downtime static content updates
  • Reduced filesystem I/O and faster response times
  • Lower bandwidth usage via smart cache headers

🧪 Testing

  • Verified automatic refresh after file modifications.
  • Tested 304 responses with consistent ETag and Last-Modified headers.
  • Confirmed memory cache stability under multiple concurrent requests.

Summary by CodeRabbit

  • New Features

    • Static file serving now uses a filesystem-backed cache with live watching, conditional (304) responses, and exposes route path and handler getters.
  • Improvements

    • Unified MIME handling across serving and uploads via an external MIME utility.
    • Centralized currency dataset exported for consistent validation.
  • Updates

    • Added a development script and several new dependencies; minor dependency bumps.
  • Chores

    • Removed bundled static MIME/extension datasets; added an ignore rule for a development directory.

Integrates Cachify into HyperCloud to handle static file caching.
Static routes are now lazily loaded into memory on first request,
served from cache thereafter, and automatically refreshed on file changes
via Overwatch — eliminating the need to restart the server for content updates.
@ahmadnasriya ahmadnasriya self-assigned this Nov 6, 2025
@ahmadnasriya ahmadnasriya added enhancement New feature or request feature request Request a feature performance Issues or enhancements related to optimizing performance. labels Nov 6, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 6, 2025

Warning

Rate limit exceeded

@ahmadnasriya has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 27 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 9c95ccd and 89dca00.

📒 Files selected for processing (1)
  • src/services/routes/assets/staticRoute.ts (2 hunks)

Walkthrough

Replaced local MIME/extension and currency datasets with external libraries, added a cachify route cache client, implemented a filesystem-backed, watchable StaticRoute serving pipeline with ETag/Last-Modified handling, updated several handlers to use mimex, adjusted typings/exports, updated dependencies and added .dev to .gitignore.

Changes

Cohort / File(s) Summary
Config & deps
\.gitignore, package.json
Added .dev to .gitignore; added dev script; introduced deps @nasriya/atomix, @nasriya/cachify, @nasriya/mimex, @nasriya/overwatch; bumped tldts; updated @types/node and ts-jest.
Currencies module
src/data/currencies.ts
New exported currencies constant, Currency type alias, and default export (replaces prior inline/internal currency list).
Removed static data
src/data/extensions.json, src/data/mimes.json
Deleted static JSON datasets for extensions and MIME types; code now uses @nasriya/mimex.
Docs / types
src/docs/docs.ts
Re-exported Currency; simplified MimeType to `Mime
Route cache client
src/services/cache/routeCache.ts
New module creating and default-exporting a cachify client and setting file TTL policy to keep.
Asset response
src/services/handler/assets/response.ts
Replaced local MIME/extension loading with @nasriya/mimex lookups/fallbacks; updated content-type selection and typings.
Static routing & watcher
src/services/routes/assets/router.ts, src/services/routes/assets/staticRoute.ts
Added runtime path validation (atomix); rewrote StaticRoute to preload/watch filesystem (overwatch), build/serve from routeCache, enforce traversal guards, and implement ETag/If-Modified-Since responses; added get path() and get handler().
Upload MIME validation
src/services/uploads/assets/handler.ts
Replaced static MIME array with mimex.isMime() validation.
Helpers / validation
src/utils/helpers.ts
Removed internal currency cache; currency validation now uses imported currencies/Currency type via docs re-export.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Router
    participant StaticRoute
    participant RouteCache as routeCache
    participant Overwatch as overwatch
    participant Mimex

    rect rgb(245,250,255)
    Note over StaticRoute,RouteCache: Init — preload + watch
    StaticRoute->>RouteCache: scan root -> preload entries
    StaticRoute->>Overwatch: watchFolder(root)
    Overwatch-->>StaticRoute: file add/remove events
    StaticRoute->>RouteCache: update cache entry
    end

    rect rgb(245,255,245)
    Note over Client,StaticRoute: Request handling
    Client->>Router: GET /asset
    Router->>StaticRoute: dispatch request
    StaticRoute->>RouteCache: lookup path
    RouteCache-->>StaticRoute: cached metadata & content
    StaticRoute->>Mimex: getMimes(ext) / isMime()
    Mimex-->>StaticRoute: mime(s)
    alt Conditional match (ETag/Last-Modified)
        StaticRoute-->>Client: 304 Not Modified
    else
        StaticRoute-->>Client: 200 + content + headers (Cachify-Status, ETag, Last-Modified)
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review on src/services/routes/assets/staticRoute.ts for initialization, watcher lifecycle, cache consistency, path-traversal guards, conditional-response correctness.
  • Verify src/services/cache/routeCache.ts TTL policy and client configuration.
  • Confirm mimex usage and fallbacks in src/services/handler/assets/response.ts and src/services/uploads/assets/handler.ts.
  • Check type/export changes in src/docs/docs.ts and src/utils/helpers.ts for downstream compatibility.

Poem

🐰 i hopped through bytes at break of dawn,

caches warmed up, old files moved on,
mimes now fetched from wiser streams,
watchers tend the folder dreams,
the rabbit hums — cache sings on.

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 clearly describes the main change: implementing static route caching with Cachify integration, which is the primary objective of this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

Copy link

@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

🧹 Nitpick comments (3)
src/services/uploads/assets/handler.ts (1)

109-115: Verify type safety with MIME validation.

The MIME type is cast to MimeType at line 105 before validation at line 113. If the validation fails and throws an error, the type cast is safe. However, ensure that mimex.isMime() correctly validates all MIME types that the MimeType type accepts, or there could be a type safety gap.

Consider validating before casting:

-                    const details = {
-                        fieldName: matchFile[1],
-                        fileName: matchFile[2],
-                        mime: matchFile[3] as MimeType,
-                        tempPath: this.#_request.server.uploads.directory
-                    }
-
-                    if (!details.fieldName || !details.fileName || !details.mime) {
-                        throw new Error(`The header is invalid`)
-                    }
-
-                    if (!mimex.isMime(details.mime)) {
+                    const mime = matchFile[3];
+                    
+                    if (!matchFile[1] || !matchFile[2] || !mime) {
+                        throw new Error(`The header is invalid`)
+                    }
+                    
+                    if (!mimex.isMime(mime)) {
                         throw new Error(`The request mime type is not supported: ${details.mime}`)
                     }
+                    
+                    const details = {
+                        fieldName: matchFile[1],
+                        fileName: matchFile[2],
+                        mime: mime as MimeType,
+                        tempPath: this.#_request.server.uploads.directory
+                    }
package.json (1)

49-49: Clarify the purpose of the dev script.

The new dev script "node ./.dev" points to a directory that's now in .gitignore. This suggests it contains development/testing code. Consider:

  1. Documenting its purpose in the README
  2. Providing a template or example in the repository
  3. Whether this script should be part of the published package
src/services/routes/assets/router.ts (1)

38-44: Potential redundancy with optional chaining.

At line 40, options.path?.startsWith('/') uses optional chaining, but the check at line 39 already verifies that options.path exists and is a valid string. The optional chaining is safe but redundant.

Consider simplifying:

             const routePath = (() => {
                 if (options && hasOwnProp(options, 'path') && atomix.valueIs.validString(options.path)) {
-                    return options.path?.startsWith('/') ? options.path : `/${options.path}`;
+                    return options.path.startsWith('/') ? options.path : `/${options.path}`;
                 } else {
                     return '/';
                 }
             })()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d36b42a and 5f30e5e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • .gitignore (1 hunks)
  • package.json (2 hunks)
  • src/data/currencies.ts (2 hunks)
  • src/data/extensions.json (0 hunks)
  • src/data/mimes.json (0 hunks)
  • src/docs/docs.ts (3 hunks)
  • src/services/cache/routeCache.ts (1 hunks)
  • src/services/handler/assets/response.ts (3 hunks)
  • src/services/routes/assets/router.ts (2 hunks)
  • src/services/routes/assets/staticRoute.ts (2 hunks)
  • src/services/uploads/assets/handler.ts (2 hunks)
  • src/utils/helpers.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • src/data/extensions.json
  • src/data/mimes.json
🧰 Additional context used
🧬 Code graph analysis (3)
src/data/currencies.ts (1)
src/docs/docs.ts (1)
  • Currency (12-12)
src/services/routes/assets/staticRoute.ts (3)
src/services/handler/assets/request.ts (1)
  • path (49-49)
src/services/routes/assets/route.ts (1)
  • path (56-56)
src/docs/docs.ts (3)
  • MimeType (45-45)
  • HyperCloudRequestHandler (25-25)
  • StaticRouteOptions (643-652)
src/utils/helpers.ts (3)
src/services/handler/assets/user.ts (1)
  • currency (175-175)
src/data/currencies.ts (1)
  • Currency (164-164)
src/docs/docs.ts (1)
  • Currency (12-12)
🔇 Additional comments (7)
.gitignore (1)

5-5: LGTM!

The .dev ignore entry aligns with the new development script added in package.json.

src/data/currencies.ts (1)

1-166: LGTM!

The refactor to centralize currency data with a const assertion and type alias is well-structured. The as const assertion enables precise type inference, and exporting both the data and type provides good flexibility for consumers.

src/utils/helpers.ts (1)

4-5: LGTM!

The refactor to use the external currencies dataset is clean and eliminates code duplication. The type cast at line 120 is appropriate for validation purposes.

Also applies to: 117-124

src/services/routes/assets/router.ts (1)

1-1: Good defensive programming with atomix utilities.

The integration of atomix for runtime validation of the path option enhances safety by ensuring only valid strings are used. The fallback to '/' prevents unexpected behavior.

Also applies to: 11-11

src/services/handler/assets/response.ts (2)

771-790: Type casts used for MIME validation.

The code casts contentType to Mime type when checking mimes.includes(). This is safe for validation purposes, but ensure that the Mime type from mimex is compatible with MimeType used elsewhere in the codebase.


1-1: Migration to mimex library is well-integrated.

The replacement of local MIME data files with the external mimex library reduces maintenance burden and improves consistency. The integration appears clean overall.

src/services/cache/routeCache.ts (1)

1-4: Verify cachify version stability and default behavior with package maintainers.

The package @nasriya/cachify is a beta release (^0.0.9-beta) with no published documentation on npm. Default behavior cannot be verified through available sources. The original review concerns are valid—before using this in production route caching:

  • Confirm the beta version is intentional and acceptable for your stability requirements
  • Access the package source directly (GitHub repo, if available) to review default configuration
  • Document why the singleton pattern is appropriate for your use case
  • Monitor for breaking changes across beta updates

Copy link

@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: 5

🧹 Nitpick comments (2)
src/services/routes/assets/staticRoute.ts (2)

80-91: Replace blocking I/O with async operations during initialization

fs.readdirSync blocks the event loop, which can cause noticeable delays during server startup when scanning large directory trees. This impacts overall server responsiveness.

Apply this diff to use async operations:

-path: (dir: string, setPromises: Promise<void>[]) => {
-    const content = fs.readdirSync(dir, { withFileTypes: true });
+path: async (dir: string, setPromises: Promise<void>[]) => {
+    const content = await fs.promises.readdir(dir, { withFileTypes: true });
     for (const item of content) {
         const contentPath = path.join(dir, item.name);
         if (item.isDirectory()) {
-            this.#_utils.cache.path(contentPath, setPromises);
+            await this.#_utils.cache.path(contentPath, setPromises);
         } else {
             const createPromise = this.#_utils.cache.createRecord(contentPath)
             setPromises.push(createPromise);
         }
     }
 },

Also update the caller at line 97:

 if (stats.isDirectory()) {
-    this.#_utils.cache.path(this.#_root, promises);
+    await this.#_utils.cache.path(this.#_root, promises);
 } else {

186-189: Consider structured logging for better observability

The current error logging uses console.error, which works but limits observability in production. Consider using a structured logger that can integrate with monitoring systems.

Example refactor (if a logger is available):

import logger from '../../../utils/logger'; // hypothetical

try {
    // ... handler logic ...
} catch (error) {
    logger.error('Static route handler error', {
        path: request.path,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined
    });
    response.pages.serverError({ error: error as Error });
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f30e5e and 48a580b.

📒 Files selected for processing (1)
  • src/services/routes/assets/staticRoute.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.
🔇 Additional comments (2)
src/services/routes/assets/staticRoute.ts (2)

193-204: Constructor initialization properly addresses race condition

The handler is now set synchronously (line 202) before the async cache initialization begins, and the initialization promise is properly handled with error logging. This correctly addresses the race condition noted in the previous review.

However, be aware that there's still a brief window where requests may arrive before the cache is fully populated. In this scenario, the handler will call next() for files not yet cached, which is acceptable behavior for a lazy-loading cache.


72-78: Dotfiles filtering is consistent between cache creation and request handling

Good implementation: dotfiles are filtered during cache creation (lines 74-76) and also checked during request handling (lines 135-138). This ensures the policy is enforced at both stages.

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48a580b and 583298b.

📒 Files selected for processing (1)
  • src/services/routes/assets/staticRoute.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.
📚 Learning: 2025-11-06T14:15:52.879Z
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.

Applied to files:

  • src/services/routes/assets/staticRoute.ts
🧬 Code graph analysis (1)
src/services/routes/assets/staticRoute.ts (4)
src/services/handler/assets/request.ts (1)
  • path (49-49)
src/services/routes/assets/route.ts (1)
  • path (56-56)
src/docs/docs.ts (3)
  • MimeType (45-45)
  • HyperCloudRequestHandler (25-25)
  • StaticRouteOptions (643-652)
src/services/routes/assets/router.ts (1)
  • options (209-212)
⏰ 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). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
src/services/routes/assets/staticRoute.ts (1)

201-212: Constructor initialization order is correct.

The handler is now set synchronously (line 210) before the async route initialization (line 211), ensuring consumers get a valid handler immediately. The fire-and-forget pattern with .catch(console.error) properly handles errors without creating unhandled rejections.

Copy link

@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: 0

♻️ Duplicate comments (5)
src/services/routes/assets/staticRoute.ts (5)

148-148: Use 403 Forbidden for denied dotfile access instead of 401 Unauthorized.

HTTP 401 indicates authentication is required, while 403 indicates the request is understood but access is refused. For dotfile access denial based on configuration policy (not authentication), 403 is semantically correct.

Apply this diff:

-if (this.#_configs.dotfiles === 'deny') { return response.pages.unauthorized() }
+if (this.#_configs.dotfiles === 'deny') { return response.pages.forbidden() }

170-170: Handle weak ETags in conditional request logic.

The current ETag normalization only strips quotes but doesn't handle weak ETags prefixed with W/. Per RFC 7232, weak ETags should be supported for 304 responses, but the regex /(^"|"$)/g won't correctly match W/"value".

Apply this diff to handle weak ETags:

-    const normalizedIfNoneMatch = ifNoneMatch?.replace(/(^"|"$)/g, '');
+    const normalizedIfNoneMatch = ifNoneMatch?.replace(/^W\//, '').replace(/(^"|"$)/g, '');

122-130: 🔴 CRITICAL: Path traversal vulnerability remains unaddressed.

The parseFile function still constructs file paths from user-controlled input without validating that the resolved path stays within #_root. An attacker can use ../ sequences to read arbitrary files outside the intended directory.

Example attack:

  • Root: /var/www/static
  • Request: GET /static/../../etc/passwd
  • Resolved: /etc/passwd

Apply this diff to add path validation:

 parseFile: (_reqPath: string[]) => {
     // Remove the initial path (the virtual path) and keep the root path
     const reqPath = _reqPath.slice(this.#_configs.path.length, _reqPath.length).join(path.sep);
     const filePath = path.join(this.#_root, reqPath);
+    
+    // Prevent path traversal attacks
+    const resolvedPath = path.resolve(filePath);
+    const resolvedRoot = path.resolve(this.#_root);
+    if (!resolvedPath.startsWith(resolvedRoot + path.sep) && resolvedPath !== resolvedRoot) {
+        throw new Error('Path traversal attempt detected');
+    }
+    
     const fileName = path.basename(filePath);
     const mimeType = this.#_utils.getFileMime(filePath) as MimeType;

-    return { path: filePath, name: fileName, mimeType }
+    return { path: resolvedPath, name: fileName, mimeType }
 }

189-197: Remove unsafe non-null assertion on cache read.

The non-null assertion (!) at line 193 assumes routeCache.files.read() will always return a value. If the cache entry was evicted, expired, or corrupted between inspect() and read(), this will throw an uncaught error and crash the request.

Apply this diff to handle the null case:

 const readResponse = (await routeCache.files.read({
     key: fileRecord.key,
     scope: CACHE_SCOPE,
     caseSensitive: this.#_configs.caseSensitive
-}))!;
+}));
+
+if (!readResponse) {
+    // Cache entry was evicted or expired between inspect and read
+    return next();
+}

 response.setHeader('Cachify-Status', readResponse.status)
 response.send(readResponse.content, reqFile.mimeType);

186-186: 🔴 CRITICAL: Wrap ETag value in quotes to comply with RFC 7232.

ETags must be quoted strings per RFC 7232 (opaque-tag = DQUOTE *etagc DQUOTE). The current implementation sets unquoted hash values, resulting in malformed headers that break HTTP caching and cause non-compliance with the specification.

Apply this diff:

-response.setHeader('etag', eTag);
+response.setHeader('etag', `"${eTag}"`);

Also update the ETag comparison at line 177 to account for quoted format:

-    const isEtagMatch = normalizedIfNoneMatch === eTag;
+    const isEtagMatch = normalizedIfNoneMatch === eTag || normalizedIfNoneMatch === `"${eTag}"`;

Or alternatively, store and generate ETags in quoted format from the beginning in the cache module.

🧹 Nitpick comments (2)
src/services/routes/assets/staticRoute.ts (2)

88-88: Consider making cache TTL configurable for flexibility.

The hardcoded 1-hour TTL is reasonable, but different use cases may benefit from different cache durations. For frequently updated content, a shorter TTL may be preferable; for stable assets, longer caching could reduce overhead.

Consider adding a cacheTTL option to StaticRouteOptions:

// In StaticRouteOptions interface (docs.ts)
cacheTTL?: number; // milliseconds

// In constructor
this.#_configs.cacheTTL = options.cacheTTL ?? 3_600_000; // default 1 hour

// In createRecord
ttl: this.#_configs.cacheTTL

92-102: Consider using async filesystem operations for large directories.

The recursive fs.readdirSync call executes synchronously and could block the event loop during initialization if the static directory tree is large. While this is init-time (not per-request), using fs.promises.readdir would be more scalable.

Apply this diff to use async operations:

-            path: (dir: string, setPromises: Promise<void>[]) => {
-                const content = fs.readdirSync(dir, { withFileTypes: true });
+            path: async (dir: string, setPromises: Promise<void>[]) => {
+                const content = await fs.promises.readdir(dir, { withFileTypes: true });
                 for (const item of content) {
                     const contentPath = path.join(dir, item.name);
                     if (item.isDirectory()) {
-                        this.#_utils.cache.path(contentPath, setPromises);
+                        await this.#_utils.cache.path(contentPath, setPromises);
                     } else {
                         const createPromise = this.#_utils.cache.createRecord(contentPath)
                         setPromises.push(createPromise);
                     }
                 }
             },

Also update line 108 to await this.#_utils.cache.path(this.#_root, promises);.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 583298b and 87cfe20.

📒 Files selected for processing (2)
  • src/services/cache/routeCache.ts (1 hunks)
  • src/services/routes/assets/staticRoute.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/services/cache/routeCache.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.
📚 Learning: 2025-11-06T14:15:52.879Z
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.

Applied to files:

  • src/services/routes/assets/staticRoute.ts
🧬 Code graph analysis (1)
src/services/routes/assets/staticRoute.ts (2)
src/services/handler/assets/request.ts (1)
  • path (49-49)
src/docs/docs.ts (3)
  • MimeType (45-45)
  • HyperCloudRequestHandler (25-25)
  • StaticRouteOptions (643-652)

Copy link

@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: 0

🧹 Nitpick comments (1)
src/services/routes/assets/staticRoute.ts (1)

92-105: Consider using async filesystem operations to avoid blocking the event loop.

The readdirSync (line 93) and statSync (line 105) calls block the event loop during cache initialization. For large static directories with thousands of files, this can cause noticeable delays and prevent other operations from executing.

Consider refactoring to use async versions:

-cache: {
+cache: {
     createRecord: async (filePath: string) => {
         const fileName = path.basename(filePath);
         if (fileName.startsWith('.') && this.#_configs.dotfiles !== 'allow') {
             return;
         }

         return routeCache.files.set(filePath, {
             scope: CACHE_SCOPE,
             ttl: 1_000 * 60 * 60 // 1 hour
         });
     },
-    path: (dir: string, setPromises: Promise<void>[]) => {
-        const content = fs.readdirSync(dir, { withFileTypes: true });
+    path: async (dir: string, setPromises: Promise<void>[]) => {
+        const content = await fs.promises.readdir(dir, { withFileTypes: true });
         for (const item of content) {
             const contentPath = path.join(dir, item.name);
             if (item.isDirectory()) {
-                this.#_utils.cache.path(contentPath, setPromises);
+                await this.#_utils.cache.path(contentPath, setPromises);
             } else {
                 const createPromise = this.#_utils.cache.createRecord(contentPath)
                 setPromises.push(createPromise);
             }
         }
     },
     route: async () => {
-        const stats = fs.statSync(this.#_root);
+        const stats = await fs.promises.stat(this.#_root);
         const promises: Promise<void>[] = [];

         if (stats.isDirectory()) {
-            this.#_utils.cache.path(this.#_root, promises);
+            await this.#_utils.cache.path(this.#_root, promises);
         } else {
             const createPromise = this.#_utils.cache.createRecord(this.#_root);
             promises.push(createPromise);
         }

         await Promise.all(promises);
     }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87cfe20 and e9a1f65.

📒 Files selected for processing (1)
  • src/services/routes/assets/staticRoute.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.
📚 Learning: 2025-11-06T14:15:52.879Z
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.

Applied to files:

  • src/services/routes/assets/staticRoute.ts
🧬 Code graph analysis (1)
src/services/routes/assets/staticRoute.ts (3)
src/services/handler/assets/request.ts (1)
  • path (49-49)
src/services/routes/assets/route.ts (1)
  • path (56-56)
src/docs/docs.ts (3)
  • MimeType (45-45)
  • HyperCloudRequestHandler (25-25)
  • StaticRouteOptions (643-652)

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9a1f65 and 729eea7.

📒 Files selected for processing (1)
  • src/services/routes/assets/staticRoute.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.
📚 Learning: 2025-11-06T14:15:52.879Z
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.

Applied to files:

  • src/services/routes/assets/staticRoute.ts
🧬 Code graph analysis (1)
src/services/routes/assets/staticRoute.ts (3)
src/services/handler/assets/request.ts (1)
  • path (49-49)
src/docs/docs.ts (3)
  • MimeType (45-45)
  • HyperCloudRequestHandler (25-25)
  • StaticRouteOptions (643-652)
src/services/routes/assets/router.ts (1)
  • options (209-212)

Copy link

@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: 0

🧹 Nitpick comments (2)
src/services/routes/assets/staticRoute.ts (2)

9-9: Remove unused HTTPError import.

The HTTPError import is not referenced anywhere in this file.

Apply this diff to remove the unused import:

-import HTTPError from "../../../utils/errors/HTTPError";

92-115: Consider using async filesystem operations to avoid blocking the event loop.

The cache initialization uses synchronous fs.readdirSync() (line 93) and fs.statSync() (line 105), which can block the event loop when populating large directory trees. While this runs in a fire-and-forget background task, it may still cause latency spikes during startup or when Overwatch detects new files.

Consider refactoring to use async operations:

-            path: (dir: string, setPromises: Promise<void>[]) => {
-                const content = fs.readdirSync(dir, { withFileTypes: true });
+            path: async (dir: string, setPromises: Promise<void>[]) => {
+                const content = await fs.promises.readdir(dir, { withFileTypes: true });
                 for (const item of content) {
                     const contentPath = path.join(dir, item.name);
                     if (item.isDirectory()) {
-                        this.#_utils.cache.path(contentPath, setPromises);
+                        await this.#_utils.cache.path(contentPath, setPromises);
                     } else {
                         const createPromise = this.#_utils.cache.createRecord(contentPath)
                         setPromises.push(createPromise);
                     }
                 }
             },
             route: async () => {
-                const stats = fs.statSync(this.#_root);
+                const stats = await fs.promises.stat(this.#_root);
                 const promises: Promise<void>[] = [];

                 if (stats.isDirectory()) {
-                    this.#_utils.cache.path(this.#_root, promises);
+                    await this.#_utils.cache.path(this.#_root, promises);
                 } else {
                     const createPromise = this.#_utils.cache.createRecord(this.#_root);
                     promises.push(createPromise);
                 }

                 await Promise.all(promises);
             }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 729eea7 and 9c95ccd.

📒 Files selected for processing (1)
  • src/services/routes/assets/staticRoute.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: src/services/routes/assets/staticRoute.ts:160-219
Timestamp: 2025-11-06T16:00:48.902Z
Learning: In the HyperCloud repository (nasriyasoftware/HyperCloud), Cachify's TTL policy is set to 'keep' for static route caching. With the 'keep' policy, when a cached file expires after TTL (1 hour), Cachify retains the metadata but clears the content from memory. On subsequent requests, Cachify automatically repopulates the content from disk and returns a cache status of 'miss'. This means expired records are not evicted from cache, ensuring zero-downtime availability without manual rehydration logic.
📚 Learning: 2025-11-06T14:15:52.879Z
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: package.json:82-85
Timestamp: 2025-11-06T14:15:52.879Z
Learning: In the HyperCloud repository (nasriya/hypercloud), the maintainer ahmadnasriya uses beta versions of nasriya/cachify and nasriya/mimex for integration testing before releasing stable versions. This is an intentional development workflow.

Applied to files:

  • src/services/routes/assets/staticRoute.ts
📚 Learning: 2025-11-06T16:00:48.902Z
Learnt from: ahmadnasriya
Repo: nasriyasoftware/HyperCloud PR: 17
File: src/services/routes/assets/staticRoute.ts:160-219
Timestamp: 2025-11-06T16:00:48.902Z
Learning: In the HyperCloud repository (nasriyasoftware/HyperCloud), Cachify's TTL policy is set to 'keep' for static route caching. With the 'keep' policy, when a cached file expires after TTL (1 hour), Cachify retains the metadata but clears the content from memory. On subsequent requests, Cachify automatically repopulates the content from disk and returns a cache status of 'miss'. This means expired records are not evicted from cache, ensuring zero-downtime availability without manual rehydration logic.

Applied to files:

  • src/services/routes/assets/staticRoute.ts
🧬 Code graph analysis (1)
src/services/routes/assets/staticRoute.ts (3)
src/services/handler/assets/request.ts (1)
  • path (49-49)
src/services/routes/assets/route.ts (1)
  • path (56-56)
src/docs/docs.ts (3)
  • MimeType (45-45)
  • HyperCloudRequestHandler (25-25)
  • StaticRouteOptions (643-652)

@ahmadnasriya ahmadnasriya merged commit 71f38a9 into main Nov 6, 2025
6 checks passed
@ahmadnasriya ahmadnasriya deleted the feature/cachify-static-routes branch November 6, 2025 16:28
ahmadnasriya added a commit that referenced this pull request Nov 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request feature request Request a feature performance Issues or enhancements related to optimizing performance.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants