refactor: migrate to domain-driven architecture separating HTTP and WebSocket concerns#26
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/websocket/routing/metadata-scanner.ts (1)
181-191:⚠️ Potential issue | 🟠 MajorObject keys inside arrays are not sorted — diverges from
MessageRouterand breaks deterministic matching.
sortObjectKeyshere short-circuits onArray.isArray(value), so arrays are embedded as-is and objects within them keep their original key order. Compare withMessageRouter.sortValue(lines 179–187 ofmessage-router.ts), which recursively sorts through arrays.Consequence:
getMethodNameForEventwill fail to match patterns thatMessageRouter.hasHandler/routehappily match. For example, pattern{ cmd: 'batch', items: [{ b: 1, a: 2 }] }vs event{ cmd: 'batch', items: [{ a: 2, b: 1 }] }matches viaMessageRouterbut not viaMetadataScanner. This is contrary to the docstring ongetHandlerKey(claims sorted "recursively") and to the existing test atmessage-router.spec.tslines 172–201.🐛 Proposed fix — recurse into arrays
private sortObjectKeys(obj: Record<string, unknown>): Record<string, unknown> { const sorted: Record<string, unknown> = {}; for (const key of Object.keys(obj).sort()) { - const value = obj[key]; - sorted[key] = - value !== null && typeof value === 'object' && !Array.isArray(value) - ? this.sortObjectKeys(value as Record<string, unknown>) - : value; + sorted[key] = this.sortValue(obj[key]); } return sorted; } + + private sortValue(value: unknown): unknown { + if (value === null || typeof value !== 'object') { + return value; + } + if (Array.isArray(value)) { + return value.map((item) => this.sortValue(item)); + } + return this.sortObjectKeys(value as Record<string, unknown>); + }Also add a parity test in
metadata-scanner.spec.tsmirroring the array-of-objects case frommessage-router.spec.ts.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/routing/metadata-scanner.ts` around lines 181 - 191, The sortObjectKeys implementation currently short-circuits on arrays so objects inside arrays keep original key order; update sortObjectKeys to recurse into arrays by mapping each element and, when an element is a non-null object, call sortObjectKeys on it (mirror MessageRouter.sortValue behavior), leaving primitives/other types unchanged; update getMethodNameForEvent usage remains the same but will now produce deterministically sorted keys for array-contained objects; add a parity unit test in metadata-scanner.spec.ts echoing the array-of-objects case from message-router.spec.ts to ensure matching behavior.src/websocket/adapter/uws.adapter.ts (1)
211-221:⚠️ Potential issue | 🔴 CriticalCritical: lifecycle hooks are now invoked with the gateway name string, not the instance.
The
gatewaysMap was flipped toMap<object, string>(key = instance, value = name), butMap.prototype.forEachinvokes its callback with(value, key, map). So the parameter namedgatewayhere is actually the name string, meaningcallConnectionHook(gateway, extWs)is passed"ChatGateway"instead of the instance — the gateway'shandleConnectionwill never fire, and the catch branch'sgateway.constructor?.nameevaluates to"String".The same defect exists at lines 268–278 for the disconnect hook.
findHandlerForEvent(line 688) correctly uses.keys(), which is why routing still works and this may be masked in the current tests.🐛 Proposed fix — iterate keys (gateway instances) explicitly
- this.gateways.forEach((gateway) => { - try { - this.lifecycleHooksManager.callConnectionHook(gateway, extWs); - } catch (error) { - this.logger.error( - `Connection hook error for ${gateway.constructor?.name}: ${this.formatError(error)}` - ); - } - }); + for (const gateway of this.gateways.keys()) { + try { + this.lifecycleHooksManager.callConnectionHook(gateway, extWs); + } catch (error) { + this.logger.error( + `Connection hook error for ${gateway.constructor?.name}: ${this.formatError(error)}` + ); + } + }Apply the equivalent change at lines 270–278 for
callDisconnectHook.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/adapter/uws.adapter.ts` around lines 211 - 221, The gateways Map was flipped to Map<object, string>, so the forEach callback currently receives the name string instead of the gateway instance; update the connection and disconnect hook loops to iterate the Map's keys (gateway instances) and pass those instances to lifecycleHooksManager.callConnectionHook and lifecycleHooksManager.callDisconnectHook, and use the instance when computing the logger message (gateway.constructor?.name) so it reports the real class name; apply the same keys()-based iteration change for the disconnect hook block that mirrors the connection hook and keep the try/catch per gateway as before.src/http/body/multipart-handler.ts (1)
244-268:⚠️ Potential issue | 🟠 Major
field.file.truncatedcaptures the flag before the stream is consumed — it will almost always be stale.
busboyonly setsstream.truncated = truewhen the per-file byte limit is hit during stream consumption, which can occur after thefileevent is emitted. By the timeexecuteHandlerruns (line 251),stream.truncatedis still its initialundefined/false. Assigning it as a primitive on line 258 therefore yields a snapshot that is effectively alwaysfalse, even for files that ultimately get truncated. Consumers who rely onfield.file.truncatedwill silently miss truncations.The spec already demonstrates the correct pattern: checking
field.file.stream.truncateddirectly after consuming the stream (see line 474). The fix is to exposetruncatedas a getter that delegates to the live stream property:Suggested fix
- try { - await this.executeHandler(handler, { - name, - encoding: info.encoding, - mimeType: info.mimeType, - file: { - filename: info.filename, - stream, - truncated: stream.truncated, - }, - }); + try { + await this.executeHandler(handler, { + name, + encoding: info.encoding, + mimeType: info.mimeType, + file: { + filename: info.filename, + stream, + get truncated() { + return stream.truncated === true; + }, + } as MultipartField['file'], + });And update the interface to clarify it is a live flag:
file?: { filename: string; stream: Readable; - truncated?: boolean; + /** Live flag — only meaningful after consuming the file stream. */ + readonly truncated?: boolean; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/body/multipart-handler.ts` around lines 244 - 268, In handleFile, the code captures stream.truncated as a primitive and passes it into executeHandler (field.file.truncated) too early, so it will be stale; change the file object passed to executeHandler to expose truncated as a getter that returns stream.truncated (i.e., a live boolean delegating to the stream) and update the corresponding interface/type used by executeHandler/MultipartHandler to document truncated as a live flag (boolean getter) rather than a copied value; keep the rest of handleFile behavior (including the resume in finally) intact so consumers reading field.file.truncated after stream consumption see the correct truncation state.
🧹 Nitpick comments (19)
src/websocket/routing/metadata-scanner.ts (1)
104-107: Extract stable-key serialization into a single shared helper.
MessageRouterandMetadataScannerboth carry their ownsortObjectKeys(and the router additionally hassortValue). The precomputedmessageKeyhere is only safely interchangeable with the router's key if both implementations stay byte-for-byte identical — which they currently don't. A singlestableStringifyutility (ideally undersrc/shared, consistent with the PR's DDD goal) consumed by both would prevent future drift and make the pre-computedmessageKeyusable anywhere the router's key is expected.Also applies to: 155-172
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/routing/metadata-scanner.ts` around lines 104 - 107, Extract the deterministic serialization logic into a single shared helper named stableStringify (e.g., under src/shared) and replace the local sortObjectKeys/sortValue implementations in MetadataScanner and MessageRouter: implement stableStringify to produce a byte-for-byte stable JSON string by recursively sorting object keys and normalizing values consistently, then use stableStringify when computing messageKey in MetadataScanner and wherever MessageRouter builds its key so both components share identical behavior and you can safely remove the duplicated sortObjectKeys/sortValue functions.src/websocket/routing/message-router.spec.ts (1)
203-287: Solid expansion of error-path and object-pattern coverage.Splitting sync/async/non-Error throws clarifies intent, and the new
hasHandlercases for object patterns (including key-order insensitivity and negative mismatches) match the behavior documented ingetHandlerKey.One parity gap worth noting elsewhere:
MessageRouterhas test coverage for patterns with objects inside arrays (L172–201), butMetadataScannerdoes not — and itssortObjectKeysimplementation diverges there (see the comment onmetadata-scanner.ts). Consider mirroring that test inmetadata-scanner.spec.tsonce the scanner sort is fixed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/routing/message-router.spec.ts` around lines 203 - 287, The metadata scanner's sortObjectKeys implementation doesn't handle objects nested inside arrays; update the MetadataScanner.sortObjectKeys method to recursively sort keys for all plain objects encountered, including objects within arrays (preserve arrays order but normalize any object elements), and ensure it treats key order insensitively the same way getHandlerKey does. After fixing sortObjectKeys, add tests in metadata-scanner.spec.ts mirroring the MessageRouter object-in-array cases (create test cases that assert equivalent normalized keys for object elements in arrays and that differing values still produce different keys) to ensure parity with MessageRouter coverage.src/shared/interfaces/cors-options.interface.ts (2)
26-37: Minor: clarify whethermethodsaccepts comma-separated strings.The type accepts
string | string[], but the docstring doesn't specify whether a single-string form is expected to be a single method ('GET') or a comma-separated list ('GET,POST') as emitted in theAccess-Control-Allow-Methodsheader. Worth pinning down to avoid handler ambiguity. Same applies toallowedHeadersandexposedHeadersbelow.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/interfaces/cors-options.interface.ts` around lines 26 - 37, Clarify the docs for the cors options by stating whether the string form for methods, allowedHeaders, and exposedHeaders expects a single token (e.g., "GET") or a comma-separated list (e.g., "GET,POST") and how it will be interpreted when building the Access-Control-Allow-* headers; update the JSDoc for the methods?: string | string[] field and the corresponding allowedHeaders and exposedHeaders declarations to explicitly say that a single string may be either a single method/name or a comma-separated list and describe whether the implementation will split comma-separated strings into arrays (or treat the string verbatim) so callers know which form to use.
18-24: Consider documenting thecredentials: true+ wildcard origin incompatibility.When
credentials: true, per the CORS spec browsers reject responses whereAccess-Control-Allow-Originis*. Since this interface explicitly notes thatorigin: trueandorigin: '*'are equivalent and unrestricted, a short doc hint here (or enforcement in the handler) would save users from a common misconfiguration. Not blocking.📝 Proposed doc addition
/** * Allow credentials (cookies, authorization headers, TLS client certificates) + * + * Note: Per the CORS spec, `credentials: true` is incompatible with a wildcard + * origin (`'*'` or `true`). When credentials are enabled, the handler must echo + * a specific origin back to the client. * `@default` false */ credentials?: boolean;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/interfaces/cors-options.interface.ts` around lines 18 - 24, The CorsOptions interface should warn that credentials: true is incompatible with a wildcard/unrestricted origin; update the doc comments for origin and/or credentials in the CorsOptions (the origin property and credentials?: boolean) to state that when credentials is true browsers reject Access-Control-Allow-Origin: '*' (or an unrestricted origin equivalent such as origin: true), and recommend using an explicit origin string or a dynamic function that echoes the request origin; alternatively, add a runtime check in the CORS handler to throw/log when credentials is true and origin is '*' or origin is configured as unrestricted to prevent misconfiguration.src/shared/index.ts (1)
1-2: LGTM — double-check for name collisions as the surface grows.
export *from two barrels is fine today but can silently cause ambiguity errors if both sides later export an identically named symbol. Consider switching to explicit named re-exports (or namespaced re-exports) if the shared surface expands. Optional.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/index.ts` around lines 1 - 2, The current barrel uses wildcard re-exports via "export * from './di';" and "export * from './interfaces';", which can create ambiguous symbol collisions as the surface grows; update the barrel to use explicit named re-exports (list the specific symbols you want to expose from di and interfaces) or use namespaced re-exports (e.g., export * as di from './di' and export * as interfaces from './interfaces') so consumers cannot silently get conflicting identifiers and future additions won't cause ambiguity.src/websocket/adapter/uws.adapter.ts (1)
110-111: RedundantgatewaySetnow thatgatewaysis keyed by instance.With
gateways: Map<object, string>, membership checks (gatewaySet.has(gateway)at line 353, reset at 471) are equivalent tothis.gateways.has(gateway)/ the map's existing lifecycle. Dropping the parallelWeakSetremoves a source of drift between the two collections.Also applies to: 471-471
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/adapter/uws.adapter.ts` around lines 110 - 111, The WeakSet gatewaySet is redundant because gateways (Map<object,string>) already keys gateway instances; remove the gatewaySet declaration and all uses (e.g., gatewaySet.has(gateway), gatewaySet.add(...), gatewaySet.delete(...), and any reset logic) and replace them with the Map's lifecycle operations: use this.gateways.has(gateway) for membership checks, this.gateways.set(gateway, name) when registering, this.gateways.delete(gateway) when unregistering, and clear/reset via this.gateways.clear() or appropriate map ops; update any functions referencing gatewaySet accordingly so the Map remains the single source of truth.src/websocket/middleware/pipes/pipes.integration.spec.ts (1)
60-68: Fresh-copy pattern is a good improvement, butPIPES_METADATAandPARAM_ARGS_METADATAon the class itself are still mutated globally.The cloned
existingPipesmap prevents shared-map mutation, but the subsequentReflect.defineMetadata(PIPES_METADATA:params, existingPipes, gatewayClass, methodName)(line 68),Reflect.defineMetadata(PIPES_METADATA, classPipes, gatewayClass)(line 73), andReflect.defineMetadata(PARAM_ARGS_METADATA, newParams, gatewayClass.prototype, methodName)(line 56) still write onto the class/prototype. Since many tests redeclare theirTestGatewaylocally insideit(...)this is currently safe, but any test that reuses a class across invocations (e.g., the class-level pipes block at lines 231–257 which callsexecuteHandlertwice on the same class) will accumulate/override metadata in order-dependent ways.Consider either (a) instantiating a fresh class per call via a factory, or (b) capturing and restoring prior metadata in a
try/finally, to make test isolation fully robust as the suite grows.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/middleware/pipes/pipes.integration.spec.ts` around lines 60 - 68, The test mutates metadata on the gateway class/prototype (PIPES_METADATA, PIPES_METADATA:params, PARAM_ARGS_METADATA) and must restore prior state to avoid cross-test pollution; capture current metadata values for keys `${PIPES_METADATA}:params` (on gatewayClass+methodName), `PIPES_METADATA` (on gatewayClass), and `PARAM_ARGS_METADATA` (on gatewayClass.prototype+methodName) before changing them, then perform the existing modifications to existingPipes, classPipes, and newParams, and finally in a try/finally restore the captured metadata (using Reflect.defineMetadata or Reflect.deleteMetadata when value was undefined) so executeHandler/TestGateway runs don’t leave global mutations; use the same symbols (existingPipes, existingPipesMap, newParams, gatewayClass, methodName) to locate the code to wrap.src/shared/interfaces/platform-options.interface.ts (1)
1-2: Change toimport typeto clarify type-only usage and prevent bundler circular dependency issues.
platform-options.interface.tsimports from bothhttp/interfacesandwebsocket/interfacesbarrels, which in turn import back intoshared/interfaces. While all usages here are type-only (interface composition), bundlers can struggle with cycles even for types. Usingimport typemakes the intent explicit and allows bundlers to safely elide these imports:🔧 Suggested fix
-import { UwsAdapterOptions } from '../../websocket/interfaces'; -import { HttpOptions } from '../../http/interfaces'; +import type { UwsAdapterOptions } from '../../websocket/interfaces'; +import type { HttpOptions } from '../../http/interfaces';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/interfaces/platform-options.interface.ts` around lines 1 - 2, Change the two imports in platform-options.interface.ts to type-only imports to avoid bundler circular dependency issues: replace the current imports that bring in UwsAdapterOptions and HttpOptions with type-only imports (i.e., import type for UwsAdapterOptions and HttpOptions) so the compiler treats them as erased types and bundlers can safely elide the module edges; locate the import statements referencing UwsAdapterOptions and HttpOptions and update them to use import type.src/http/core/request.spec.ts (1)
985-993: Timer restoration timing.
jest.useRealTimers()at line 992 runs synchronously right afterjest.runAllTimers(), but the asyncfor await ... ofconsumer and thedone()callback complete on subsequent microtask ticks. That should be fine in practice sincesetImmediatewas already drained byrunAllTimers(), but if flakiness ever appears around this test, consider awaiting the async iteration explicitly (e.g., storing the IIFE's promise and awaiting it) before restoring real timers, so the test's lifecycle is deterministic regardless of future refactors.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/core/request.spec.ts` around lines 985 - 993, The test restores real timers immediately after jest.runAllTimers(), which can race with the async for-await consumer and done(); capture and await the async iteration's promise (the IIFE that performs the for await ... of) instead of letting it run implicitly, then call jest.useRealTimers() only after that promise resolves so the test lifecycle is deterministic; reference the existing async IIFE/for-await block and the jest.runAllTimers()/jest.useRealTimers() calls when locating where to store and await the promise.src/http/body/body-parser.spec.ts (1)
129-150: Test restructure looks correct.Calling
parser.buffer()before sending chunks now exercises the post-limit rejection path explicitly, and the secondbuffer()call at line 149 verifies the rejected state is memoized (not re-triggering another buffer collection). This aligns with the immediate-rejection behavior also tested at line 165.Minor: since
content-length: 100with a50limit would already trigger the synchronous rejection path (per the test at line 165), the subsequentonDataCallbackinvocations at lines 137/140 are mostly exercising the "bytes still counted after rejection" invariant. Consider a brief comment or assertion thatmockUwsRes.closewas called exactly once to make the intent explicit.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/body/body-parser.spec.ts` around lines 129 - 150, Add an explicit assertion that the uWS response was closed exactly once to make the test intent clear: after triggering the chunks and before the final expectations, assert mockUwsRes.close was called once (e.g., expect(mockUwsRes.close).toHaveBeenCalledTimes(1)); also add a one-line comment near the onDataCallback calls stating these are only to exercise the "bytes still counted after rejection" invariant.src/http/core/index.ts (1)
1-3: LGTM — explicit named re-exports.Explicit re-exports keep the public surface of
coreintentional. Ifrequest.ts/response.ts/context.tsexpose companion types consumers may need (e.g., option/config interfaces), consider addingexport type { ... }lines alongside these — otherwise consumers would still need to import those types from the deeper module paths.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/core/index.ts` around lines 1 - 3, The public core index currently exports only the runtime symbols UwsRequest, UwsResponse, and HttpExecutionContext; if the underlying modules (request, response, context) also expose companion types or interfaces that consumers may need (for example options/config types), add explicit type re-exports alongside the existing exports — e.g., add export type { SomeRequestOptions } from './request' or export type { ResponseConfig } from './response' and export type { HttpContextOptions } from './context' to make those types part of the core public surface while keeping imports shallow and intentional.package.json (1)
29-30: Keyword additions LGTM (minor nit).
httpandconcurrencyimprove discoverability.uwestjsduplicates the package name — npm already indexes packages by theirname, so this keyword is effectively a no-op for search. Not a blocker.Also applies to: 40-40
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` around lines 29 - 30, Remove the redundant "uwestjs" keyword from the package.json keywords array (it duplicates the package name and is a no-op for npm search); keep the other keywords such as "http" and "concurrency" intact and ensure keywords remains a valid array in package.json.src/http/core/response.spec.ts (1)
1160-1162: Two-ticksetImmediatewait is fine; minor readability nit.Replacing timer-based waits with two chained
await new Promise(setImmediate)calls is a reasonable way to let the pipe error propagate through microtasks + one macrotask without being sensitive to timer values. Minor:new Promise(setImmediate)implicitly passes the Promiseresolveas thesetImmediatecallback — works, butnew Promise((r) => setImmediate(r))is slightly more explicit and avoids readers wondering whether extra args are being forwarded. Purely a nit.Also applies to: 1190-1192
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/core/response.spec.ts` around lines 1160 - 1162, Replace the implicit callback usage new Promise(setImmediate) with the more explicit form new Promise((resolve) => setImmediate(resolve)) in the test to improve readability; update both occurrences (the two chained awaits around the comment and the similar block around lines ~1190-1192) so readers clearly see the promise resolve being passed to setImmediate.src/http/body/multipart-handler.spec.ts (1)
232-268: Redundantjest.useRealTimers()call with the newafterEach.With the
afterEachhook added at lines 233-235 that always restores real timers for this suite, the explicitjest.useRealTimers()at line 267 (inside theshould handle async handlers sequentiallytest) is now redundant and can be removed for consistency with the new cleanup strategy.♻️ Proposed cleanup
// Verify pause/resume were called for backpressure expect(mockUwsRes.pause).toHaveBeenCalled(); expect(mockUwsRes.resume).toHaveBeenCalled(); - - jest.useRealTimers(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/body/multipart-handler.spec.ts` around lines 232 - 268, Remove the redundant jest.useRealTimers() call inside the "should handle async handlers sequentially" test since an afterEach hook already restores real timers for the describe('backpressure handling') suite; specifically, delete the trailing jest.useRealTimers() at the end of the it('should handle async handlers sequentially', ...) test so cleanup is centralized, leaving the afterEach and the rest of the test (setupMultipartRequest, req.multipart handler, sendMultipartData, expectations including mockUwsRes.pause/resume checks) intact.src/http/core/request.ts (1)
1020-1026: Defensive re-check looks safe but likely unreachable.Between the initial
doneReadingDatacheck at line 984 and the listener registrations at lines 1017-1019, there are no awaits or yields — the executor body runs synchronously, anddoneReadingDatais only flipped insidehandleIncomingChunk(driven by uWSonData). In single-threaded JS, noonDatacallback can fire in the middle of this synchronous block, so the re-check should not be reachable in practice.That said, the re-check is harmless (Promise resolution is idempotent and
cleanup()is safe to call before the listeners fire) so this is fine to keep as a belt-and-suspenders guard. Consider a brief code comment clarifying the intended scenario, or removing it if you cannot identify a concrete race path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/core/request.ts` around lines 1020 - 1026, The extra re-check of this.doneReadingData after registering listeners is defensively safe but likely unreachable; update the code around the block that contains the re-check to add a brief comment explaining that doneReadingData can be flipped by handleIncomingChunk (uWS onData) and that because the executor body runs synchronously in single-threaded JS the race is unlikely, but the check is left as a belt-and-suspenders guard before calling cleanup() and resolve(this.getBufferedData()); mention the related symbols doneReadingData, handleIncomingChunk, getBufferedData, and cleanup in the comment so future readers understand the intent; alternatively, if you prefer removal, delete the re-check and its cleanup/resolve branch.src/http/handlers/static/file-worker-pool.ts (1)
92-122: Good consolidation and clean-exit rejection.Extracting
rejectAllPendingTasksremoves duplication acrosserror/exithandlers and guaranteespendingTaskKeysis cleared. Also rejecting pending tasks on the clean-exit branch is the right call — previously those promises would hang until the pool was torn down.Optional nit: the two
exitbranches flip the order ofmarkAsDead()vsrejectAllPendingTasks(...). It's behaviorally identical, but aligning them (e.g., alwaysmarkAsDead()→rejectAllPendingTasks(...)→onDeath(this)) reads cleaner.🧹 Optional ordering tweak
} else { // Worker exited cleanly but unexpectedly - still mark as dead // Reject pending tasks since they'll never complete - this.rejectAllPendingTasks(new Error('Worker thread exited unexpectedly')); - - this.markAsDead(); + this.markAsDead(); + this.rejectAllPendingTasks(new Error('Worker thread exited unexpectedly')); this.onDeath(this); }Also applies to: 150-162
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/handlers/static/file-worker-pool.ts` around lines 92 - 122, Unify the ordering in the worker 'exit' handler to match the error path: call markAsDead(), then rejectAllPendingTasks(...) and then this.onDeath(this) so both non-zero and zero exit branches follow the same sequence; update the branch where a clean exit currently calls rejectAllPendingTasks before markAsDead to instead call markAsDead() first, then rejectAllPendingTasks, then this.onDeath(this), keeping the same messages and using the existing methods rejectAllPendingTasks, markAsDead, and onDeath for consistency.src/http/test-helpers.ts (1)
96-101: Optional: clarify thattryEndCompleteis independent ofwriteSuccess.In real uWS,
tryEnd()returns[ok, hasResponded]— two independent booleans:okcan betruewhilehasRespondedremainsfalsefor a streaming, incomplete response. DefaultingtryEndComplete = writeSuccessis a handy shortcut, but the JSDoc on line 99 reads as if the two are semantically linked. Consider noting they're orthogonal in uWS so test authors remember they can override them independently./** * Whether tryEnd should indicate completion (done=true) - * `@default` true when writeSuccess is true + * + * Mirrors the second element of uWS's `tryEnd()` return tuple (`hasResponded`), + * which is independent of the first (`ok` / writeSuccess). Defaulted to + * `writeSuccess` purely for ergonomic reasons in common test cases; override + * explicitly to simulate partial streaming (e.g., `{ writeSuccess: true, tryEndComplete: false }`). */ tryEndComplete?: boolean;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/test-helpers.ts` around lines 96 - 101, Update the JSDoc for the tryEndComplete property to clarify it's independent from writeSuccess: explain that tryEndComplete corresponds to the uWS tryEnd() second boolean (hasResponded) and can be set separately from writeSuccess (the first boolean/ok), noting the default convenience of setting tryEndComplete to writeSuccess but that they are orthogonal and may be overridden independently; reference tryEnd(), tryEndComplete, and writeSuccess in the comment so test authors understand the mapping to uWS semantics.src/http/body/multipart-handler.ts (1)
280-292: Nit: normalize to a promise viaPromise.resolveto also cover thenables.
instanceof Promisemisses non-native thenables (e.g., handlers returning aBluebirdpromise or an async function compiled by some older targets).Promise.resolve(handler(field))flattens both promises and thenables and is idiomatic here.- // Execute handler and wrap in promise for consistent handling - const result = handler(field); - const handlerPromise = result instanceof Promise ? result : Promise.resolve(); + // Execute handler and normalize the result (handles promises and thenables) + const handlerPromise = Promise.resolve(handler(field));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/body/multipart-handler.ts` around lines 280 - 292, Replace the instanceof Promise branching with Promise.resolve to normalize handler results (including thenables) into a real Promise: call Promise.resolve(handler(field)) and assign that to handlerPromise, set this.multipartPromise = handlerPromise before this.request.resume(), await handlerPromise, then clear this.multipartPromise; this ensures functions like handler and the multipartPromise handling in multipart-handler.ts correctly handle non-native thenables.src/http/platform/uws-platform.adapter.ts (1)
782-816: Minor: Consider checking.cause?.codefor defensive error handling.The current implementation only checks
error.codefor ENOENT detection. While this works for current error flows (worker pool errors are handled internally, and streaming errors preserve.code), error wrapping patterns exist elsewhere in the codebase (file-worker-pool.ts). If error wrapping evolves here, the check would miss wrapped ENOENT errors. The suggested fix adds robustness:Suggested change
- const isNotFound = (error as { code?: string })?.code === 'ENOENT'; + const rootCode = + (error as { code?: string })?.code ?? + (error as { cause?: { code?: string } })?.cause?.code; + const isNotFound = rootCode === 'ENOENT';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/platform/uws-platform.adapter.ts` around lines 782 - 816, In staticAssetHandler, make the ENOENT detection defensive by also checking for a wrapped error's cause code: when computing isNotFound (currently (error as { code?: string })?.code === 'ENOENT'), extend it to check (error as any).cause?.code === 'ENOENT' as well (or traverse a single .cause chain) so that wrapped errors from handler.serve/file-worker-pool are recognized; keep the existing logging and response behavior (res.status, res.send) and still guard with !res.headersSent && !res.isAborted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/http/handlers/static/file-worker-pool.spec.ts`:
- Around line 127-139: The test's name claims it verifies completing an
in-flight task during termination, but the current test awaits
pool.readFile(...) to finish before calling pool.terminate(), so there is no
race; update the test to either rename it to reflect the sequential behavior
(e.g., "should read a file and then terminate cleanly") or change the flow to
start the read without awaiting (e.g., call pool.readFile(testFilePath) to get a
Promise, then call await pool.terminate() and finally await the read Promise) to
exercise the true in-flight termination behavior for FileWorkerPool.readFile and
FileWorkerPool.terminate.
In `@src/http/routing/route-registry.ts`:
- Around line 277-278: RouteRegistry now uses the nullish coalescing operator
allowing an explicit maxBodySize of 0 to be passed to _initBodyParser; add a
defensive invariant in RouteRegistry's constructor to validate
options.maxBodySize (> 0) and throw a clear Error if it's not (e.g.,
"RouteRegistry requires options.maxBodySize > 0"), so direct construction (e.g.,
in tests) cannot silently accept 0; leave fastAbort handling as-is.
- Around line 462-465: The guard handling should surface empty-observable or
explicit-false cases instead of silently denying; replace the current
lastValueFrom(..., { defaultValue: false }) usage for observable guard results
with a try/catch around await lastValueFrom(guardResult) so that an EmptyError
is caught and a warning is logged (include guard.constructor?.name and relevant
context info), and also log a warning whenever a non-observable or resolved
observable result is strictly false before returning false; keep behavior of
returning false but ensure warnings are emitted for empty/false guard results
(references: guard.canActivate, isObservable, lastValueFrom, ExecutionContext).
In `@src/websocket/adapter/uws.adapter.spec.ts`:
- Around line 89-93: The test's inline comment is misleading because
UwsAdapter.create() does not listen or use the passed _port and simply returns a
uWS.App(); update the test to remove or replace the "Let OS decide the port"
comment and either call create() without implying port selection or pass the
configured 8099 to match existing setup; ensure references to adapter.create()
and adapter.close() remain and the assertion behavior is unchanged.
In `@src/websocket/exceptions/ws-exception.ts`:
- Around line 19-21: The constructor in WsException currently restores the
prototype using Object.setPrototypeOf(this, WsException.prototype), which breaks
instanceof for subclasses; change it to Object.setPrototypeOf(this,
new.target.prototype) so the actual invoked constructor's prototype (not the
base class) is restored and subclass instanceof checks (e.g., for
AuthWsException) work correctly when constructing the error in WsException's
constructor.
In `@src/websocket/interfaces/uws-options.interface.ts`:
- Around line 108-134: ResolvedUwsAdapterOptions currently drops SSL fields and
UwsAdapter always calls uWS.App(), so SSL options passed to the constructor are
ignored; update ResolvedUwsAdapterOptions to include cert_file_name,
key_file_name, passphrase, dh_params_file_name, and ssl_prefer_low_memory_usage,
modify UwsAdapter.constructor to extract and store these fields from the
incoming UwsAdapterOptions, and change UwsAdapter.create() to conditionally
instantiate uWS.SSLApp(...) with the SSL config (cert_file_name, key_file_name,
passphrase, dh_params_file_name, ssl_prefer_low_memory_usage) when both cert and
key are present, falling back to uWS.App() otherwise.
In `@src/websocket/middleware/pipes/pipe-executor.ts`:
- Around line 72-76: paramPipe.type (from ParamType) is being unsafely cast to
Nest's ArgumentMetadata['type'] causing invalid runtime values (e.g.,
'messageBody'); replace the cast by mapping paramPipe.type to a valid NestJS
literal and fallback to 'custom'. Add a helper like
toArgumentMetadataType(paramPipe.type) that returns only 'body' | 'query' |
'param' or 'custom', and use it when constructing metadata (the metadata
variable and the code that reads paramPipe.type in pipe-executor.ts). Ensure all
branches of ParamType map to 'custom' when no direct match exists so downstream
pipes (e.g., ValidationPipe) see a valid ArgumentMetadata.type.
In `@src/websocket/routing/handler-executor.ts`:
- Around line 112-124: The guard denial is creating an HttpException
(ForbiddenException) which is serialized as a generic internal error; replace
that with a WebSocket-specific exception so the existing serialize path handles
it properly: in handler-executor.ts, construct and throw/pass a WsException
(e.g., new WsException('Forbidden resource')) instead of new
ForbiddenException('Forbidden resource') when building the host for
filterExecutor.catch (keep instance, methodName, client, data as-is), or
alternatively update exception-filter-executor.ts::serializeException to
special-case HttpException by extracting status/response so ForbiddenException
is serialized correctly; prefer the first approach (use WsException) to keep
semantics within the WS domain and to let filterExecutor.catch return the proper
getError() payload.
---
Outside diff comments:
In `@src/http/body/multipart-handler.ts`:
- Around line 244-268: In handleFile, the code captures stream.truncated as a
primitive and passes it into executeHandler (field.file.truncated) too early, so
it will be stale; change the file object passed to executeHandler to expose
truncated as a getter that returns stream.truncated (i.e., a live boolean
delegating to the stream) and update the corresponding interface/type used by
executeHandler/MultipartHandler to document truncated as a live flag (boolean
getter) rather than a copied value; keep the rest of handleFile behavior
(including the resume in finally) intact so consumers reading
field.file.truncated after stream consumption see the correct truncation state.
In `@src/websocket/adapter/uws.adapter.ts`:
- Around line 211-221: The gateways Map was flipped to Map<object, string>, so
the forEach callback currently receives the name string instead of the gateway
instance; update the connection and disconnect hook loops to iterate the Map's
keys (gateway instances) and pass those instances to
lifecycleHooksManager.callConnectionHook and
lifecycleHooksManager.callDisconnectHook, and use the instance when computing
the logger message (gateway.constructor?.name) so it reports the real class
name; apply the same keys()-based iteration change for the disconnect hook block
that mirrors the connection hook and keep the try/catch per gateway as before.
In `@src/websocket/routing/metadata-scanner.ts`:
- Around line 181-191: The sortObjectKeys implementation currently
short-circuits on arrays so objects inside arrays keep original key order;
update sortObjectKeys to recurse into arrays by mapping each element and, when
an element is a non-null object, call sortObjectKeys on it (mirror
MessageRouter.sortValue behavior), leaving primitives/other types unchanged;
update getMethodNameForEvent usage remains the same but will now produce
deterministically sorted keys for array-contained objects; add a parity unit
test in metadata-scanner.spec.ts echoing the array-of-objects case from
message-router.spec.ts to ensure matching behavior.
---
Nitpick comments:
In `@package.json`:
- Around line 29-30: Remove the redundant "uwestjs" keyword from the
package.json keywords array (it duplicates the package name and is a no-op for
npm search); keep the other keywords such as "http" and "concurrency" intact and
ensure keywords remains a valid array in package.json.
In `@src/http/body/body-parser.spec.ts`:
- Around line 129-150: Add an explicit assertion that the uWS response was
closed exactly once to make the test intent clear: after triggering the chunks
and before the final expectations, assert mockUwsRes.close was called once
(e.g., expect(mockUwsRes.close).toHaveBeenCalledTimes(1)); also add a one-line
comment near the onDataCallback calls stating these are only to exercise the
"bytes still counted after rejection" invariant.
In `@src/http/body/multipart-handler.spec.ts`:
- Around line 232-268: Remove the redundant jest.useRealTimers() call inside the
"should handle async handlers sequentially" test since an afterEach hook already
restores real timers for the describe('backpressure handling') suite;
specifically, delete the trailing jest.useRealTimers() at the end of the
it('should handle async handlers sequentially', ...) test so cleanup is
centralized, leaving the afterEach and the rest of the test
(setupMultipartRequest, req.multipart handler, sendMultipartData, expectations
including mockUwsRes.pause/resume checks) intact.
In `@src/http/body/multipart-handler.ts`:
- Around line 280-292: Replace the instanceof Promise branching with
Promise.resolve to normalize handler results (including thenables) into a real
Promise: call Promise.resolve(handler(field)) and assign that to handlerPromise,
set this.multipartPromise = handlerPromise before this.request.resume(), await
handlerPromise, then clear this.multipartPromise; this ensures functions like
handler and the multipartPromise handling in multipart-handler.ts correctly
handle non-native thenables.
In `@src/http/core/index.ts`:
- Around line 1-3: The public core index currently exports only the runtime
symbols UwsRequest, UwsResponse, and HttpExecutionContext; if the underlying
modules (request, response, context) also expose companion types or interfaces
that consumers may need (for example options/config types), add explicit type
re-exports alongside the existing exports — e.g., add export type {
SomeRequestOptions } from './request' or export type { ResponseConfig } from
'./response' and export type { HttpContextOptions } from './context' to make
those types part of the core public surface while keeping imports shallow and
intentional.
In `@src/http/core/request.spec.ts`:
- Around line 985-993: The test restores real timers immediately after
jest.runAllTimers(), which can race with the async for-await consumer and
done(); capture and await the async iteration's promise (the IIFE that performs
the for await ... of) instead of letting it run implicitly, then call
jest.useRealTimers() only after that promise resolves so the test lifecycle is
deterministic; reference the existing async IIFE/for-await block and the
jest.runAllTimers()/jest.useRealTimers() calls when locating where to store and
await the promise.
In `@src/http/core/request.ts`:
- Around line 1020-1026: The extra re-check of this.doneReadingData after
registering listeners is defensively safe but likely unreachable; update the
code around the block that contains the re-check to add a brief comment
explaining that doneReadingData can be flipped by handleIncomingChunk (uWS
onData) and that because the executor body runs synchronously in single-threaded
JS the race is unlikely, but the check is left as a belt-and-suspenders guard
before calling cleanup() and resolve(this.getBufferedData()); mention the
related symbols doneReadingData, handleIncomingChunk, getBufferedData, and
cleanup in the comment so future readers understand the intent; alternatively,
if you prefer removal, delete the re-check and its cleanup/resolve branch.
In `@src/http/core/response.spec.ts`:
- Around line 1160-1162: Replace the implicit callback usage new
Promise(setImmediate) with the more explicit form new Promise((resolve) =>
setImmediate(resolve)) in the test to improve readability; update both
occurrences (the two chained awaits around the comment and the similar block
around lines ~1190-1192) so readers clearly see the promise resolve being passed
to setImmediate.
In `@src/http/handlers/static/file-worker-pool.ts`:
- Around line 92-122: Unify the ordering in the worker 'exit' handler to match
the error path: call markAsDead(), then rejectAllPendingTasks(...) and then
this.onDeath(this) so both non-zero and zero exit branches follow the same
sequence; update the branch where a clean exit currently calls
rejectAllPendingTasks before markAsDead to instead call markAsDead() first, then
rejectAllPendingTasks, then this.onDeath(this), keeping the same messages and
using the existing methods rejectAllPendingTasks, markAsDead, and onDeath for
consistency.
In `@src/http/platform/uws-platform.adapter.ts`:
- Around line 782-816: In staticAssetHandler, make the ENOENT detection
defensive by also checking for a wrapped error's cause code: when computing
isNotFound (currently (error as { code?: string })?.code === 'ENOENT'), extend
it to check (error as any).cause?.code === 'ENOENT' as well (or traverse a
single .cause chain) so that wrapped errors from handler.serve/file-worker-pool
are recognized; keep the existing logging and response behavior (res.status,
res.send) and still guard with !res.headersSent && !res.isAborted.
In `@src/http/test-helpers.ts`:
- Around line 96-101: Update the JSDoc for the tryEndComplete property to
clarify it's independent from writeSuccess: explain that tryEndComplete
corresponds to the uWS tryEnd() second boolean (hasResponded) and can be set
separately from writeSuccess (the first boolean/ok), noting the default
convenience of setting tryEndComplete to writeSuccess but that they are
orthogonal and may be overridden independently; reference tryEnd(),
tryEndComplete, and writeSuccess in the comment so test authors understand the
mapping to uWS semantics.
In `@src/shared/index.ts`:
- Around line 1-2: The current barrel uses wildcard re-exports via "export *
from './di';" and "export * from './interfaces';", which can create ambiguous
symbol collisions as the surface grows; update the barrel to use explicit named
re-exports (list the specific symbols you want to expose from di and interfaces)
or use namespaced re-exports (e.g., export * as di from './di' and export * as
interfaces from './interfaces') so consumers cannot silently get conflicting
identifiers and future additions won't cause ambiguity.
In `@src/shared/interfaces/cors-options.interface.ts`:
- Around line 26-37: Clarify the docs for the cors options by stating whether
the string form for methods, allowedHeaders, and exposedHeaders expects a single
token (e.g., "GET") or a comma-separated list (e.g., "GET,POST") and how it will
be interpreted when building the Access-Control-Allow-* headers; update the
JSDoc for the methods?: string | string[] field and the corresponding
allowedHeaders and exposedHeaders declarations to explicitly say that a single
string may be either a single method/name or a comma-separated list and describe
whether the implementation will split comma-separated strings into arrays (or
treat the string verbatim) so callers know which form to use.
- Around line 18-24: The CorsOptions interface should warn that credentials:
true is incompatible with a wildcard/unrestricted origin; update the doc
comments for origin and/or credentials in the CorsOptions (the origin property
and credentials?: boolean) to state that when credentials is true browsers
reject Access-Control-Allow-Origin: '*' (or an unrestricted origin equivalent
such as origin: true), and recommend using an explicit origin string or a
dynamic function that echoes the request origin; alternatively, add a runtime
check in the CORS handler to throw/log when credentials is true and origin is
'*' or origin is configured as unrestricted to prevent misconfiguration.
In `@src/shared/interfaces/platform-options.interface.ts`:
- Around line 1-2: Change the two imports in platform-options.interface.ts to
type-only imports to avoid bundler circular dependency issues: replace the
current imports that bring in UwsAdapterOptions and HttpOptions with type-only
imports (i.e., import type for UwsAdapterOptions and HttpOptions) so the
compiler treats them as erased types and bundlers can safely elide the module
edges; locate the import statements referencing UwsAdapterOptions and
HttpOptions and update them to use import type.
In `@src/websocket/adapter/uws.adapter.ts`:
- Around line 110-111: The WeakSet gatewaySet is redundant because gateways
(Map<object,string>) already keys gateway instances; remove the gatewaySet
declaration and all uses (e.g., gatewaySet.has(gateway), gatewaySet.add(...),
gatewaySet.delete(...), and any reset logic) and replace them with the Map's
lifecycle operations: use this.gateways.has(gateway) for membership checks,
this.gateways.set(gateway, name) when registering, this.gateways.delete(gateway)
when unregistering, and clear/reset via this.gateways.clear() or appropriate map
ops; update any functions referencing gatewaySet accordingly so the Map remains
the single source of truth.
In `@src/websocket/middleware/pipes/pipes.integration.spec.ts`:
- Around line 60-68: The test mutates metadata on the gateway class/prototype
(PIPES_METADATA, PIPES_METADATA:params, PARAM_ARGS_METADATA) and must restore
prior state to avoid cross-test pollution; capture current metadata values for
keys `${PIPES_METADATA}:params` (on gatewayClass+methodName), `PIPES_METADATA`
(on gatewayClass), and `PARAM_ARGS_METADATA` (on
gatewayClass.prototype+methodName) before changing them, then perform the
existing modifications to existingPipes, classPipes, and newParams, and finally
in a try/finally restore the captured metadata (using Reflect.defineMetadata or
Reflect.deleteMetadata when value was undefined) so executeHandler/TestGateway
runs don’t leave global mutations; use the same symbols (existingPipes,
existingPipesMap, newParams, gatewayClass, methodName) to locate the code to
wrap.
In `@src/websocket/routing/message-router.spec.ts`:
- Around line 203-287: The metadata scanner's sortObjectKeys implementation
doesn't handle objects nested inside arrays; update the
MetadataScanner.sortObjectKeys method to recursively sort keys for all plain
objects encountered, including objects within arrays (preserve arrays order but
normalize any object elements), and ensure it treats key order insensitively the
same way getHandlerKey does. After fixing sortObjectKeys, add tests in
metadata-scanner.spec.ts mirroring the MessageRouter object-in-array cases
(create test cases that assert equivalent normalized keys for object elements in
arrays and that differing values still produce different keys) to ensure parity
with MessageRouter coverage.
In `@src/websocket/routing/metadata-scanner.ts`:
- Around line 104-107: Extract the deterministic serialization logic into a
single shared helper named stableStringify (e.g., under src/shared) and replace
the local sortObjectKeys/sortValue implementations in MetadataScanner and
MessageRouter: implement stableStringify to produce a byte-for-byte stable JSON
string by recursively sorting object keys and normalizing values consistently,
then use stableStringify when computing messageKey in MetadataScanner and
wherever MessageRouter builds its key so both components share identical
behavior and you can safely remove the duplicated sortObjectKeys/sortValue
functions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0dd42810-6eb9-4f66-af18-07a2485b6d2f
⛔ Files ignored due to path filters (2)
assets/uWestJS.pngis excluded by!**/*.pngpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (107)
.npmignoreREADME.mdpackage.jsonsrc/decorators/index.tssrc/exceptions/index.tssrc/http/body/body-parser.spec.tssrc/http/body/body-parser.tssrc/http/body/index.tssrc/http/body/multipart-handler.spec.tssrc/http/body/multipart-handler.tssrc/http/core/context.spec.tssrc/http/core/context.tssrc/http/core/index.tssrc/http/core/request.spec.tssrc/http/core/request.tssrc/http/core/response.spec.tssrc/http/core/response.tssrc/http/handlers/compression/compression-handler.spec.tssrc/http/handlers/compression/compression-handler.tssrc/http/handlers/compression/index.tssrc/http/handlers/cors/cors-handler.spec.tssrc/http/handlers/cors/cors-handler.tssrc/http/handlers/cors/index.tssrc/http/handlers/index.tssrc/http/handlers/static/file-worker-pool.spec.tssrc/http/handlers/static/file-worker-pool.tssrc/http/handlers/static/index.tssrc/http/handlers/static/static-file-handler.spec.tssrc/http/handlers/static/static-file-handler.tssrc/http/index.tssrc/http/interfaces/http-options.interface.tssrc/http/interfaces/index.tssrc/http/platform/index.tssrc/http/platform/uws-platform.adapter.spec.tssrc/http/platform/uws-platform.adapter.tssrc/http/routing/index.tssrc/http/routing/route-registry-middleware.spec.tssrc/http/routing/route-registry.spec.tssrc/http/routing/route-registry.tssrc/http/test-helpers.tssrc/index.tssrc/interfaces/index.tssrc/interfaces/websocket-client.interface.tssrc/middleware/filters/index.tssrc/middleware/guards/index.tssrc/middleware/index.tssrc/middleware/pipes/index.tssrc/platform/index.tssrc/shared/di/index.tssrc/shared/di/module-ref.tssrc/shared/index.tssrc/shared/interfaces/cors-options.interface.tssrc/shared/interfaces/index.tssrc/shared/interfaces/logger.interface.tssrc/shared/interfaces/platform-options.interface.tssrc/socket/index.tssrc/websocket/adapter/index.tssrc/websocket/adapter/lifecycle-hooks.spec.tssrc/websocket/adapter/lifecycle-hooks.tssrc/websocket/adapter/uws.adapter.integration.spec.tssrc/websocket/adapter/uws.adapter.spec.tssrc/websocket/adapter/uws.adapter.tssrc/websocket/core/broadcast-operator.spec.tssrc/websocket/core/broadcast-operator.tssrc/websocket/core/index.tssrc/websocket/core/socket.spec.tssrc/websocket/core/socket.tssrc/websocket/decorators/connected-socket.decorator.tssrc/websocket/decorators/decorators.spec.tssrc/websocket/decorators/index.tssrc/websocket/decorators/message-body.decorator.tssrc/websocket/decorators/param-decorator.utils.tssrc/websocket/decorators/payload.decorator.tssrc/websocket/exceptions/index.tssrc/websocket/exceptions/ws-exception.tssrc/websocket/index.tssrc/websocket/interfaces/index.tssrc/websocket/interfaces/uws-options.interface.tssrc/websocket/interfaces/uws-socket.interface.tssrc/websocket/interfaces/websocket-client.interface.tssrc/websocket/middleware/filters/exception-filter-executor.spec.tssrc/websocket/middleware/filters/exception-filter-executor.tssrc/websocket/middleware/filters/filters.integration.spec.tssrc/websocket/middleware/filters/index.tssrc/websocket/middleware/filters/use-filters.decorator.tssrc/websocket/middleware/guards/guard-executor.spec.tssrc/websocket/middleware/guards/guard-executor.tssrc/websocket/middleware/guards/guards.integration.spec.tssrc/websocket/middleware/guards/index.tssrc/websocket/middleware/guards/use-guards.decorator.tssrc/websocket/middleware/index.tssrc/websocket/middleware/pipes/index.tssrc/websocket/middleware/pipes/pipe-executor.spec.tssrc/websocket/middleware/pipes/pipe-executor.tssrc/websocket/middleware/pipes/pipes.integration.spec.tssrc/websocket/middleware/pipes/use-pipes.decorator.tssrc/websocket/middleware/ws-context.tssrc/websocket/rooms/index.tssrc/websocket/rooms/room-manager.spec.tssrc/websocket/rooms/room-manager.tssrc/websocket/routing/handler-executor.spec.tssrc/websocket/routing/handler-executor.tssrc/websocket/routing/index.tssrc/websocket/routing/message-router.spec.tssrc/websocket/routing/message-router.tssrc/websocket/routing/metadata-scanner.spec.tssrc/websocket/routing/metadata-scanner.ts
💤 Files with no reviewable changes (10)
- src/interfaces/websocket-client.interface.ts
- src/middleware/guards/index.ts
- src/socket/index.ts
- src/exceptions/index.ts
- src/middleware/pipes/index.ts
- src/middleware/filters/index.ts
- src/platform/index.ts
- src/middleware/index.ts
- src/decorators/index.ts
- src/interfaces/index.ts
…ebSocket concerns - Reorganize src/ into domain-based structure (http/, websocket/, shared/) - Move HTTP code from platform/ to http/ with subdirectories (core, routing, body, handlers, platform) - Move WebSocket code to websocket/ with subdirectories (core, rooms, routing, decorators, middleware, adapter, exceptions) - Extract shared code to shared/ (DI abstraction and interfaces) - Fix all inline imports and update import paths across codebase - Remove old flat directory structure (adapter/, decorators/, exceptions/, middleware/, platform/, rooms/, router/, socket/) Fixes #25
New Structure
Fixes #25
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores
compressibledependency for MIME-based content compression detection.