@nmtjs/protocol 0.6.0 → 0.6.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../lib/server/protocol.ts"],"sourcesContent":["import { type Callback, createPromise, defer, throwError } from '@nmtjs/common'\nimport { type Container, Hook, type Logger, Scope } from '@nmtjs/core'\nimport { concat, decodeNumber, encodeNumber } from '../common/binary.ts'\nimport type { ProtocolBlob, ProtocolBlobMetadata } from '../common/blob.ts'\nimport { ErrorCode, ServerMessageType } from '../common/enums.ts'\nimport type { ProtocolRPC } from '../common/types.ts'\nimport type { ProtocolApi, ProtocolApiCallResult } from './api.ts'\nimport {\n Connection,\n ConnectionContext,\n type ConnectionOptions,\n} from './connection.ts'\nimport type { Format } from './format.ts'\nimport type { ProtocolRegistry } from './registry.ts'\nimport { ProtocolClientStream, ProtocolServerStream } from './stream.ts'\nimport type { Transport } from './transport.ts'\nimport { type ResolveFormatParams, getFormat } from './utils.ts'\n\nexport class ProtocolError extends Error {\n code: string\n data?: any\n\n constructor(code: string, message?: string, data?: any) {\n super(message)\n this.code = code\n this.data = data\n }\n\n get message() {\n return `${this.code} ${super.message}`\n }\n\n toString() {\n return `${this.code} ${this.message}`\n }\n\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n data: this.data,\n }\n }\n}\n\nexport class ProtocolConnections {\n readonly #collection = new Map<\n string,\n {\n connection: Connection\n context: ConnectionContext\n transport: Transport\n }\n >()\n\n constructor(\n private readonly application: {\n logger: Logger\n registry: ProtocolRegistry\n format: Format\n container: Container\n },\n ) {}\n\n get(connectionId: string) {\n const connection = this.#collection.get(connectionId)\n if (!connection) throwError('Connection not found')\n return connection\n }\n\n async add<T>(\n transport: Transport<any>,\n options: ConnectionOptions<T>,\n params: ResolveFormatParams,\n ) {\n const connection = new Connection(options)\n const format = getFormat(this.application.format, params)\n const container = this.application.container.fork(Scope.Connection)\n const context = new ConnectionContext(container, format)\n\n this.#collection.set(connection.id, { connection, context, transport })\n\n await this.application.registry.hooks.call(\n Hook.OnConnect,\n { concurrent: false },\n connection,\n )\n\n return { connection, context }\n }\n\n async remove(connectionId: string) {\n const { connection, context } = this.get(connectionId)\n\n this.application.registry.hooks.call(\n Hook.OnDisconnect,\n { concurrent: true },\n connection,\n )\n\n this.#collection.delete(connectionId)\n\n const { calls, serverStreams, clientStreams, container } = context\n\n for (const call of calls.values()) {\n call.reject(new Error('Connection closed'))\n }\n\n for (const stream of clientStreams.values()) {\n stream.destroy(new Error('Connection closed'))\n }\n\n for (const stream of serverStreams.values()) {\n stream.destroy(new Error('Connection closed'))\n }\n\n try {\n await container.dispose()\n } catch (error) {\n this.application.logger.error(\n { error, connection },\n 'Error during closing connection',\n )\n }\n }\n}\n\nexport class ProtocolClientStreams {\n constructor(private readonly connections: ProtocolConnections) {}\n\n get(connectionId: string, streamId: number) {\n const { context } = this.connections.get(connectionId)\n const { clientStreams } = context\n const stream = clientStreams.get(streamId) ?? throwError('Stream not found')\n return stream\n }\n\n remove(connectionId: string, streamId: number) {\n const { context } = this.connections.get(connectionId)\n const { clientStreams } = context\n clientStreams.get(streamId) || throwError('Stream not found')\n clientStreams.delete(streamId)\n }\n\n add(\n connectionId: string,\n streamId: number,\n metadata: ProtocolBlobMetadata,\n read: Callback,\n ) {\n const { context } = this.connections.get(connectionId)\n const { clientStreams } = context\n const stream = new ProtocolClientStream(streamId, metadata, { read })\n clientStreams.set(streamId, stream)\n return stream\n }\n\n push(connectionId: string, streamId: number, chunk: ArrayBuffer) {\n const stream = this.get(connectionId, streamId)\n stream.push(Buffer.from(chunk))\n }\n\n end(connectionId: string, streamId: number) {\n const stream = this.get(connectionId, streamId)\n stream.push(null)\n this.remove(connectionId, streamId)\n }\n\n abort(connectionId: string, streamId: number, error = new Error('Aborted')) {\n const stream = this.get(connectionId, streamId)\n stream.destroy(error)\n this.remove(connectionId, streamId)\n }\n}\n\nexport class ProtocolServerStreams {\n constructor(private readonly connections: ProtocolConnections) {}\n\n get(connectionId: string, streamId: number) {\n const { context } = this.connections.get(connectionId)\n const { serverStreams } = context\n const stream = serverStreams.get(streamId) ?? throwError('Stream not found')\n return stream\n }\n\n add(connectionId: string, streamId: number, blob: ProtocolBlob) {\n const { context } = this.connections.get(connectionId)\n const { serverStreams } = context\n const stream = new ProtocolServerStream(streamId, blob)\n serverStreams.set(streamId, stream)\n return stream\n }\n\n remove(connectionId: string, streamId: number) {\n const { context } = this.connections.get(connectionId)\n const { serverStreams } = context\n serverStreams.has(streamId) || throwError('Stream not found')\n serverStreams.delete(streamId)\n }\n\n pull(connectionId: string, streamId: number) {\n const stream = this.get(connectionId, streamId)\n stream.resume()\n }\n\n abort(connectionId: string, streamId: number, error = new Error('Aborted')) {\n const stream = this.get(connectionId, streamId)\n stream.destroy(error)\n this.remove(connectionId, streamId)\n }\n}\n\nexport class Protocol {\n readonly connections: ProtocolConnections\n readonly clientStreams: ProtocolClientStreams\n readonly serverStreams: ProtocolServerStreams\n\n constructor(\n protected readonly application: {\n logger: Logger\n format: Format\n container: Container\n registry: ProtocolRegistry\n api: ProtocolApi\n },\n ) {\n this.connections = new ProtocolConnections(this.application)\n this.clientStreams = new ProtocolClientStreams(this.connections)\n this.serverStreams = new ProtocolServerStreams(this.connections)\n }\n\n async call(\n connectionId: string,\n rpc: ProtocolRPC,\n params: { signal?: AbortSignal } = {},\n ) {\n const { connection, context, transport } =\n this.connections.get(connectionId)\n const { calls, format } = context\n const { callId, namespace, procedure, payload } = rpc\n const abortController = new AbortController()\n const signal = params.signal\n ? AbortSignal.any([params.signal, abortController.signal])\n : abortController.signal\n\n const call = Object.assign(createPromise<ProtocolApiCallResult>(), {\n abort: () => abortController.abort(),\n })\n\n calls.set(callId, call)\n call.promise.finally(() => calls.delete(callId))\n\n const callIdEncoded = encodeNumber(callId, 'Uint32')\n const container = context.container.fork(Scope.Call)\n\n try {\n const response = await this.application.api.call({\n connection,\n container,\n namespace,\n payload,\n procedure,\n signal,\n })\n\n const responseEncoded = format.encoder.encodeRPC(\n {\n callId,\n payload: response.output,\n },\n {\n addStream: (blob) => {\n const id = context.streamId++\n const stream = this.serverStreams.add(connectionId, id, blob)\n stream.on('data', (chunk) => {\n stream.pause()\n transport.send(\n connection,\n ServerMessageType.ServerStreamPush,\n concat(\n encodeNumber(id, 'Uint32'),\n Buffer.from(chunk).buffer as ArrayBuffer,\n ),\n )\n })\n stream.on('error', (err) => {\n transport.send(\n connection,\n ServerMessageType.ServerStreamAbort,\n encodeNumber(id, 'Uint32'),\n )\n })\n stream.on('end', () => {\n transport.send(\n connection,\n ServerMessageType.ServerStreamEnd,\n encodeNumber(id, 'Uint32'),\n )\n })\n return stream\n },\n getStream: (id) => {\n return this.clientStreams.get(connectionId, id)\n },\n },\n )\n\n if ('subscription' in response) {\n throwError('Unimplemented')\n } else if ('iterable' in response) {\n transport.send(\n connection,\n ServerMessageType.RpcStreamResponse,\n responseEncoded,\n )\n try {\n const iterable =\n typeof response.iterable === 'function'\n ? response.iterable()\n : response.iterable\n for await (const chunk of iterable) {\n const chunkEncoded = format.encoder.encode(chunk)\n transport.send(\n connection,\n ServerMessageType.RpcStreamChunk,\n concat(callIdEncoded, chunkEncoded),\n )\n }\n } catch (error) {\n this.application.logger.error(error)\n transport.send(\n connection,\n ServerMessageType.RpcStreamAbort,\n callIdEncoded,\n )\n }\n } else {\n transport.send(\n connection,\n ServerMessageType.RpcResponse,\n responseEncoded,\n )\n }\n } catch (error) {\n if (error instanceof ProtocolError === false) {\n this.application.logger.error(\n { error, connection },\n 'Error during RPC call',\n )\n\n // biome-ignore lint/suspicious/noCatchAssign:\n error = new ProtocolError(\n ErrorCode.InternalServerError,\n 'Internal server error',\n )\n }\n\n const payload = format.encoder.encodeRPC(\n { callId, error },\n {\n addStream(blob) {\n throwError('Cannot handle stream for error response')\n },\n getStream(id) {\n throwError('Cannot handle stream for error response')\n },\n },\n )\n transport.send(connection, ServerMessageType.RpcResponse, payload)\n } finally {\n container.dispose().catch((error) => {\n this.application.logger.error(\n { error, connection },\n \"Error during disposing connection's container\",\n )\n })\n }\n }\n\n async callRaw(\n connectionId: string,\n buffer: ArrayBuffer,\n params: { signal?: AbortSignal } = {},\n ) {\n const { connection, context, transport } =\n this.connections.get(connectionId)\n\n const { format } = context\n\n const rpc = format.decoder.decodeRPC(buffer, {\n addStream: (id, metadata) => {\n return this.clientStreams.add(connectionId, id, metadata, (size) => {\n transport.send(\n connection,\n ServerMessageType.ClientStreamPull,\n concat(encodeNumber(id, 'Uint32'), encodeNumber(size, 'Uint32')),\n )\n })\n },\n getStream: (id) => {\n return this.serverStreams.get(connectionId, id)\n },\n })\n\n return await this.call(connectionId, rpc, params)\n }\n\n callAbort(connectionId: string, callId: number) {\n const { context } = this.connections.get(connectionId)\n const call = context.calls.get(callId) ?? throwError('Call not found')\n call.abort()\n }\n\n callAbortRaw(connectionId: string, buffer: ArrayBuffer) {\n const callId = decodeNumber(buffer, 'Uint32')\n return this.callAbort(connectionId, callId)\n }\n\n notify(connectionId: string, event, payload) {\n throw Error('Unimplemented')\n }\n}\n"],"names":["createPromise","throwError","Hook","Scope","concat","decodeNumber","encodeNumber","ErrorCode","ServerMessageType","Connection","ConnectionContext","ProtocolClientStream","ProtocolServerStream","getFormat","ProtocolError","Error","code","data","constructor","message","toString","toJSON","ProtocolConnections","application","Map","get","connectionId","connection","add","transport","options","params","format","container","fork","context","set","id","registry","hooks","call","OnConnect","concurrent","remove","OnDisconnect","delete","calls","serverStreams","clientStreams","values","reject","stream","destroy","dispose","error","logger","ProtocolClientStreams","connections","streamId","metadata","read","push","chunk","Buffer","from","end","abort","ProtocolServerStreams","blob","has","pull","resume","Protocol","rpc","callId","namespace","procedure","payload","abortController","AbortController","signal","AbortSignal","any","Object","assign","promise","finally","callIdEncoded","Call","response","api","responseEncoded","encoder","encodeRPC","output","addStream","on","pause","send","ServerStreamPush","buffer","err","ServerStreamAbort","ServerStreamEnd","getStream","RpcStreamResponse","iterable","chunkEncoded","encode","RpcStreamChunk","RpcStreamAbort","RpcResponse","InternalServerError","catch","callRaw","decoder","decodeRPC","size","ClientStreamPull","callAbort","callAbortRaw","notify","event"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../lib/server/protocol.ts"],"sourcesContent":["import { type Callback, createPromise, defer, throwError } from '@nmtjs/common'\nimport { type Container, Hook, type Logger, Scope } from '@nmtjs/core'\nimport { concat, decodeNumber, encodeNumber } from '../common/binary.ts'\nimport type { ProtocolBlob, ProtocolBlobMetadata } from '../common/blob.ts'\nimport { ErrorCode, ServerMessageType } from '../common/enums.ts'\nimport type { ProtocolRPC } from '../common/types.ts'\nimport type { ProtocolApi, ProtocolApiCallResult } from './api.ts'\nimport {\n Connection,\n ConnectionContext,\n type ConnectionOptions,\n} from './connection.ts'\nimport type { Format } from './format.ts'\nimport type { ProtocolRegistry } from './registry.ts'\nimport { ProtocolClientStream, ProtocolServerStream } from './stream.ts'\nimport type { Transport } from './transport.ts'\nimport { type ResolveFormatParams, getFormat } from './utils.ts'\n\nexport class ProtocolError extends Error {\n code: string\n data?: any\n\n constructor(code: string, message?: string, data?: any) {\n super(message)\n this.code = code\n this.data = data\n }\n\n get message() {\n return `${this.code} ${super.message}`\n }\n\n toString() {\n return `${this.code} ${this.message}`\n }\n\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n data: this.data,\n }\n }\n}\n\nexport class ProtocolConnections {\n readonly #collection = new Map<\n string,\n {\n connection: Connection\n context: ConnectionContext\n transport: Transport\n }\n >()\n\n constructor(\n private readonly application: {\n logger: Logger\n registry: ProtocolRegistry\n format: Format\n container: Container\n },\n ) {}\n\n get(connectionId: string) {\n const connection = this.#collection.get(connectionId)\n if (!connection) throwError('Connection not found')\n return connection\n }\n\n async add<T>(\n transport: Transport<any>,\n options: ConnectionOptions<T>,\n params: ResolveFormatParams,\n ) {\n const connection = new Connection(options)\n const format = getFormat(this.application.format, params)\n const container = this.application.container.fork(Scope.Connection)\n const context = new ConnectionContext(container, format)\n\n this.#collection.set(connection.id, { connection, context, transport })\n\n await this.application.registry.hooks.call(\n Hook.OnConnect,\n { concurrent: false },\n connection,\n )\n\n return { connection, context }\n }\n\n async remove(connectionId: string) {\n const { connection, context } = this.get(connectionId)\n\n this.application.registry.hooks.call(\n Hook.OnDisconnect,\n { concurrent: true },\n connection,\n )\n\n this.#collection.delete(connectionId)\n\n const { calls, serverStreams, clientStreams, rpcStreams, container } =\n context\n\n for (const call of calls.values()) {\n call.reject(new Error('Connection closed'))\n }\n\n for (const stream of clientStreams.values()) {\n stream.destroy(new Error('Connection closed'))\n }\n\n for (const stream of serverStreams.values()) {\n stream.destroy(new Error('Connection closed'))\n }\n\n for (const stream of rpcStreams.values()) {\n stream.abort(new Error('Connection closed'))\n }\n\n try {\n await container.dispose()\n } catch (error) {\n this.application.logger.error(\n { error, connection },\n 'Error during closing connection',\n )\n }\n }\n}\n\nexport class ProtocolClientStreams {\n constructor(private readonly connections: ProtocolConnections) {}\n\n get(connectionId: string, streamId: number) {\n const { context } = this.connections.get(connectionId)\n const { clientStreams } = context\n const stream = clientStreams.get(streamId) ?? throwError('Stream not found')\n return stream\n }\n\n remove(connectionId: string, streamId: number) {\n const { context } = this.connections.get(connectionId)\n const { clientStreams } = context\n clientStreams.get(streamId) || throwError('Stream not found')\n clientStreams.delete(streamId)\n }\n\n add(\n connectionId: string,\n streamId: number,\n metadata: ProtocolBlobMetadata,\n read: Callback,\n ) {\n const { context } = this.connections.get(connectionId)\n const { clientStreams } = context\n const stream = new ProtocolClientStream(streamId, metadata, { read })\n clientStreams.set(streamId, stream)\n return stream\n }\n\n push(connectionId: string, streamId: number, chunk: ArrayBuffer) {\n const stream = this.get(connectionId, streamId)\n stream.push(Buffer.from(chunk))\n }\n\n end(connectionId: string, streamId: number) {\n const stream = this.get(connectionId, streamId)\n stream.push(null)\n this.remove(connectionId, streamId)\n }\n\n abort(connectionId: string, streamId: number, error = new Error('Aborted')) {\n const stream = this.get(connectionId, streamId)\n stream.destroy(error)\n this.remove(connectionId, streamId)\n }\n}\n\nexport class ProtocolServerStreams {\n constructor(private readonly connections: ProtocolConnections) {}\n\n get(connectionId: string, streamId: number) {\n const { context } = this.connections.get(connectionId)\n const { serverStreams } = context\n const stream = serverStreams.get(streamId) ?? throwError('Stream not found')\n return stream\n }\n\n add(connectionId: string, streamId: number, blob: ProtocolBlob) {\n const { context } = this.connections.get(connectionId)\n const { serverStreams } = context\n const stream = new ProtocolServerStream(streamId, blob)\n serverStreams.set(streamId, stream)\n return stream\n }\n\n remove(connectionId: string, streamId: number) {\n const { context } = this.connections.get(connectionId)\n const { serverStreams } = context\n serverStreams.has(streamId) || throwError('Stream not found')\n serverStreams.delete(streamId)\n }\n\n pull(connectionId: string, streamId: number) {\n const stream = this.get(connectionId, streamId)\n stream.resume()\n }\n\n abort(connectionId: string, streamId: number, error = new Error('Aborted')) {\n const stream = this.get(connectionId, streamId)\n stream.destroy(error)\n this.remove(connectionId, streamId)\n }\n}\n\nexport class Protocol {\n readonly connections: ProtocolConnections\n readonly clientStreams: ProtocolClientStreams\n readonly serverStreams: ProtocolServerStreams\n\n constructor(\n protected readonly application: {\n logger: Logger\n format: Format\n container: Container\n registry: ProtocolRegistry\n api: ProtocolApi\n },\n ) {\n this.connections = new ProtocolConnections(this.application)\n this.clientStreams = new ProtocolClientStreams(this.connections)\n this.serverStreams = new ProtocolServerStreams(this.connections)\n }\n\n async rpc(\n connectionId: string,\n rpc: ProtocolRPC,\n params: { signal?: AbortSignal } = {},\n ) {\n const { connection, context, transport } =\n this.connections.get(connectionId)\n const { calls, format } = context\n const { callId, namespace, procedure, payload } = rpc\n const abortController = new AbortController()\n const signal = params.signal\n ? AbortSignal.any([params.signal, abortController.signal])\n : abortController.signal\n\n const call = Object.assign(createPromise<ProtocolApiCallResult>(), {\n abort: () => abortController.abort(),\n })\n\n calls.set(callId, call)\n call.promise.finally(() => calls.delete(callId))\n\n const callIdEncoded = encodeNumber(callId, 'Uint32')\n const container = context.container.fork(Scope.Call)\n\n try {\n const response = await this.application.api.call({\n connection,\n container,\n namespace,\n payload,\n procedure,\n signal,\n })\n\n const responseEncoded = format.encoder.encodeRPC(\n {\n callId,\n payload: response.output,\n },\n {\n addStream: (blob) => {\n const id = context.streamId++\n const stream = this.serverStreams.add(connectionId, id, blob)\n stream.on('data', (chunk) => {\n stream.pause()\n transport.send(\n connection,\n ServerMessageType.ServerStreamPush,\n concat(\n encodeNumber(id, 'Uint32'),\n Buffer.from(chunk).buffer as ArrayBuffer,\n ),\n )\n })\n stream.on('error', (err) => {\n transport.send(\n connection,\n ServerMessageType.ServerStreamAbort,\n encodeNumber(id, 'Uint32'),\n )\n })\n stream.on('end', () => {\n transport.send(\n connection,\n ServerMessageType.ServerStreamEnd,\n encodeNumber(id, 'Uint32'),\n )\n })\n return stream\n },\n getStream: (id) => {\n return this.clientStreams.get(connectionId, id)\n },\n },\n )\n\n if ('subscription' in response) {\n throwError('Unimplemented')\n } else if ('iterable' in response) {\n transport.send(\n connection,\n ServerMessageType.RpcStreamResponse,\n responseEncoded,\n )\n try {\n const ab = new AbortController()\n context.rpcStreams.set(callId, ab)\n const iterable =\n typeof response.iterable === 'function'\n ? response.iterable()\n : response.iterable\n for await (const chunk of iterable) {\n if (ab.signal.aborted) break\n const chunkEncoded = format.encoder.encode(chunk)\n transport.send(\n connection,\n ServerMessageType.RpcStreamChunk,\n concat(callIdEncoded, chunkEncoded),\n )\n }\n } catch (error) {\n this.application.logger.error(error)\n transport.send(\n connection,\n ServerMessageType.RpcStreamAbort,\n callIdEncoded,\n )\n } finally {\n context.rpcStreams.delete(callId)\n response.onFinish && defer(response.onFinish)\n }\n } else {\n transport.send(\n connection,\n ServerMessageType.RpcResponse,\n responseEncoded,\n )\n }\n } catch (error) {\n if (error instanceof ProtocolError === false) {\n this.application.logger.error(\n { error, connection },\n 'Error during RPC call',\n )\n\n // biome-ignore lint/suspicious/noCatchAssign:\n error = new ProtocolError(\n ErrorCode.InternalServerError,\n 'Internal server error',\n )\n }\n\n const payload = format.encoder.encodeRPC(\n { callId, error },\n {\n addStream(blob) {\n throwError('Cannot handle stream for error response')\n },\n getStream(id) {\n throwError('Cannot handle stream for error response')\n },\n },\n )\n transport.send(connection, ServerMessageType.RpcResponse, payload)\n } finally {\n container.dispose().catch((error) => {\n this.application.logger.error(\n { error, connection },\n \"Error during disposing connection's container\",\n )\n })\n }\n }\n\n async rpcRaw(\n connectionId: string,\n buffer: ArrayBuffer,\n params: { signal?: AbortSignal } = {},\n ) {\n const { connection, context, transport } =\n this.connections.get(connectionId)\n\n const { format } = context\n\n const rpc = format.decoder.decodeRPC(buffer, {\n addStream: (id, metadata) => {\n return this.clientStreams.add(connectionId, id, metadata, (size) => {\n transport.send(\n connection,\n ServerMessageType.ClientStreamPull,\n concat(encodeNumber(id, 'Uint32'), encodeNumber(size, 'Uint32')),\n )\n })\n },\n getStream: (id) => {\n return this.serverStreams.get(connectionId, id)\n },\n })\n\n return await this.rpc(connectionId, rpc, params)\n }\n\n rpcAbort(connectionId: string, callId: number) {\n const { context } = this.connections.get(connectionId)\n const call = context.calls.get(callId) ?? throwError('Call not found')\n call.abort()\n }\n\n rpcAbortRaw(connectionId: string, buffer: ArrayBuffer) {\n const callId = decodeNumber(buffer, 'Uint32')\n return this.rpcAbort(connectionId, callId)\n }\n\n rpcStreamAbort(connectionId: string, callId: number) {\n const { context } = this.connections.get(connectionId)\n const ab =\n context.rpcStreams.get(callId) ?? throwError('Call stream not found')\n ab.abort()\n }\n\n rpcStreamAbortRaw(connectionId: string, buffer: ArrayBuffer) {\n const callId = decodeNumber(buffer, 'Uint32')\n return this.rpcStreamAbort(connectionId, callId)\n }\n\n notify(connectionId: string, event, payload) {\n throw Error('Unimplemented')\n }\n}\n"],"names":["createPromise","defer","throwError","Hook","Scope","concat","decodeNumber","encodeNumber","ErrorCode","ServerMessageType","Connection","ConnectionContext","ProtocolClientStream","ProtocolServerStream","getFormat","ProtocolError","Error","code","data","constructor","message","toString","toJSON","ProtocolConnections","application","Map","get","connectionId","connection","add","transport","options","params","format","container","fork","context","set","id","registry","hooks","call","OnConnect","concurrent","remove","OnDisconnect","delete","calls","serverStreams","clientStreams","rpcStreams","values","reject","stream","destroy","abort","dispose","error","logger","ProtocolClientStreams","connections","streamId","metadata","read","push","chunk","Buffer","from","end","ProtocolServerStreams","blob","has","pull","resume","Protocol","rpc","callId","namespace","procedure","payload","abortController","AbortController","signal","AbortSignal","any","Object","assign","promise","finally","callIdEncoded","Call","response","api","responseEncoded","encoder","encodeRPC","output","addStream","on","pause","send","ServerStreamPush","buffer","err","ServerStreamAbort","ServerStreamEnd","getStream","RpcStreamResponse","ab","iterable","aborted","chunkEncoded","encode","RpcStreamChunk","RpcStreamAbort","onFinish","RpcResponse","InternalServerError","catch","rpcRaw","decoder","decodeRPC","size","ClientStreamPull","rpcAbort","rpcAbortRaw","rpcStreamAbort","rpcStreamAbortRaw","notify","event"],"mappings":"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"}
@@ -2,19 +2,6 @@ import type { Callback } from '@nmtjs/common'
2
2
 
3
3
  export type EventMap = { [K: string]: any[] }
4
4
 
5
- export function untilAborted(signal: AbortSignal) {
6
- return new Promise((_, reject) => {
7
- const handler = () => reject(new Error('aborted'))
8
- const options = { once: true }
9
- signal.addEventListener('abort', handler, options)
10
- })
11
- }
12
-
13
- export function onAbort(signal: AbortSignal, listener: () => void) {
14
- signal.addEventListener('abort', listener, { once: true })
15
- return () => signal.removeEventListener('abort', listener)
16
- }
17
-
18
5
  /**
19
6
  * Very simple node-like event emitter wrapper around EventTarget
20
7
  *
@@ -27,6 +14,10 @@ export class EventEmitter<
27
14
  string
28
15
  >,
29
16
  > {
17
+ static once(ee: EventEmitter, event: string) {
18
+ return new Promise((resolve) => ee.once(event, resolve))
19
+ }
20
+
30
21
  #target = new EventTarget()
31
22
  #listeners = new Map<Callback, Callback>()
32
23
 
@@ -61,6 +52,3 @@ export class EventEmitter<
61
52
  return this.#target.dispatchEvent(new CustomEvent(event, { detail: args }))
62
53
  }
63
54
  }
64
-
65
- export const once = (ee: EventEmitter, event: string) =>
66
- new Promise((resolve) => ee.once(event, resolve))
@@ -1,7 +1,15 @@
1
- import { type InteractivePromise, createPromise } from '@nmtjs/common'
1
+ import {
2
+ type InteractivePromise,
3
+ createPromise,
4
+ onceAborted,
5
+ } from '@nmtjs/common'
2
6
  import { concat, decodeNumber, encodeNumber } from '../common/binary.ts'
3
7
  import type { ProtocolBlobMetadata } from '../common/blob.ts'
4
- import { ClientMessageType, ServerMessageType } from '../common/enums.ts'
8
+ import {
9
+ ClientMessageType,
10
+ ErrorCode,
11
+ ServerMessageType,
12
+ } from '../common/enums.ts'
5
13
  import type { ProtocolRPC } from '../common/types.ts'
6
14
  import { EventEmitter } from './events.ts'
7
15
  import type { BaseClientFormat } from './format.ts'
@@ -151,6 +159,18 @@ export class ProtocolBaseTransformer {
151
159
  export type ProtocolClientCall = InteractivePromise<any> &
152
160
  Pick<ProtocolRPC, 'namespace' | 'procedure'>
153
161
 
162
+ export type ProtocolBaseClientOptions = {
163
+ transport: ProtocolTransport
164
+ format: BaseClientFormat
165
+ transformer?: ProtocolBaseTransformer
166
+ timeout?: number
167
+ }
168
+
169
+ export type ProtocolBaseClientCallOptions = {
170
+ signal?: AbortSignal
171
+ timeout?: number
172
+ }
173
+
154
174
  export abstract class ProtocolBaseClient<
155
175
  T extends Record<string, Record<string, any>>,
156
176
  > extends EventEmitter<
@@ -164,29 +184,34 @@ export abstract class ProtocolBaseClient<
164
184
  > {
165
185
  readonly #clientStreams: ProtocolClientStreams
166
186
  readonly #serverStreams: ProtocolServerStreams
167
- readonly #serverRPCStreams: ProtocolServerStreams
168
- readonly #serverRPCStreamCalls = new Map<
187
+ readonly #rpcStreams: ProtocolServerStreams
188
+ readonly #rpcStreamData = new Map<
169
189
  number,
170
190
  Pick<ProtocolRPC, 'namespace' | 'procedure'>
171
191
  >()
172
192
  readonly #calls = new Map<number, ProtocolClientCall>()
173
193
 
194
+ readonly #transport: ProtocolTransport
195
+ readonly #format: BaseClientFormat
196
+ readonly transformer: ProtocolBaseTransformer = new ProtocolBaseTransformer()
197
+ readonly #timeout: number
198
+
174
199
  #callId = 0
175
200
  #streamId = 0
176
201
 
177
- constructor(
178
- protected readonly transport: ProtocolTransport,
179
- protected readonly format: BaseClientFormat,
180
- protected readonly transformer: ProtocolBaseTransformer = new ProtocolBaseTransformer(),
181
- ) {
202
+ constructor(options: ProtocolBaseClientOptions) {
182
203
  super()
183
204
 
205
+ this.#transport = options.transport
206
+ this.#format = options.format
207
+ this.#timeout = options.timeout ?? 60000
208
+
184
209
  this.#clientStreams = new ProtocolClientStreams()
185
210
  this.#serverStreams = new ProtocolServerStreams()
186
- this.#serverRPCStreams = new ProtocolServerStreams()
211
+ this.#rpcStreams = new ProtocolServerStreams()
187
212
 
188
- this.transport.on(`${ServerMessageType.Event}`, (buffer) => {
189
- const [namespace, event, payload] = this.format.decode(buffer)
213
+ this.#transport.on(`${ServerMessageType.Event}`, (buffer) => {
214
+ const [namespace, event, payload] = this.#format.decode(buffer)
190
215
  const name = `${namespace}/${event}`
191
216
  const transformed = this.transformer.decodeEvent(
192
217
  namespace,
@@ -196,69 +221,72 @@ export abstract class ProtocolBaseClient<
196
221
  this.emit(name, transformed)
197
222
  })
198
223
 
199
- this.transport.on(`${ServerMessageType.RpcResponse}`, (buffer) => {
224
+ this.#transport.on(`${ServerMessageType.RpcResponse}`, (buffer) => {
200
225
  const { call, error, payload } = this.#handleResponse(buffer)
201
226
  if (error) call.reject(error)
202
227
  else call.resolve(payload)
203
228
  })
204
229
 
205
- this.transport.on(`${ServerMessageType.RpcStreamResponse}`, (buffer) => {
230
+ this.#transport.on(`${ServerMessageType.RpcStreamResponse}`, (buffer) => {
206
231
  const { call, response, payload, error } = this.#handleResponse(buffer)
207
232
  if (error) return call.reject(error)
208
233
  console.log('Creating RPC stream', response)
209
234
  const stream = new ProtocolServerStream()
210
- this.#serverRPCStreams.add(response.callId, stream)
211
- this.#serverRPCStreamCalls.set(response.callId, {
235
+ this.#rpcStreams.add(response.callId, stream)
236
+ this.#rpcStreamData.set(response.callId, {
212
237
  namespace: call.namespace,
213
238
  procedure: call.procedure,
214
239
  })
215
- call.resolve([payload, stream])
240
+ call.resolve({ payload, stream })
216
241
  })
217
242
 
218
- this.transport.on(`${ServerMessageType.RpcStreamChunk}`, async (buffer) => {
219
- const callId = decodeNumber(buffer, 'Uint32')
220
- console.log('RPC stream chunk', callId)
243
+ this.#transport.on(
244
+ `${ServerMessageType.RpcStreamChunk}`,
245
+ async (buffer) => {
246
+ const callId = decodeNumber(buffer, 'Uint32')
247
+ console.log('RPC stream chunk', callId)
221
248
 
222
- const chunk = buffer.slice(Uint32Array.BYTES_PER_ELEMENT)
223
- if (chunk.byteLength === 0) {
224
- this.#serverRPCStreams.end(callId)
225
- this.#serverRPCStreamCalls.delete(callId)
226
- } else {
227
- const call = this.#serverRPCStreamCalls.get(callId)
228
- console.log('RPC stream call', call)
229
- if (call) {
230
- const payload = this.format.decode(chunk)
231
- console.log('RPC stream payload', payload)
232
- try {
233
- const transformed = this.transformer.decodeRPCChunk(
234
- call.namespace,
235
- call.procedure,
236
- payload,
237
- )
238
- await this.#serverRPCStreams.push(callId, transformed)
239
- } catch (error) {
240
- this._send(
241
- ClientMessageType.RpcStreamAbort,
242
- encodeNumber(callId, 'Uint32'),
243
- )
244
- this.#serverRPCStreams.remove(callId)
245
- this.#serverRPCStreamCalls.delete(callId)
249
+ const chunk = buffer.slice(Uint32Array.BYTES_PER_ELEMENT)
250
+ if (chunk.byteLength === 0) {
251
+ this.#rpcStreams.end(callId)
252
+ this.#rpcStreamData.delete(callId)
253
+ } else {
254
+ const call = this.#rpcStreamData.get(callId)
255
+ console.log('RPC stream call', call)
256
+ if (call) {
257
+ const payload = this.#format.decode(chunk)
258
+ console.log('RPC stream payload', payload)
259
+ try {
260
+ const transformed = this.transformer.decodeRPCChunk(
261
+ call.namespace,
262
+ call.procedure,
263
+ payload,
264
+ )
265
+ await this.#rpcStreams.push(callId, transformed)
266
+ } catch (error) {
267
+ this.#send(
268
+ ClientMessageType.RpcStreamAbort,
269
+ encodeNumber(callId, 'Uint32'),
270
+ )
271
+ this.#rpcStreams.remove(callId)
272
+ this.#rpcStreamData.delete(callId)
273
+ }
246
274
  }
247
275
  }
248
- }
249
- })
276
+ },
277
+ )
250
278
 
251
- this.transport.on(`${ServerMessageType.RpcStreamAbort}`, (buffer) => {
279
+ this.#transport.on(`${ServerMessageType.RpcStreamAbort}`, (buffer) => {
252
280
  const callId = decodeNumber(buffer, 'Uint32')
253
281
  console.log('RPC stream abort', callId)
254
282
  const call = this.#calls.get(callId)
255
283
  if (call) {
256
284
  this.#serverStreams.end(callId)
257
- this.#serverRPCStreams.abort(callId)
285
+ this.#rpcStreams.abort(callId)
258
286
  }
259
287
  })
260
288
 
261
- this.transport.on(
289
+ this.#transport.on(
262
290
  `${ServerMessageType.ServerStreamPush}`,
263
291
  async (buffer) => {
264
292
  const streamId = decodeNumber(buffer, 'Uint32')
@@ -266,12 +294,12 @@ export abstract class ProtocolBaseClient<
266
294
  console.log('Server stream push', streamId, chunk.byteLength)
267
295
  try {
268
296
  await this.#serverStreams.push(streamId, chunk)
269
- this._send(
297
+ this.#send(
270
298
  ClientMessageType.ServerStreamPull,
271
299
  encodeNumber(streamId, 'Uint32'),
272
300
  )
273
301
  } catch (error) {
274
- this._send(
302
+ this.#send(
275
303
  ClientMessageType.ServerStreamAbort,
276
304
  encodeNumber(streamId, 'Uint32'),
277
305
  )
@@ -280,25 +308,25 @@ export abstract class ProtocolBaseClient<
280
308
  },
281
309
  )
282
310
 
283
- this.transport.on(`${ServerMessageType.ServerStreamEnd}`, (buffer) => {
311
+ this.#transport.on(`${ServerMessageType.ServerStreamEnd}`, (buffer) => {
284
312
  const streamId = decodeNumber(buffer, 'Uint32')
285
313
  console.log('Server stream end', streamId)
286
314
  this.#serverStreams.end(streamId)
287
315
  })
288
316
 
289
- this.transport.on(`${ServerMessageType.ServerStreamAbort}`, (buffer) => {
317
+ this.#transport.on(`${ServerMessageType.ServerStreamAbort}`, (buffer) => {
290
318
  const streamId = decodeNumber(buffer, 'Uint32')
291
319
  console.log('Server stream abort', streamId)
292
320
  this.#serverStreams.abort(streamId)
293
321
  })
294
322
 
295
- this.transport.on(`${ServerMessageType.ClientStreamAbort}`, (buffer) => {
323
+ this.#transport.on(`${ServerMessageType.ClientStreamAbort}`, (buffer) => {
296
324
  const streamId = decodeNumber(buffer, 'Uint32')
297
325
  console.log('Client stream abort', streamId)
298
326
  this.#clientStreams.abort(streamId)
299
327
  })
300
328
 
301
- this.transport.on(
329
+ this.#transport.on(
302
330
  `${ServerMessageType.ClientStreamPull}`,
303
331
  async (buffer) => {
304
332
  const streamId = decodeNumber(buffer, 'Uint32')
@@ -312,51 +340,56 @@ export abstract class ProtocolBaseClient<
312
340
  try {
313
341
  const chunk = await this.#clientStreams.pull(streamId, size)
314
342
  if (chunk) {
315
- this._send(
343
+ this.#send(
316
344
  ClientMessageType.ClientStreamPush,
317
345
  concat(streamIdEncoded, chunk),
318
346
  )
319
347
  } else {
320
- this._send(ClientMessageType.ClientStreamEnd, streamIdEncoded)
348
+ this.#send(ClientMessageType.ClientStreamEnd, streamIdEncoded)
321
349
  this.#clientStreams.end(streamId)
322
350
  }
323
351
  } catch (error) {
324
352
  console.error(error)
325
- this._send(ClientMessageType.ClientStreamAbort, streamIdEncoded)
353
+ this.#send(ClientMessageType.ClientStreamAbort, streamIdEncoded)
326
354
  }
327
355
  },
328
356
  )
329
357
  }
330
358
 
331
359
  async connect(auth: any) {
332
- return await this.transport.connect(auth, this.format.contentType)
360
+ return await this.#transport.connect(auth, this.#format.contentType)
333
361
  }
334
362
 
335
363
  async disconnect() {
336
- return await this.transport.disconnect()
364
+ return await this.#transport.disconnect()
337
365
  }
338
366
 
339
- protected async _send(messageType: ClientMessageType, buffer: ArrayBuffer) {
367
+ async #send(messageType: ClientMessageType, buffer: ArrayBuffer) {
340
368
  console.log(
341
369
  'Client transport send',
342
370
  ClientMessageType[messageType],
343
371
  buffer.byteLength,
344
372
  )
345
- return await this.transport.send(messageType, buffer)
373
+ return await this.#transport.send(messageType, buffer)
346
374
  }
347
375
 
348
376
  protected async _call(
349
377
  namespace: string,
350
378
  procedure: string,
351
379
  payload: any,
352
- options = {},
380
+ options: ProtocolBaseClientCallOptions = {},
353
381
  ) {
382
+ const timeoutSignal = AbortSignal.timeout(options.timeout || this.#timeout)
383
+ const signal = options.signal
384
+ ? AbortSignal.any([options.signal, timeoutSignal])
385
+ : timeoutSignal
386
+
354
387
  const callId = ++this.#callId
355
388
  const call = Object.assign(createPromise(), {
356
389
  namespace,
357
390
  procedure,
358
391
  })
359
- const buffer = this.format.encodeRPC(
392
+ const buffer = this.#format.encodeRPC(
360
393
  {
361
394
  callId,
362
395
  namespace,
@@ -380,20 +413,27 @@ export abstract class ProtocolBaseClient<
380
413
  },
381
414
  )
382
415
 
383
- this.transport.send(ClientMessageType.Rpc, buffer).catch(console.error)
416
+ this.#transport.send(ClientMessageType.Rpc, buffer).catch(console.error)
384
417
 
385
418
  this.#calls.set(callId, call)
386
419
 
387
- return call.promise
420
+ const onAborted = onceAborted(signal).then(() => {
421
+ if (this.#calls.has(callId)) {
422
+ this.#send(ClientMessageType.RpcAbort, encodeNumber(callId, 'Uint32'))
423
+ }
424
+ throw new ProtocolError(ErrorCode.RequestTimeout, 'Request timeout')
425
+ })
426
+
427
+ return Promise.race([call.promise, onAborted])
388
428
  }
389
429
 
390
430
  #handleResponse(buffer: ArrayBuffer) {
391
431
  const callStreams: ProtocolServerBlobStream[] = []
392
- const response = this.format.decodeRPC(buffer, {
432
+ const response = this.#format.decodeRPC(buffer, {
393
433
  addStream: (id, metadata) => {
394
434
  console.log('Client transport blob stream', id, metadata)
395
435
  const stream = new ProtocolServerBlobStream(id, metadata, () => {
396
- this._send(
436
+ this.#send(
397
437
  ClientMessageType.ServerStreamPull,
398
438
  encodeNumber(id, 'Uint32'),
399
439
  )
@@ -22,7 +22,7 @@ export class ProtocolClientBlobStream extends TransformStream<
22
22
  if (chunk instanceof ArrayBuffer) {
23
23
  controller.enqueue(chunk)
24
24
  } else if (chunk instanceof Uint8Array) {
25
- controller.enqueue(chunk.buffer)
25
+ controller.enqueue(chunk.buffer as unknown as ArrayBuffer)
26
26
  } else if (typeof chunk === 'string') {
27
27
  controller.enqueue(encodeText(chunk))
28
28
  } else {
@@ -61,13 +61,20 @@ export class ProtocolClientBlobStream extends TransformStream<
61
61
  }
62
62
 
63
63
  async end() {
64
- if (!this.writable.locked && (await this.writable.getWriter().closed)) {
65
- await this.writable.close()
66
- }
64
+ if (!this.writable.locked) await this.writable.close()
65
+ else await this.writable.abort(new Error('Stream closed'))
67
66
  }
68
67
  }
69
68
 
70
- export class ProtocolServerStream<T = any> extends TransformStream<any, T> {
69
+ export interface ProtocolServerStreamInterface<T = any> {
70
+ [Symbol.asyncIterator](): AsyncGenerator<T>
71
+ abort(error?: Error): void
72
+ }
73
+
74
+ export class ProtocolServerStream<T = any>
75
+ extends TransformStream<any, T>
76
+ implements ProtocolServerStreamInterface<T>
77
+ {
71
78
  #writer: WritableStreamDefaultWriter
72
79
 
73
80
  constructor(start?: Callback) {
package/lib/server/api.ts CHANGED
@@ -13,9 +13,7 @@ export type ProtocolApiCallOptions = {
13
13
 
14
14
  export type ProtocolAnyIterable<T> =
15
15
  | (() => AsyncGenerator<T>)
16
- // | (() => Generator<T>)
17
16
  | AsyncIterable<T>
18
- // | Iterable<T>
19
17
 
20
18
  export interface ProtocolApiCallBaseResult {
21
19
  output: unknown
@@ -28,6 +26,7 @@ export interface ProtocolApiCallSubscriptionResult
28
26
  export interface ProtocolApiCallIterableResult
29
27
  extends ProtocolApiCallBaseResult {
30
28
  iterable: ProtocolAnyIterable<unknown>
29
+ onFinish?: () => void
31
30
  }
32
31
 
33
32
  export type ProtocolApiCallResult =
@@ -1,22 +1,10 @@
1
1
  import { randomUUID } from 'node:crypto'
2
- // import type { TAnyEventContract } from '@nmtjs/contract'
3
2
  import type { InteractivePromise } from '@nmtjs/common'
4
3
  import type { Container } from '@nmtjs/core'
5
4
  import type { ProtocolApiCallResult } from './api.ts'
6
5
  import type { BaseServerDecoder, BaseServerEncoder } from './format.ts'
7
6
  import type { ProtocolClientStream, ProtocolServerStream } from './stream.ts'
8
7
 
9
- // export type NotifyFn = <T extends TAnyEventContract>(
10
- // connection: Connection,
11
- // contract: T,
12
- // payload: t.infer.input.decoded<T['payload']>,
13
- // ) => Promise<boolean>
14
-
15
- // export type ConnectionNotifyFn = (
16
- // contract: TAnyEventContract,
17
- // payload: unknown,
18
- // ) => Promise<boolean>
19
-
20
8
  export type ConnectionOptions<Data = unknown> = {
21
9
  id?: string
22
10
  data: Data
@@ -41,6 +29,7 @@ export class ConnectionContext {
41
29
  calls = new Map<number, ConnectionCall<ProtocolApiCallResult>>()
42
30
  clientStreams = new Map<number, ProtocolClientStream>()
43
31
  serverStreams = new Map<number, ProtocolServerStream>()
32
+ rpcStreams = new Map<number, AbortController>()
44
33
  container: Container
45
34
  format: {
46
35
  encoder: BaseServerEncoder