@nxtedition/deepstream.io-client-js 32.0.20 → 32.0.22
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/package.json +11 -11
- package/src/client.d.ts +4 -57
- package/src/client.test-d.ts +2 -96
- package/src/event/event-handler.d.ts +1 -5
- package/src/record/record-handler.d.ts +20 -84
- package/src/record/record.d.ts +36 -23
- package/src/rpc/rpc-handler.d.ts +4 -7
- package/src/rpc/rpc-response.d.ts +0 -1
- package/src/rpc/rpc-handler.test-d.ts +0 -40
- package/src/utils/multicast-listener.js +0 -252
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/deepstream.io-client-js",
|
|
3
|
-
"version": "32.0.
|
|
3
|
+
"version": "32.0.22",
|
|
4
4
|
"description": "the javascript client for deepstream.io",
|
|
5
5
|
"homepage": "http://deepstream.io",
|
|
6
6
|
"type": "module",
|
|
@@ -36,16 +36,16 @@
|
|
|
36
36
|
"singleQuote": true
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@nxtedition/json-path": "1.0.
|
|
40
|
-
"bufferutil": "4.0.8",
|
|
41
|
-
"component-emitter2": "1.3.5",
|
|
42
|
-
"invariant": "2.2.4",
|
|
43
|
-
"lodash.clonedeep": "4.5.0",
|
|
44
|
-
"utf-8-validate": "6.0.
|
|
45
|
-
"varint": "6.0.0",
|
|
46
|
-
"ws": "8.
|
|
47
|
-
"xuid": "4.1.
|
|
48
|
-
"xxhash-wasm": "^1.
|
|
39
|
+
"@nxtedition/json-path": "^1.0.8",
|
|
40
|
+
"bufferutil": "^4.0.8",
|
|
41
|
+
"component-emitter2": "^1.3.5",
|
|
42
|
+
"invariant": "^2.2.4",
|
|
43
|
+
"lodash.clonedeep": "^4.5.0",
|
|
44
|
+
"utf-8-validate": "^6.0.5",
|
|
45
|
+
"varint": "^6.0.0",
|
|
46
|
+
"ws": "^8.18.0",
|
|
47
|
+
"xuid": "^4.1.3",
|
|
48
|
+
"xxhash-wasm": "^1.0.2"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^22.10.3",
|
package/src/client.d.ts
CHANGED
|
@@ -7,21 +7,10 @@ import type { EventStats } from './event/event-handler.js'
|
|
|
7
7
|
import type RpcHandler from './rpc/rpc-handler.js'
|
|
8
8
|
import type { RpcStats, RpcMethodDef } from './rpc/rpc-handler.js'
|
|
9
9
|
|
|
10
|
-
export interface DeepstreamClientOptions {
|
|
11
|
-
reconnectIntervalIncrement?: number
|
|
12
|
-
maxReconnectInterval?: number
|
|
13
|
-
maxReconnectAttempts?: number
|
|
14
|
-
maxPacketSize?: number
|
|
15
|
-
batchSize?: number
|
|
16
|
-
schedule?: ((fn: () => void) => void) | null
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
-
logger?: any
|
|
19
|
-
}
|
|
20
|
-
|
|
21
10
|
export default function <
|
|
22
11
|
Records extends Record<string, unknown> = Record<string, unknown>,
|
|
23
12
|
Methods extends Record<string, RpcMethodDef> = Record<string, RpcMethodDef>,
|
|
24
|
-
>(url: string, options?:
|
|
13
|
+
>(url: string, options?: unknown): DeepstreamClient<Records, Methods>
|
|
25
14
|
|
|
26
15
|
export type {
|
|
27
16
|
DsRecord,
|
|
@@ -33,8 +22,6 @@ export type {
|
|
|
33
22
|
SyncOptions,
|
|
34
23
|
Paths,
|
|
35
24
|
Get,
|
|
36
|
-
ConnectionStateName,
|
|
37
|
-
DeepstreamErrorEventName,
|
|
38
25
|
}
|
|
39
26
|
|
|
40
27
|
type RecordStateConstants = Readonly<{
|
|
@@ -90,36 +77,6 @@ type EventConstants = Readonly<{
|
|
|
90
77
|
}>
|
|
91
78
|
type EventKey = keyof EventConstants
|
|
92
79
|
type EventName = EventConstants[EventKey]
|
|
93
|
-
type DeepstreamErrorEventName = Exclude<
|
|
94
|
-
EventName,
|
|
95
|
-
'connectionStateChanged' | 'connected' | 'MAX_RECONNECTION_ATTEMPTS_REACHED'
|
|
96
|
-
>
|
|
97
|
-
|
|
98
|
-
export interface DeepstreamError extends Error {
|
|
99
|
-
topic?: string
|
|
100
|
-
event?: EventName | null
|
|
101
|
-
data?: unknown
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export interface DeepstreamMessage {
|
|
105
|
-
raw: string | null
|
|
106
|
-
topic: string | null
|
|
107
|
-
action: string | null
|
|
108
|
-
data: string[]
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export interface DeepstreamClientEventMap {
|
|
112
|
-
connectionStateChanged: (state: ConnectionStateName) => void
|
|
113
|
-
connected: (connected: boolean) => void
|
|
114
|
-
MAX_RECONNECTION_ATTEMPTS_REACHED: (attempt: number) => void
|
|
115
|
-
error: (error: DeepstreamError) => void
|
|
116
|
-
recv: (message: DeepstreamMessage) => void
|
|
117
|
-
send: (message: DeepstreamMessage) => void
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
type DeepstreamErrorEventMap = {
|
|
121
|
-
[K in DeepstreamErrorEventName]: (error: DeepstreamError) => void
|
|
122
|
-
}
|
|
123
80
|
|
|
124
81
|
export interface DeepstreamClient<
|
|
125
82
|
Records extends Record<string, unknown> = Record<string, unknown>,
|
|
@@ -130,21 +87,11 @@ export interface DeepstreamClient<
|
|
|
130
87
|
rpc: RpcHandler<Methods>
|
|
131
88
|
record: RecordHandler<Records>
|
|
132
89
|
user: string | null
|
|
133
|
-
on
|
|
134
|
-
|
|
135
|
-
callback: (DeepstreamClientEventMap & DeepstreamErrorEventMap)[K],
|
|
136
|
-
): this
|
|
137
|
-
off<K extends keyof (DeepstreamClientEventMap & DeepstreamErrorEventMap)>(
|
|
138
|
-
evt: K,
|
|
139
|
-
callback: (DeepstreamClientEventMap & DeepstreamErrorEventMap)[K],
|
|
140
|
-
): this
|
|
90
|
+
on: (evt: EventName, callback: (...args: unknown[]) => void) => void
|
|
91
|
+
off: (evt: EventName, callback: (...args: unknown[]) => void) => void
|
|
141
92
|
getConnectionState: () => ConnectionStateName
|
|
142
93
|
close: () => void
|
|
143
|
-
login
|
|
144
|
-
login(
|
|
145
|
-
authParams: Record<string, unknown>,
|
|
146
|
-
callback: (success: boolean, authData: unknown) => void,
|
|
147
|
-
): this
|
|
94
|
+
login: unknown
|
|
148
95
|
stats: {
|
|
149
96
|
record: RecordStats
|
|
150
97
|
rpc: RpcStats
|
package/src/client.test-d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import make, { type DeepstreamClient
|
|
2
|
+
import make, { type DeepstreamClient } from './client.js'
|
|
3
3
|
import { expectAssignable, expectError, expectType } from 'tsd'
|
|
4
4
|
import type { Observable } from 'rxjs'
|
|
5
|
-
import type { EmptyObject } from 'type-fest'
|
|
6
5
|
|
|
7
6
|
interface Records extends Record<string, unknown> {
|
|
8
7
|
o: {
|
|
@@ -23,11 +22,6 @@ interface Records extends Record<string, unknown> {
|
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
|
-
possiblyEmpty:
|
|
27
|
-
| {
|
|
28
|
-
pe1: string
|
|
29
|
-
}
|
|
30
|
-
| EmptyObject
|
|
31
25
|
c: Circular
|
|
32
26
|
m: {
|
|
33
27
|
m1: string
|
|
@@ -68,7 +62,7 @@ expectAssignable<{ n0?: { n1: { n2: { n3: string } } } } | undefined>(await ds.r
|
|
|
68
62
|
expectAssignable<{ n1: { n2: { n3: string } } } | undefined>(await ds.record.get('n', 'n0'))
|
|
69
63
|
|
|
70
64
|
// set withouth path
|
|
71
|
-
ds.record.set('
|
|
65
|
+
ds.record.set('n', {}) // empty should always work
|
|
72
66
|
ds.record.set('n', { n0: { n1: { n2: { n3: 'test' } } } })
|
|
73
67
|
expectError(ds.record.set('n', { n0: {} })) // nested props are required
|
|
74
68
|
|
|
@@ -102,14 +96,10 @@ expectError(ds.record.set('n', 'n1.x2', {}))
|
|
|
102
96
|
expectError(ds.record.set('n', 'n1.n2.n3', { n4: 22 }))
|
|
103
97
|
|
|
104
98
|
expectAssignable<string>(await ds.record.get('p', 'p1'))
|
|
105
|
-
expectAssignable<{ name: string; version: string; state: number; data: string }>(
|
|
106
|
-
await ds.record.get2('p', 'p1'),
|
|
107
|
-
)
|
|
108
99
|
expectAssignable<string>(await ds.record.get('p', 'p1', { signal: new AbortController().signal }))
|
|
109
100
|
expectAssignable<string>(await ds.record.get('p', { path: 'p1' }))
|
|
110
101
|
expectAssignable<string | undefined>(await ds.record.get('p', 'p2'))
|
|
111
102
|
expectAssignable<unknown>(await ds.record.get('p', 'x1'))
|
|
112
|
-
expectAssignable<string | undefined>(await ds.record.get('possiblyEmpty', 'pe1'))
|
|
113
103
|
|
|
114
104
|
// observe with options
|
|
115
105
|
expectAssignable<Observable<{ p1: string; p2?: string; p3: { p4: string } }>>(
|
|
@@ -189,16 +179,6 @@ expectAssignable<Promise<void>>(
|
|
|
189
179
|
)
|
|
190
180
|
expectAssignable<Promise<void>>(ds.record.update('p', 'p1', (data) => data, { timeout: 5000 }))
|
|
191
181
|
|
|
192
|
-
// update: updater receives version as second argument
|
|
193
|
-
ds.record.update('p', (data, version) => {
|
|
194
|
-
expectType<string>(version)
|
|
195
|
-
return data
|
|
196
|
-
})
|
|
197
|
-
ds.record.update('p', 'p1', (data, version) => {
|
|
198
|
-
expectType<string>(version)
|
|
199
|
-
return data
|
|
200
|
-
})
|
|
201
|
-
|
|
202
182
|
// Circular
|
|
203
183
|
expectAssignable<string | undefined>(await ds.record.get('c', 'a.b1'))
|
|
204
184
|
|
|
@@ -230,19 +210,6 @@ expectAssignable<Promise<typeof rec>>(rec.when({ state: 2, timeout: 5000 }))
|
|
|
230
210
|
expectAssignable<Promise<typeof rec>>(rec.when(2, { timeout: 5000 }))
|
|
231
211
|
expectAssignable<Promise<typeof rec>>(rec.when(2, { signal: new AbortController().signal }))
|
|
232
212
|
|
|
233
|
-
// Record.subscribe: callback receives (record, opaque)
|
|
234
|
-
rec.subscribe((record, opaque) => {
|
|
235
|
-
expectType<typeof rec>(record)
|
|
236
|
-
expectType<unknown>(opaque)
|
|
237
|
-
})
|
|
238
|
-
rec.subscribe((record, opaque) => {}, 'my-opaque-token')
|
|
239
|
-
|
|
240
|
-
// Record.unsubscribe: same callback signature
|
|
241
|
-
rec.unsubscribe((record, opaque) => {
|
|
242
|
-
expectType<typeof rec>(record)
|
|
243
|
-
expectType<unknown>(opaque)
|
|
244
|
-
})
|
|
245
|
-
|
|
246
213
|
// Record.update with options
|
|
247
214
|
expectAssignable<Promise<void>>(rec.update((x) => x, { signal: new AbortController().signal }))
|
|
248
215
|
expectAssignable<Promise<void>>(rec.update((x) => x, { timeout: 5000 }))
|
|
@@ -253,68 +220,7 @@ expectAssignable<Promise<void>>(
|
|
|
253
220
|
expectAssignable<Promise<void>>(rec.update('o0', (x) => x, { timeout: 5000 }))
|
|
254
221
|
expectAssignable<Promise<void>>(rec.update('o0', (x) => x, { state: 2 }))
|
|
255
222
|
|
|
256
|
-
// Record.update: updater receives version as second argument
|
|
257
|
-
rec.update((x, version) => {
|
|
258
|
-
expectType<string>(version)
|
|
259
|
-
return x
|
|
260
|
-
})
|
|
261
|
-
rec.update('o0', (x, version) => {
|
|
262
|
-
expectType<string>(version)
|
|
263
|
-
return x
|
|
264
|
-
})
|
|
265
|
-
|
|
266
223
|
const state = 'VOID'
|
|
267
224
|
expectType<0>(ds.record.STATE[state])
|
|
268
225
|
const unknownState: string = 'VOID'
|
|
269
226
|
expectType<number>(ds.record.STATE[unknownState])
|
|
270
|
-
|
|
271
|
-
// record.getRecord: [Symbol.dispose] is present
|
|
272
|
-
const recDispose = ds.record.getRecord('o')
|
|
273
|
-
recDispose[Symbol.dispose]()
|
|
274
|
-
|
|
275
|
-
// record.provide: returns Disposer | void
|
|
276
|
-
expectAssignable<(() => void) | void>(ds.record.provide('pattern*', () => ({})))
|
|
277
|
-
const recordDisposer = ds.record.provide('pattern*', () => ({}))
|
|
278
|
-
if (recordDisposer) {
|
|
279
|
-
recordDisposer()
|
|
280
|
-
recordDisposer[Symbol.dispose]()
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// event.provide: returns (() => void) | void
|
|
284
|
-
expectAssignable<(() => void) | void>(ds.event.provide('pattern*', () => {}, {}))
|
|
285
|
-
|
|
286
|
-
// client.on/off: 'error' is a valid event name
|
|
287
|
-
ds.on('error', (err) => {})
|
|
288
|
-
ds.off('error', (err) => {})
|
|
289
|
-
expectError(ds.on('unknownEvent', () => {}))
|
|
290
|
-
|
|
291
|
-
// client.on: callback arg types per event
|
|
292
|
-
ds.on('error', (err) => {
|
|
293
|
-
expectType<DeepstreamError>(err)
|
|
294
|
-
})
|
|
295
|
-
ds.on('connectionError', (err) => {
|
|
296
|
-
expectType<DeepstreamError>(err)
|
|
297
|
-
})
|
|
298
|
-
ds.on('connectionStateChanged', (state) => {
|
|
299
|
-
expectType<
|
|
300
|
-
| 'CLOSED'
|
|
301
|
-
| 'AWAITING_CONNECTION'
|
|
302
|
-
| 'CHALLENGING'
|
|
303
|
-
| 'AWAITING_AUTHENTICATION'
|
|
304
|
-
| 'AUTHENTICATING'
|
|
305
|
-
| 'OPEN'
|
|
306
|
-
| 'ERROR'
|
|
307
|
-
| 'RECONNECTING'
|
|
308
|
-
>(state)
|
|
309
|
-
})
|
|
310
|
-
ds.on('connected', (connected) => {
|
|
311
|
-
expectType<boolean>(connected)
|
|
312
|
-
})
|
|
313
|
-
ds.on('MAX_RECONNECTION_ATTEMPTS_REACHED', (attempt) => {
|
|
314
|
-
expectType<number>(attempt)
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
// client.on: wrong callback arg types are errors
|
|
318
|
-
expectError(ds.on('connectionStateChanged', (state: number) => {}))
|
|
319
|
-
expectError(ds.on('connected', (connected: string) => {}))
|
|
320
|
-
expectError(ds.on('MAX_RECONNECTION_ATTEMPTS_REACHED', (attempt: string) => {}))
|
|
@@ -10,11 +10,7 @@ export default class EventHandler {
|
|
|
10
10
|
off: (name: string, callback: (data: unknown) => void) => this
|
|
11
11
|
observe: <Data>(name: string) => Observable<Data>
|
|
12
12
|
emit: <Data>(name: string, data?: Data) => void
|
|
13
|
-
provide: (
|
|
14
|
-
pattern: string,
|
|
15
|
-
callback: (name: string) => void,
|
|
16
|
-
options: unknown,
|
|
17
|
-
) => (() => void) | void
|
|
13
|
+
provide: (pattern: string, callback: (name: string) => void, options: unknown) => () => void
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
export interface EventStats {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { Observable } from 'rxjs'
|
|
2
2
|
import type DsRecord from './record.js'
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
EmptyObject,
|
|
5
|
+
Get,
|
|
6
|
+
UpdateOptions,
|
|
7
|
+
ObserveOptions,
|
|
8
|
+
ObserveOptionsWithPath,
|
|
9
|
+
} from './record.js'
|
|
4
10
|
|
|
5
11
|
type Lookup<Table, Name> = Name extends keyof Table ? Table[Name] : unknown
|
|
6
12
|
|
|
@@ -26,8 +32,8 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
JSON: {
|
|
29
|
-
EMPTY:
|
|
30
|
-
EMPTY_OBJ:
|
|
35
|
+
EMPTY: EmptyObject
|
|
36
|
+
EMPTY_OBJ: EmptyObject
|
|
31
37
|
EMPTY_ARR: []
|
|
32
38
|
}
|
|
33
39
|
|
|
@@ -40,13 +46,13 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
40
46
|
pattern: string,
|
|
41
47
|
callback: (key: string) => unknown,
|
|
42
48
|
optionsOrRecursive?: ProvideOptions | boolean,
|
|
43
|
-
) => Disposer
|
|
49
|
+
) => Disposer
|
|
44
50
|
|
|
45
51
|
sync: (options?: SyncOptions) => Promise<void>
|
|
46
52
|
|
|
47
53
|
set: {
|
|
48
54
|
// without path:
|
|
49
|
-
<Name extends string>(name: Name, data: Lookup<Records, Name>): void
|
|
55
|
+
<Name extends string>(name: Name, data: Lookup<Records, Name> | EmptyObject): void
|
|
50
56
|
|
|
51
57
|
// with path:
|
|
52
58
|
<Name extends string, Path extends string | string[]>(
|
|
@@ -61,17 +67,14 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
61
67
|
update: {
|
|
62
68
|
<Name extends string>(
|
|
63
69
|
name: Name,
|
|
64
|
-
updater: (data: Lookup<Records, Name
|
|
70
|
+
updater: (data: Lookup<Records, Name>) => Lookup<Records, Name> | EmptyObject,
|
|
65
71
|
options?: UpdateOptions,
|
|
66
72
|
): Promise<void>
|
|
67
73
|
|
|
68
74
|
<Name extends string, Path extends string | string[]>(
|
|
69
75
|
name: Name,
|
|
70
76
|
path: Path,
|
|
71
|
-
updater: (
|
|
72
|
-
data: Get<Lookup<Records, Name>, Path>,
|
|
73
|
-
version: string,
|
|
74
|
-
) => Get<Lookup<Records, Name>, Path>,
|
|
77
|
+
updater: (data: Get<Lookup<Records, Name>, Path>) => Get<Lookup<Records, Name>, Path>,
|
|
75
78
|
options?: UpdateOptions,
|
|
76
79
|
): Promise<void>
|
|
77
80
|
}
|
|
@@ -110,73 +113,6 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
110
113
|
): Observable<Get<Lookup<Records, Name>, Path>>
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
observe2: {
|
|
114
|
-
<Name extends string>(
|
|
115
|
-
name: Name,
|
|
116
|
-
options: ObserveOptions,
|
|
117
|
-
): Observable<{
|
|
118
|
-
name: string
|
|
119
|
-
version: string
|
|
120
|
-
state: number
|
|
121
|
-
data: Lookup<Records, Name>
|
|
122
|
-
}>
|
|
123
|
-
|
|
124
|
-
<Name extends string, Path extends string | string[]>(
|
|
125
|
-
name: Name,
|
|
126
|
-
options: ObserveOptionsWithPath<Path>,
|
|
127
|
-
): Observable<{
|
|
128
|
-
name: string
|
|
129
|
-
version: string
|
|
130
|
-
state: number
|
|
131
|
-
data: Get<Lookup<Records, Name>, Path>
|
|
132
|
-
}>
|
|
133
|
-
|
|
134
|
-
<Name extends string>(
|
|
135
|
-
name: Name,
|
|
136
|
-
state?: number,
|
|
137
|
-
options?: ObserveOptions,
|
|
138
|
-
): Observable<{
|
|
139
|
-
name: string
|
|
140
|
-
version: string
|
|
141
|
-
state: number
|
|
142
|
-
data: Lookup<Records, Name>
|
|
143
|
-
}>
|
|
144
|
-
|
|
145
|
-
<Name extends string, Path extends string | string[]>(
|
|
146
|
-
name: Name,
|
|
147
|
-
state?: number,
|
|
148
|
-
options?: ObserveOptionsWithPath<Path>,
|
|
149
|
-
): Observable<{
|
|
150
|
-
name: string
|
|
151
|
-
version: string
|
|
152
|
-
state: number
|
|
153
|
-
data: Get<Lookup<Records, Name>, Path>
|
|
154
|
-
}>
|
|
155
|
-
|
|
156
|
-
<Name extends string, Path extends string | string[]>(
|
|
157
|
-
name: Name,
|
|
158
|
-
path: Path,
|
|
159
|
-
options?: ObserveOptionsWithPath<Path>,
|
|
160
|
-
): Observable<{
|
|
161
|
-
name: string
|
|
162
|
-
version: string
|
|
163
|
-
state: number
|
|
164
|
-
data: Get<Lookup<Records, Name>, Path>
|
|
165
|
-
}>
|
|
166
|
-
|
|
167
|
-
<Name extends string, Path extends string | string[]>(
|
|
168
|
-
name: Name,
|
|
169
|
-
path: Path,
|
|
170
|
-
state?: number,
|
|
171
|
-
options?: ObserveOptionsWithPath<Path>,
|
|
172
|
-
): Observable<{
|
|
173
|
-
name: string
|
|
174
|
-
version: string
|
|
175
|
-
state: number
|
|
176
|
-
data: Get<Lookup<Records, Name>, Path>
|
|
177
|
-
}>
|
|
178
|
-
}
|
|
179
|
-
|
|
180
116
|
get: {
|
|
181
117
|
<Name extends string>(name: Name, options: ObserveOptions): Promise<Lookup<Records, Name>>
|
|
182
118
|
|
|
@@ -211,11 +147,11 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
211
147
|
): Promise<Get<Lookup<Records, Name>, Path>>
|
|
212
148
|
}
|
|
213
149
|
|
|
214
|
-
|
|
150
|
+
observe2: {
|
|
215
151
|
<Name extends string>(
|
|
216
152
|
name: Name,
|
|
217
153
|
options: ObserveOptions,
|
|
218
|
-
):
|
|
154
|
+
): Observable<{
|
|
219
155
|
name: string
|
|
220
156
|
version: string
|
|
221
157
|
state: number
|
|
@@ -225,7 +161,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
225
161
|
<Name extends string, Path extends string | string[]>(
|
|
226
162
|
name: Name,
|
|
227
163
|
options: ObserveOptionsWithPath<Path>,
|
|
228
|
-
):
|
|
164
|
+
): Observable<{
|
|
229
165
|
name: string
|
|
230
166
|
version: string
|
|
231
167
|
state: number
|
|
@@ -236,7 +172,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
236
172
|
name: Name,
|
|
237
173
|
state?: number,
|
|
238
174
|
options?: ObserveOptions,
|
|
239
|
-
):
|
|
175
|
+
): Observable<{
|
|
240
176
|
name: string
|
|
241
177
|
version: string
|
|
242
178
|
state: number
|
|
@@ -247,7 +183,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
247
183
|
name: Name,
|
|
248
184
|
state?: number,
|
|
249
185
|
options?: ObserveOptionsWithPath<Path>,
|
|
250
|
-
):
|
|
186
|
+
): Observable<{
|
|
251
187
|
name: string
|
|
252
188
|
version: string
|
|
253
189
|
state: number
|
|
@@ -258,7 +194,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
258
194
|
name: Name,
|
|
259
195
|
path: Path,
|
|
260
196
|
options?: ObserveOptionsWithPath<Path>,
|
|
261
|
-
):
|
|
197
|
+
): Observable<{
|
|
262
198
|
name: string
|
|
263
199
|
version: string
|
|
264
200
|
state: number
|
|
@@ -270,7 +206,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
|
|
|
270
206
|
path: Path,
|
|
271
207
|
state?: number,
|
|
272
208
|
options?: ObserveOptionsWithPath<Path>,
|
|
273
|
-
):
|
|
209
|
+
): Observable<{
|
|
274
210
|
name: string
|
|
275
211
|
version: string
|
|
276
212
|
state: number
|
package/src/record/record.d.ts
CHANGED
|
@@ -1,15 +1,36 @@
|
|
|
1
1
|
import type RecordHandler from './record-handler.js'
|
|
2
|
-
import type { Get
|
|
3
|
-
export type { Paths } from 'type-fest'
|
|
2
|
+
import type { Get, EmptyObject, SingleKeyObject } from 'type-fest'
|
|
3
|
+
export type { Get, Paths, EmptyObject } from 'type-fest'
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
// When getting, for convenience, we say the data might be partial under some
|
|
6
|
+
// circumstances.
|
|
7
|
+
//
|
|
8
|
+
// When you e.g. do record.get or record.update, there is always a possibility
|
|
9
|
+
// that the data object is empty. The naive correct type for that would be
|
|
10
|
+
// `Data | EmptyObject`. However, that forces the user to always type guard
|
|
11
|
+
// against the empty object case. This type tries to allow the user to skip
|
|
12
|
+
// that check in some cases, where it should be safe to do so.
|
|
13
|
+
export type GettablePossibleEmpty<Data> = keyof Data extends never
|
|
14
|
+
? EmptyObject // If there are no keys at all
|
|
15
|
+
: Partial<Data> extends Data
|
|
16
|
+
? // All properties in Data are already optional, so we can safely return it
|
|
17
|
+
// as is. The user just need to check the properties themselves instead.
|
|
18
|
+
Data
|
|
19
|
+
: SingleKeyObject<Data> extends never
|
|
20
|
+
? // There are more than one property in Data, and some of them are
|
|
21
|
+
// required. That means that the user must always check for the empty
|
|
22
|
+
// object case.
|
|
23
|
+
Data | EmptyObject
|
|
24
|
+
: // There is exactly one property in Data, and it is required. In this
|
|
25
|
+
// particular case, we can safely use Data as the "empty" type, but
|
|
26
|
+
// with the single property turned optional.
|
|
27
|
+
{
|
|
28
|
+
[K in keyof Data]+?: Data[K]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// When setting the data must fully adhere to the Data type, or exactly an
|
|
32
|
+
// empty object.
|
|
33
|
+
export type SettablePossibleEmpty<Data> = Data | EmptyObject
|
|
13
34
|
|
|
14
35
|
export interface WhenOptions {
|
|
15
36
|
signal?: AbortSignal
|
|
@@ -24,7 +45,6 @@ export interface UpdateOptions {
|
|
|
24
45
|
}
|
|
25
46
|
|
|
26
47
|
export interface ObserveOptions {
|
|
27
|
-
key?: string
|
|
28
48
|
signal?: AbortSignal
|
|
29
49
|
timeout?: number
|
|
30
50
|
state?: number
|
|
@@ -47,15 +67,8 @@ export default class Record<Data = unknown> {
|
|
|
47
67
|
|
|
48
68
|
ref(): Record<Data>
|
|
49
69
|
unref(): Record<Data>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
callback: (record: Record<Data>, opaque: unknown) => void,
|
|
53
|
-
opaque?: unknown,
|
|
54
|
-
): Record<Data>
|
|
55
|
-
unsubscribe(
|
|
56
|
-
callback: (record: Record<Data>, opaque: unknown) => void,
|
|
57
|
-
opaque?: unknown,
|
|
58
|
-
): Record<Data>
|
|
70
|
+
subscribe(callback: (record: Record<Data>) => void, opaque?: unknown): Record<Data>
|
|
71
|
+
unsubscribe(callback: (record: Record<Data>) => void, opaque?: unknown): Record<Data>
|
|
59
72
|
|
|
60
73
|
get: {
|
|
61
74
|
// with path
|
|
@@ -72,7 +85,7 @@ export default class Record<Data = unknown> {
|
|
|
72
85
|
dataAtPath: unknown extends Get<Data, P> ? never : Get<Data, P>,
|
|
73
86
|
): void
|
|
74
87
|
// without path
|
|
75
|
-
(data: Data): void
|
|
88
|
+
(data: SettablePossibleEmpty<Data>): void
|
|
76
89
|
}
|
|
77
90
|
|
|
78
91
|
when: {
|
|
@@ -84,13 +97,13 @@ export default class Record<Data = unknown> {
|
|
|
84
97
|
update: {
|
|
85
98
|
// without path
|
|
86
99
|
(
|
|
87
|
-
updater: (data: Readonly<Data
|
|
100
|
+
updater: (data: Readonly<Data>) => SettablePossibleEmpty<Data>,
|
|
88
101
|
options?: UpdateOptions,
|
|
89
102
|
): Promise<void>
|
|
90
103
|
// with path
|
|
91
104
|
<P extends string | string[]>(
|
|
92
105
|
path: P,
|
|
93
|
-
updater: (dataAtPath: Readonly<Get<Data, P
|
|
106
|
+
updater: (dataAtPath: Readonly<Get<Data, P>>) => Get<Data, P>,
|
|
94
107
|
options?: UpdateOptions,
|
|
95
108
|
): Promise<void>
|
|
96
109
|
}
|
package/src/rpc/rpc-handler.d.ts
CHANGED
|
@@ -10,11 +10,8 @@ export default class RpcHandler<
|
|
|
10
10
|
|
|
11
11
|
provide: <Name extends keyof Methods>(
|
|
12
12
|
name: Name,
|
|
13
|
-
callback: (
|
|
14
|
-
|
|
15
|
-
response: RpcResponse<Methods[Name][1]>,
|
|
16
|
-
) => Methods[Name][1] | Promise<Methods[Name][1]> | void,
|
|
17
|
-
) => UnprovideFn | void
|
|
13
|
+
callback: (args: Methods[Name][0], response: RpcResponse<Methods[Name][1]>) => void,
|
|
14
|
+
) => UnprovideFn
|
|
18
15
|
|
|
19
16
|
unprovide: <Name extends keyof Methods>(name: Name) => void
|
|
20
17
|
|
|
@@ -25,7 +22,7 @@ export default class RpcHandler<
|
|
|
25
22
|
ReturnValue extends Name extends keyof Methods ? Methods[Name][1] : unknown,
|
|
26
23
|
>(
|
|
27
24
|
name: Name,
|
|
28
|
-
args
|
|
25
|
+
args: Args,
|
|
29
26
|
): Promise<ReturnValue>
|
|
30
27
|
<
|
|
31
28
|
Name extends keyof Methods | string,
|
|
@@ -33,7 +30,7 @@ export default class RpcHandler<
|
|
|
33
30
|
ReturnValue extends Name extends keyof Methods ? Methods[Name][1] : unknown,
|
|
34
31
|
>(
|
|
35
32
|
name: Name,
|
|
36
|
-
args: Args
|
|
33
|
+
args: Args,
|
|
37
34
|
callback: (error: unknown, response: ReturnValue) => void,
|
|
38
35
|
): void
|
|
39
36
|
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import make from '../client.js'
|
|
2
|
-
import { expectAssignable, expectError, expectType } from 'tsd'
|
|
3
|
-
|
|
4
|
-
interface Methods extends Record<string, [unknown, unknown]> {
|
|
5
|
-
greet: [{ name: string }, { message: string }]
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const ds = make<Record<string, unknown>, Methods>('')
|
|
9
|
-
|
|
10
|
-
// provide: callback may return void, a value, or a Promise — all valid
|
|
11
|
-
ds.rpc.provide('greet', (_args, _response) => {})
|
|
12
|
-
ds.rpc.provide('greet', (_args, _response) => ({ message: 'hello' }))
|
|
13
|
-
ds.rpc.provide('greet', async (_args, _response) => ({ message: 'hello' }))
|
|
14
|
-
// async callback that uses response.send() directly — returns Promise<void>
|
|
15
|
-
ds.rpc.provide('greet', async (_args, response) => {
|
|
16
|
-
response.send({ message: 'hello' })
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
// provide: returning the wrong shape is an error
|
|
20
|
-
expectError(ds.rpc.provide('greet', (_args, _response) => ({ notMessage: 'hello' })))
|
|
21
|
-
|
|
22
|
-
// provide: response.completed is boolean
|
|
23
|
-
ds.rpc.provide('greet', (_args, response) => {
|
|
24
|
-
expectType<boolean>(response.completed)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
// provide: return type is UnprovideFn | void
|
|
28
|
-
expectAssignable<(() => void) | void>(ds.rpc.provide('greet', () => {}))
|
|
29
|
-
|
|
30
|
-
// make: args is optional (no args)
|
|
31
|
-
expectAssignable<Promise<{ message: string }>>(ds.rpc.make('greet'))
|
|
32
|
-
// make: args provided
|
|
33
|
-
expectAssignable<Promise<{ message: string }>>(ds.rpc.make('greet', { name: 'world' }))
|
|
34
|
-
// make: args explicitly undefined
|
|
35
|
-
expectAssignable<Promise<{ message: string }>>(ds.rpc.make('greet', undefined))
|
|
36
|
-
// make: callback form — args required positionally but can be undefined
|
|
37
|
-
ds.rpc.make('greet', undefined, (err, res) => {
|
|
38
|
-
expectType<unknown>(err)
|
|
39
|
-
expectAssignable<{ message: string } | undefined>(res)
|
|
40
|
-
})
|
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import * as rxjs from 'rxjs'
|
|
2
|
-
import * as C from '../constants/constants.js'
|
|
3
|
-
import { h64ToString, findBigIntPaths } from '../utils/utils.js'
|
|
4
|
-
|
|
5
|
-
export default class Listener {
|
|
6
|
-
constructor(topic, pattern, callback, handler, { recursive = false, stringify = null } = {}) {
|
|
7
|
-
this._topic = topic
|
|
8
|
-
this._pattern = pattern
|
|
9
|
-
this._callback = callback
|
|
10
|
-
this._handler = handler
|
|
11
|
-
this._client = this._handler._client
|
|
12
|
-
this._connection = this._handler._connection
|
|
13
|
-
this._subscriptions = new Map()
|
|
14
|
-
this._recursive = recursive
|
|
15
|
-
this._stringify = stringify || JSON.stringify
|
|
16
|
-
|
|
17
|
-
this._$onConnectionStateChange()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
get connected() {
|
|
21
|
-
return this._connection.connected
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
get stats() {
|
|
25
|
-
return {
|
|
26
|
-
subscriptions: this._subscriptions.size,
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
_$destroy() {
|
|
31
|
-
this._reset()
|
|
32
|
-
|
|
33
|
-
if (this.connected) {
|
|
34
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.UNLISTEN, [this._pattern])
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
_$onMessage(message) {
|
|
39
|
-
if (!this.connected) {
|
|
40
|
-
this._client._$onError(
|
|
41
|
-
C.TOPIC.RECORD,
|
|
42
|
-
C.EVENT.NOT_CONNECTED,
|
|
43
|
-
new Error('received message while not connected'),
|
|
44
|
-
message,
|
|
45
|
-
)
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const name = message.data[1]
|
|
50
|
-
|
|
51
|
-
if (message.action === C.ACTIONS.SUBSCRIPTION_FOR_PATTERN_FOUND) {
|
|
52
|
-
if (this._subscriptions.has(name)) {
|
|
53
|
-
this._error(name, 'invalid add: listener exists')
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// TODO (refactor): Move to class
|
|
58
|
-
const provider = {
|
|
59
|
-
name,
|
|
60
|
-
value$: null,
|
|
61
|
-
sending: false,
|
|
62
|
-
accepted: false,
|
|
63
|
-
version: null,
|
|
64
|
-
timeout: null,
|
|
65
|
-
patternSubscription: null,
|
|
66
|
-
valueSubscription: null,
|
|
67
|
-
}
|
|
68
|
-
provider.stop = () => {
|
|
69
|
-
if (this.connected && provider.accepted) {
|
|
70
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [
|
|
71
|
-
this._pattern,
|
|
72
|
-
provider.name,
|
|
73
|
-
])
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
provider.value$ = null
|
|
77
|
-
provider.version = null
|
|
78
|
-
provider.accepted = false
|
|
79
|
-
provider.sending = false
|
|
80
|
-
|
|
81
|
-
clearTimeout(provider.timeout)
|
|
82
|
-
provider.timeout = null
|
|
83
|
-
|
|
84
|
-
provider.patternSubscription?.unsubscribe()
|
|
85
|
-
provider.patternSubscription = null
|
|
86
|
-
|
|
87
|
-
provider.valueSubscription?.unsubscribe()
|
|
88
|
-
provider.valueSubscription = null
|
|
89
|
-
}
|
|
90
|
-
provider.send = () => {
|
|
91
|
-
provider.sending = false
|
|
92
|
-
|
|
93
|
-
if (!provider.patternSubscription) {
|
|
94
|
-
return
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const accepted = Boolean(provider.value$)
|
|
98
|
-
if (provider.accepted === accepted) {
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
this._connection.sendMsg(
|
|
103
|
-
this._topic,
|
|
104
|
-
accepted ? C.ACTIONS.LISTEN_ACCEPT : C.ACTIONS.LISTEN_REJECT,
|
|
105
|
-
[this._pattern, provider.name],
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
provider.version = null
|
|
109
|
-
provider.accepted = accepted
|
|
110
|
-
}
|
|
111
|
-
provider.next = (value$) => {
|
|
112
|
-
if (!value$) {
|
|
113
|
-
value$ = null
|
|
114
|
-
} else if (typeof value$.subscribe !== 'function') {
|
|
115
|
-
value$ = rxjs.of(value$) // Compat for recursive with value
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (Boolean(provider.value$) !== Boolean(value$) && !provider.sending) {
|
|
119
|
-
provider.sending = true
|
|
120
|
-
queueMicrotask(provider.send)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
provider.value$ = value$
|
|
124
|
-
|
|
125
|
-
if (provider.valueSubscription) {
|
|
126
|
-
provider.valueSubscription.unsubscribe()
|
|
127
|
-
provider.valueSubscription = provider.value$?.subscribe(provider.observer)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
provider.error = (err) => {
|
|
131
|
-
provider.stop()
|
|
132
|
-
// TODO (feat): backoff retryCount * delay?
|
|
133
|
-
// TODO (feat): backoff option?
|
|
134
|
-
provider.timeout = setTimeout(() => {
|
|
135
|
-
provider.start()
|
|
136
|
-
}, 10e3)
|
|
137
|
-
this._error(provider.name, err)
|
|
138
|
-
}
|
|
139
|
-
provider.observer = {
|
|
140
|
-
next: (value) => {
|
|
141
|
-
if (value == null) {
|
|
142
|
-
provider.next(null) // TODO (fix): This is weird...
|
|
143
|
-
return
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (this._topic === C.TOPIC.EVENT) {
|
|
147
|
-
this._handler.emit(provider.name, value)
|
|
148
|
-
} else if (this._topic === C.TOPIC.RECORD) {
|
|
149
|
-
if (typeof value !== 'object' && typeof value !== 'string') {
|
|
150
|
-
this._error(provider.name, 'invalid value')
|
|
151
|
-
return
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (typeof value !== 'string') {
|
|
155
|
-
try {
|
|
156
|
-
value = this._stringify(value)
|
|
157
|
-
} catch (err) {
|
|
158
|
-
const bigIntPaths = /BigInt/.test(err.message) ? findBigIntPaths(value) : undefined
|
|
159
|
-
this._error(
|
|
160
|
-
Object.assign(new Error(`invalid value: ${value}`), {
|
|
161
|
-
cause: err,
|
|
162
|
-
data: { name: provider.name, bigIntPaths },
|
|
163
|
-
}),
|
|
164
|
-
)
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const body = value
|
|
170
|
-
const hash = h64ToString(body)
|
|
171
|
-
const version = `INF-${hash}`
|
|
172
|
-
|
|
173
|
-
if (provider.version !== version) {
|
|
174
|
-
provider.version = version
|
|
175
|
-
this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, [
|
|
176
|
-
provider.name,
|
|
177
|
-
version,
|
|
178
|
-
body,
|
|
179
|
-
])
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
error: provider.error,
|
|
184
|
-
}
|
|
185
|
-
provider.start = () => {
|
|
186
|
-
try {
|
|
187
|
-
const ret$ = this._callback(name)
|
|
188
|
-
if (this._recursive && typeof ret$?.subscribe === 'function') {
|
|
189
|
-
provider.patternSubscription = ret$.subscribe(provider)
|
|
190
|
-
} else {
|
|
191
|
-
provider.patternSubscription = rxjs.of(ret$).subscribe(provider)
|
|
192
|
-
}
|
|
193
|
-
} catch (err) {
|
|
194
|
-
this._error(provider.name, err)
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
provider.start()
|
|
199
|
-
|
|
200
|
-
this._subscriptions.set(provider.name, provider)
|
|
201
|
-
} else if (message.action === C.ACTIONS.LISTEN_ACCEPT) {
|
|
202
|
-
const provider = this._subscriptions.get(name)
|
|
203
|
-
if (!provider?.value$) {
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (provider.valueSubscription) {
|
|
208
|
-
this._error(
|
|
209
|
-
name,
|
|
210
|
-
'invalid accept: listener started (pattern:' + this._pattern + ' name:' + name + ')',
|
|
211
|
-
)
|
|
212
|
-
} else {
|
|
213
|
-
// TODO (fix): provider.version = message.data[2]
|
|
214
|
-
provider.valueSubscription = provider.value$.subscribe(provider.observer)
|
|
215
|
-
}
|
|
216
|
-
} else if (message.action === C.ACTIONS.SUBSCRIPTION_FOR_PATTERN_REMOVED) {
|
|
217
|
-
const provider = this._subscriptions.get(name)
|
|
218
|
-
|
|
219
|
-
if (!provider) {
|
|
220
|
-
this._error(
|
|
221
|
-
name,
|
|
222
|
-
'invalid remove: listener missing (pattern:' + this._pattern + ' name:' + name + ')',
|
|
223
|
-
)
|
|
224
|
-
} else {
|
|
225
|
-
provider.stop()
|
|
226
|
-
this._subscriptions.delete(provider.name)
|
|
227
|
-
}
|
|
228
|
-
} else {
|
|
229
|
-
return false
|
|
230
|
-
}
|
|
231
|
-
return true
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
_$onConnectionStateChange() {
|
|
235
|
-
if (this.connected) {
|
|
236
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN, [this._pattern])
|
|
237
|
-
} else {
|
|
238
|
-
this._reset()
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
_error(name, err) {
|
|
243
|
-
this._client._$onError(this._topic, C.EVENT.LISTENER_ERROR, err, [this._pattern, name])
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
_reset() {
|
|
247
|
-
for (const provider of this._subscriptions.values()) {
|
|
248
|
-
provider.stop()
|
|
249
|
-
}
|
|
250
|
-
this._subscriptions.clear()
|
|
251
|
-
}
|
|
252
|
-
}
|