@nmtjs/client 0.15.3 → 0.16.0-beta.10

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.
Files changed (60) hide show
  1. package/dist/client.d.ts +68 -0
  2. package/dist/client.js +101 -0
  3. package/dist/client.js.map +1 -0
  4. package/dist/clients/runtime.d.ts +6 -12
  5. package/dist/clients/runtime.js +58 -57
  6. package/dist/clients/runtime.js.map +1 -1
  7. package/dist/clients/static.d.ts +4 -9
  8. package/dist/clients/static.js +20 -20
  9. package/dist/clients/static.js.map +1 -1
  10. package/dist/core.d.ts +36 -83
  11. package/dist/core.js +315 -690
  12. package/dist/core.js.map +1 -1
  13. package/dist/events.d.ts +1 -2
  14. package/dist/events.js +74 -11
  15. package/dist/events.js.map +1 -1
  16. package/dist/index.d.ts +4 -0
  17. package/dist/index.js +4 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/layers/ping.d.ts +6 -0
  20. package/dist/layers/ping.js +65 -0
  21. package/dist/layers/ping.js.map +1 -0
  22. package/dist/layers/rpc.d.ts +19 -0
  23. package/dist/layers/rpc.js +557 -0
  24. package/dist/layers/rpc.js.map +1 -0
  25. package/dist/layers/streams.d.ts +25 -0
  26. package/dist/layers/streams.js +207 -0
  27. package/dist/layers/streams.js.map +1 -0
  28. package/dist/plugins/browser.js +28 -9
  29. package/dist/plugins/browser.js.map +1 -1
  30. package/dist/plugins/heartbeat.js +10 -10
  31. package/dist/plugins/heartbeat.js.map +1 -1
  32. package/dist/plugins/index.d.ts +1 -1
  33. package/dist/plugins/index.js +0 -1
  34. package/dist/plugins/index.js.map +1 -1
  35. package/dist/plugins/logging.js.map +1 -1
  36. package/dist/plugins/reconnect.js +11 -94
  37. package/dist/plugins/reconnect.js.map +1 -1
  38. package/dist/plugins/types.d.ts +27 -11
  39. package/dist/streams.js.map +1 -1
  40. package/dist/transformers.js.map +1 -1
  41. package/dist/transport.d.ts +49 -31
  42. package/dist/types.d.ts +23 -13
  43. package/dist/types.js.map +1 -1
  44. package/package.json +9 -10
  45. package/src/client.ts +232 -0
  46. package/src/clients/runtime.ts +93 -79
  47. package/src/clients/static.ts +46 -38
  48. package/src/core.ts +408 -901
  49. package/src/events.ts +113 -14
  50. package/src/index.ts +4 -0
  51. package/src/layers/ping.ts +99 -0
  52. package/src/layers/rpc.ts +767 -0
  53. package/src/layers/streams.ts +320 -0
  54. package/src/plugins/browser.ts +39 -9
  55. package/src/plugins/heartbeat.ts +10 -10
  56. package/src/plugins/index.ts +8 -1
  57. package/src/plugins/reconnect.ts +12 -119
  58. package/src/plugins/types.ts +30 -13
  59. package/src/transport.ts +75 -46
  60. package/src/types.ts +34 -19
@@ -1,53 +1,71 @@
1
1
  import type { ConnectionType, ProtocolBlobMetadata, ProtocolVersion } from '@nmtjs/protocol';
2
2
  import type { BaseClientFormat } from '@nmtjs/protocol/client';
