-
Notifications
You must be signed in to change notification settings - Fork 0
🚀 Implement Static Route Caching with Cachify Integration #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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.
|
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 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. 📒 Files selected for processing (1)
WalkthroughReplaced 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 Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
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. Comment |
There was a problem hiding this 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
MimeTypeat line 105 before validation at line 113. If the validation fails and throws an error, the type cast is safe. However, ensure thatmimex.isMime()correctly validates all MIME types that theMimeTypetype 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:
- Documenting its purpose in the README
- Providing a template or example in the repository
- 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 thatoptions.pathexists 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
⛔ Files ignored due to path filters (1)
package-lock.jsonis 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
.devignore 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
constassertion and type alias is well-structured. Theas constassertion 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
currenciesdataset 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
pathoption 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
contentTypetoMimetype when checkingmimes.includes(). This is safe for validation purposes, but ensure that theMimetype from mimex is compatible withMimeTypeused elsewhere in the codebase.
1-1: Migration to mimex library is well-integrated.The replacement of local MIME data files with the external
mimexlibrary 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
There was a problem hiding this 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.readdirSyncblocks 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 observabilityThe 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
📒 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 conditionThe 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 handlingGood 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.
There was a problem hiding this 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
📒 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.
There was a problem hiding this 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/(^"|"$)/gwon't correctly matchW/"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
parseFilefunction 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 assumesrouteCache.files.read()will always return a value. If the cache entry was evicted, expired, or corrupted betweeninspect()andread(), 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
cacheTTLoption toStaticRouteOptions:// 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.readdirSynccall 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), usingfs.promises.readdirwould 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
📒 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)
There was a problem hiding this 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) andstatSync(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
📒 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)
There was a problem hiding this 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
📒 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)
There was a problem hiding this 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
HTTPErrorimport 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) andfs.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
📒 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)
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
Static files are loaded into memory only on their first request.
Subsequent requests are served directly from memory for faster responses.
Uses Overwatch to detect file modifications and refresh cached content instantly.
Automatically handles
ETag,Last-Modified, and304 Not Modifiedresponses via HyperCloud.Static content updates are reflected immediately — ideal for static sites and CMS-like workflows.
🧩 Implementation Details
Cachifymiddleware to static route handling.✅ Benefits
🧪 Testing
ETagandLast-Modifiedheaders.Summary by CodeRabbit
New Features
Improvements
Updates
Chores