@nmtjs/client 0.15.2 → 0.16.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +64 -0
- package/dist/client.js +97 -0
- package/dist/client.js.map +1 -0
- package/dist/clients/runtime.d.ts +6 -12
- package/dist/clients/runtime.js +58 -57
- package/dist/clients/runtime.js.map +1 -1
- package/dist/clients/static.d.ts +4 -9
- package/dist/clients/static.js +20 -20
- package/dist/clients/static.js.map +1 -1
- package/dist/core.d.ts +33 -83
- package/dist/core.js +305 -690
- package/dist/core.js.map +1 -1
- package/dist/events.d.ts +0 -1
- package/dist/events.js +74 -11
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/layers/ping.d.ts +6 -0
- package/dist/layers/ping.js +65 -0
- package/dist/layers/ping.js.map +1 -0
- package/dist/layers/rpc.d.ts +19 -0
- package/dist/layers/rpc.js +521 -0
- package/dist/layers/rpc.js.map +1 -0
- package/dist/layers/streams.d.ts +20 -0
- package/dist/layers/streams.js +194 -0
- package/dist/layers/streams.js.map +1 -0
- package/dist/plugins/browser.js +28 -9
- package/dist/plugins/browser.js.map +1 -1
- package/dist/plugins/heartbeat.js +10 -10
- package/dist/plugins/heartbeat.js.map +1 -1
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.js +0 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/reconnect.js +11 -94
- package/dist/plugins/reconnect.js.map +1 -1
- package/dist/plugins/types.d.ts +27 -11
- package/dist/transport.d.ts +49 -31
- package/dist/types.d.ts +21 -5
- package/package.json +10 -10
- package/src/client.ts +216 -0
- package/src/clients/runtime.ts +93 -79
- package/src/clients/static.ts +46 -38
- package/src/core.ts +394 -901
- package/src/events.ts +113 -14
- package/src/index.ts +4 -0
- package/src/layers/ping.ts +99 -0
- package/src/layers/rpc.ts +725 -0
- package/src/layers/streams.ts +277 -0
- package/src/plugins/browser.ts +39 -9
- package/src/plugins/heartbeat.ts +10 -10
- package/src/plugins/index.ts +8 -1
- package/src/plugins/reconnect.ts +12 -119
- package/src/plugins/types.ts +30 -13
- package/src/transport.ts +75 -46
- package/src/types.ts +33 -8
package/dist/types.d.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import type { CallTypeProvider, OneOf, TypeProvider } from '@nmtjs/common';
|
|
2
2
|
import type { TAnyProcedureContract, TAnyRouterContract } from '@nmtjs/contract';
|
|
3
3
|
import type { ProtocolBlobInterface } from '@nmtjs/protocol';
|
|
4
|
-
import type { ProtocolError,
|
|
4
|
+
import type { ProtocolError, ProtocolServerBlobConsumer } from '@nmtjs/protocol/client';
|
|
5
5
|
import type { BaseTypeAny, PlainType, t } from '@nmtjs/type';
|
|
6
6
|
export declare const ResolvedType: unique symbol;
|
|
7
7
|
export type ResolvedType = typeof ResolvedType;
|
|
8
|
-
export type
|
|
8
|
+
export type RpcCallOptions = {
|
|
9
9
|
timeout?: number;
|
|
10
10
|
signal?: AbortSignal;
|
|
11
|
+
};
|
|
12
|
+
export type StreamCallOptions = RpcCallOptions & {
|
|
13
|
+
autoReconnect?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type ClientCallOptions = StreamCallOptions & {
|
|
11
16
|
/**
|
|
12
17
|
* @internal
|
|
13
18
|
*/
|
|
14
19
|
_stream_response?: boolean;
|
|
15
20
|
};
|
|
16
|
-
export type
|
|
21
|
+
export type BlobSubscriptionOptions = {
|
|
17
22
|
signal?: AbortSignal;
|
|
18
|
-
}
|
|
23
|
+
};
|
|
24
|
+
export type StreamSubscriptionOptions = Partial<StreamCallOptions>;
|
|
25
|
+
export type ClientOutputType<T> = T extends ProtocolBlobInterface ? ProtocolServerBlobConsumer : T extends {
|
|
19
26
|
[PlainType]?: true;
|
|
20
27
|
} ? {
|
|
21
28
|
[K in keyof Omit<T, PlainType>]: ClientOutputType<T[K]>;
|
|
@@ -55,7 +62,16 @@ export type ResolveAPIRouterRoutes<T extends TAnyRouterContract, InputTypeProvid
|
|
|
55
62
|
} : T['routes'][K] extends TAnyRouterContract ? ResolveAPIRouterRoutes<T['routes'][K], InputTypeProvider, OutputTypeProvider> : never;
|
|
56
63
|
};
|
|
57
64
|
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 ? [
|
|
65
|
+
export type ClientCaller<Procedure extends AnyResolvedContractProcedure, SafeCall extends boolean> = (...args: Procedure['input'] extends t.NeverType ? [
|
|
66
|
+
data?: undefined,
|
|
67
|
+
options?: Partial<Procedure['stream'] extends true ? StreamCallOptions : RpcCallOptions>
|
|
68
|
+
] : undefined extends t.infer.encode.input<Procedure['contract']['input']> ? [
|
|
69
|
+
data?: Procedure['input'],
|
|
70
|
+
options?: Partial<Procedure['stream'] extends true ? StreamCallOptions : RpcCallOptions>
|
|
71
|
+
] : [
|
|
72
|
+
data: Procedure['input'],
|
|
73
|
+
options?: Partial<Procedure['stream'] extends true ? StreamCallOptions : RpcCallOptions>
|
|
74
|
+
]) => SafeCall extends true ? Promise<OneOf<[{
|
|
59
75
|
result: Procedure['output'];
|
|
60
76
|
}, {
|
|
61
77
|
error: ProtocolError;
|
package/package.json
CHANGED
|
@@ -20,17 +20,17 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@nmtjs/
|
|
24
|
-
"@nmtjs/
|
|
25
|
-
"@nmtjs/
|
|
26
|
-
"@nmtjs/
|
|
23
|
+
"@nmtjs/type": "0.16.0-beta.1",
|
|
24
|
+
"@nmtjs/contract": "0.16.0-beta.1",
|
|
25
|
+
"@nmtjs/common": "0.16.0-beta.1",
|
|
26
|
+
"@nmtjs/protocol": "0.16.0-beta.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@nmtjs/tests-integration": "0.
|
|
30
|
-
"@nmtjs/
|
|
31
|
-
"@nmtjs/
|
|
32
|
-
"@nmtjs/
|
|
33
|
-
"@nmtjs/
|
|
29
|
+
"@nmtjs/tests-integration": "0.16.0-beta.1",
|
|
30
|
+
"@nmtjs/common": "0.16.0-beta.1",
|
|
31
|
+
"@nmtjs/type": "0.16.0-beta.1",
|
|
32
|
+
"@nmtjs/protocol": "0.16.0-beta.1",
|
|
33
|
+
"@nmtjs/contract": "0.16.0-beta.1"
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"dist",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"LICENSE.md",
|
|
39
39
|
"README.md"
|
|
40
40
|
],
|
|
41
|
-
"version": "0.
|
|
41
|
+
"version": "0.16.0-beta.1",
|
|
42
42
|
"scripts": {
|
|
43
43
|
"clean-build": "rm -rf ./dist"
|
|
44
44
|
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { TypeProvider } from '@nmtjs/common'
|
|
2
|
+
import type { TAnyRouterContract } from '@nmtjs/contract'
|
|
3
|
+
import type { ProtocolBlobMetadata, ProtocolVersion } from '@nmtjs/protocol'
|
|
4
|
+
import type { BaseClientFormat } from '@nmtjs/protocol/client'
|
|
5
|
+
import { noopFn } from '@nmtjs/common'
|
|
6
|
+
import { ProtocolBlob } from '@nmtjs/protocol'
|
|
7
|
+
|
|
8
|
+
import type { ClientCoreOptions, ConnectionState } from './core.ts'
|
|
9
|
+
import type { PingLayerApi } from './layers/ping.ts'
|
|
10
|
+
import type { RpcLayerApi } from './layers/rpc.ts'
|
|
11
|
+
import type { StreamLayerApi } from './layers/streams.ts'
|
|
12
|
+
import type { ClientPlugin } from './plugins/types.ts'
|
|
13
|
+
import type { BaseClientTransformer } from './transformers.ts'
|
|
14
|
+
import type { ClientTransportFactory } from './transport.ts'
|
|
15
|
+
import type {
|
|
16
|
+
AnyResolvedContractRouter,
|
|
17
|
+
ClientCallers,
|
|
18
|
+
ResolveAPIRouterRoutes,
|
|
19
|
+
} from './types.ts'
|
|
20
|
+
import { ClientCore } from './core.ts'
|
|
21
|
+
import { createPingLayer } from './layers/ping.ts'
|
|
22
|
+
import { createRpcLayer } from './layers/rpc.ts'
|
|
23
|
+
import { createStreamLayer } from './layers/streams.ts'
|
|
24
|
+
|
|
25
|
+
export interface ClientOptions<
|
|
26
|
+
RouterContract extends TAnyRouterContract = TAnyRouterContract,
|
|
27
|
+
SafeCall extends boolean = false,
|
|
28
|
+
> {
|
|
29
|
+
contract: RouterContract
|
|
30
|
+
protocol: ProtocolVersion
|
|
31
|
+
format: BaseClientFormat
|
|
32
|
+
application?: string
|
|
33
|
+
timeout?: number
|
|
34
|
+
plugins?: ClientPlugin[]
|
|
35
|
+
safe?: SafeCall
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type BaseClientOptions<
|
|
39
|
+
RouterContract extends TAnyRouterContract = TAnyRouterContract,
|
|
40
|
+
SafeCall extends boolean = false,
|
|
41
|
+
> = ClientOptions<RouterContract, SafeCall>
|
|
42
|
+
|
|
43
|
+
export interface ClientCallersFactory<
|
|
44
|
+
Routes extends AnyResolvedContractRouter,
|
|
45
|
+
SafeCall extends boolean,
|
|
46
|
+
> {
|
|
47
|
+
call: ClientCallers<Routes, SafeCall, false>
|
|
48
|
+
stream: ClientCallers<Routes, SafeCall, true>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type ClientRoutes<
|
|
52
|
+
RouterContract extends TAnyRouterContract,
|
|
53
|
+
InputTypeProvider extends TypeProvider,
|
|
54
|
+
OutputTypeProvider extends TypeProvider,
|
|
55
|
+
> = ResolveAPIRouterRoutes<
|
|
56
|
+
RouterContract,
|
|
57
|
+
InputTypeProvider,
|
|
58
|
+
OutputTypeProvider
|
|
59
|
+
>
|
|
60
|
+
|
|
61
|
+
export class Client<
|
|
62
|
+
TransportFactory extends ClientTransportFactory<
|
|
63
|
+
any,
|
|
64
|
+
any
|
|
65
|
+
> = ClientTransportFactory<any, any>,
|
|
66
|
+
RouterContract extends TAnyRouterContract = TAnyRouterContract,
|
|
67
|
+
SafeCall extends boolean = false,
|
|
68
|
+
InputTypeProvider extends TypeProvider = TypeProvider,
|
|
69
|
+
OutputTypeProvider extends TypeProvider = TypeProvider,
|
|
70
|
+
> {
|
|
71
|
+
_!: {
|
|
72
|
+
routes: ResolveAPIRouterRoutes<
|
|
73
|
+
RouterContract,
|
|
74
|
+
InputTypeProvider,
|
|
75
|
+
OutputTypeProvider
|
|
76
|
+
>
|
|
77
|
+
safe: SafeCall
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
readonly core: ClientCore
|
|
81
|
+
protected readonly rpcLayer: RpcLayerApi
|
|
82
|
+
protected readonly streamLayer: StreamLayerApi
|
|
83
|
+
protected readonly pingLayer: PingLayerApi
|
|
84
|
+
protected readonly transformer: BaseClientTransformer
|
|
85
|
+
|
|
86
|
+
readonly call: ClientCallers<
|
|
87
|
+
ClientRoutes<RouterContract, InputTypeProvider, OutputTypeProvider>,
|
|
88
|
+
SafeCall,
|
|
89
|
+
false
|
|
90
|
+
>
|
|
91
|
+
readonly stream: ClientCallers<
|
|
92
|
+
ClientRoutes<RouterContract, InputTypeProvider, OutputTypeProvider>,
|
|
93
|
+
SafeCall,
|
|
94
|
+
true
|
|
95
|
+
>
|
|
96
|
+
|
|
97
|
+
readonly on: ClientCore['on']
|
|
98
|
+
readonly once: ClientCore['once']
|
|
99
|
+
readonly off: ClientCore['off']
|
|
100
|
+
|
|
101
|
+
constructor(
|
|
102
|
+
readonly options: ClientOptions<RouterContract, SafeCall>,
|
|
103
|
+
readonly transportFactory: TransportFactory,
|
|
104
|
+
readonly transportOptions: TransportFactory extends ClientTransportFactory<
|
|
105
|
+
any,
|
|
106
|
+
infer Options
|
|
107
|
+
>
|
|
108
|
+
? Options
|
|
109
|
+
: never,
|
|
110
|
+
transformer: BaseClientTransformer,
|
|
111
|
+
buildCallers: (rpc: RpcLayerApi) => { call: unknown; stream: unknown },
|
|
112
|
+
) {
|
|
113
|
+
this.transformer = transformer
|
|
114
|
+
|
|
115
|
+
const transport = this.transportFactory(
|
|
116
|
+
{ protocol: this.options.protocol, format: this.options.format },
|
|
117
|
+
this.transportOptions,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
const coreOptions: ClientCoreOptions = {
|
|
121
|
+
protocol: this.options.protocol,
|
|
122
|
+
format: this.options.format,
|
|
123
|
+
application: this.options.application,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.core = new ClientCore(coreOptions, transport)
|
|
127
|
+
this.streamLayer = createStreamLayer(this.core)
|
|
128
|
+
this.rpcLayer = createRpcLayer(this.core, this.streamLayer, transformer, {
|
|
129
|
+
timeout: this.options.timeout,
|
|
130
|
+
safe: this.options.safe,
|
|
131
|
+
})
|
|
132
|
+
this.pingLayer = createPingLayer(this.core)
|
|
133
|
+
|
|
134
|
+
this.core.setMessageContextFactory(() => ({
|
|
135
|
+
encoder: this.core.format,
|
|
136
|
+
decoder: this.core.format,
|
|
137
|
+
transport: {
|
|
138
|
+
send: (buffer) => {
|
|
139
|
+
this.core.send(buffer).catch(noopFn)
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
streamId: () => this.streamLayer.getStreamId(),
|
|
143
|
+
addClientStream: (blob) => this.streamLayer.addClientStream(blob),
|
|
144
|
+
addServerStream: (streamId, metadata) =>
|
|
145
|
+
this.streamLayer.createServerBlobStream(streamId, metadata),
|
|
146
|
+
}))
|
|
147
|
+
|
|
148
|
+
this.core.initPlugins(this.options.plugins, {
|
|
149
|
+
core: this.core,
|
|
150
|
+
ping: this.pingLayer,
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const callers = buildCallers(this.rpcLayer)
|
|
154
|
+
this.call = callers.call as ClientCallers<
|
|
155
|
+
ClientRoutes<RouterContract, InputTypeProvider, OutputTypeProvider>,
|
|
156
|
+
SafeCall,
|
|
157
|
+
false
|
|
158
|
+
>
|
|
159
|
+
this.stream = callers.stream as ClientCallers<
|
|
160
|
+
ClientRoutes<RouterContract, InputTypeProvider, OutputTypeProvider>,
|
|
161
|
+
SafeCall,
|
|
162
|
+
true
|
|
163
|
+
>
|
|
164
|
+
|
|
165
|
+
this.on = this.core.on.bind(this.core)
|
|
166
|
+
this.once = this.core.once.bind(this.core)
|
|
167
|
+
this.off = this.core.off.bind(this.core)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get state(): ConnectionState {
|
|
171
|
+
return this.core.state
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get transportType() {
|
|
175
|
+
return this.core.transportType
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
get lastDisconnectReason() {
|
|
179
|
+
return this.core.lastDisconnectReason
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
get auth() {
|
|
183
|
+
return this.core.auth
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
set auth(value: any) {
|
|
187
|
+
this.core.auth = value
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
isDisposed() {
|
|
191
|
+
return this.core.isDisposed()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
connect() {
|
|
195
|
+
return this.core.connect()
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
disconnect() {
|
|
199
|
+
return this.core.disconnect()
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
ping(timeout: number, signal?: AbortSignal) {
|
|
203
|
+
return this.pingLayer.ping(timeout, signal)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
blob(
|
|
207
|
+
source: Blob | ReadableStream | string | AsyncIterable<Uint8Array>,
|
|
208
|
+
metadata?: ProtocolBlobMetadata,
|
|
209
|
+
) {
|
|
210
|
+
return ProtocolBlob.from(source, metadata)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
dispose() {
|
|
214
|
+
this.core.dispose()
|
|
215
|
+
}
|
|
216
|
+
}
|
package/src/clients/runtime.ts
CHANGED
|
@@ -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 '../
|
|
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 {
|
|
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 = (
|
|
23
|
-
if (IsRouterContract(
|
|
24
|
-
for (const [key,
|
|
25
|
-
registerProcedures(
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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(
|
|
36
|
-
const
|
|
37
|
-
if (!
|
|
38
|
-
return
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
}
|
package/src/clients/static.ts
CHANGED
|
@@ -1,16 +1,48 @@
|
|
|
1
1
|
import type { TAnyRouterContract } from '@nmtjs/contract'
|
|
2
2
|
|
|
3
|
-
import type { BaseClientOptions } from '../
|
|
3
|
+
import type { BaseClientOptions } from '../client.ts'
|
|
4
|
+
import type { RpcLayerApi } from '../layers/rpc.ts'
|
|
4
5
|
import type { ClientTransportFactory } from '../transport.ts'
|
|
5
6
|
import type {
|
|
6
|
-
ClientCallers,
|
|
7
7
|
ClientCallOptions,
|
|
8
8
|
StaticInputContractTypeProvider,
|
|
9
9
|
StaticOutputContractTypeProvider,
|
|
10
10
|
} from '../types.ts'
|
|
11
|
-
import {
|
|
11
|
+
import { Client } from '../client.ts'
|
|
12
12
|
import { BaseClientTransformer } from '../transformers.ts'
|
|
13
13
|
|
|
14
|
+
const buildStaticCallers = (
|
|
15
|
+
rpc: RpcLayerApi,
|
|
16
|
+
isStream: boolean,
|
|
17
|
+
path: string[] = [],
|
|
18
|
+
): Record<string, unknown> => {
|
|
19
|
+
const createProxy = <T>(
|
|
20
|
+
target: Record<string, unknown>,
|
|
21
|
+
current: string[],
|
|
22
|
+
) => {
|
|
23
|
+
return new Proxy(target, {
|
|
24
|
+
get: (obj, prop) => {
|
|
25
|
+
if (prop === 'then') return obj
|
|
26
|
+
|
|
27
|
+
const nextPath = [...current, String(prop)]
|
|
28
|
+
const caller = (
|
|
29
|
+
payload?: unknown,
|
|
30
|
+
options?: Partial<ClientCallOptions>,
|
|
31
|
+
) => {
|
|
32
|
+
return rpc.call(nextPath.join('/'), payload, {
|
|
33
|
+
...options,
|
|
34
|
+
_stream_response: isStream || options?._stream_response,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return createProxy(caller as any, nextPath)
|
|
39
|
+
},
|
|
40
|
+
}) as T
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return createProxy(Object.create(null), path)
|
|
44
|
+
}
|
|
45
|
+
|
|
14
46
|
export class StaticClient<
|
|
15
47
|
Transport extends ClientTransportFactory<any, any> = ClientTransportFactory<
|
|
16
48
|
any,
|
|
@@ -18,15 +50,13 @@ export class StaticClient<
|
|
|
18
50
|
>,
|
|
19
51
|
RouterContract extends TAnyRouterContract = TAnyRouterContract,
|
|
20
52
|
SafeCall extends boolean = false,
|
|
21
|
-
> extends
|
|
53
|
+
> extends Client<
|
|
22
54
|
Transport,
|
|
23
55
|
RouterContract,
|
|
24
56
|
SafeCall,
|
|
25
57
|
StaticInputContractTypeProvider,
|
|
26
58
|
StaticOutputContractTypeProvider
|
|
27
59
|
> {
|
|
28
|
-
protected readonly transformer: BaseClientTransformer
|
|
29
|
-
|
|
30
60
|
constructor(
|
|
31
61
|
options: BaseClientOptions<RouterContract, SafeCall>,
|
|
32
62
|
transport: Transport,
|
|
@@ -37,37 +67,15 @@ export class StaticClient<
|
|
|
37
67
|
? Options
|
|
38
68
|
: never,
|
|
39
69
|
) {
|
|
40
|
-
super(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
protected createProxy<T>(
|
|
53
|
-
target: Record<string, unknown>,
|
|
54
|
-
isStream: boolean,
|
|
55
|
-
path: string[] = [],
|
|
56
|
-
) {
|
|
57
|
-
return new Proxy(target, {
|
|
58
|
-
get: (obj, prop) => {
|
|
59
|
-
if (prop === 'then') return obj
|
|
60
|
-
const newPath = [...path, String(prop)]
|
|
61
|
-
const caller = (
|
|
62
|
-
payload?: unknown,
|
|
63
|
-
options?: Partial<ClientCallOptions>,
|
|
64
|
-
) =>
|
|
65
|
-
this._call(newPath.join('/'), payload, {
|
|
66
|
-
...options,
|
|
67
|
-
_stream_response: isStream || options?._stream_response,
|
|
68
|
-
})
|
|
69
|
-
return this.createProxy(caller as any, isStream, newPath)
|
|
70
|
-
},
|
|
71
|
-
}) as T
|
|
70
|
+
super(
|
|
71
|
+
options,
|
|
72
|
+
transport,
|
|
73
|
+
transportOptions,
|
|
74
|
+
new BaseClientTransformer(),
|
|
75
|
+
(rpc) => ({
|
|
76
|
+
call: buildStaticCallers(rpc, false),
|
|
77
|
+
stream: buildStaticCallers(rpc, true),
|
|
78
|
+
}),
|
|
79
|
+
)
|
|
72
80
|
}
|
|
73
81
|
}
|