3
- export type ClientTransportMessageOptions = {
4
- signal?: AbortSignal;
5
- _stream_response?: boolean;
6
- };
7
- export interface ClientTransportStartParams {
3
+ export type ClientDisconnectReason = 'client' | 'server' | (string & {});
4
+ export interface TransportConnectParams {
8
5
  auth?: string;
9
6
  application?: string;
10
- onMessage: (message: ArrayBufferView) => any;
11
- onConnect: () => any;
12
- onDisconnect: (reason: 'client' | 'server' | (string & {})) => any;
7
+ onMessage: (message: ArrayBufferView) => void;
8
+ onConnect: () => void;
9
+ onDisconnect: (reason: ClientDisconnectReason) => void;
13
10
  }
14
- export interface ClientTransportRpcParams {
15
- format: BaseClientFormat;
11
+ export interface TransportSendOptions {
12
+ signal?: AbortSignal;
13
+ }
14
+ export interface TransportCallContext {
15
+ contentType: string;
16
16
  auth?: string;
17
17
  application?: string;
18
18
  }
19
- export type ClientCallResponse = {
19
+ export interface TransportRpcParams {
20
+ callId: number;
21
+ procedure: string;
22
+ payload: ArrayBufferView;
23
+ blob?: {
24
+ source: ReadableStream;
25
+ metadata: ProtocolBlobMetadata;
26
+ };
27
+ }
28
+ export interface TransportCallOptions {
29
+ signal?: AbortSignal;
30
+ streamResponse?: boolean;
31
+ }
32
+ export interface TransportRpcResponse {
20
33
  type: 'rpc';
21
34
  result: ArrayBufferView;
22
- } | {
35
+ }
36
+ export interface TransportRpcStreamResponse {
23
37
  type: 'rpc_stream';
24
38
  stream: ReadableStream<ArrayBufferView>;
25
- } | {
39
+ }
40
+ export interface TransportBlobResponse {
26
41
  type: 'blob';
27
42
  metadata: ProtocolBlobMetadata;
28
43
  source: ReadableStream<ArrayBufferView>;
29
- };
30
- export type ClientTransport<T extends ConnectionType = ConnectionType> = T extends ConnectionType.Bidirectional ? {
44
+ }
45
+ export interface TransportErrorResponse {
46
+ type: 'error';
47
+ error: ArrayBufferView;
48
+ status?: number;
49
+ statusText?: string;
50
+ }
51
+ export type TransportCallResponse = TransportRpcResponse | TransportRpcStreamResponse | TransportBlobResponse | TransportErrorResponse;
52
+ export interface BidirectionalTransport {
31
53
  type: ConnectionType.Bidirectional;
32
- connect(params: ClientTransportStartParams): Promise<void>;
54
+ connect(params: TransportConnectParams): Promise<void>;
33
55
  disconnect(): Promise<void>;
34
- send(message: ArrayBufferView, options: ClientTransportMessageOptions): Promise<void>;
35
- } : {
56
+ send(message: ArrayBufferView, options: TransportSendOptions): Promise<void>;
57
+ }
58
+ export interface UnidirectionalTransport {
36
59
  type: ConnectionType.Unidirectional;
37
- connect?(params: ClientTransportStartParams): Promise<void>;
38
- disconnect?(): Promise<void>;
39
- call(client: {
40
- format: BaseClientFormat;
41
- auth?: string;
42
- application?: string;
43
- }, rpc: {
44
- callId: number;
45
- procedure: string;
46
- payload: any;
47
- }, options: ClientTransportMessageOptions): Promise<ClientCallResponse>;
48
- };
60
+ call(context: TransportCallContext, rpc: TransportRpcParams, options: TransportCallOptions): Promise<TransportCallResponse>;
61
+ }
62
+ export type ClientTransport = BidirectionalTransport | UnidirectionalTransport;
49
63
  export interface ClientTransportParams {
50
64
  protocol: ProtocolVersion;
51
65
  format: BaseClientFormat;
52
66
  }
53
- export type ClientTransportFactory<Type extends ConnectionType, Options = unknown, Transport extends ClientTransport<Type> = ClientTransport<Type>> = (params: ClientTransportParams, options: Options) => Transport;
67
+ export type ClientTransportFactory<Transport extends ClientTransport = ClientTransport, Options = unknown> = (params: ClientTransportParams, options: Options) => Transport;
68
+ export type ClientTransportMessageOptions = TransportSendOptions;
69
+ export type ClientTransportStartParams = TransportConnectParams;
70
+ export type ClientTransportRpcParams = TransportCallContext;
71
+ export type ClientCallResponse = TransportCallResponse;
package/dist/types.d.ts CHANGED
@@ -1,25 +1,26 @@
1
1
  import type { CallTypeProvider, OneOf, TypeProvider } from '@nmtjs/common';
2
2
  import type { TAnyProcedureContract, TAnyRouterContract } from '@nmtjs/contract';
3
- import type { ProtocolBlobInterface } from '@nmtjs/protocol';
4
- import type { ProtocolError, ProtocolServerBlobStream } from '@nmtjs/protocol/client';
5
- import type { BaseTypeAny, PlainType, t } from '@nmtjs/type';
3
+ import type { ProtocolError } from '@nmtjs/protocol/client';
4
+ import type { BaseTypeAny, t } from '@nmtjs/type';
6
5
  export declare const ResolvedType: unique symbol;
7
6
  export type ResolvedType = typeof ResolvedType;
8
- export type ClientCallOptions = {
7
+ export type RpcCallOptions = {
9
8
  timeout?: number;
10
9
  signal?: AbortSignal;
10
+ };
11
+ export type StreamCallOptions = RpcCallOptions & {
12
+ autoReconnect?: boolean;
13
+ };
14
+ export type ClientCallOptions = StreamCallOptions & {
11
15
  /**
12
16
  * @internal
13
17
  */
14
18
  _stream_response?: boolean;
15
19
  };
16
- export type ClientOutputType<T> = T extends ProtocolBlobInterface ? (options?: {
20
+ export type BlobSubscriptionOptions = {
17
21
  signal?: AbortSignal;
18
- }) => ProtocolServerBlobStream : T extends {
19
- [PlainType]?: true;
20
- } ? {
21
- [K in keyof Omit<T, PlainType>]: ClientOutputType<T[K]>;
22
- } : T;
22
+ };
23
+ export type StreamSubscriptionOptions = Partial<StreamCallOptions>;
23
24
  export interface StaticInputContractTypeProvider extends TypeProvider {
24
25
  output: this['input'] extends BaseTypeAny ? t.infer.decode.input<this['input']> : never;
25
26
  }
@@ -27,10 +28,10 @@ export interface RuntimeInputContractTypeProvider extends TypeProvider {
27
28
  output: this['input'] extends BaseTypeAny ? t.infer.encode.input<this['input']> : never;
28
29
  }
29
30
  export interface StaticOutputContractTypeProvider extends TypeProvider {
30
- output: this['input'] extends BaseTypeAny ? ClientOutputType<t.infer.encodeRaw.output<this['input']>> : never;
31
+ output: this['input'] extends BaseTypeAny ? t.infer.encode.output<this['input']> : never;
31
32
  }
32
33
  export interface RuntimeOutputContractTypeProvider extends TypeProvider {
33
- output: this['input'] extends BaseTypeAny ? ClientOutputType<t.infer.decodeRaw.output<this['input']>> : never;
34
+ output: this['input'] extends BaseTypeAny ? t.infer.decode.output<this['input']> : never;
34
35
  }
35
36
  export type AnyResolvedContractProcedure = {
36
37
  [ResolvedType]: 'procedure';
@@ -55,7 +56,16 @@ export type ResolveAPIRouterRoutes<T extends TAnyRouterContract, InputTypeProvid
55
56
  } : T['routes'][K] extends TAnyRouterContract ? ResolveAPIRouterRoutes<T['routes'][K], InputTypeProvider, OutputTypeProvider> : never;
56
57
  };
57
58
  export type ResolveContract<C extends TAnyRouterContract = TAnyRouterContract, InputTypeProvider extends TypeProvider = TypeProvider, OutputTypeProvider extends TypeProvider = TypeProvider> = ResolveAPIRouterRoutes<C, InputTypeProvider, OutputTypeProvider>;
58
- export type ClientCaller<Procedure extends AnyResolvedContractProcedure, SafeCall extends boolean> = (...args: Procedure['input'] extends t.NeverType ? [data?: undefined, options?: Partial<ClientCallOptions>] : undefined extends t.infer.encode.input<Procedure['contract']['input']> ? [data?: Procedure['input'], options?: Partial<ClientCallOptions>] : [data: Procedure['input'], options?: Partial<ClientCallOptions>]) => SafeCall extends true ? Promise<OneOf<[{
59
+ export type ClientCaller<Procedure extends AnyResolvedContractProcedure, SafeCall extends boolean> = (...args: Procedure['input'] extends t.NeverType ? [
60
+ data?: undefined,
61
+ options?: Partial<Procedure['stream'] extends true ? StreamCallOptions : RpcCallOptions>
62
+ ] : undefined extends t.infer.encode.input<Procedure['contract']['input']> ? [
63
+ data?: Procedure['input'],
64
+ options?: Partial<Procedure['stream'] extends true ? StreamCallOptions : RpcCallOptions>
65
+ ] : [
66
+ data: Procedure['input'],
67
+ options?: Partial<Procedure['stream'] extends true ? StreamCallOptions : RpcCallOptions>
68
+ ]) => SafeCall extends true ? Promise<OneOf<[{
59
69
  result: Procedure['output'];
60
70
  }, {
61
71
  error: ProtocolError;
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,YAAY,GAAkB,MAAM,CAAC,cAAc,CAAC,CAAA"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,YAAY,GAAkB,MAAM,CAAC,cAAc,CAAC,CAAA"}
package/package.json CHANGED
@@ -20,17 +20,16 @@
20
20
  }
21
21
  },
22
22
  "peerDependencies": {
23
- "@nmtjs/type": "0.15.3",
24
- "@nmtjs/contract": "0.15.3",
25
- "@nmtjs/protocol": "0.15.3",
26
- "@nmtjs/common": "0.15.3"
23
+ "@nmtjs/type": "0.16.0-beta.10",
24
+ "@nmtjs/common": "0.16.0-beta.10",
25
+ "@nmtjs/contract": "0.16.0-beta.10",
26
+ "@nmtjs/protocol": "0.16.0-beta.10"
27
27
  },
28
28
  "devDependencies": {
29
- "@nmtjs/tests-integration": "0.15.3",
30
- "@nmtjs/type": "0.15.3",
31
- "@nmtjs/contract": "0.15.3",
32
- "@nmtjs/common": "0.15.3",
33
- "@nmtjs/protocol": "0.15.3"
29
+ "@nmtjs/contract": "0.16.0-beta.10",
30
+ "@nmtjs/type": "0.16.0-beta.10",
31
+ "@nmtjs/protocol": "0.16.0-beta.10",
32
+ "@nmtjs/common": "0.16.0-beta.10"
34
33
  },
35
34
  "files": [
36
35
  "dist",
@@ -38,7 +37,7 @@
38
37
  "LICENSE.md",
39
38
  "README.md"
40
39
  ],
41
- "version": "0.15.3",
40
+ "version": "0.16.0-beta.10",
42
41
  "scripts": {
43
42
  "clean-build": "rm -rf ./dist"
44
43
  }
package/src/client.ts ADDED
@@ -0,0 +1,232 @@
1
+ import type { TypeProvider } from '@nmtjs/common'
2
+ import type { TAnyRouterContract } from '@nmtjs/contract'
3
+ import type {
4
+ ProtocolBlobInterface,
5
+ ProtocolBlobMetadata,
6
+ ProtocolVersion,
7
+ } from '@nmtjs/protocol'
8
+ import type {
9
+ BaseClientFormat,
10
+ ProtocolServerBlobStream,
11
+ } from '@nmtjs/protocol/client'
12
+ import { noopFn } from '@nmtjs/common'
13
+ import { ProtocolBlob } from '@nmtjs/protocol'
14
+
15
+ import type { ClientCoreOptions, ConnectionState } from './core.ts'
16
+ import type { PingLayerApi } from './layers/ping.ts'
17
+ import type { RpcLayerApi } from './layers/rpc.ts'
18
+ import type { StreamLayerApi } from './layers/streams.ts'
19
+ import type { ClientPlugin } from './plugins/types.ts'
20
+ import type { BaseClientTransformer } from './transformers.ts'
21
+ import type { ClientTransportFactory } from './transport.ts'
22
+ import type {
23
+ AnyResolvedContractRouter,
24
+ ClientCallers,
25
+ ResolveAPIRouterRoutes,
26
+ } from './types.ts'
27
+ import { ClientCore } from './core.ts'
28
+ import { createPingLayer } from './layers/ping.ts'
29
+ import { createRpcLayer } from './layers/rpc.ts'
30
+ import { createStreamLayer } from './layers/streams.ts'
31
+
32
+ export interface ClientOptions<
33
+ RouterContract extends TAnyRouterContract = TAnyRouterContract,
34
+ SafeCall extends boolean = false,
35
+ > {
36
+ contract: RouterContract
37
+ protocol: ProtocolVersion
38
+ format: BaseClientFormat
39
+ application?: string
40
+ autoConnect?: boolean
41
+ timeout?: number
42
+ plugins?: ClientPlugin[]
43
+ safe?: SafeCall
44
+ }
45
+
46
+ export type BaseClientOptions<
47
+ RouterContract extends TAnyRouterContract = TAnyRouterContract,
48
+ SafeCall extends boolean = false,
49
+ > = ClientOptions<RouterContract, SafeCall>
50
+
51
+ export interface ClientCallersFactory<
52
+ Routes extends AnyResolvedContractRouter,
53
+ SafeCall extends boolean,
54
+ > {
55
+ call: ClientCallers<Routes, SafeCall, false>
56
+ stream: ClientCallers<Routes, SafeCall, true>
57
+ }
58
+
59
+ type ClientRoutes<
60
+ RouterContract extends TAnyRouterContract,
61
+ InputTypeProvider extends TypeProvider,
62
+ OutputTypeProvider extends TypeProvider,
63
+ > = ResolveAPIRouterRoutes<
64
+ RouterContract,
65
+ InputTypeProvider,
66
+ OutputTypeProvider
67
+ >
68
+
69
+ export class Client<
70
+ TransportFactory extends ClientTransportFactory<
71
+ any,
72
+ any
73
+ > = ClientTransportFactory<any, any>,
74
+ RouterContract extends TAnyRouterContract = TAnyRouterContract,
75
+ SafeCall extends boolean = false,
76
+ InputTypeProvider extends TypeProvider = TypeProvider,
77
+ OutputTypeProvider extends TypeProvider = TypeProvider,
78
+ > {
79
+ _!: {
80
+ routes: ResolveAPIRouterRoutes<
81
+ RouterContract,
82
+ InputTypeProvider,
83
+ OutputTypeProvider
84
+ >
85
+ safe: SafeCall
86
+ }
87
+
88
+ readonly core: ClientCore
89
+ protected readonly rpcLayer: RpcLayerApi
90
+ protected readonly streamLayer: StreamLayerApi
91
+ protected readonly pingLayer: PingLayerApi
92
+ protected readonly transformer: BaseClientTransformer
93
+
94
+ readonly call: ClientCallers<
95
+ ClientRoutes<RouterContract, InputTypeProvider, OutputTypeProvider>,
96
+ SafeCall,
97
+ false
98
+ >
99
+ readonly stream: ClientCallers<
100
+ ClientRoutes<RouterContract, InputTypeProvider, OutputTypeProvider>,
101
+ SafeCall,
102
+ true
103
+ >
104
+
105
+ readonly on: ClientCore['on']
106
+ readonly once: ClientCore['once']
107
+ readonly off: ClientCore['off']
108
+
109
+ constructor(
110
+ readonly options: ClientOptions<RouterContract, SafeCall>,
111
+ readonly transportFactory: TransportFactory,
112
+ readonly transportOptions: TransportFactory extends ClientTransportFactory<
113
+ any,
114
+ infer Options
115
+ >
116
+ ? Options
117
+ : never,
118
+ transformer: BaseClientTransformer,
119
+ buildCallers: (rpc: RpcLayerApi) => { call: unknown; stream: unknown },
120
+ ) {
121
+ this.transformer = transformer
122
+
123
+ const transport = this.transportFactory(
124
+ { protocol: this.options.protocol, format: this.options.format },
125
+ this.transportOptions,
126
+ )
127
+
128
+ const coreOptions: ClientCoreOptions = {
129
+ protocol: this.options.protocol,
130
+ format: this.options.format,
131
+ application: this.options.application,
132
+ autoConnect: this.options.autoConnect,
133
+ }
134
+
135
+ this.core = new ClientCore(coreOptions, transport)
136
+ this.streamLayer = createStreamLayer(this.core)
137
+ this.rpcLayer = createRpcLayer(this.core, this.streamLayer, transformer, {
138
+ timeout: this.options.timeout,
139
+ safe: this.options.safe,
140
+ })
141
+ this.pingLayer = createPingLayer(this.core)
142
+
143
+ this.core.setMessageContextFactory(() => ({
144
+ encoder: this.core.format,
145
+ decoder: this.core.format,
146
+ transport: {
147
+ send: (buffer) => {
148
+ this.core.send(buffer).catch(noopFn)
149
+ },
150
+ },
151
+ streamId: () => this.streamLayer.getStreamId(),
152
+ addClientStream: (blob) => this.streamLayer.addClientStream(blob),
153
+ addServerStream: (streamId, metadata) =>
154
+ this.streamLayer.createServerBlob(streamId, metadata),
155
+ }))
156
+
157
+ this.core.initPlugins(this.options.plugins, {
158
+ core: this.core,
159
+ ping: this.pingLayer,
160
+ })
161
+
162
+ const callers = buildCallers(this.rpcLayer)
163
+ this.call = callers.call as ClientCallers<
164
+ ClientRoutes<RouterContract, InputTypeProvider, OutputTypeProvider>,
165
+ SafeCall,
166
+ false
167
+ >
168
+ this.stream = callers.stream as ClientCallers<
169
+ ClientRoutes<RouterContract, InputTypeProvider, OutputTypeProvider>,
170
+ SafeCall,
171
+ true
172
+ >
173
+
174
+ this.on = this.core.on.bind(this.core)
175
+ this.once = this.core.once.bind(this.core)
176
+ this.off = this.core.off.bind(this.core)
177
+ }
178
+
179
+ get state(): ConnectionState {
180
+ return this.core.state
181
+ }
182
+
183
+ get transportType() {
184
+ return this.core.transportType
185
+ }
186
+
187
+ get lastDisconnectReason() {
188
+ return this.core.lastDisconnectReason
189
+ }
190
+
191
+ get auth() {
192
+ return this.core.auth
193
+ }
194
+
195
+ set auth(value: any) {
196
+ this.core.auth = value
197
+ }
198
+
199
+ isDisposed() {
200
+ return this.core.isDisposed()
201
+ }
202
+
203
+ connect() {
204
+ return this.core.connect()
205
+ }
206
+
207
+ disconnect() {
208
+ return this.core.disconnect()
209
+ }
210
+
211
+ ping(timeout: number, signal?: AbortSignal) {
212
+ return this.pingLayer.ping(timeout, signal)
213
+ }
214
+
215
+ createBlob(
216
+ source: Blob | ReadableStream | string | AsyncIterable<Uint8Array>,
217
+ metadata?: ProtocolBlobMetadata,
218
+ ) {
219
+ return ProtocolBlob.from(source, metadata)
220
+ }
221
+
222
+ consumeBlob(
223
+ blob: ProtocolBlobInterface,
224
+ options?: { signal?: AbortSignal },
225
+ ): ProtocolServerBlobStream {
226
+ return this.streamLayer.consumeServerBlob(blob, options)
227
+ }
228
+
229
+ dispose() {
230
+ this.core.dispose()
231
+ }
232
+ }
@@ -5,44 +5,111 @@ import type {
5
5
  } from '@nmtjs/contract'
6
6
  import { IsProcedureContract, IsRouterContract } from '@nmtjs/contract'
7
7
 
8
- import type { BaseClientOptions } from '../core.ts'
8
+ import type { BaseClientOptions } from '../client.ts'
9
+ import type { RpcLayerApi } from '../layers/rpc.ts'
9
10
  import type { ClientTransportFactory } from '../transport.ts'
10
11
  import type {
11
- ClientCallers,
12
12
  ClientCallOptions,
13
13
  RuntimeInputContractTypeProvider,
14
14
  RuntimeOutputContractTypeProvider,
15
15
  } from '../types.ts'
16
- import { BaseClient } from '../core.ts'
16
+ import { Client } from '../client.ts'
17
17
 
18
18
  export class RuntimeContractTransformer {
19
19
  #procedures = new Map<string, TAnyProcedureContract>()
20
20
 
21
21
  constructor(router: TAnyRouterContract) {
22
- const registerProcedures = (r: TRouteContract, path: string[] = []) => {
23
- if (IsRouterContract(r)) {
24
- for (const [key, route] of Object.entries(r.routes)) {
25
- registerProcedures(route, [...path, key])
22
+ const registerProcedures = (route: TRouteContract, path: string[] = []) => {
23
+ if (IsRouterContract(route)) {
24
+ for (const [key, child] of Object.entries(route.routes)) {
25
+ registerProcedures(child, [...path, key])
26
26
  }
27
- } else if (IsProcedureContract(r)) {
28
- const fullName = [...path].join('/')
29
- this.#procedures.set(fullName, r)
27
+ return
28
+ }
29
+
30
+ if (IsProcedureContract(route)) {
31
+ this.#procedures.set(path.join('/'), route)
30
32
  }
31
33
  }
34
+
32
35
  registerProcedures(router)
33
36
  }
34
37
 
35
- encode(_procedure: string, payload: any) {
36
- const procedure = this.#procedures.get(_procedure)
37
- if (!procedure) throw new Error(`Procedure not found: ${_procedure}`)
38
- return procedure.input.encode(payload)
38
+ encode(procedure: string, payload: any) {
39
+ const contract = this.#procedures.get(procedure)
40
+ if (!contract) throw new Error(`Procedure not found: ${procedure}`)
41
+ return contract.input.encode(payload)
42
+ }
43
+
44
+ decode(procedure: string, payload: any) {
45
+ const contract = this.#procedures.get(procedure)
46
+ if (!contract) throw new Error(`Procedure not found: ${procedure}`)
47
+ return contract.output.decode(payload)
39
48
  }
49
+ }
50
+
51
+ const assignNested = (
52
+ root: Record<string, any>,
53
+ name: string,
54
+ value: unknown,
55
+ ) => {
56
+ const parts = name.split('/')
57
+ let current = root
58
+
59
+ for (let i = 0; i < parts.length; i++) {
60
+ const part = parts[i]
61
+ if (i === parts.length - 1) {
62
+ current[part] = value
63
+ } else {
64
+ current[part] = current[part] ?? Object.create(null)
65
+ current = current[part]
66
+ }
67
+ }
68
+ }
69
+
70
+ const buildRuntimeCallers = (
71
+ rpc: RpcLayerApi,
72
+ contract: TAnyRouterContract,
73
+ ) => {
74
+ const procedures = new Map<string, TAnyProcedureContract>()
75
+
76
+ const resolveProcedures = (
77
+ router: TAnyRouterContract,
78
+ path: string[] = [],
79
+ ) => {
80
+ for (const [key, route] of Object.entries(router.routes)) {
81
+ if (IsRouterContract(route)) {
82
+ resolveProcedures(route, [...path, key])
83
+ } else if (IsProcedureContract(route)) {
84
+ procedures.set([...path, key].join('/'), route)
85
+ }
86
+ }
87
+ }
88
+
89
+ resolveProcedures(contract)
90
+
91
+ const callers: Record<string, any> = Object.create(null)
92
+ const streams: Record<string, any> = Object.create(null)
93
+
94
+ for (const [name, procedure] of procedures) {
95
+ const invoke = (
96
+ payload?: unknown,
97
+ options?: Partial<ClientCallOptions>,
98
+ ) => {
99
+ return rpc.call(name, payload, {
100
+ ...options,
101
+ _stream_response: !!procedure.stream,
102
+ })
103
+ }
40
104
 
41
- decode(_procedure: string, payload: any) {
42
- const procedure = this.#procedures.get(_procedure)
43
- if (!procedure) throw new Error(`Procedure not found: ${_procedure}`)
44
- return procedure.output.decode(payload)
105
+ if (procedure.stream) {
106
+ assignNested(streams, name, invoke)
107
+ } else {
108
+ assignNested(callers, name, invoke)
109
+ }
45
110
  }
111
+
112
+ return { call: callers, stream: streams }
46
113
  }
47
114
 
48
115
  export class RuntimeClient<
@@ -52,18 +119,13 @@ export class RuntimeClient<
52
119
  >,
53
120
  RouterContract extends TAnyRouterContract = TAnyRouterContract,
54
121
  SafeCall extends boolean = false,
55
- > extends BaseClient<
122
+ > extends Client<
56
123
  Transport,
57
124
  RouterContract,
58
125
  SafeCall,
59
126
  RuntimeInputContractTypeProvider,
60
127
  RuntimeOutputContractTypeProvider
61
128
  > {
62
- protected readonly transformer: RuntimeContractTransformer
63
-
64
- readonly #procedures = new Map<string, TAnyProcedureContract>()
65
- readonly #callers!: ClientCallers<this['_']['routes'], SafeCall, boolean>
66
-
67
129
  constructor(
68
130
  options: BaseClientOptions<RouterContract, SafeCall>,
69
131
  transport: Transport,
@@ -74,60 +136,12 @@ export class RuntimeClient<
74
136
  ? Options
75
137
  : never,
76
138
  ) {
77
- super(options, transport, transportOptions)
78
-
79
- this.resolveProcedures(this.options.contract)
80
- this.transformer = new RuntimeContractTransformer(this.options.contract)
81
- this.#callers = this.buildCallers()
82
- }
83
-
84
- override get call(): ClientCallers<this['_']['routes'], SafeCall, false> {
85
- return this.#callers as any
86
- }
87
-
88
- override get stream(): ClientCallers<this['_']['routes'], SafeCall, true> {
89
- return this.#callers as any
90
- }
91
-
92
- protected resolveProcedures(router: TAnyRouterContract, path: string[] = []) {
93
- for (const [key, route] of Object.entries(router.routes)) {
94
- if (IsRouterContract(route)) {
95
- this.resolveProcedures(route, [...path, key])
96
- } else if (IsProcedureContract(route)) {
97
- const fullName = [...path, key].join('/')
98
- this.#procedures.set(fullName, route)
99
- }
100
- }
101
- }
102
-
103
- protected buildCallers(): ClientCallers<
104
- this['_']['routes'],
105
- SafeCall,
106
- boolean
107
- > {
108
- const callers: Record<string, any> = Object.create(null)
109
-
110
- for (const [name, { stream }] of this.#procedures) {
111
- const parts = name.split('/')
112
- let current = callers
113
- for (let i = 0; i < parts.length; i++) {
114
- const part = parts[i]
115
- if (i === parts.length - 1) {
116
- current[part] = (
117
- payload?: unknown,
118
- options?: Partial<ClientCallOptions>,
119
- ) =>
120
- this._call(name, payload, {
121
- ...options,
122
- _stream_response: !!stream,
123
- })
124
- } else {
125
- current[part] = current[part] ?? Object.create(null)
126
- current = current[part]
127
- }
128
- }
129
- }
130
-
131
- return callers as ClientCallers<this['_']['routes'], SafeCall, boolean>
139
+ super(
140
+ options,
141
+ transport,
142
+ transportOptions,
143
+ new RuntimeContractTransformer(options.contract),
144
+ (rpc) => buildRuntimeCallers(rpc, options.contract),
145
+ )
132
146
  }
133
147
  }