@nmtjs/client 0.15.0-beta.1 → 0.15.0-beta.11

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.
@@ -0,0 +1,20 @@
1
+ import type { TAnyRouterContract } from '@nmtjs/contract';
2
+ import type { BaseClientOptions } from '../core.ts';
3
+ import type { ClientTransportFactory } from '../transport.ts';
4
+ import type { ClientCallers, RuntimeInputContractTypeProvider, RuntimeOutputContractTypeProvider } from '../types.ts';
5
+ import { BaseClient } from '../core.ts';
6
+ export declare class RuntimeContractTransformer {
7
+ #private;
8
+ constructor(router: TAnyRouterContract);
9
+ encode(_procedure: string, payload: any): unknown;
10
+ decode(_procedure: string, payload: any): unknown;
11
+ }
12
+ export declare class RuntimeClient<Transport extends ClientTransportFactory<any, any> = ClientTransportFactory<any, any>, RouterContract extends TAnyRouterContract = TAnyRouterContract, SafeCall extends boolean = false> extends BaseClient<Transport, RouterContract, SafeCall, RuntimeInputContractTypeProvider, RuntimeOutputContractTypeProvider> {
13
+ #private;
14
+ protected readonly transformer: RuntimeContractTransformer;
15
+ constructor(options: BaseClientOptions<RouterContract, SafeCall>, transport: Transport, transportOptions: Transport extends ClientTransportFactory<any, infer Options> ? Options : never);
16
+ get call(): ClientCallers<this["_"]["routes"], SafeCall, false>;
17
+ get stream(): ClientCallers<this["_"]["routes"], SafeCall, true>;
18
+ protected resolveProcedures(router: TAnyRouterContract, path?: string[]): void;
19
+ protected buildCallers(): ClientCallers<this['_']['routes'], SafeCall, boolean>;
20
+ }
@@ -0,0 +1,81 @@
1
+ import { IsProcedureContract, IsRouterContract } from '@nmtjs/contract';
2
+ import { BaseClient } from "../core.js";
3
+ export class RuntimeContractTransformer {
4
+ #procedures = new Map();
5
+ constructor(router) {
6
+ const registerProcedures = (r, path = []) => {
7
+ if (IsRouterContract(r)) {
8
+ for (const [key, route] of Object.entries(r.routes)) {
9
+ registerProcedures(route, [...path, key]);
10
+ }
11
+ }
12
+ else if (IsProcedureContract(r)) {
13
+ const fullName = [...path].join('/');
14
+ this.#procedures.set(fullName, r);
15
+ }
16
+ };
17
+ registerProcedures(router);
18
+ }
19
+ encode(_procedure, payload) {
20
+ const procedure = this.#procedures.get(_procedure);
21
+ if (!procedure)
22
+ throw new Error(`Procedure not found: ${_procedure}`);
23
+ return procedure.input.encode(payload);
24
+ }
25
+ decode(_procedure, payload) {
26
+ const procedure = this.#procedures.get(_procedure);
27
+ if (!procedure)
28
+ throw new Error(`Procedure not found: ${_procedure}`);
29
+ return procedure.output.decode(payload);
30
+ }
31
+ }
32
+ export class RuntimeClient extends BaseClient {
33
+ transformer;
34
+ #procedures = new Map();
35
+ #callers;
36
+ constructor(options, transport, transportOptions) {
37
+ super(options, transport, transportOptions);
38
+ this.resolveProcedures(this.options.contract);
39
+ this.transformer = new RuntimeContractTransformer(this.options.contract);
40
+ this.#callers = this.buildCallers();
41
+ }
42
+ get call() {
43
+ return this.#callers;
44
+ }
45
+ get stream() {
46
+ return this.#callers;
47
+ }
48
+ resolveProcedures(router, path = []) {
49
+ for (const [key, route] of Object.entries(router.routes)) {
50
+ if (IsRouterContract(route)) {
51
+ this.resolveProcedures(route, [...path, key]);
52
+ }
53
+ else if (IsProcedureContract(route)) {
54
+ const fullName = [...path, key].join('/');
55
+ this.#procedures.set(fullName, route);
56
+ }
57
+ }
58
+ }
59
+ buildCallers() {
60
+ const callers = Object.create(null);
61
+ for (const [name, { stream }] of this.#procedures) {
62
+ const parts = name.split('/');
63
+ let current = callers;
64
+ for (let i = 0; i < parts.length; i++) {
65
+ const part = parts[i];
66
+ if (i === parts.length - 1) {
67
+ current[part] = (payload, options) => this._call(name, payload, {
68
+ ...options,
69
+ _stream_response: !!stream,
70
+ });
71
+ }
72
+ else {
73
+ current[part] = current[part] ?? Object.create(null);
74
+ current = current[part];
75
+ }
76
+ }
77
+ }
78
+ return callers;
79
+ }
80
+ }
81
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/clients/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { TAnyRouterContract } from '@nmtjs/contract';
2
+ import type { BaseClientOptions } from '../core.ts';
3
+ import type { ClientTransportFactory } from '../transport.ts';
4
+ import type { ClientCallers, StaticInputContractTypeProvider, StaticOutputContractTypeProvider } from '../types.ts';
5
+ import { BaseClient } from '../core.ts';
6
+ import { BaseClientTransformer } from '../transformers.ts';
7
+ export declare class StaticClient<Transport extends ClientTransportFactory<any, any> = ClientTransportFactory<any, any>, RouterContract extends TAnyRouterContract = TAnyRouterContract, SafeCall extends boolean = false> extends BaseClient<Transport, RouterContract, SafeCall, StaticInputContractTypeProvider, StaticOutputContractTypeProvider> {
8
+ protected readonly transformer: BaseClientTransformer;
9
+ constructor(options: BaseClientOptions<RouterContract, SafeCall>, transport: Transport, transportOptions: Transport extends ClientTransportFactory<any, infer Options> ? Options : never);
10
+ get call(): ClientCallers<this["_"]["routes"], SafeCall, false>;
11
+ get stream(): ClientCallers<this["_"]["routes"], SafeCall, true>;
12
+ protected createProxy<T>(target: Record<string, unknown>, isStream: boolean, path?: string[]): T;
13
+ }
@@ -0,0 +1,27 @@
1
+ import { BaseClient } from "../core.js";
2
+ import { BaseClientTransformer } from "../transformers.js";
3
+ export class StaticClient extends BaseClient {
4
+ transformer;
5
+ constructor(options, transport, transportOptions) {
6
+ super(options, transport, transportOptions);
7
+ this.transformer = new BaseClientTransformer();
8
+ }
9
+ get call() {
10
+ return this.createProxy(Object.create(null), false);
11
+ }
12
+ get stream() {
13
+ return this.createProxy(Object.create(null), true);
14
+ }
15
+ createProxy(target, isStream, path = []) {
16
+ return new Proxy(target, {
17
+ get: (obj, prop) => {
18
+ if (prop === 'then')
19
+ return obj;
20
+ const newPath = [...path, String(prop)];
21
+ const caller = (payload, options) => this._call(newPath.join('/'), payload, options);
22
+ return this.createProxy(caller, isStream, newPath);
23
+ },
24
+ });
25
+ }
26
+ }
27
+ //# sourceMappingURL=static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static.js","sourceRoot":"","sources":["../../src/clients/static.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,MAAM,OAAO,YAOX,SAAQ,UAMT;IACoB,WAAW,CAAuB;IAErD,YACE,OAAoD,EACpD,SAAoB,EACpB,gBAKS;QAET,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAA;QAC3C,IAAI,CAAC,WAAW,GAAG,IAAI,qBAAqB,EAAE,CAAA;IAChD,CAAC;IAED,IAAa,IAAI;QACf,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,CAIjD,CAAA;IACH,CAAC;IAED,IAAa,MAAM;QACjB,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,CAIhD,CAAA;IACH,CAAC;IAES,WAAW,CACnB,MAA+B,EAC/B,QAAiB,EACjB,OAAiB,EAAE;QAEnB,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE;YACvB,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBACjB,IAAI,IAAI,KAAK,MAAM;oBAAE,OAAO,GAAG,CAAA;gBAC/B,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;gBACvC,MAAM,MAAM,GAAG,CACb,OAAiB,EACjB,OAAoC,EACpC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;gBACpD,OAAO,IAAI,CAAC,WAAW,CAAC,MAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;YAC3D,CAAC;SACF,CAAM,CAAA;IACT,CAAC;CACF"}
package/dist/core.d.ts ADDED
@@ -0,0 +1,71 @@
1
+ import type { Future, TypeProvider } from '@nmtjs/common';
2
+ import type { TAnyRouterContract } from '@nmtjs/contract';
3
+ import type { ProtocolBlobMetadata, ProtocolVersion } from '@nmtjs/protocol';
4
+ import type { BaseClientFormat, MessageContext, ProtocolVersionInterface } from '@nmtjs/protocol/client';
5
+ import { ProtocolBlob } from '@nmtjs/protocol';
6
+ import { ProtocolError } from '@nmtjs/protocol/client';
7
+ import type { BaseClientTransformer } from './transformers.ts';
8
+ import type { ClientTransportFactory } from './transport.ts';
9
+ import type { ClientCallers, ClientCallOptions, ResolveAPIRouterRoutes } from './types.ts';
10
+ import { EventEmitter } from './events.ts';
11
+ import { ClientStreams, ServerStreams } from './streams.ts';
12
+ export { ErrorCode, ProtocolBlob, type ProtocolBlobMetadata, } from '@nmtjs/protocol';
13
+ export * from './types.ts';
14
+ export declare class ClientError extends ProtocolError {
15
+ }
16
+ export type ProtocolClientCall = Future<any> & {
17
+ procedure: string;
18
+ signal?: AbortSignal;
19
+ };
20
+ export interface BaseClientOptions<RouterContract extends TAnyRouterContract = TAnyRouterContract, SafeCall extends boolean = false> {
21
+ contract: RouterContract;
22
+ protocol: ProtocolVersion;
23
+ format: BaseClientFormat;
24
+ application?: string;
25
+ timeout?: number;
26
+ autoreconnect?: boolean;
27
+ safe?: SafeCall;
28
+ }
29
+ /**
30
+ * @todo Add error logging in ClientStreamPull rejection handler for easier debugging
31
+ * @todo Consider edge case where callId/streamId overflow at MAX_UINT32 with existing entries
32
+ */
33
+ export declare abstract class BaseClient<TransportFactory extends ClientTransportFactory<any, any> = ClientTransportFactory<any, any>, RouterContract extends TAnyRouterContract = TAnyRouterContract, SafeCall extends boolean = false, InputTypeProvider extends TypeProvider = TypeProvider, OutputTypeProvider extends TypeProvider = TypeProvider> extends EventEmitter<{
34
+ connected: [];
35
+ disconnected: [reason: 'server' | 'client' | (string & {})];
36
+ }> {
37
+ #private;
38
+ readonly options: BaseClientOptions<RouterContract, SafeCall>;
39
+ readonly transportFactory: TransportFactory;
40
+ readonly transportOptions: TransportFactory extends ClientTransportFactory<any, infer U> ? U : never;
41
+ _: {
42
+ routes: ResolveAPIRouterRoutes<RouterContract, InputTypeProvider, OutputTypeProvider>;
43
+ safe: SafeCall;
44
+ };
45
+ protected abstract readonly transformer: BaseClientTransformer;
46
+ abstract call: ClientCallers<this['_']['routes'], SafeCall, false>;
47
+ abstract stream: ClientCallers<this['_']['routes'], SafeCall, true>;
48
+ protected calls: Map<number, ProtocolClientCall>;
49
+ protected transport: TransportFactory extends ClientTransportFactory<any, any, infer T> ? T : never;
50
+ protected protocol: ProtocolVersionInterface;
51
+ protected messageContext: MessageContext | null;
52
+ protected clientStreams: ClientStreams;
53
+ protected serverStreams: ServerStreams<import("@nmtjs/protocol/client").ProtocolServerStreamInterface<unknown>>;
54
+ protected rpcStreams: ServerStreams<import("@nmtjs/protocol/client").ProtocolServerStreamInterface<unknown>>;
55
+ protected callId: number;
56
+ protected streamId: number;
57
+ protected cab: AbortController | null;
58
+ protected reconnectTimeout: number;
59
+ protected connecting: Promise<void> | null;
60
+ protected state: 'connected' | 'disconnected';
61
+ constructor(options: BaseClientOptions<RouterContract, SafeCall>, transportFactory: TransportFactory, transportOptions: TransportFactory extends ClientTransportFactory<any, infer U> ? U : never);
62
+ get auth(): any;
63
+ set auth(value: any);
64
+ connect(): Promise<void>;
65
+ disconnect(): Promise<void>;
66
+ blob(source: Blob | ReadableStream | string | AsyncIterable<Uint8Array>, metadata?: ProtocolBlobMetadata): ProtocolBlob;
67
+ protected _call(procedure: string, payload: any, options?: ClientCallOptions): Promise<any>;
68
+ protected onConnect(): Promise<void>;
69
+ protected onDisconnect(reason: 'client' | 'server' | (string & {})): Promise<void>;
70
+ protected onMessage(buffer: ArrayBufferView): Promise<void>;
71
+ }
package/dist/core.js ADDED
@@ -0,0 +1,426 @@
1
+ import { anyAbortSignal, createFuture, MAX_UINT32, noopFn } from '@nmtjs/common';
2
+ import { ClientMessageType, ConnectionType, ErrorCode, ProtocolBlob, ServerMessageType, } from '@nmtjs/protocol';
3
+ import { ProtocolError, ProtocolServerBlobStream, ProtocolServerRPCStream, ProtocolServerStream, versions, } from '@nmtjs/protocol/client';
4
+ import { EventEmitter } from "./events.js";
5
+ import { ClientStreams, ServerStreams } from "./streams.js";
6
+ export { ErrorCode, ProtocolBlob, } from '@nmtjs/protocol';
7
+ export * from "./types.js";
8
+ export class ClientError extends ProtocolError {
9
+ }
10
+ const DEFAULT_RECONNECT_TIMEOUT = 1000;
11
+ const DEFAULT_MAX_RECONNECT_TIMEOUT = 60000;
12
+ /**
13
+ * @todo Add error logging in ClientStreamPull rejection handler for easier debugging
14
+ * @todo Consider edge case where callId/streamId overflow at MAX_UINT32 with existing entries
15
+ */
16
+ export class BaseClient extends EventEmitter {
17
+ options;
18
+ transportFactory;
19
+ transportOptions;
20
+ _;
21
+ calls = new Map();
22
+ transport;
23
+ protocol;
24
+ messageContext;
25
+ clientStreams = new ClientStreams();
26
+ serverStreams = new ServerStreams();
27
+ rpcStreams = new ServerStreams();
28
+ callId = 0;
29
+ streamId = 0;
30
+ cab = null;
31
+ reconnectTimeout = DEFAULT_RECONNECT_TIMEOUT;
32
+ connecting = null;
33
+ state = 'disconnected';
34
+ #auth;
35
+ constructor(options, transportFactory, transportOptions) {
36
+ super();
37
+ this.options = options;
38
+ this.transportFactory = transportFactory;
39
+ this.transportOptions = transportOptions;
40
+ this.protocol = versions[options.protocol];
41
+ const { format, protocol } = this.options;
42
+ this.transport = this.transportFactory({ protocol, format }, this.transportOptions);
43
+ if (this.transport.type === ConnectionType.Bidirectional &&
44
+ this.options.autoreconnect) {
45
+ this.on('disconnected', async (reason) => {
46
+ while (this.state === 'disconnected') {
47
+ const timeout = new Promise((resolve) => setTimeout(resolve, this.reconnectTimeout));
48
+ this.reconnectTimeout = Math.min(this.reconnectTimeout * 2, DEFAULT_MAX_RECONNECT_TIMEOUT);
49
+ await timeout;
50
+ await this.connect().catch(noopFn);
51
+ }
52
+ });
53
+ this.on('connected', () => {
54
+ this.reconnectTimeout = DEFAULT_RECONNECT_TIMEOUT;
55
+ });
56
+ if (globalThis.window) {
57
+ globalThis.window.addEventListener('pageshow', () => {
58
+ if (this.state === 'disconnected')
59
+ this.connect();
60
+ });
61
+ }
62
+ }
63
+ }
64
+ get auth() {
65
+ return this.#auth;
66
+ }
67
+ set auth(value) {
68
+ this.#auth = value;
69
+ }
70
+ connect() {
71
+ if (this.state === 'connected')
72
+ return Promise.resolve();
73
+ if (this.connecting)
74
+ return this.connecting;
75
+ const _connect = async () => {
76
+ if (this.transport.type === ConnectionType.Bidirectional) {
77
+ this.cab = new AbortController();
78
+ const protocol = this.protocol;
79
+ const serverStreams = this.serverStreams;
80
+ const transport = {
81
+ send: (buffer) => {
82
+ this.#send(buffer).catch(noopFn);
83
+ },
84
+ };
85
+ this.messageContext = {
86
+ transport,
87
+ encoder: this.options.format,
88
+ decoder: this.options.format,
89
+ addClientStream: (blob) => {
90
+ const streamId = this.#getStreamId();
91
+ return this.clientStreams.add(blob.source, streamId, blob.metadata);
92
+ },
93
+ addServerStream(streamId, metadata) {
94
+ const stream = new ProtocolServerBlobStream(metadata, {
95
+ pull: (controller) => {
96
+ transport.send(protocol.encodeMessage(this, ClientMessageType.ServerStreamPull, { streamId, size: 65535 /* 64kb by default */ }));
97
+ },
98
+ close: () => {
99
+ serverStreams.remove(streamId);
100
+ },
101
+ readableStrategy: { highWaterMark: 0 },
102
+ });
103
+ serverStreams.add(streamId, stream);
104
+ return ({ signal } = {}) => {
105
+ if (signal)
106
+ signal.addEventListener('abort', () => {
107
+ transport.send(protocol.encodeMessage(this, ClientMessageType.ServerStreamAbort, { streamId }));
108
+ serverStreams.abort(streamId);
109
+ }, { once: true });
110
+ return stream;
111
+ };
112
+ },
113
+ streamId: this.#getStreamId.bind(this),
114
+ };
115
+ return this.transport.connect({
116
+ auth: this.auth,
117
+ application: this.options.application,
118
+ onMessage: this.onMessage.bind(this),
119
+ onConnect: this.onConnect.bind(this),
120
+ onDisconnect: this.onDisconnect.bind(this),
121
+ });
122
+ }
123
+ };
124
+ this.connecting = _connect()
125
+ .then(() => {
126
+ this.state = 'connected';
127
+ })
128
+ .finally(() => {
129
+ this.connecting = null;
130
+ });
131
+ return this.connecting;
132
+ }
133
+ async disconnect() {
134
+ if (this.transport.type === ConnectionType.Bidirectional) {
135
+ this.cab.abort();
136
+ await this.transport.disconnect();
137
+ this.messageContext = null;
138
+ this.cab = null;
139
+ }
140
+ }
141
+ blob(source, metadata) {
142
+ return ProtocolBlob.from(source, metadata);
143
+ }
144
+ async _call(procedure, payload, options = {}) {
145
+ const timeout = options.timeout ?? this.options.timeout;
146
+ const controller = new AbortController();
147
+ // attach all abort signals
148
+ const signals = [controller.signal];
149
+ if (timeout)
150
+ signals.push(AbortSignal.timeout(timeout));
151
+ if (options.signal)
152
+ signals.push(options.signal);
153
+ if (this.cab?.signal)
154
+ signals.push(this.cab.signal);
155
+ const signal = signals.length ? anyAbortSignal(...signals) : undefined;
156
+ const callId = this.#getCallId();
157
+ const call = createFuture();
158
+ call.procedure = procedure;
159
+ call.signal = signal;
160
+ this.calls.set(callId, call);
161
+ // Check if signal is already aborted before proceeding
162
+ if (signal?.aborted) {
163
+ this.calls.delete(callId);
164
+ const error = new ProtocolError(ErrorCode.ClientRequestError, signal.reason);
165
+ call.reject(error);
166
+ }
167
+ else {
168
+ if (signal) {
169
+ signal.addEventListener('abort', () => {
170
+ call.reject(new ProtocolError(ErrorCode.ClientRequestError, signal.reason));
171
+ if (this.transport.type === ConnectionType.Bidirectional &&
172
+ this.messageContext) {
173
+ const buffer = this.protocol.encodeMessage(this.messageContext, ClientMessageType.RpcAbort, { callId });
174
+ this.#send(buffer).catch(noopFn);
175
+ }
176
+ }, { once: true });
177
+ }
178
+ try {
179
+ const transformedPayload = this.transformer.encode(procedure, payload);
180
+ if (this.transport.type === ConnectionType.Bidirectional) {
181
+ const buffer = this.protocol.encodeMessage(this.messageContext, ClientMessageType.Rpc, { callId, procedure, payload: transformedPayload });
182
+ await this.#send(buffer, signal);
183
+ }
184
+ else {
185
+ const response = await this.transport.call({
186
+ application: this.options.application,
187
+ format: this.options.format,
188
+ auth: this.auth,
189
+ }, { callId, procedure, payload: transformedPayload }, { signal, _stream_response: options._stream_response });
190
+ this.#handleCallResponse(callId, response);
191
+ }
192
+ }
193
+ catch (error) {
194
+ call.reject(error);
195
+ }
196
+ }
197
+ const result = call.promise.then((value) => {
198
+ if (value instanceof ProtocolServerRPCStream) {
199
+ return value.createAsyncIterable(() => {
200
+ controller.abort();
201
+ });
202
+ }
203
+ controller.abort();
204
+ return value;
205
+ }, (err) => {
206
+ controller.abort();
207
+ throw err;
208
+ });
209
+ if (this.options.safe) {
210
+ return await result
211
+ .then((result) => ({ result }))
212
+ .catch((error) => ({ error }))
213
+ .finally(() => {
214
+ this.calls.delete(callId);
215
+ });
216
+ }
217
+ else {
218
+ return await result.finally(() => {
219
+ this.calls.delete(callId);
220
+ });
221
+ }
222
+ }
223
+ async onConnect() {
224
+ this.state = 'connected';
225
+ this.emit('connected');
226
+ }
227
+ async onDisconnect(reason) {
228
+ this.state = 'disconnected';
229
+ this.emit('disconnected', reason);
230
+ this.clientStreams.clear(reason);
231
+ this.serverStreams.clear(reason);
232
+ this.rpcStreams.clear(reason);
233
+ }
234
+ async onMessage(buffer) {
235
+ if (!this.messageContext)
236
+ return;
237
+ const message = this.protocol.decodeMessage(this.messageContext, buffer);
238
+ switch (message.type) {
239
+ case ServerMessageType.RpcResponse:
240
+ this.#handleRPCResponseMessage(message);
241
+ break;
242
+ case ServerMessageType.RpcStreamResponse:
243
+ this.#handleRPCStreamResponseMessage(message);
244
+ break;
245
+ case ServerMessageType.RpcStreamChunk:
246
+ this.rpcStreams.push(message.callId, message.chunk);
247
+ break;
248
+ case ServerMessageType.RpcStreamEnd:
249
+ this.rpcStreams.end(message.callId);
250
+ this.calls.delete(message.callId);
251
+ break;
252
+ case ServerMessageType.RpcStreamAbort:
253
+ this.rpcStreams.abort(message.callId);
254
+ this.calls.delete(message.callId);
255
+ break;
256
+ case ServerMessageType.ServerStreamPush:
257
+ this.serverStreams.push(message.streamId, message.chunk);
258
+ break;
259
+ case ServerMessageType.ServerStreamEnd:
260
+ this.serverStreams.end(message.streamId);
261
+ break;
262
+ case ServerMessageType.ServerStreamAbort:
263
+ this.serverStreams.abort(message.streamId);
264
+ break;
265
+ case ServerMessageType.ClientStreamPull:
266
+ this.clientStreams.pull(message.streamId, message.size).then((chunk) => {
267
+ if (chunk) {
268
+ const buffer = this.protocol.encodeMessage(this.messageContext, ClientMessageType.ClientStreamPush, { streamId: message.streamId, chunk });
269
+ this.#send(buffer).catch(noopFn);
270
+ }
271
+ else {
272
+ const buffer = this.protocol.encodeMessage(this.messageContext, ClientMessageType.ClientStreamEnd, { streamId: message.streamId });
273
+ this.#send(buffer).catch(noopFn);
274
+ this.clientStreams.end(message.streamId);
275
+ }
276
+ }, () => {
277
+ const buffer = this.protocol.encodeMessage(this.messageContext, ClientMessageType.ClientStreamAbort, { streamId: message.streamId });
278
+ this.#send(buffer).catch(noopFn);
279
+ this.clientStreams.remove(message.streamId);
280
+ });
281
+ break;
282
+ case ServerMessageType.ClientStreamAbort:
283
+ this.clientStreams.abort(message.streamId);
284
+ break;
285
+ }
286
+ }
287
+ #handleRPCResponseMessage(message) {
288
+ const { callId, result, error } = message;
289
+ const call = this.calls.get(callId);
290
+ if (!call)
291
+ return;
292
+ if (error) {
293
+ call.reject(new ProtocolError(error.code, error.message, error.data));
294
+ }
295
+ else {
296
+ try {
297
+ const transformed = this.transformer.decode(call.procedure, result);
298
+ call.resolve(transformed);
299
+ }
300
+ catch (error) {
301
+ call.reject(new ProtocolError(ErrorCode.ClientRequestError, 'Unable to decode response', error));
302
+ }
303
+ }
304
+ }
305
+ #handleRPCStreamResponseMessage(message) {
306
+ const call = this.calls.get(message.callId);
307
+ if (message.error) {
308
+ if (!call)
309
+ return;
310
+ call.reject(new ProtocolError(message.error.code, message.error.message, message.error.data));
311
+ }
312
+ else {
313
+ if (call) {
314
+ const { procedure, signal } = call;
315
+ const stream = new ProtocolServerRPCStream({
316
+ start: (controller) => {
317
+ if (signal) {
318
+ if (signal.aborted)
319
+ controller.error(signal.reason);
320
+ else
321
+ signal.addEventListener('abort', () => {
322
+ controller.error(signal.reason);
323
+ if (this.rpcStreams.has(message.callId)) {
324
+ this.rpcStreams.remove(message.callId);
325
+ this.calls.delete(message.callId);
326
+ if (this.messageContext) {
327
+ const buffer = this.protocol.encodeMessage(this.messageContext, ClientMessageType.RpcAbort, { callId: message.callId, reason: signal.reason });
328
+ this.#send(buffer).catch(noopFn);
329
+ }
330
+ }
331
+ }, { once: true });
332
+ }
333
+ },
334
+ transform: (chunk) => {
335
+ return this.transformer.decode(procedure, this.options.format.decode(chunk));
336
+ },
337
+ pull: () => {
338
+ const buffer = this.protocol.encodeMessage(this.messageContext, ClientMessageType.RpcPull, { callId: message.callId });
339
+ this.#send(buffer).catch(noopFn);
340
+ },
341
+ readableStrategy: { highWaterMark: 0 },
342
+ });
343
+ this.rpcStreams.add(message.callId, stream);
344
+ call.resolve(stream);
345
+ }
346
+ else {
347
+ // Call not found, but stream response received
348
+ // This can happen if the call was aborted or timed out
349
+ // Need to send an abort for the stream to avoid resource leaks from server side
350
+ if (this.messageContext) {
351
+ const buffer = this.protocol.encodeMessage(this.messageContext, ClientMessageType.RpcAbort, { callId: message.callId });
352
+ this.#send(buffer).catch(noopFn);
353
+ }
354
+ }
355
+ }
356
+ }
357
+ #handleCallResponse(callId, response) {
358
+ const call = this.calls.get(callId);
359
+ if (response.type === 'rpc_stream') {
360
+ if (call) {
361
+ const stream = new ProtocolServerStream({
362
+ transform: (chunk) => {
363
+ return this.transformer.decode(call.procedure, this.options.format.decode(chunk));
364
+ },
365
+ });
366
+ this.rpcStreams.add(callId, stream);
367
+ call.resolve(({ signal }) => {
368
+ response.stream.pipeTo(stream.writable, { signal }).catch(noopFn);
369
+ return stream;
370
+ });
371
+ }
372
+ else {
373
+ // Call not found, but stream response received
374
+ // This can happen if the call was aborted or timed out
375
+ // Need to cancel the stream to avoid resource leaks from server side
376
+ response.stream.cancel().catch(noopFn);
377
+ }
378
+ }
379
+ else if (response.type === 'blob') {
380
+ if (call) {
381
+ const { metadata, source } = response;
382
+ const stream = new ProtocolServerBlobStream(metadata);
383
+ this.serverStreams.add(this.#getStreamId(), stream);
384
+ call.resolve(({ signal }) => {
385
+ source.pipeTo(stream.writable, { signal }).catch(noopFn);
386
+ return stream;
387
+ });
388
+ }
389
+ else {
390
+ // Call not found, but blob response received
391
+ // This can happen if the call was aborted or timed out
392
+ // Need to cancel the stream to avoid resource leaks from server side
393
+ response.source.cancel().catch(noopFn);
394
+ }
395
+ }
396
+ else if (response.type === 'rpc') {
397
+ if (!call)
398
+ return;
399
+ try {
400
+ const transformed = this.transformer.decode(call.procedure, response.result);
401
+ call.resolve(transformed);
402
+ }
403
+ catch (error) {
404
+ call.reject(new ProtocolError(ErrorCode.ClientRequestError, 'Unable to decode response', error));
405
+ }
406
+ }
407
+ }
408
+ #send(buffer, signal) {
409
+ if (this.transport.type === ConnectionType.Unidirectional)
410
+ throw new Error('Invalid transport type for send');
411
+ return this.transport.send(buffer, { signal });
412
+ }
413
+ #getStreamId() {
414
+ if (this.streamId >= MAX_UINT32) {
415
+ this.streamId = 0;
416
+ }
417
+ return this.streamId++;
418
+ }
419
+ #getCallId() {
420
+ if (this.callId >= MAX_UINT32) {
421
+ this.callId = 0;
422
+ }
423
+ return this.callId++;
424
+ }
425
+ }
426
+ //# sourceMappingURL=core.js.map