@nmtjs/client 0.15.0-beta.9 → 0.15.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/clients/runtime.d.ts +2 -2
- package/dist/clients/runtime.js +1 -1
- package/dist/clients/runtime.js.map +1 -1
- package/dist/clients/static.d.ts +2 -2
- package/dist/clients/static.js +6 -3
- package/dist/clients/static.js.map +1 -1
- package/dist/core.d.ts +38 -8
- package/dist/core.js +414 -66
- package/dist/core.js.map +1 -1
- package/dist/events.d.ts +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/plugins/browser.d.ts +2 -0
- package/dist/plugins/browser.js +41 -0
- package/dist/plugins/browser.js.map +1 -0
- package/dist/plugins/heartbeat.d.ts +6 -0
- package/dist/plugins/heartbeat.js +86 -0
- package/dist/plugins/heartbeat.js.map +1 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.js +6 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/logging.d.ts +9 -0
- package/dist/plugins/logging.js +30 -0
- package/dist/plugins/logging.js.map +1 -0
- package/dist/plugins/reconnect.d.ts +6 -0
- package/dist/plugins/reconnect.js +98 -0
- package/dist/plugins/reconnect.js.map +1 -0
- package/dist/plugins/types.d.ts +63 -0
- package/dist/plugins/types.js +2 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/streams.d.ts +3 -3
- package/dist/streams.js.map +1 -1
- package/dist/transformers.js.map +1 -1
- package/dist/types.d.ts +1 -4
- package/dist/types.js.map +1 -1
- package/package.json +27 -17
- package/src/clients/runtime.ts +4 -4
- package/src/clients/static.ts +9 -13
- package/src/core.ts +476 -77
- package/src/index.ts +1 -0
- package/src/plugins/browser.ts +61 -0
- package/src/plugins/heartbeat.ts +111 -0
- package/src/plugins/index.ts +5 -0
- package/src/plugins/logging.ts +42 -0
- package/src/plugins/reconnect.ts +130 -0
- package/src/plugins/types.ts +72 -0
- package/src/streams.ts +3 -3
- package/src/types.ts +1 -17
package/src/index.ts
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ClientPlugin } from './types.ts'
|
|
2
|
+
|
|
3
|
+
export const browserConnectivityPlugin = (): ClientPlugin => {
|
|
4
|
+
return (client) => {
|
|
5
|
+
const cleanup: Array<() => void> = []
|
|
6
|
+
|
|
7
|
+
const maybeConnect = () => {
|
|
8
|
+
if (client.state === 'disconnected' && !client.isDisposed()) {
|
|
9
|
+
client.connect().catch(() => void 0)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
name: 'browser-connectivity',
|
|
15
|
+
onInit: () => {
|
|
16
|
+
if (globalThis.window) {
|
|
17
|
+
const onPageShow = () => maybeConnect()
|
|
18
|
+
globalThis.window.addEventListener('pageshow', onPageShow)
|
|
19
|
+
cleanup.push(() =>
|
|
20
|
+
globalThis.window?.removeEventListener('pageshow', onPageShow),
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const onOnline = () => maybeConnect()
|
|
24
|
+
globalThis.window.addEventListener('online', onOnline)
|
|
25
|
+
cleanup.push(() =>
|
|
26
|
+
globalThis.window?.removeEventListener('online', onOnline),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const onFocus = () => maybeConnect()
|
|
30
|
+
globalThis.window.addEventListener('focus', onFocus)
|
|
31
|
+
cleanup.push(() =>
|
|
32
|
+
globalThis.window?.removeEventListener('focus', onFocus),
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (globalThis.document) {
|
|
37
|
+
const onVisibilityChange = () => {
|
|
38
|
+
if (globalThis.document?.visibilityState === 'visible') {
|
|
39
|
+
maybeConnect()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
globalThis.document.addEventListener(
|
|
44
|
+
'visibilitychange',
|
|
45
|
+
onVisibilityChange,
|
|
46
|
+
)
|
|
47
|
+
cleanup.push(() =>
|
|
48
|
+
globalThis.document?.removeEventListener(
|
|
49
|
+
'visibilitychange',
|
|
50
|
+
onVisibilityChange,
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
dispose: () => {
|
|
56
|
+
for (const stop of cleanup) stop()
|
|
57
|
+
cleanup.length = 0
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { ConnectionType } from '@nmtjs/protocol'
|
|
2
|
+
|
|
3
|
+
import type { ClientPlugin } from './types.ts'
|
|
4
|
+
|
|
5
|
+
const DEFAULT_HEARTBEAT_INTERVAL = 15000
|
|
6
|
+
const DEFAULT_HEARTBEAT_TIMEOUT = 5000
|
|
7
|
+
|
|
8
|
+
const sleep = (ms: number, signal?: AbortSignal) => {
|
|
9
|
+
return new Promise<void>((resolve) => {
|
|
10
|
+
if (signal?.aborted) return resolve()
|
|
11
|
+
const timer = setTimeout(resolve, ms)
|
|
12
|
+
if (signal) {
|
|
13
|
+
signal.addEventListener(
|
|
14
|
+
'abort',
|
|
15
|
+
() => {
|
|
16
|
+
clearTimeout(timer)
|
|
17
|
+
resolve()
|
|
18
|
+
},
|
|
19
|
+
{ once: true },
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const isPaused = () => {
|
|
26
|
+
if (globalThis.window && 'navigator' in globalThis.window) {
|
|
27
|
+
if (globalThis.window.navigator?.onLine === false) return true
|
|
28
|
+
}
|
|
29
|
+
if (globalThis.document) {
|
|
30
|
+
if (globalThis.document.visibilityState === 'hidden') return true
|
|
31
|
+
}
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface HeartbeatPluginOptions {
|
|
36
|
+
interval?: number
|
|
37
|
+
timeout?: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const heartbeatPlugin = (
|
|
41
|
+
options: HeartbeatPluginOptions = {},
|
|
42
|
+
): ClientPlugin => {
|
|
43
|
+
return (client) => {
|
|
44
|
+
const interval = options.interval ?? DEFAULT_HEARTBEAT_INTERVAL
|
|
45
|
+
const timeout = options.timeout ?? DEFAULT_HEARTBEAT_TIMEOUT
|
|
46
|
+
|
|
47
|
+
let heartbeatAbortController: AbortController | null = null
|
|
48
|
+
let heartbeatTask: Promise<void> | null = null
|
|
49
|
+
|
|
50
|
+
const stopHeartbeat = () => {
|
|
51
|
+
heartbeatAbortController?.abort()
|
|
52
|
+
heartbeatAbortController = null
|
|
53
|
+
heartbeatTask = null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const startHeartbeat = () => {
|
|
57
|
+
if (heartbeatTask) return
|
|
58
|
+
if (client.transportType !== ConnectionType.Bidirectional) return
|
|
59
|
+
|
|
60
|
+
heartbeatAbortController = new AbortController()
|
|
61
|
+
const signal = heartbeatAbortController.signal
|
|
62
|
+
|
|
63
|
+
heartbeatTask = (async () => {
|
|
64
|
+
while (
|
|
65
|
+
!signal.aborted &&
|
|
66
|
+
!client.isDisposed() &&
|
|
67
|
+
client.state === 'connected'
|
|
68
|
+
) {
|
|
69
|
+
if (isPaused()) {
|
|
70
|
+
await sleep(1000, signal)
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await sleep(interval, signal)
|
|
75
|
+
|
|
76
|
+
if (
|
|
77
|
+
signal.aborted ||
|
|
78
|
+
client.isDisposed() ||
|
|
79
|
+
client.state !== 'connected'
|
|
80
|
+
) {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
await client.ping(timeout, signal)
|
|
86
|
+
} catch {
|
|
87
|
+
if (
|
|
88
|
+
!signal.aborted &&
|
|
89
|
+
!client.isDisposed() &&
|
|
90
|
+
client.state === 'connected'
|
|
91
|
+
) {
|
|
92
|
+
await client
|
|
93
|
+
.requestReconnect('heartbeat_timeout')
|
|
94
|
+
.catch(() => void 0)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
})().finally(() => {
|
|
99
|
+
heartbeatTask = null
|
|
100
|
+
heartbeatAbortController = null
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
name: 'heartbeat',
|
|
106
|
+
onConnect: startHeartbeat,
|
|
107
|
+
onDisconnect: () => stopHeartbeat(),
|
|
108
|
+
dispose: stopHeartbeat,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ClientPlugin, ClientPluginEvent } from './types.ts'
|
|
2
|
+
|
|
3
|
+
export type ClientLogEvent = ClientPluginEvent
|
|
4
|
+
|
|
5
|
+
export interface LoggingPluginOptions {
|
|
6
|
+
includeBodies?: boolean
|
|
7
|
+
onEvent(event: ClientLogEvent): void | Promise<void>
|
|
8
|
+
mapEvent?(event: ClientLogEvent): ClientLogEvent | null
|
|
9
|
+
onSinkError?(error: unknown, event: ClientLogEvent): void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const stripEventBody = (event: ClientLogEvent): ClientLogEvent => {
|
|
13
|
+
if (!('body' in event)) return event
|
|
14
|
+
|
|
15
|
+
const { body: _body, ...rest } = event
|
|
16
|
+
return rest as ClientLogEvent
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const loggingPlugin = (options: LoggingPluginOptions): ClientPlugin => {
|
|
20
|
+
const includeBodies = options.includeBodies ?? false
|
|
21
|
+
|
|
22
|
+
return () => ({
|
|
23
|
+
name: 'logging',
|
|
24
|
+
onClientEvent: (event) => {
|
|
25
|
+
const eventToMap = includeBodies ? event : stripEventBody(event)
|
|
26
|
+
const mappedEvent = options.mapEvent
|
|
27
|
+
? options.mapEvent(eventToMap)
|
|
28
|
+
: eventToMap
|
|
29
|
+
|
|
30
|
+
if (!mappedEvent) return
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const sinkResult = options.onEvent(mappedEvent)
|
|
34
|
+
Promise.resolve(sinkResult).catch((error) => {
|
|
35
|
+
options.onSinkError?.(error, mappedEvent)
|
|
36
|
+
})
|
|
37
|
+
} catch (error) {
|
|
38
|
+
options.onSinkError?.(error, mappedEvent)
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { ConnectionType } from '@nmtjs/protocol'
|
|
2
|
+
|
|
3
|
+
import type { ClientDisconnectReason, ClientPlugin } from './types.ts'
|
|
4
|
+
|
|
5
|
+
const DEFAULT_RECONNECT_TIMEOUT = 1000
|
|
6
|
+
const DEFAULT_MAX_RECONNECT_TIMEOUT = 60000
|
|
7
|
+
|
|
8
|
+
const sleep = (ms: number, signal?: AbortSignal) => {
|
|
9
|
+
return new Promise<void>((resolve) => {
|
|
10
|
+
if (signal?.aborted) return resolve()
|
|
11
|
+
const timer = setTimeout(resolve, ms)
|
|
12
|
+
if (signal) {
|
|
13
|
+
signal.addEventListener(
|
|
14
|
+
'abort',
|
|
15
|
+
() => {
|
|
16
|
+
clearTimeout(timer)
|
|
17
|
+
resolve()
|
|
18
|
+
},
|
|
19
|
+
{ once: true },
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const computeReconnectDelay = (ms: number) => {
|
|
26
|
+
if (globalThis.window) {
|
|
27
|
+
const jitter = Math.floor(ms * 0.2 * Math.random())
|
|
28
|
+
return ms + jitter
|
|
29
|
+
}
|
|
30
|
+
return ms
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const isReconnectPaused = () => {
|
|
34
|
+
if (globalThis.window && 'navigator' in globalThis.window) {
|
|
35
|
+
if (globalThis.window.navigator?.onLine === false) return true
|
|
36
|
+
}
|
|
37
|
+
if (globalThis.document) {
|
|
38
|
+
if (globalThis.document.visibilityState === 'hidden') return true
|
|
39
|
+
}
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ReconnectPluginOptions {
|
|
44
|
+
initialTimeout?: number
|
|
45
|
+
maxTimeout?: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const reconnectPlugin = (
|
|
49
|
+
options: ReconnectPluginOptions = {},
|
|
50
|
+
): ClientPlugin => {
|
|
51
|
+
return (client) => {
|
|
52
|
+
let reconnecting: Promise<void> | null = null
|
|
53
|
+
let reconnectAbortController: AbortController | null = null
|
|
54
|
+
let reconnectTimeout = options.initialTimeout ?? DEFAULT_RECONNECT_TIMEOUT
|
|
55
|
+
|
|
56
|
+
const cancelReconnect = () => {
|
|
57
|
+
reconnectAbortController?.abort()
|
|
58
|
+
reconnectAbortController = null
|
|
59
|
+
reconnecting = null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const ensureReconnectLoop = () => {
|
|
63
|
+
if (reconnecting) return
|
|
64
|
+
|
|
65
|
+
reconnectAbortController = new AbortController()
|
|
66
|
+
const signal = reconnectAbortController.signal
|
|
67
|
+
|
|
68
|
+
reconnecting = (async () => {
|
|
69
|
+
while (
|
|
70
|
+
!signal.aborted &&
|
|
71
|
+
!client.isDisposed() &&
|
|
72
|
+
client.state === 'disconnected' &&
|
|
73
|
+
client.lastDisconnectReason !== 'client'
|
|
74
|
+
) {
|
|
75
|
+
if (isReconnectPaused()) {
|
|
76
|
+
await sleep(1000, signal)
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const delay = computeReconnectDelay(reconnectTimeout)
|
|
81
|
+
await sleep(delay, signal)
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
signal.aborted ||
|
|
85
|
+
client.isDisposed() ||
|
|
86
|
+
client.state !== 'disconnected' ||
|
|
87
|
+
client.lastDisconnectReason === 'client'
|
|
88
|
+
) {
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const previousTimeout = reconnectTimeout
|
|
93
|
+
await client.connect().catch(() => void 0)
|
|
94
|
+
|
|
95
|
+
if (client.state === 'disconnected') {
|
|
96
|
+
reconnectTimeout = Math.min(
|
|
97
|
+
previousTimeout * 2,
|
|
98
|
+
options.maxTimeout ?? DEFAULT_MAX_RECONNECT_TIMEOUT,
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
})().finally(() => {
|
|
103
|
+
reconnecting = null
|
|
104
|
+
reconnectAbortController = null
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const onDisconnect = (reason: ClientDisconnectReason) => {
|
|
109
|
+
if (
|
|
110
|
+
client.transportType !== ConnectionType.Bidirectional ||
|
|
111
|
+
reason === 'client' ||
|
|
112
|
+
client.isDisposed()
|
|
113
|
+
) {
|
|
114
|
+
cancelReconnect()
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
ensureReconnectLoop()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
name: 'reconnect',
|
|
122
|
+
onConnect: () => {
|
|
123
|
+
reconnectTimeout = options.initialTimeout ?? DEFAULT_RECONNECT_TIMEOUT
|
|
124
|
+
cancelReconnect()
|
|
125
|
+
},
|
|
126
|
+
onDisconnect,
|
|
127
|
+
dispose: cancelReconnect,
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { BaseClient } from '../core.ts'
|
|
2
|
+
|
|
3
|
+
export type ClientDisconnectReason = 'client' | 'server' | (string & {})
|
|
4
|
+
|
|
5
|
+
export type ClientPluginEvent =
|
|
6
|
+
| {
|
|
7
|
+
kind: 'connected'
|
|
8
|
+
timestamp: number
|
|
9
|
+
transportType: 'bidirectional' | 'unidirectional'
|
|
10
|
+
}
|
|
11
|
+
| { kind: 'disconnected'; timestamp: number; reason: ClientDisconnectReason }
|
|
12
|
+
| {
|
|
13
|
+
kind: 'server_message'
|
|
14
|
+
timestamp: number
|
|
15
|
+
messageType: number | string
|
|
16
|
+
rawByteLength: number
|
|
17
|
+
body?: unknown
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
kind: 'rpc_request'
|
|
21
|
+
timestamp: number
|
|
22
|
+
callId: number
|
|
23
|
+
procedure: string
|
|
24
|
+
body?: unknown
|
|
25
|
+
}
|
|
26
|
+
| {
|
|
27
|
+
kind: 'rpc_response'
|
|
28
|
+
timestamp: number
|
|
29
|
+
callId: number
|
|
30
|
+
procedure: string
|
|
31
|
+
body?: unknown
|
|
32
|
+
stream?: boolean
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
kind: 'rpc_error'
|
|
36
|
+
timestamp: number
|
|
37
|
+
callId: number
|
|
38
|
+
procedure: string
|
|
39
|
+
error: unknown
|
|
40
|
+
}
|
|
41
|
+
| {
|
|
42
|
+
kind: 'stream_event'
|
|
43
|
+
timestamp: number
|
|
44
|
+
direction: 'incoming' | 'outgoing'
|
|
45
|
+
streamType: 'rpc' | 'client_blob' | 'server_blob'
|
|
46
|
+
action: 'response' | 'pull' | 'push' | 'end' | 'abort'
|
|
47
|
+
callId?: number
|
|
48
|
+
streamId?: number
|
|
49
|
+
byteLength?: number
|
|
50
|
+
reason?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Client plugin lifecycle contract.
|
|
55
|
+
*
|
|
56
|
+
* Ordering guarantees:
|
|
57
|
+
* - `onInit`, `onConnect`, `onServerMessage`, `onClientEvent`: registration order
|
|
58
|
+
* - `onDisconnect`, `dispose`: reverse registration order
|
|
59
|
+
*/
|
|
60
|
+
export interface ClientPluginInstance {
|
|
61
|
+
name?: string
|
|
62
|
+
onInit?(): void
|
|
63
|
+
onConnect?(): void | Promise<void>
|
|
64
|
+
onDisconnect?(reason: ClientDisconnectReason): void | Promise<void>
|
|
65
|
+
onServerMessage?(message: unknown, raw: ArrayBufferView): void
|
|
66
|
+
onClientEvent?(event: ClientPluginEvent): void | Promise<void>
|
|
67
|
+
dispose?(): void
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type ClientPlugin = (
|
|
71
|
+
client: BaseClient<any, any, any, any, any>,
|
|
72
|
+
) => ClientPluginInstance
|
package/src/streams.ts
CHANGED
|
@@ -29,7 +29,7 @@ export class ClientStreams {
|
|
|
29
29
|
this.#collection.delete(streamId)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async abort(streamId: number, reason?:
|
|
32
|
+
async abort(streamId: number, reason?: any) {
|
|
33
33
|
const stream = this.#collection.get(streamId)
|
|
34
34
|
if (!stream) return // Stream already cleaned up
|
|
35
35
|
await stream.abort(reason)
|
|
@@ -46,7 +46,7 @@ export class ClientStreams {
|
|
|
46
46
|
this.remove(streamId)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
async clear(reason?:
|
|
49
|
+
async clear(reason?: any) {
|
|
50
50
|
if (reason) {
|
|
51
51
|
const abortPromises = [...this.#collection.values()].map((stream) =>
|
|
52
52
|
stream.abort(reason),
|
|
@@ -118,7 +118,7 @@ export class ServerStreams<
|
|
|
118
118
|
this.remove(streamId)
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
async clear(reason?:
|
|
121
|
+
async clear(reason?: any) {
|
|
122
122
|
if (reason) {
|
|
123
123
|
const abortPromises = [...this.#writers.values()].map((writer) =>
|
|
124
124
|
writer.abort(reason).finally(() => writer.releaseLock()),
|
package/src/types.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type { ProtocolBlobInterface } from '@nmtjs/protocol'
|
|
|
4
4
|
import type {
|
|
5
5
|
ProtocolError,
|
|
6
6
|
ProtocolServerBlobStream,
|
|
7
|
-
ProtocolServerStreamInterface,
|
|
8
7
|
} from '@nmtjs/protocol/client'
|
|
9
8
|
import type { BaseTypeAny, PlainType, t } from '@nmtjs/type'
|
|
10
9
|
|
|
@@ -60,9 +59,7 @@ export type AnyResolvedContractProcedure = {
|
|
|
60
59
|
|
|
61
60
|
export type AnyResolvedContractRouter = {
|
|
62
61
|
[ResolvedType]: 'router'
|
|
63
|
-
[key: string]:
|
|
64
|
-
| AnyResolvedContractProcedure
|
|
65
|
-
| { [ResolvedType]: 'router'; [key: string]: AnyResolvedContractProcedure }
|
|
62
|
+
[key: string]: AnyResolvedContractProcedure | AnyResolvedContractRouter
|
|
66
63
|
}
|
|
67
64
|
|
|
68
65
|
export type ResolveAPIRouterRoutes<
|
|
@@ -114,19 +111,6 @@ type OmitType<T extends object, E> = {
|
|
|
114
111
|
[K in keyof T as T[K] extends E ? never : K]: T[K]
|
|
115
112
|
}
|
|
116
113
|
|
|
117
|
-
// export type FilterResolvedContractRouter<
|
|
118
|
-
// Resolved extends AnyResolvedContractRouter,
|
|
119
|
-
// Stream extends boolean,
|
|
120
|
-
// > = {
|
|
121
|
-
// [K in keyof Resolved]: Resolved[K] extends AnyResolvedContractProcedure
|
|
122
|
-
// ? Resolved[K]['stream'] extends Stream
|
|
123
|
-
// ? Resolved[K]
|
|
124
|
-
// : never
|
|
125
|
-
// : Resolved[K] extends AnyResolvedContractRouter
|
|
126
|
-
// ? FilterResolvedContractRouter<Resolved[K], Stream>
|
|
127
|
-
// : never
|
|
128
|
-
// }
|
|
129
|
-
|
|
130
114
|
export type ClientCallers<
|
|
131
115
|
Resolved extends AnyResolvedContractRouter,
|
|
132
116
|
SafeCall extends boolean,
|