@webpieces/http-server 0.4.806 → 0.4.807

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-server",
3
- "version": "0.4.806",
3
+ "version": "0.4.807",
4
4
  "description": "WebPieces server with filter chain and dependency injection",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,10 +22,10 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@webpieces/core-context": "0.4.806",
26
- "@webpieces/core-util": "0.4.806",
27
- "@webpieces/gcp-identity": "0.4.806",
28
- "@webpieces/http-routing": "0.4.806",
25
+ "@webpieces/core-context": "0.4.807",
26
+ "@webpieces/core-util": "0.4.807",
27
+ "@webpieces/gcp-identity": "0.4.807",
28
+ "@webpieces/http-routing": "0.4.807",
29
29
  "cors": "2.8.5",
30
30
  "express": "5.1.0",
31
31
  "inversify": "7.10.4"
@@ -23,7 +23,7 @@ class SseResponseChannel {
23
23
  writer;
24
24
  constructor(res, route, validator) {
25
25
  this.res = res;
26
- this.writer = new core_util_1.StreamWriter((envelope) => this.deliver(envelope), (value) => validator.validate(route.streaming.responseEventClass, value, 'response'), route.streaming?.supportsNonTerminalFailures);
26
+ this.writer = new core_util_1.StreamWriter((envelope) => this.deliver(envelope), (value) => validator.validate(route.streaming.responseSchema, value, 'response'), route.streaming?.supportsNonTerminalFailures);
27
27
  }
28
28
  isEnded() {
29
29
  return this.ended;
@@ -216,7 +216,7 @@ class StreamExpressWrapper {
216
216
  }
217
217
  const envelope = this.parseEnvelope(value);
218
218
  if (envelope.kind === 'event') {
219
- this.validator.validate(this.route.streaming.requestEventClass, envelope.value, 'request');
219
+ this.validator.validate(this.route.streaming.requestSchema, envelope.value, 'request');
220
220
  await writer.event(envelope.value, envelope.correlation);
221
221
  }
222
222
  else if (envelope.kind === 'failure') {
@@ -1 +1 @@
1
- {"version":3,"file":"StreamExpressWrapper.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/StreamExpressWrapper.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from 'node:events';\nimport { NextFunction, Request, Response } from 'express';\nimport {\n ApiBadRequestError,\n ApiErrorCodec,\n DtoValue,\n RequestStream,\n ResponseStream,\n RouteMetadata,\n StreamCorrelation,\n StreamEnvelope,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n toError,\n} from '@webpieces/core-util';\nimport { HttpRequest, RequestContext, RequestContextHeaders } from '@webpieces/core-context';\nimport { ExpressWrapper } from './ExpressWrapper';\n\nconst KEEP_ALIVE_MILLIS = 15_000;\nconst MAX_LINE_BYTES = 1024 * 1024;\nconst MAX_HANDSHAKE_BUFFER_BYTES = 64 * 1024;\n\n/** Owns ordered SSE writes and the small pre-handshake response buffer. */\nclass SseResponseChannel {\n private opened = false;\n private ended = false;\n private queuedBytes = 0;\n private readonly queued: string[] = [];\n private finish: () => void = () => undefined;\n private pendingWrite: Promise<void> = Promise.resolve();\n readonly finished = new Promise<void>((resolve: () => void): void => {\n this.finish = resolve;\n });\n readonly writer: StreamWriter<DtoValue>;\n\n constructor(\n private readonly res: Response,\n route: RouteMetadata,\n validator: StreamEventValidator,\n ) {\n this.writer = new StreamWriter<DtoValue>(\n (envelope: StreamEnvelope<DtoValue>): Promise<void> => this.deliver(envelope),\n (value: DtoValue): void =>\n validator.validate(route.streaming!.responseEventClass, value, 'response'),\n route.streaming?.supportsNonTerminalFailures,\n );\n }\n\n isEnded(): boolean {\n return this.ended;\n }\n\n async open(): Promise<void> {\n this.res.status(200);\n this.res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n this.res.setHeader('Cache-Control', 'no-cache, no-transform');\n this.res.setHeader('Connection', 'keep-alive');\n this.res.setHeader('X-Accel-Buffering', 'no');\n this.res.flushHeaders();\n this.opened = true;\n for (const frame of this.queued) await this.enqueueWrite(frame);\n this.queued.length = 0;\n if (this.ended) this.res.end();\n }\n\n async keepAlive(): Promise<void> {\n if (!this.ended) await this.enqueueWrite(': keepalive\\n\\n');\n }\n\n // webpieces-disable no-any-unknown -- cancellation reasons are transport-neutral\n async cancel(reason?: unknown): Promise<void> {\n await this.writer.cancel(reason);\n this.finish();\n }\n\n private async deliver(envelope: StreamEnvelope<DtoValue>): Promise<void> {\n const frame = `event: message\\ndata: ${JSON.stringify(envelope)}\\n\\n`;\n if (this.opened) {\n await this.enqueueWrite(frame);\n } else {\n this.queuedBytes += Buffer.byteLength(frame);\n if (this.queuedBytes > MAX_HANDSHAKE_BUFFER_BYTES) {\n throw new StreamTransportError(\n `Streaming controller wrote more than ${MAX_HANDSHAKE_BUFFER_BYTES} bytes before its request writer was ready.`,\n );\n }\n this.queued.push(frame);\n }\n if (envelope.kind === 'complete' || (envelope.kind === 'failure' && envelope.terminal)) {\n this.ended = true;\n if (this.opened) this.res.end();\n this.finish();\n }\n }\n\n private enqueueWrite(data: string): Promise<void> {\n const delivery = this.pendingWrite.then((): Promise<void> => this.write(data));\n this.pendingWrite = delivery.catch((): void => undefined);\n return delivery;\n }\n\n private async write(data: string): Promise<void> {\n if (this.res.destroyed || this.res.writableEnded) {\n throw new StreamTransportError('Cannot write to a closed streaming HTTP response.');\n }\n if (this.res.write(data)) return;\n await Promise.race([\n once(this.res, 'drain').then((): void => undefined),\n once(this.res, 'close').then((): never => {\n throw new StreamTransportError('Streaming HTTP peer disconnected during write.');\n }),\n once(this.res, 'error').then((args): never => {\n throw new StreamTransportError(\n 'Streaming HTTP response failed.',\n undefined,\n undefined,\n toError(args[0]),\n );\n }),\n ]);\n }\n}\n\n/** Express adapter for a `@WpStream` request-NDJSON / response-SSE contract. */\nexport class StreamExpressWrapper {\n private readonly ordinaryErrors: ExpressWrapper;\n private readonly validator = new StreamEventValidator();\n\n constructor(\n // webpieces-disable no-any-unknown -- stream contract types are erased at the express boundary\n private readonly clientMethod: (...args: unknown[]) => Promise<unknown>,\n private readonly route: RouteMetadata,\n private readonly headers: RequestContextHeaders,\n ) {\n this.ordinaryErrors = new ExpressWrapper(\n clientMethod,\n route.path,\n headers,\n false,\n false,\n undefined,\n route,\n );\n }\n\n async execute(req: Request, res: Response, next: NextFunction): Promise<void> {\n void next;\n await RequestContext.run(async () => this.executeInContext(req, res));\n }\n\n private async executeInContext(req: Request, res: Response): Promise<void> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- owning streaming HTTP error boundary; handshake failures still use ordinary status/error mapping\n try {\n this.headers.fillFromRequest(this.toWebpiecesRequest(req));\n this.requireNdjson(req);\n const outbound = new SseResponseChannel(res, this.route, this.validator);\n const candidate = await this.clientMethod(outbound.writer as ResponseStream<DtoValue>);\n if (!this.isRequestWriter(candidate)) {\n throw new StreamTransportError(\n `${this.route.apiName ?? 'API'}.${this.route.methodName} did not return a RequestStream.`,\n );\n }\n const inbound = candidate;\n await outbound.open();\n if (outbound.isEnded()) {\n await inbound.cancel(new StreamTransportError('Response stream completed.'));\n return;\n }\n await this.runOpenStream(req, res, inbound, outbound);\n } catch (err: unknown) {\n const error = toError(err);\n this.ordinaryErrors.handleError(res, error);\n }\n }\n\n private async runOpenStream(\n req: Request,\n res: Response,\n inbound: RequestStream<DtoValue>,\n outbound: SseResponseChannel,\n ): Promise<void> {\n let disconnected = false;\n const cancel = async (): Promise<void> => {\n if (disconnected || outbound.isEnded()) return;\n disconnected = true;\n const error = new StreamTransportError('Streaming HTTP peer disconnected.');\n await inbound.cancel(error);\n await outbound.cancel(error);\n };\n req.once('aborted', (): void => void cancel());\n res.once('close', (): void => void cancel());\n const keepAlive = setInterval((): void => {\n if (!disconnected) void outbound.keepAlive().catch(() => cancel());\n }, KEEP_ALIVE_MILLIS);\n keepAlive.unref();\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- after SSE opens, wire failures must become an in-band typed terminal event\n try {\n const inboundFinished = this.consumeNdjson(req, inbound);\n const responseFinishedFirst = await Promise.race([\n inboundFinished.then((): boolean => false),\n outbound.finished.then((): boolean => true),\n ]);\n if (responseFinishedFirst) {\n if (!disconnected) {\n await inbound.cancel(new StreamTransportError('Response stream completed.'));\n }\n req.destroy();\n await inboundFinished.catch((): void => undefined);\n return;\n }\n await outbound.finished;\n } catch (err: unknown) {\n const error = toError(err);\n const streamError =\n err instanceof StreamTransportError\n ? err\n : new StreamTransportError(\n 'Streaming HTTP request failed.',\n undefined,\n undefined,\n error,\n );\n if (!disconnected) {\n if (!outbound.isEnded()) await outbound.writer.fail(streamError);\n await inbound.fail(streamError);\n }\n } finally {\n clearInterval(keepAlive);\n }\n }\n\n private async consumeNdjson(req: Request, writer: RequestStream<DtoValue>): Promise<void> {\n let pending = Buffer.alloc(0);\n for await (const raw of req) {\n const chunk = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);\n pending = Buffer.concat([pending, chunk]);\n if (pending.length > MAX_LINE_BYTES && pending.indexOf(10) < 0) {\n throw new StreamTransportError(`NDJSON frame exceeds ${MAX_LINE_BYTES} bytes.`);\n }\n let newline = pending.indexOf(10);\n while (newline >= 0) {\n const line = pending.subarray(0, newline).toString('utf8').trim();\n pending = pending.subarray(newline + 1);\n if (line !== '') await this.dispatchLine(line, writer);\n newline = pending.indexOf(10);\n }\n }\n const finalLine = pending.toString('utf8').trim();\n if (finalLine !== '') await this.dispatchLine(finalLine, writer);\n }\n\n private async dispatchLine(line: string, writer: RequestStream<DtoValue>): Promise<void> {\n let value: DtoValue;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- JSON.parse is the untrusted NDJSON boundary and is translated to StreamTransportError\n try {\n value = JSON.parse(line) as DtoValue;\n } catch (err: unknown) {\n const error = toError(err);\n throw new StreamTransportError(\n 'Malformed NDJSON stream frame.',\n undefined,\n undefined,\n error,\n );\n }\n const envelope = this.parseEnvelope(value);\n if (envelope.kind === 'event') {\n this.validator.validate(\n this.route.streaming!.requestEventClass,\n envelope.value,\n 'request',\n );\n await writer.event(envelope.value!, envelope.correlation);\n } else if (envelope.kind === 'failure') {\n await writer.fail(ApiErrorCodec.decode(envelope.error), envelope.correlation, {\n terminal: envelope.terminal,\n });\n } else {\n await writer.complete();\n }\n }\n\n private parseEnvelope(value: DtoValue): StreamEnvelope<DtoValue> {\n if (typeof value !== 'object' || value === null) {\n throw new StreamTransportError('NDJSON stream frame must be an object.');\n }\n const record = value as Record<string, DtoValue | undefined>;\n const kind = record['kind'];\n const correlation = this.parseCorrelation(record['correlation']);\n if (kind === 'event')\n return new StreamEnvelope('event', record['value'], undefined, correlation);\n if (kind === 'complete') {\n return new StreamEnvelope<DtoValue>('complete', undefined, undefined, correlation);\n }\n if (kind === 'failure' && ApiErrorCodec.isPayload(record['error'])) {\n const terminal = record['terminal'];\n if (terminal !== undefined && typeof terminal !== 'boolean') {\n throw new StreamTransportError('NDJSON failure terminal flag must be boolean.');\n }\n const error = ApiErrorCodec.encode(ApiErrorCodec.decode(record['error']));\n return new StreamEnvelope<DtoValue>('failure', undefined, error, correlation, terminal);\n }\n throw new StreamTransportError(\n `Unknown or malformed NDJSON stream frame kind '${String(kind)}'.`,\n );\n }\n\n private parseCorrelation(value: DtoValue | undefined): StreamCorrelation | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'object' || value === null) {\n throw new StreamTransportError('Stream correlation must be an object.');\n }\n const record = value as Record<string, DtoValue | undefined>;\n const key = record['key'];\n const requestId = record['requestId'];\n if (\n (typeof key !== 'string' && typeof key !== 'number') ||\n (requestId !== undefined && typeof requestId !== 'string')\n ) {\n throw new StreamTransportError('Stream correlation has an invalid key or requestId.');\n }\n return new StreamCorrelation(key, requestId);\n }\n\n private requireNdjson(req: Request): void {\n const contentType = req.get('content-type')?.split(';')[0]?.trim().toLowerCase();\n if (contentType !== 'application/x-ndjson') {\n throw new ApiBadRequestError(\n 'Streaming endpoints require Content-Type application/x-ndjson.',\n );\n }\n }\n\n // webpieces-disable no-any-unknown -- runtime guard narrows an erased API return value\n private isRequestWriter(value: unknown): value is RequestStream<DtoValue> {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Record<string, DtoValue | undefined>;\n return ['event', 'fail', 'complete', 'cancel'].every(\n (name: string) => typeof candidate[name] === 'function',\n );\n }\n\n private toWebpiecesRequest(req: Request): HttpRequest {\n const headers = new Map<string, string[]>();\n for (const [name, value] of Object.entries(req.headers)) {\n if (typeof value === 'string') headers.set(name.toLowerCase(), [value]);\n else if (Array.isArray(value)) headers.set(name.toLowerCase(), value);\n }\n return new HttpRequest(req.method, req.path ?? this.route.path, headers);\n }\n}\n"]}
1
+ {"version":3,"file":"StreamExpressWrapper.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/StreamExpressWrapper.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from 'node:events';\nimport { NextFunction, Request, Response } from 'express';\nimport {\n ApiBadRequestError,\n ApiErrorCodec,\n DtoValue,\n RequestStream,\n ResponseStream,\n RouteMetadata,\n StreamCorrelation,\n StreamEnvelope,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n toError,\n} from '@webpieces/core-util';\nimport { HttpRequest, RequestContext, RequestContextHeaders } from '@webpieces/core-context';\nimport { ExpressWrapper } from './ExpressWrapper';\n\nconst KEEP_ALIVE_MILLIS = 15_000;\nconst MAX_LINE_BYTES = 1024 * 1024;\nconst MAX_HANDSHAKE_BUFFER_BYTES = 64 * 1024;\n\n/** Owns ordered SSE writes and the small pre-handshake response buffer. */\nclass SseResponseChannel {\n private opened = false;\n private ended = false;\n private queuedBytes = 0;\n private readonly queued: string[] = [];\n private finish: () => void = () => undefined;\n private pendingWrite: Promise<void> = Promise.resolve();\n readonly finished = new Promise<void>((resolve: () => void): void => {\n this.finish = resolve;\n });\n readonly writer: StreamWriter<DtoValue>;\n\n constructor(\n private readonly res: Response,\n route: RouteMetadata,\n validator: StreamEventValidator,\n ) {\n this.writer = new StreamWriter<DtoValue>(\n (envelope: StreamEnvelope<DtoValue>): Promise<void> => this.deliver(envelope),\n (value: DtoValue): void =>\n validator.validate(route.streaming!.responseSchema, value, 'response'),\n route.streaming?.supportsNonTerminalFailures,\n );\n }\n\n isEnded(): boolean {\n return this.ended;\n }\n\n async open(): Promise<void> {\n this.res.status(200);\n this.res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n this.res.setHeader('Cache-Control', 'no-cache, no-transform');\n this.res.setHeader('Connection', 'keep-alive');\n this.res.setHeader('X-Accel-Buffering', 'no');\n this.res.flushHeaders();\n this.opened = true;\n for (const frame of this.queued) await this.enqueueWrite(frame);\n this.queued.length = 0;\n if (this.ended) this.res.end();\n }\n\n async keepAlive(): Promise<void> {\n if (!this.ended) await this.enqueueWrite(': keepalive\\n\\n');\n }\n\n // webpieces-disable no-any-unknown -- cancellation reasons are transport-neutral\n async cancel(reason?: unknown): Promise<void> {\n await this.writer.cancel(reason);\n this.finish();\n }\n\n private async deliver(envelope: StreamEnvelope<DtoValue>): Promise<void> {\n const frame = `event: message\\ndata: ${JSON.stringify(envelope)}\\n\\n`;\n if (this.opened) {\n await this.enqueueWrite(frame);\n } else {\n this.queuedBytes += Buffer.byteLength(frame);\n if (this.queuedBytes > MAX_HANDSHAKE_BUFFER_BYTES) {\n throw new StreamTransportError(\n `Streaming controller wrote more than ${MAX_HANDSHAKE_BUFFER_BYTES} bytes before its request writer was ready.`,\n );\n }\n this.queued.push(frame);\n }\n if (envelope.kind === 'complete' || (envelope.kind === 'failure' && envelope.terminal)) {\n this.ended = true;\n if (this.opened) this.res.end();\n this.finish();\n }\n }\n\n private enqueueWrite(data: string): Promise<void> {\n const delivery = this.pendingWrite.then((): Promise<void> => this.write(data));\n this.pendingWrite = delivery.catch((): void => undefined);\n return delivery;\n }\n\n private async write(data: string): Promise<void> {\n if (this.res.destroyed || this.res.writableEnded) {\n throw new StreamTransportError('Cannot write to a closed streaming HTTP response.');\n }\n if (this.res.write(data)) return;\n await Promise.race([\n once(this.res, 'drain').then((): void => undefined),\n once(this.res, 'close').then((): never => {\n throw new StreamTransportError('Streaming HTTP peer disconnected during write.');\n }),\n once(this.res, 'error').then((args): never => {\n throw new StreamTransportError(\n 'Streaming HTTP response failed.',\n undefined,\n undefined,\n toError(args[0]),\n );\n }),\n ]);\n }\n}\n\n/** Express adapter for a `@WpStream` request-NDJSON / response-SSE contract. */\nexport class StreamExpressWrapper {\n private readonly ordinaryErrors: ExpressWrapper;\n private readonly validator = new StreamEventValidator();\n\n constructor(\n // webpieces-disable no-any-unknown -- stream contract types are erased at the express boundary\n private readonly clientMethod: (...args: unknown[]) => Promise<unknown>,\n private readonly route: RouteMetadata,\n private readonly headers: RequestContextHeaders,\n ) {\n this.ordinaryErrors = new ExpressWrapper(\n clientMethod,\n route.path,\n headers,\n false,\n false,\n undefined,\n route,\n );\n }\n\n async execute(req: Request, res: Response, next: NextFunction): Promise<void> {\n void next;\n await RequestContext.run(async () => this.executeInContext(req, res));\n }\n\n private async executeInContext(req: Request, res: Response): Promise<void> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- owning streaming HTTP error boundary; handshake failures still use ordinary status/error mapping\n try {\n this.headers.fillFromRequest(this.toWebpiecesRequest(req));\n this.requireNdjson(req);\n const outbound = new SseResponseChannel(res, this.route, this.validator);\n const candidate = await this.clientMethod(outbound.writer as ResponseStream<DtoValue>);\n if (!this.isRequestWriter(candidate)) {\n throw new StreamTransportError(\n `${this.route.apiName ?? 'API'}.${this.route.methodName} did not return a RequestStream.`,\n );\n }\n const inbound = candidate;\n await outbound.open();\n if (outbound.isEnded()) {\n await inbound.cancel(new StreamTransportError('Response stream completed.'));\n return;\n }\n await this.runOpenStream(req, res, inbound, outbound);\n } catch (err: unknown) {\n const error = toError(err);\n this.ordinaryErrors.handleError(res, error);\n }\n }\n\n private async runOpenStream(\n req: Request,\n res: Response,\n inbound: RequestStream<DtoValue>,\n outbound: SseResponseChannel,\n ): Promise<void> {\n let disconnected = false;\n const cancel = async (): Promise<void> => {\n if (disconnected || outbound.isEnded()) return;\n disconnected = true;\n const error = new StreamTransportError('Streaming HTTP peer disconnected.');\n await inbound.cancel(error);\n await outbound.cancel(error);\n };\n req.once('aborted', (): void => void cancel());\n res.once('close', (): void => void cancel());\n const keepAlive = setInterval((): void => {\n if (!disconnected) void outbound.keepAlive().catch(() => cancel());\n }, KEEP_ALIVE_MILLIS);\n keepAlive.unref();\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- after SSE opens, wire failures must become an in-band typed terminal event\n try {\n const inboundFinished = this.consumeNdjson(req, inbound);\n const responseFinishedFirst = await Promise.race([\n inboundFinished.then((): boolean => false),\n outbound.finished.then((): boolean => true),\n ]);\n if (responseFinishedFirst) {\n if (!disconnected) {\n await inbound.cancel(new StreamTransportError('Response stream completed.'));\n }\n req.destroy();\n await inboundFinished.catch((): void => undefined);\n return;\n }\n await outbound.finished;\n } catch (err: unknown) {\n const error = toError(err);\n const streamError =\n err instanceof StreamTransportError\n ? err\n : new StreamTransportError(\n 'Streaming HTTP request failed.',\n undefined,\n undefined,\n error,\n );\n if (!disconnected) {\n if (!outbound.isEnded()) await outbound.writer.fail(streamError);\n await inbound.fail(streamError);\n }\n } finally {\n clearInterval(keepAlive);\n }\n }\n\n private async consumeNdjson(req: Request, writer: RequestStream<DtoValue>): Promise<void> {\n let pending = Buffer.alloc(0);\n for await (const raw of req) {\n const chunk = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);\n pending = Buffer.concat([pending, chunk]);\n if (pending.length > MAX_LINE_BYTES && pending.indexOf(10) < 0) {\n throw new StreamTransportError(`NDJSON frame exceeds ${MAX_LINE_BYTES} bytes.`);\n }\n let newline = pending.indexOf(10);\n while (newline >= 0) {\n const line = pending.subarray(0, newline).toString('utf8').trim();\n pending = pending.subarray(newline + 1);\n if (line !== '') await this.dispatchLine(line, writer);\n newline = pending.indexOf(10);\n }\n }\n const finalLine = pending.toString('utf8').trim();\n if (finalLine !== '') await this.dispatchLine(finalLine, writer);\n }\n\n private async dispatchLine(line: string, writer: RequestStream<DtoValue>): Promise<void> {\n let value: DtoValue;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- JSON.parse is the untrusted NDJSON boundary and is translated to StreamTransportError\n try {\n value = JSON.parse(line) as DtoValue;\n } catch (err: unknown) {\n const error = toError(err);\n throw new StreamTransportError(\n 'Malformed NDJSON stream frame.',\n undefined,\n undefined,\n error,\n );\n }\n const envelope = this.parseEnvelope(value);\n if (envelope.kind === 'event') {\n this.validator.validate(this.route.streaming!.requestSchema, envelope.value, 'request');\n await writer.event(envelope.value!, envelope.correlation);\n } else if (envelope.kind === 'failure') {\n await writer.fail(ApiErrorCodec.decode(envelope.error), envelope.correlation, {\n terminal: envelope.terminal,\n });\n } else {\n await writer.complete();\n }\n }\n\n private parseEnvelope(value: DtoValue): StreamEnvelope<DtoValue> {\n if (typeof value !== 'object' || value === null) {\n throw new StreamTransportError('NDJSON stream frame must be an object.');\n }\n const record = value as Record<string, DtoValue | undefined>;\n const kind = record['kind'];\n const correlation = this.parseCorrelation(record['correlation']);\n if (kind === 'event')\n return new StreamEnvelope('event', record['value'], undefined, correlation);\n if (kind === 'complete') {\n return new StreamEnvelope<DtoValue>('complete', undefined, undefined, correlation);\n }\n if (kind === 'failure' && ApiErrorCodec.isPayload(record['error'])) {\n const terminal = record['terminal'];\n if (terminal !== undefined && typeof terminal !== 'boolean') {\n throw new StreamTransportError('NDJSON failure terminal flag must be boolean.');\n }\n const error = ApiErrorCodec.encode(ApiErrorCodec.decode(record['error']));\n return new StreamEnvelope<DtoValue>('failure', undefined, error, correlation, terminal);\n }\n throw new StreamTransportError(\n `Unknown or malformed NDJSON stream frame kind '${String(kind)}'.`,\n );\n }\n\n private parseCorrelation(value: DtoValue | undefined): StreamCorrelation | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'object' || value === null) {\n throw new StreamTransportError('Stream correlation must be an object.');\n }\n const record = value as Record<string, DtoValue | undefined>;\n const key = record['key'];\n const requestId = record['requestId'];\n if (\n (typeof key !== 'string' && typeof key !== 'number') ||\n (requestId !== undefined && typeof requestId !== 'string')\n ) {\n throw new StreamTransportError('Stream correlation has an invalid key or requestId.');\n }\n return new StreamCorrelation(key, requestId);\n }\n\n private requireNdjson(req: Request): void {\n const contentType = req.get('content-type')?.split(';')[0]?.trim().toLowerCase();\n if (contentType !== 'application/x-ndjson') {\n throw new ApiBadRequestError(\n 'Streaming endpoints require Content-Type application/x-ndjson.',\n );\n }\n }\n\n // webpieces-disable no-any-unknown -- runtime guard narrows an erased API return value\n private isRequestWriter(value: unknown): value is RequestStream<DtoValue> {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Record<string, DtoValue | undefined>;\n return ['event', 'fail', 'complete', 'cancel'].every(\n (name: string) => typeof candidate[name] === 'function',\n );\n }\n\n private toWebpiecesRequest(req: Request): HttpRequest {\n const headers = new Map<string, string[]>();\n for (const [name, value] of Object.entries(req.headers)) {\n if (typeof value === 'string') headers.set(name.toLowerCase(), [value]);\n else if (Array.isArray(value)) headers.set(name.toLowerCase(), value);\n }\n return new HttpRequest(req.method, req.path ?? this.route.path, headers);\n }\n}\n"]}