v2.0.0 HTTP Support for uWestJS#27
Conversation
…2.0.0 HTTP support Phase 0: Project Setup - Add HTTP dependencies: cookie, cookie-signature, mime-types, statuses, busboy - Create src/platform/ directory structure for HTTP platform code - Define HttpOptions and PlatformOptions interfaces - Add isolatedModules to tsconfig.json for proper module resolution Phase 1: Request/Response Wrappers - Implement UwsRequest with stack-allocated uWS.HttpRequest caching - Implement lazy header evaluation with HTTP/1.1 spec compliance - Add query parameter parsing with URL-encoding and array support - Implement UwsResponse with cork management for batched writes - Add chainable API for status, headers, and cookies - Implement cookie signing with cookie-signature - Add auto JSON detection and serialization - Handle connection abort scenarios
- Add BodyParser class with multi-mode parsing (awaiting, buffering, streaming) - Implement backpressure management (pause/resume) - Add size limit enforcement with connection closure - Support chunked transfer encoding - Add body parsing methods to UwsRequest (buffer, json, text, urlencoded) - Implement promise caching for concurrent body access - Add auto-detection via body getter based on content-type - Add comprehensive test coverage (59 tests, all passing)
- Add response chunk batching with configurable watermark and flush interval - Implement streaming support with backpressure handling for large files - Add cookie support with signing and parsing capabilities - Implement content-type helpers (type, attachment, redirect, location) - Add proper filename escaping using content-disposition package - Refactor test suites for DRY principles (53% reduction in test code) - All 502 tests passing with >80% coverage
feat: Implement Body Parsing and HTTP Response Enhancements
Implement complete NestJS middleware integration with guards, pipes, exception filters, and interceptors. Add HTTP execution context and comprehensive test coverage.
feat: implement NestJS middleware pipeline
…, compression) - Add multipart/form-data support with streaming file uploads - Implement static file serving with range requests and caching - Add CORS handler with preflight and origin validation - Implement request/response compression (gzip/deflate/brotli) - Add hybrid readable stream with lazy activation and backpressure - Implement file worker pool for optimized static file serving
feat: implement advanced HTTP features (multipart, static files, CORS, compression)
…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
refactor: migrate to domain-driven architecture separating HTTP and WebSocket concerns
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughAdds comprehensive HTTP platform and WebSocket reorganizations: new UwsRequest/UwsResponse/BodyParser, multipart/compression/CORS/static handlers, RouteRegistry and UwsPlatformAdapter, many middleware improvements (guards/pipes/filters), shared interfaces/DI barrels, plus numerous tests and package/config updates. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as HTTP Client
participant Adapter as UwsPlatformAdapter
participant Registry as RouteRegistry
participant Middleware as Middleware Pipeline
participant Handler as Route Handler
participant Response as UwsResponse
Client->>Adapter: send HTTP request
Adapter->>Registry: lookup route & metadata
Registry->>Middleware: run guards/pipes/filters
Middleware->>Handler: invoke handler
Handler->>Response: write/send response
Response->>Client: deliver HTTP response
rect rgba(200, 100, 100, 0.5)
Note over Middleware: on error run exception filters
end
sequenceDiagram
participant uws as uWS incoming
participant UwsReq as UwsRequest
participant Parser as BodyParser
participant Stream as BufferStore
uws->>UwsReq: onData(chunk, isLast)
UwsReq->>Parser: route chunk
Parser->>Stream: buffer/append
alt buffer > WATERMARK
Parser->>uws: pause()
end
uws->>UwsReq: onData(final, isLast=true)
Parser->>Stream: finalize & resolve buffer()
Stream-->>UwsReq: Buffer ready
sequenceDiagram
participant Client as HTTP Client
participant Static as StaticFileHandler
participant Pool as FileWorkerPool
participant Worker as WorkerThread
Client->>Static: GET /path
Static->>Static: validate path & conditionals
Static->>Pool: readFile(path)
Pool->>Worker: dispatch read task
Worker-->>Pool: return ArrayBuffer (transferred)
Pool-->>Static: Buffer
Static->>Client: stream or send response
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/websocket/decorators/decorators.spec.ts (1)
74-93:⚠️ Potential issue | 🟡 Minor
toEqualwill not detectdata: undefinedpresence.Jest’s
toEqualtreats{ a: 1 }and{ a: 1, data: undefined }as equal, so this assertion will pass whether the production code omitsdataor sets it toundefined. If the stated contract is that thedatakey is truly absent (not justundefined), add an explicitnot.toHaveProperty('data')assertion. The same concern applies to the@Payloadtest at lines 124-130.🔧 Proposed tightening
expect(fullMetadata[0]).toEqual({ index: 0, type: ParamType.MESSAGE_BODY, // Note: data property is omitted when no property name is provided }); + expect(fullMetadata[0]).not.toHaveProperty('data');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/decorators/decorators.spec.ts` around lines 74 - 93, The test uses toEqual which cannot distinguish a missing data key from data: undefined; update the assertions to explicitly verify the absence of the data property: after retrieving fullMetadata via getParamMetadata(TestGateway.prototype, 'handleWithMessageBody') add expect(fullMetadata[0]).not.toHaveProperty('data') to ensure the key is omitted, and apply the same explicit not.toHaveProperty('data') check in the `@Payload-related` test (the one around lines 124-130) that inspects its metadata so it fails if the production code sets data to undefined instead of omitting it.src/websocket/middleware/filters/exception-filter-executor.ts (1)
148-160:⚠️ Potential issue | 🟡 MinorSee related comment on
handler-executor.tsguard-denial flow.The generic
Unhandled exceptionlog +Internal server errorpayload is the downstream behavior that makes guard denials inHandlerExecutor.executeuser-visible as server errors. Fix is preferably at the caller (route aWsExceptioninstead ofForbiddenException), but an alternative is to also handleHttpExceptionhere. Flagging only the downstream here for context; root cause comment is onhandler-executor.ts.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/middleware/filters/exception-filter-executor.ts` around lines 148 - 160, The serializeException method currently treats only WsException specially and turns all other errors into a generic internal error; update serializeException to also detect Nest's HttpException (e.g., check instanceof HttpException) and return a client-appropriate payload/status when encountered (use the exception's getResponse() or message/status data) while still logging unexpected errors; reference serializeException and WsException and ensure this complements the higher-level fix in HandlerExecutor.execute so guard-denials (ForbiddenException/other HttpExceptions) are serialized as their intended client-visible HTTP/WebSocket errors rather than masked as "Internal server error."src/websocket/routing/handler-executor.spec.ts (1)
337-343:⚠️ Potential issue | 🟡 MinorTighten guard-denial assertions — current tests hide the response-shape regression.
expect(result.response).toBeDefined()andexpect(filterCalled).toBe(true)pass even when the returnedresponseis the generic{ error: 'Internal server error', message: 'An unexpected error occurred' }produced byserializeExceptionfor non-WsExceptionerrors. That is exactly the regression called out onhandler-executor.ts. Consider asserting:
- the filter's
catchreceives aForbiddenException(or whichever exception you decide on), andresult.responsematches the intended forbidden payload (not the generic internal-server-error fallback).💚 Example tightening
- expect(result.response).toBeDefined(); // Guard denials now go through exception filters + expect(result.response).not.toEqual({ + error: 'Internal server error', + message: 'An unexpected error occurred', + }); + // Optionally: assert the intended forbidden payload, e.g. + // expect(result.response).toMatchObject({ status: 'error', message: 'Forbidden resource' });And in
should execute filter when guard fails, capture and assert the exception passed to the filter:class TestFilter implements ExceptionFilter { - catch(): void { + catch(exception: unknown): void { filterCalled = true; + expect(exception).toBeInstanceOf(ForbiddenException); } }Also applies to: 737-764
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/routing/handler-executor.spec.ts` around lines 337 - 343, Tighten the "should execute filter when guard fails" assertions: verify that the filter's catch handler actually received the ForbiddenException (assert mockFilter.catch was called with an instance of ForbiddenException) and assert that result.response equals the intended forbidden payload (e.g., the payload your exception filter returns for ForbiddenException rather than the generic serializeException internal-server-error object); update the assertions around executor.execute, mockFilter.catch and result.response to validate the exception type and the specific forbidden response shape/message.src/websocket/routing/metadata-scanner.ts (1)
181-199:⚠️ Potential issue | 🟡 MinorCycle detection has a false-positive on shared (DAG) sub-objects.
The
seenWeakSet accumulates every visited object and is never cleared when unwinding, so two sibling properties pointing at the same object throw "Circular reference detected" even though the graph is acyclic. Example pattern:{ a: shared, b: shared }whereshared = { x: 1 }. True cycle detection needs path-based tracking (add on entry, remove after processing the sub-tree) — or you can just rely onJSON.stringifyto throwTypeErroron genuine cycles.🐛 Proposed fix (path-based tracking)
private sortObjectKeys( obj: Record<string, unknown>, seen = new WeakSet<object>() ): Record<string, unknown> { if (seen.has(obj)) { throw new Error('Circular reference detected in message pattern'); } seen.add(obj); 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>, seen) : value; } + seen.delete(obj); return sorted; }🤖 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 - 199, The current sortObjectKeys uses a shared WeakSet (seen) that causes false-positive circular detection for DAGs because entries are never removed; update sortObjectKeys to perform path-based tracking by adding obj to seen on entry and removing it before returning (or use a new WeakSet copy for each recursive branch) so sibling references to the same sub-object don't trigger errors, keeping the circular check only for true cycles; refer to the function sortObjectKeys and its seen parameter when making this change.
🧹 Nitpick comments (25)
tsconfig.build.json (1)
3-10: LGTM — exclude list correctly omits test helpers and Jest mocks from the build output.Minor consideration: the pattern
**/test-helpers.tsis filename-based and will exclude any file namedtest-helpers.tsanywhere insrc/. If you ever need a non-test helper with that exact filename it would be unintentionally omitted; consider co-locating helpers under a__tests__/ortest/directory and excluding by directory (e.g.**/__tests__/**) for a more robust convention. Non-blocking.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tsconfig.build.json` around lines 3 - 10, The exclude list currently uses a filename-based pattern "**/test-helpers.ts" which will omit any file with that name anywhere; update tsconfig.build.json's exclude array to remove the filename-specific pattern and instead exclude test directories (e.g. add patterns like "**/__tests__/**" and/or "**/test/**") so test helpers are excluded by directory convention rather than by filename—modify the "exclude" entry for tsconfig.build.json (look for the "exclude" array and the "**/test-helpers.ts" pattern) accordingly.src/http/handlers/static/file-worker-pool.ts (2)
309-321: UsePromise.allSettledso cleanup always completes.If any worker's
terminate()rejects,Promise.allshort-circuits andthis.workers = []never runs, leavingsizenon-zero and staleFileWorkerreferences in the pool.allSettledkeeps cleanup reachable regardless of individual outcomes.♻️ Proposed refactor
async terminate(): Promise<void> { this.terminated = true; // Reject all pending tasks before terminating workers const terminationError = new Error('Worker pool terminated'); for (const task of this.workerTasks.values()) { task.reject(terminationError); } this.workerTasks.clear(); - await Promise.all(this.workers.map((w) => w.terminate())); - this.workers = []; + await Promise.allSettled(this.workers.map((w) => w.terminate())); + this.workers = []; }🤖 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 309 - 321, The terminate() method currently awaits Promise.all(this.workers.map((w) => w.terminate())) which can short-circuit if any worker.terminate() rejects and prevent this.workers being cleared; change it to use Promise.allSettled on the array of worker terminate promises so cleanup always runs, then clear this.workers = [] regardless of individual rejection results; keep the existing rejection of pending tasks via workerTasks and ensure the unique symbols referenced are terminate(), this.workerTasks, and this.workers in file-worker-pool.ts.
194-206: Consider a higher defaultpoolSize.Defaulting to
1effectively serializes all static-file reads behind a single worker; under concurrent static-asset requests this becomes a queue bottleneck. A default tied to CPU count (e.g.Math.max(1, Math.min(4, os.cpus().length - 1))) or at minimum2would better fit a high-performance HTTP adapter. Keeping1as a floor is fine, but the default should likely scale.🤖 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 194 - 206, The FileWorkerPool constructor currently defaults to a single worker which serializes static-file reads; change the default parameter in FileWorkerPool.constructor to a CPU-aware value (e.g. use the Node/os module and set size = Math.max(1, Math.min(4, os.cpus().length - 1))) so the pool scales with available cores but stays between 1 and 4; add the required import for os and keep the rest of the initialization (workers.push(this.createWorker())) unchanged, ensuring poolSize still stores the resolved value.src/websocket/middleware/guards/guard-executor.spec.ts (1)
270-366: Good coverage for instance-guard support.Mix of synchronous, Observable, throwing, and combined-with-class cases exercises the broadened
@UseGuardsmetadata contract well.One small gap: there is no test for an instance guard whose
canActivatereturns aPromise. Since the executor claims to handle Promise/Observable/sync uniformly, adding a Promise-based instance case would round out the matrix and guard against future regressions in the async-awaiting path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/middleware/guards/guard-executor.spec.ts` around lines 270 - 366, Add a new spec in the "guard instances" suite that covers an instance guard whose canActivate returns a Promise: create a guardInstance: CanActivate with canActivate = () => Promise.resolve(true) (and optionally another case for Promise.resolve(false)), apply it via `@UseGuards` on a TestGateway.handleMessage, call createContext(new TestGateway()) and await executor.executeGuards(context), then assert the Promise-resolving true yields result true (and for false yields result false); reference executor.executeGuards, createContext, UseGuards, CanActivate and the existing guard-instance tests to keep structure and expectations consistent.src/websocket/core/socket.spec.ts (1)
83-93: Consider asserting root cause too, not only the wrapper message.The test verifies the wrapper message
'Failed to emit event "test"'for both send failure and JSON serialization failure. That’s useful, but both failure modes now produce an identical error message — which means a regression where e.g. JSON errors stop being caught (or are swallowed) could still pass this expectation if the string matches by coincidence.Consider additionally asserting
cause(or a message fragment from the originalTypeErrorfor the circular case) so the two code paths are distinguishable in the spec.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/core/socket.spec.ts` around lines 83 - 93, Update the test to also assert the underlying cause of the thrown error so the two failure modes are distinguishable: when mocking send to throw (mockNativeSocket.send) assert the thrown error has a cause or message that includes 'Send failed' (or that the error.cause is the original Error), and when emitting a circular structure (socket.emit with circular), assert the thrown error.cause or message contains the JSON serialization TypeError fragment (e.g., 'Converting circular structure to JSON' or similar). Keep the existing wrapper assertion ('Failed to emit event "test"') and add these additional checks on error.cause or error.message fragments to ensure both code paths are validated.src/http/body/index.ts (1)
1-3: Note: AI summary misattributesBodyParser's source file.The AI summary claims
BodyParseris re-exported fromsrc/http/body/multipart-handler.ts, but the code correctly imports it from./body-parser. The code is right; the summary is not. Flagging for awareness only — no action needed on this file.Separately, exposing both a class
MultipartFormHandlerand a typeMultipartHandlerfrom the same barrel is slightly confusing for consumers (easy to import the wrong identifier). Consider renaming the type (e.g.MultipartHandlerOptionsorIMultipartHandler) for clarity, if the type name is not already part of the public API contract.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/body/index.ts` around lines 1 - 3, The comment notes an incorrect AI summary but no code change is required for the BodyParser export; however to avoid consumer confusion between the class MultipartFormHandler and the exported type MultipartHandler, rename the type in src/http/body/multipart-handler.ts and this barrel to a clearer identifier (e.g., MultipartHandlerOptions or IMultipartHandler) and update all references/usages accordingly; ensure you update the export line in this file (export type { ... } from './multipart-handler') to use the new type name and run the codebase/type-checker to fix any import sites.src/websocket/middleware/pipes/pipe-executor.spec.ts (1)
358-358: Drop theas anycasts — helper already acceptsPipeTransform.
applyPipeToParam's rest parameter was widened to(Type<PipeTransform> | PipeTransform)[](line 31), sopipeInstancecan be passed directly withoutas any. Keeping the escape hatch hides future signature regressions.♻️ Suggested cleanup (apply to each occurrence)
- applyPipeToParam(gateway, 'handleMessage', 0, pipeInstance as any); + applyPipeToParam(gateway, 'handleMessage', 0, pipeInstance); ... - applyPipeToParam(gateway, 'handleMessage', 0, PipeClass, pipeInstance as any); + applyPipeToParam(gateway, 'handleMessage', 0, PipeClass, pipeInstance);Also applies to: 388-388, 414-414, 439-439
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/middleware/pipes/pipe-executor.spec.ts` at line 358, The test uses unnecessary type-erasures: remove the "as any" casts when calling applyPipeToParam and pass pipeInstance directly since applyPipeToParam now accepts (Type<PipeTransform> | PipeTransform)[]; update calls like applyPipeToParam(gateway, 'handleMessage', 0, pipeInstance) (and the analogous calls with the same pattern for other params) so the helper's PipeTransform typing is preserved and casts are not hiding signature regressions.src/websocket/middleware/guards/guards.integration.spec.ts (1)
103-105: Consider asserting the shape/content of the exception-filter response.
toBeDefined()only guarantees theresponsefield is non-undefined. Given the behavioral change is that guard denials now flow through exception filters, a stronger assertion (e.g.,expect(unauthResult.response).toMatchObject({ event: 'exception', ... })or similar) would catch regressions where the error payload shape silently changes. Non-blocking.Also applies to: 137-138, 181-183
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/middleware/guards/guards.integration.spec.ts` around lines 103 - 105, The test currently only checks unauthResult.response is defined; strengthen it to assert the exception-filter payload shape (e.g., that unauthResult.response contains an event:'exception' and the expected error details) so regressions in the error payload are caught. Replace the loose expect(unauthResult.response).toBeDefined() with a structural assertion (e.g., toMatchObject) that checks event: 'exception' and the relevant message/status fields for unauthResult and the other two assertions referenced in the same file (the checks around the unauthResult usage at the other occurrences).src/websocket/core/socket.ts (2)
156-163: Barecatchhides unexpected errors.Returning
0is a reasonable fallback for a closed socket, but this swallows any other runtime error fromgetBufferedAmount()without visibility. Consider logging atdebuglevel so unexpected failures don't go silent.Proposed tweak
getBufferedAmount(): number { try { return this.nativeSocket.getBufferedAmount(); - } catch { + } catch (error) { // Socket is closed or invalid - return 0 as a safe fallback + // (e.g., uWS throws after close); log at debug for observability. return 0; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/core/socket.ts` around lines 156 - 163, The try/catch in getBufferedAmount() swallows all errors; update it to catch the error as a variable and log the unexpected exception at debug level before returning 0 so non-closed-socket failures are visible. Locate getBufferedAmount() and the call to this.nativeSocket.getBufferedAmount(), change the catch to capture the error (e.g., catch (err)) and call the module/class debug logger (e.g., this.logger.debug(...) or the existing debug logger) with a concise message and the error, then return 0 as the safe fallback.
17-45: Unsafe cast is acknowledged but still a latent footgun for non-objectTData.
this._data = {} as TDatawill silently produce a brokendatawhenTDatais a primitive (string,number) or has required properties — accessors will return{}typed as something it isn't. The JSDoc warns users, which is good. If you want stronger guarantees, consider either (a) accepting aninitialData?: TDataconstructor argument, or (b) makingdataTData | undefinedin the type and forcing users to assign before read.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/core/socket.ts` around lines 17 - 45, Change the unsafe default cast by making the socket data explicitly optional: change the field declaration from "private _data: TData" to "private _data: TData | undefined", add an optional constructor parameter "initialData?: TData" to the constructor signature, and set "this._data = initialData" inside the constructor; also update any internal getters/setters or methods that access "_data" (e.g., getData(), setData(), or any direct uses) to handle the possibly undefined value (throw, return default, or require callers to set data) so we no longer silently initialize a potentially invalid {} as TData.src/websocket/middleware/pipes/pipe-executor.ts (1)
65-79: Readingdesign:paramtypesfrom the prototype is correct for instance methods.Nice addition — populating
metatypeper parameter index makesValidationPipe-style DTO validation work. Note that it still requiresemitDecoratorMetadata: truein consumers'tsconfig.json; worth calling out in the docs.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/websocket/middleware/pipes/pipe-executor.ts` around lines 65 - 79, The current implementation correctly reads parameter types from the prototype (using prototype, Reflect.getMetadata('design:paramtypes', prototype, methodName), paramTypes) so ValidationPipe-style DTO validation works when metatype is set (metatype: paramTypes[paramPipe.index]), but this requires consumers to enable TypeScript's emitDecoratorMetadata; update the project docs (and any relevant README or middleware pipes documentation) to explicitly state that emitDecoratorMetadata: true must be set in tsconfig.json for ValidationPipe/DTO validation to work and reference the symbols/properties used here (prototype, paramTypes, transformedArgs, toArgumentMetadataType, ArgumentMetadata, paramPipes, metatype) so integrators know why the setting is needed.src/http/interfaces/http-options.interface.ts (1)
73-77: Consider a richertrustProxytype for future flexibility.A plain boolean limits proxy configuration compared to common expectations (Express supports
boolean | number | string | string[] | (ip, hopCount) => boolean). As written, users cannot constrain trust to specific subnets or hop counts, which is a typical production requirement whenX-Forwarded-*headers are involved. Since this is a new public interface, widening it now avoids a breaking change later.♻️ Proposed type widening
- /** - * Trust proxy headers (X-Forwarded-*) - * `@default` false - */ - trustProxy?: boolean; + /** + * Trust proxy headers (X-Forwarded-*) + * + * - `false`: do not trust any proxy + * - `true`: trust all proxies + * - `number`: trust N hops + * - `string | string[]`: trust listed IPs/CIDRs + * - `(ip, hopIndex) => boolean`: custom predicate + * + * `@default` false + */ + trustProxy?: boolean | number | string | string[] | ((ip: string, hopIndex: number) => boolean);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/interfaces/http-options.interface.ts` around lines 73 - 77, The trustProxy property currently typed as boolean should be widened to support richer Express-like values: change the trustProxy type on the interface (trustProxy?) to a union such as boolean | number | string | string[] | ((ip: string, hopCount: number) => boolean) so callers can specify hop counts, CIDRs, hostnames or a custom function; also update the JSDoc comment above trustProxy to reflect the expanded accepted types and example usage.src/websocket/routing/metadata-scanner.ts (1)
156-172: HoisteventKeyout of the.findcallback.
JSON.stringify(this.sortObjectKeys(event))is recomputed for every handler iterated. With N object-pattern handlers this is O(N·k) for a lookup that only needs one serialization. ComputeeventKeyonce before.find.♻️ Proposed refactor
getMethodNameForEvent(instance: object, event: string | Record<string, unknown>): string | null { // Auto-scan if not cached to prevent subtle bugs from forgetting to scan first const handlers = this.scanForMessageHandlers(instance); if (handlers.length === 0) return null; + const eventKey = + typeof event === 'object' && event !== null + ? JSON.stringify(this.sortObjectKeys(event)) + : undefined; + const handler = handlers.find((h) => { // String pattern matching if (typeof h.message === 'string' && typeof event === 'string') { return h.message === event; } // Object pattern matching - use pre-computed messageKey for performance if ( typeof h.message === 'object' && h.message !== null && typeof event === 'object' && event !== null ) { - const eventKey = JSON.stringify(this.sortObjectKeys(event)); return h.messageKey === eventKey; } return false; });🤖 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 156 - 172, The find callback recomputes JSON.stringify(this.sortObjectKeys(event)) for each handler; compute the eventKey once before calling handlers.find to avoid repeated serialization. Specifically, when event is an object, call this.sortObjectKeys(event) and JSON.stringify it into a const eventKey outside of the handlers.find, then inside the callback compare h.messageKey === eventKey (keep the existing string and object branches and checks around h.message/event types). This eliminates the per-handler O(N·k) serialization while preserving the logic in the handlers.find path that examines h.message, h.messageKey, and event.src/http/body/body-parser.spec.ts (1)
85-115: Tests don't simulate uWS ArrayBuffer neutering, limiting regression coverage.In real uWS, non-final
onDatachunks are neutered (detached/zeroed) after the callback returns, so any view-based buffering appears empty. The currenttoArrayBufferhelper produces independent ArrayBuffers that remain valid after the callback, so a regression that revertedBuffer.from(new Uint8Array(chunk))back toBuffer.from(chunk)(zero-copy view) would still pass these tests. Consider adding a neutering simulation (e.g.,structuredClone-based transfer or manually zeroing the backing array) for at least one multi-chunk test, so the copy semantics are actually validated.Based on learnings from PR
#23(src/platform/uws-request.ts:223-234): uWS neuters non-final ArrayBuffer chunks after the onData callback returns, requiringBuffer.from(new Uint8Array(chunk))for correct copy semantics.🤖 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 85 - 115, The tests don't simulate uWS neutering of non-final ArrayBuffer chunks, so add a neutering simulation to ensure BodyParser uses copy semantics: modify the test helper (toArrayBuffer) or add a new helper (e.g., simulateNeuteredArrayBuffer) and use it for at least one multi-chunk case so that after calling onDataCallback with a non-final chunk the underlying ArrayBuffer is zeroed/transferred (simulate structured clone transfer) before assertions; this will force the Buffer.from(new Uint8Array(chunk)) code path in BodyParser to be used and validate correct behavior (refer to BodyParser, onDataCallback, toArrayBuffer, and BUFFER_WATERMARK to locate the relevant tests and helpers).src/http/handlers/cors/cors-handler.ts (2)
161-182: Response casing foraccess-control-allow-headersis inconsistent across branches.When
allowedHeadersis explicitly configured ANDAccess-Control-Request-Headersis present (Line 163-172), the validated headers are lowercased before being joined, so the response contains'content-type'even if the user configured['Content-Type']. In the two sibling branches:
- Line 175 (permissive echo): returns the client-sent casing verbatim.
- Line 178 (explicit config, no requested headers): returns the user-configured casing verbatim.
Functionally harmless (HTTP header names are case-insensitive), but the inconsistent output is surprising and shows up in test assertions (e.g.
cors-handler.spec.tsLine 910 expects'content-type'while Line 948 expects'Content-Type, Authorization'). Consider preserving the configured casing for validated names.♻️ Proposed fix — preserve configured casing
if (requestedHeaders && this.allowedHeadersExplicitlySet) { // User explicitly configured allowedHeaders - validate requested headers - const requested = requestedHeaders.split(',').map((h) => h.trim().toLowerCase()); - const allowed = this.options.allowedHeaders.map((h) => h.toLowerCase()); - const validated = requested.filter((h) => allowed.includes(h)); + const requested = requestedHeaders.split(',').map((h) => h.trim()); + const allowedLower = this.options.allowedHeaders.map((h) => h.toLowerCase()); + // Keep only requested headers that are in the allowlist, preserving configured casing + const validated = requested + .map((h) => { + const idx = allowedLower.indexOf(h.toLowerCase()); + return idx >= 0 ? this.options.allowedHeaders[idx] : null; + }) + .filter((h): h is string => h !== null); if (validated.length === 0) { // Requested headers not allowed - reject preflight res.status(403).send(); return true; } allowedHeadersToSend = validated.join(', '); }(Test at
cors-handler.spec.ts:910would need to be updated to expect'Content-Type'.)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/handlers/cors/cors-handler.ts` around lines 161 - 182, The response lowercases validated requested headers causing inconsistent casing; update the validation in the block that checks this.allowedHeadersExplicitlySet and requestedHeaders so it matches requested names against this.options.allowedHeaders case-insensitively but builds allowedHeadersToSend using the original casing from this.options.allowedHeaders (not the lowercased requested values). In other words, when computing validated headers, map requested header names to the configured allowed header entries (this.options.allowedHeaders) by comparing toLowerCase() but push the configured entry (preserving its casing) into allowedHeadersToSend; keep the other branches (echoing requestedHeaders and joining this.options.allowedHeaders) unchanged.
161-172: Clarify the partial-allow design choice with a comment.The code silently allows a subset of requested headers when only some are in
allowedHeaders, matching Express cors middleware behavior. While standard, this can make misconfiguration harder to diagnose—the browser only blocks the actual request if a header was dropped from the preflight response. Add a comment explaining whether this partial-allow behavior is intentional (e.g., "Match Express cors behavior by allowing partial header sets") or consider whether you prefer stricter validation that rejects preflight if any requested header is disallowed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/handlers/cors/cors-handler.ts` around lines 161 - 172, Add a clarifying inline comment above the block that handles requestedHeaders (the code using this.allowedHeadersExplicitlySet, requestedHeaders, this.options.allowedHeaders, and allowedHeadersToSend) stating the design choice: that we intentionally match Express cors behavior by allowing a partial subset of requested headers (silently dropping disallowed ones) and noting the alternative stricter option (reject preflight if any requested header is disallowed) so future readers know this is deliberate and where to change behavior if stricter validation is desired.src/http/routing/route-registry.spec.ts (1)
263-341: LGTM — good documentation of the "first match wins" gotcha via tests.These tests clearly lock in registration-order semantics for complex routes sharing a wildcard prefix (e.g.
/users/:id?registered before/users/:name?always wins). Given this is a footgun — users coming from Express may expect "most specific wins" — consider documenting this precedence rule in the public README/migration guide alongside merging.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/routing/route-registry.spec.ts` around lines 263 - 341, The tests demonstrate and lock in a "first match wins" registration-order precedence for semantically equivalent routes (e.g., registry.register('GET','/users/:id?') before '/users/:name?') and you should document this behavior: update the public README and migration guide to explicitly state that route precedence is determined by registration order (not specificity), cite the registry.register API and the wildcard matching behavior (e.g., GET:/users/* and GET:/posts/*) as examples, warn that this differs from Express's "most specific wins" expectation, and include a short example showing how to avoid the footgun (register more specific handlers first or reorder registrations).src/http/core/context.ts (1)
79-90: Minor: hoist thegetNextno-op to avoid per-call allocation.Every call to
context.switchToHttp().getNext()allocates a fresh() => {}closure. On hot request paths under high RPS this is unnecessary allocation pressure. A module-level constant would serve the same purpose.♻️ Proposed refactor
import { ExecutionContext, Type } from '@nestjs/common'; import { UwsRequest } from './request'; import { UwsResponse } from './response'; /** * Handler function type for route handlers */ type RouteHandler = (req: UwsRequest, res: UwsResponse) => void | Promise<void>; + +// uWebSockets.js doesn't use Express-style next() middleware chaining, +// so `getNext()` returns this shared no-op. +const NOOP_NEXT = () => {}; @@ switchToHttp(): { getRequest: <T = UwsRequest>() => T; getResponse: <T = UwsResponse>() => T; getNext: <T = () => void>() => T; } { return { getRequest: <T = UwsRequest>() => this.request as T, getResponse: <T = UwsResponse>() => this.response as T, - // uWebSockets.js doesn't use Express-style next() middleware chaining - getNext: <T = () => void>() => (() => {}) as T, + getNext: <T = () => void>() => NOOP_NEXT as T, }; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/core/context.ts` around lines 79 - 90, The getNext implementation inside switchToHttp currently returns a new noop closure on every call (getNext: <T = () => void>() => (() => {}) as T), causing unnecessary allocations; hoist a single module-level constant noop (e.g., const NOOP_NEXT = () => {}) and change switchToHttp's getNext to return that constant (cast as T) so switchToHttp and getNext keep the same API but avoid per-call closure allocation; reference switchToHttp and getNext when making the change.src/http/platform/uws-platform.adapter.spec.ts (1)
167-191: Fragile coupling toprocess.nextTickfor the async-throw path.The test assumes the adapter uses
process.nextTickto surface the listen failure when no callback is provided. If the implementation is later refactored to usequeueMicrotask,setImmediate, orPromise.reject, this test will hang or fail in ways that are hard to diagnose. Consider asserting on an uncaught-exception or unhandled-rejection event instead, or making the async-throw mechanism part of a stable public contract.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/platform/uws-platform.adapter.spec.ts` around lines 167 - 191, The test currently spies on process.nextTick to catch the async throw from UwsPlatformAdapter.listen which couples the test to an implementation detail; change the test to listen for the platform-level async failure event instead (e.g., process.once('unhandledRejection') or process.once('uncaughtException')) and assert the Error message 'Failed to listen on 0.0.0.0:3000' from that handler; implement the Promise wrapper so it registers the one-time event listener, calls adapter.listen(3000) (with mockUwsApp.listen simulating failure), resolves/rejects from the event callback, and always restores/cleans up the listener to avoid leaks — refer to UwsPlatformAdapter.listen and the existing test case name when updating the spec.src/http/core/context.spec.ts (1)
100-106: Nit: test name is slightly misleading.The test is titled "should return same instances on multiple calls" but asserts on the identity of the inner
request/responsereturned bygetRequest()/getResponse(), not on the identity of the object returned byswitchToHttp()itself (which is re-created per call — seecontext.ts:84). Consider renaming to e.g. "should return the same request/response across multiple calls".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/core/context.spec.ts` around lines 100 - 106, Rename the test to accurately reflect what it asserts: change the test description from "should return same instances on multiple calls" to something like "should return the same request/response across multiple calls" so it references the behavior of context.switchToHttp()'s returned getRequest()/getResponse() identity; update the it(...) string in the spec where context.switchToHttp(), getRequest(), and getResponse() are used to match the new description.src/http/body/body-parser.ts (1)
298-316: Premature disconnect returns a shorter buffer as if success.When
isLast=truefires but fewer bytes thanContent-Lengthwere delivered,buffer()resolves with a truncated slice (Line 314). Callers relying onContent-Length(JSON parsing, checksum, etc.) will silently accept a corrupted body. Consider rejecting with a specific error — or at minimum emitting a'truncated'event — so consumers can distinguish truncation from a legitimately short payload.In practice, uWS signals aborts via
onAborted(handled at Line 84), soisLast=truewith short bytes should be rare; this is defense-in-depth.♻️ Sketch
this.passthroughCallback = (chunk, isLast) => { const bytesToCopy = Math.min(chunk.length, buffer.length - offset); if (bytesToCopy > 0) { chunk.copy(buffer, offset, 0, bytesToCopy); offset += bytesToCopy; } if (isLast) { this.abortCallback = undefined; this.pendingReject = undefined; - resolve(offset === buffer.length ? buffer : buffer.subarray(0, offset)); + if (offset < buffer.length) { + reject( + new Error( + `Body truncated: received ${offset} of ${buffer.length} bytes` + ) + ); + return; + } + resolve(buffer); } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/body/body-parser.ts` around lines 298 - 316, The current passthrough handler in body-parser resolves with a truncated buffer when isLast is true but offset < this.expectedBytes; change this behavior in the passthroughCallback (the closure assigned to this.passthroughCallback) so that when isLast is true and offset !== this.expectedBytes it does not resolve silently — instead reject the pending promise (using this.pendingReject) with a specific TruncatedBodyError (or emit a 'truncated' event on the parser instance) and clear this.abortCallback/this.pendingReject as you currently do on normal completion; update any code that relies on resolve to handle this rejection/event so callers can distinguish truncated bodies from legitimate shorter payloads.src/http/body/multipart-handler.ts (1)
271-293: ClearmultipartPromisein afinallyblock to avoid stale state after a handler error.If
handler(field)rejects, Line 292 is skipped, leavingmultipartPromiseset to the rejected promise. Any concurrent handler stuck in thewhileloop will re-await and re-throw that same rejection. In practice this is benign because.catch(finish)at Lines 196/202 destroys the uploader and stops further events — but afinallykeeps state cleanly defined.♻️ Proposed tweak
- this.multipartPromise = handlerPromise; - - // Now safe to resume - any new events will see multipartPromise and wait - this.request.resume(); - - // Wait for handler to complete - await handlerPromise; - this.multipartPromise = null; + this.multipartPromise = handlerPromise; + + // Now safe to resume - any new events will see multipartPromise and wait + this.request.resume(); + + try { + // Wait for handler to complete + await handlerPromise; + } finally { + this.multipartPromise = null; + }🤖 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 271 - 293, The executeHandler function can leave this.multipartPromise set to a rejected promise if handler(field) throws; change the flow to ensure this.multipartPromise is cleared in a finally block: create handlerPromise from handler(field) as done now, assign this.multipartPromise = handlerPromise before calling this.request.resume(), then await handlerPromise inside a try/finally and set this.multipartPromise = null in the finally so a rejection cannot leave stale state (refer to executeHandler, multipartPromise, handler, handlerPromise, request.pause/request.resume).src/http/handlers/compression/compression-handler.ts (1)
188-218: StackedContent-Encodingwith an unknown layer silently yields misleading errors.For
Content-Encoding: gzip, unknown, reverse-order decoding starts withunknown(no-op) and then tries to gunzip the outerunknown-wrapped bytes — which will almost always throw a confusing "Failed to decompress request body with encoding 'gzip, unknown'". Consider explicitly rejecting unknown encodings with an HTTP-appropriate error (e.g., surfacing as 415 Unsupported Media Type at the caller) rather than skipping them, so the failure mode is clear. Pure-unknownpass-through is already covered by tests and can remain intentional behavior.♻️ Sketch
- default: - // Unknown encoding - skip it and continue with other encodings - // This allows partial decompression when some encodings are recognized - break; + default: + // Unknown encoding: pass through only if it's the sole encoding + // (lenient), otherwise fail fast to surface the real problem. + if (encodings.length > 1) { + throw new Error(`Unsupported content-encoding: '${encoding}'`); + } + break;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/handlers/compression/compression-handler.ts` around lines 188 - 218, The current reverse-loop over encodings silently skips unknown values (variable encodings) which leads to misleading decompression errors later; update the loop in the decompression routine (the block that calls gunzip, inflate, brotliDecompress) to detect any unknown encoding immediately and throw a clear, specific error (include the offending encoding and full encodings list in the message) instead of continuing, so callers can map it to HTTP 415; preserve existing pass-through behavior for the single-case "unknown" test by only rejecting when an unknown encoding appears alongside others (i.e., when encodings.length > 1 or when the unknown is not the sole entry).src/http/handlers/static/static-file-handler.ts (1)
664-696:parseTokenListignores horizontal tab (0x09) as whitespace.HTTP header OWS permits both SP (0x20) and HTAB (0x09). A header like
If-None-Match: "abc",\t"def"will produce a token starting with\tthat never matches any ETag. Low impact since most clients use only spaces, but easy to harden.♻️ Proposed tweak
private skipWhitespace(str: string, start: number, len: number): number { let i = start; - while (i < len && str.charCodeAt(i) === 0x20) i++; + while (i < len && (str.charCodeAt(i) === 0x20 || str.charCodeAt(i) === 0x09)) i++; return i; }And consider tab in
extractToken's unquoted-token terminator too.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/handlers/static/static-file-handler.ts` around lines 664 - 696, The parser currently treats only SP (0x20) as whitespace, so update skipWhitespace and extractToken to recognize HTAB (0x09) as OWS: in skipWhitespace (method skipWhitespace) change the loop condition so it advances while charCodeAt(i) is 0x20 OR 0x09; in extractToken (method extractToken) treat HTAB as whitespace/terminator for unquoted tokens by adding 0x09 alongside the existing 0x20 and 0x2c checks so unquoted-token scanning stops on comma, space, or tab; this will ensure tokens like "\t\"def\"" are not prefixed with a tab and will match ETags correctly.src/http/routing/route-registry.ts (1)
687-710: Optional: guard against ReDoS on registered patterns.
new RegExp(^${regexPattern}$)builds a pattern from the path string. In practice paths are app-developer-authored, so exploitability is low, but the.replace(/\*/g, '.*')produces a greedy.*segment per*token that can, when combined with adjacent optional groups, create catastrophic-backtracking candidates (e.g.,/a/*/b/*/c/*/d). If you ever accept externally-provided route patterns (plugins, config), consider either.*?(non-greedy) or validating compiled patterns through a library likerecheck. Safe to defer for now.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/http/routing/route-registry.ts` around lines 687 - 710, The current pathToRegex builds regexPattern using .replace(/\*/g, '.*') which inserts greedy dot-star tokens and can enable ReDoS; change the wildcard handling in pathToRegex (the .replace for '*' on regexPattern) to a safer token such as a non-greedy or segment-limited matcher (e.g., use a non-greedy '.*?' or better '[^/]*' so '*' does not cross slashes), and if your app ever accepts external patterns add validation/limits before creating the RegExp; update the .replace call and keep the rest of pathToRegex (parameter handling) unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 549039b9-6059-4c12-adc4-4917909829d7
⛔ Files ignored due to path filters (2)
assets/uWestJS.pngis excluded by!**/*.pngpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (109)
.github/workflows/ci.yml.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/uws-options.interface.tssrc/interfaces/websocket-client.interface.tssrc/middleware/filters/index.tssrc/middleware/guards/index.tssrc/middleware/index.tssrc/middleware/pipes/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.tstsconfig.build.jsontsconfig.json
💤 Files with no reviewable changes (10)
- src/websocket/interfaces/index.ts
- src/socket/index.ts
- src/exceptions/index.ts
- src/middleware/guards/index.ts
- src/middleware/filters/index.ts
- src/interfaces/websocket-client.interface.ts
- src/middleware/pipes/index.ts
- src/middleware/index.ts
- src/interfaces/uws-options.interface.ts
- src/decorators/index.ts
…andling, re-entrancy issue in the send() method. Update the default poolSize to be CPU-aware
Warning: Breaking Change
Includes all the PRs in which the work has been done. This is a big breaking change as everything has been re-organized as well to be domain-driven. Future things like SSE support and anything else can be easily added.
Make sure to keep yourself updated with this one.
Not currently maintaining the CHANGELOG.md as it would've required me to add an issue for every small thing, But after this whole thing we'll be maintaining CHANGELOG.md as well and also add some benchmark workflows and also include Documentation for everything.
Summary by CodeRabbit
New Features
Enhancements
Chores