@nxtedition/deepstream.io-client-js 32.0.21 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/deepstream.io-client-js",
3
- "version": "32.0.21",
3
+ "version": "32.0.22",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "type": "module",
package/src/client.d.ts CHANGED
@@ -3,25 +3,14 @@ import type { Paths, Get } from './record/record.js'
3
3
  import type RecordHandler from './record/record-handler.js'
4
4
  import type { RecordStats, ProvideOptions, SyncOptions } from './record/record-handler.js'
5
5
  import type EventHandler from './event/event-handler.js'
6
- import type { EventStats, EventProvideOptions } from './event/event-handler.js'
6
+ 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?: DeepstreamClientOptions): DeepstreamClient<Records, Methods>
13
+ >(url: string, options?: unknown): DeepstreamClient<Records, Methods>
25
14
 
26
15
  export type {
27
16
  DsRecord,
@@ -30,12 +19,9 @@ export type {
30
19
  RpcHandler,
31
20
  RpcMethodDef,
32
21
  ProvideOptions,
33
- EventProvideOptions,
34
22
  SyncOptions,
35
23
  Paths,
36
24
  Get,
37
- ConnectionStateName,
38
- DeepstreamErrorEventName,
39
25
  }
40
26
 
41
27
  type RecordStateConstants = Readonly<{
@@ -91,36 +77,6 @@ type EventConstants = Readonly<{
91
77
  }>
92
78
  type EventKey = keyof EventConstants
93
79
  type EventName = EventConstants[EventKey]
94
- type DeepstreamErrorEventName = Exclude<
95
- EventName,
96
- 'connectionStateChanged' | 'connected' | 'MAX_RECONNECTION_ATTEMPTS_REACHED'
97
- >
98
-
99
- export interface DeepstreamError extends Error {
100
- topic?: string
101
- event?: EventName | null
102
- data?: unknown
103
- }
104
-
105
- export interface DeepstreamMessage {
106
- raw: string | null
107
- topic: string | null
108
- action: string | null
109
- data: string[]
110
- }
111
-
112
- export interface DeepstreamClientEventMap {
113
- connectionStateChanged: (state: ConnectionStateName) => void
114
- connected: (connected: boolean) => void
115
- MAX_RECONNECTION_ATTEMPTS_REACHED: (attempt: number) => void
116
- error: (error: DeepstreamError) => void
117
- recv: (message: DeepstreamMessage) => void
118
- send: (message: DeepstreamMessage) => void
119
- }
120
-
121
- type DeepstreamErrorEventMap = {
122
- [K in DeepstreamErrorEventName]: (error: DeepstreamError) => void
123
- }
124
80
 
125
81
  export interface DeepstreamClient<
126
82
  Records extends Record<string, unknown> = Record<string, unknown>,
@@ -131,21 +87,11 @@ export interface DeepstreamClient<
131
87
  rpc: RpcHandler<Methods>
132
88
  record: RecordHandler<Records>
133
89
  user: string | null
134
- on<K extends keyof (DeepstreamClientEventMap & DeepstreamErrorEventMap)>(
135
- evt: K,
136
- callback: (DeepstreamClientEventMap & DeepstreamErrorEventMap)[K],
137
- ): this
138
- off<K extends keyof (DeepstreamClientEventMap & DeepstreamErrorEventMap)>(
139
- evt: K,
140
- callback: (DeepstreamClientEventMap & DeepstreamErrorEventMap)[K],
141
- ): this
90
+ on: (evt: EventName, callback: (...args: unknown[]) => void) => void
91
+ off: (evt: EventName, callback: (...args: unknown[]) => void) => void
142
92
  getConnectionState: () => ConnectionStateName
143
93
  close: () => void
144
- login(callback: (success: boolean, authData: unknown) => void): this
145
- login(
146
- authParams: Record<string, unknown>,
147
- callback: (success: boolean, authData: unknown) => void,
148
- ): this
94
+ login: unknown
149
95
  stats: {
150
96
  record: RecordStats
151
97
  rpc: RpcStats
@@ -1,8 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import make, { type DeepstreamClient, type DeepstreamError } from './client.js'
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('possiblyEmpty', {}) // empty should always work
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) => {}))
@@ -4,22 +4,13 @@ export default class EventHandler {
4
4
  connected: boolean
5
5
  stats: EventStats
6
6
  subscribe: (name: string, callback: (data: unknown) => void) => void
7
- unsubscribe: (name: string, callback?: (data: unknown) => void) => void
7
+ unsubscribe: (name: string, callback: (data: unknown) => void) => void
8
8
  on: (name: string, callback: (data: unknown) => void) => this
9
9
  once: (name: string, callback: (data: unknown) => void) => this
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: EventProvideOptions,
17
- ) => (() => void) | void
18
- }
19
-
20
- export interface EventProvideOptions {
21
- mode?: 'unicast' | (string & {})
22
- stringify?: ((input: unknown) => string) | null
13
+ provide: (pattern: string, callback: (name: string) => void, options: unknown) => () => void
23
14
  }
24
15
 
25
16
  export interface EventStats {
@@ -71,21 +71,21 @@ EventHandler.prototype.unsubscribe = function (name, callback) {
71
71
  }
72
72
  }
73
73
 
74
- EventHandler.prototype.on = function (name, callback) {
74
+ EventHandler.on = function (name, callback) {
75
75
  this.subscribe(name, callback)
76
76
  return this
77
77
  }
78
78
 
79
- EventHandler.prototype.once = function (name, callback) {
79
+ EventHandler.once = function (name, callback) {
80
80
  const fn = (...args) => {
81
- this.unsubscribe(name, fn)
81
+ this.unsubscribe(fn)
82
82
  callback(...args)
83
83
  }
84
84
  this.subscribe(name, fn)
85
85
  return this
86
86
  }
87
87
 
88
- EventHandler.prototype.off = function (name, callback) {
88
+ EventHandler.off = function (name, callback) {
89
89
  this.unsubscribe(name, callback)
90
90
  return this
91
91
  }
@@ -1,6 +1,12 @@
1
1
  import type { Observable } from 'rxjs'
2
2
  import type DsRecord from './record.js'
3
- import type { Get, UpdateOptions, ObserveOptions, ObserveOptionsWithPath } from './record.js'
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: Record<string, unknown>
30
- EMPTY_OBJ: Record<string, unknown>
35
+ EMPTY: EmptyObject
36
+ EMPTY_OBJ: EmptyObject
31
37
  EMPTY_ARR: []
32
38
  }
33
39
 
@@ -40,44 +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 | void
44
-
45
- put: (
46
- name: string,
47
- version: string,
48
- data: Record<string, unknown> | null,
49
- parent?: string,
50
- ) => void
51
-
52
- getAsync: {
53
- <Name extends string>(
54
- name: Name,
55
- options: ObserveOptions,
56
- ):
57
- | { value: Lookup<Records, Name>; async: false }
58
- | { value: Promise<Lookup<Records, Name>>; async: true }
59
-
60
- <Name extends string, Path extends string | string[]>(
61
- name: Name,
62
- path: Path,
63
- options?: ObserveOptions,
64
- ):
65
- | { value: Get<Lookup<Records, Name>, Path>; async: false }
66
- | { value: Promise<Get<Lookup<Records, Name>, Path>>; async: true }
67
-
68
- <Name extends string>(
69
- name: Name,
70
- state?: number,
71
- ):
72
- | { value: Lookup<Records, Name>; async: false }
73
- | { value: Promise<Lookup<Records, Name>>; async: true }
74
- }
49
+ ) => Disposer
75
50
 
76
51
  sync: (options?: SyncOptions) => Promise<void>
77
52
 
78
53
  set: {
79
54
  // without path:
80
- <Name extends string>(name: Name, data: Lookup<Records, Name>): void
55
+ <Name extends string>(name: Name, data: Lookup<Records, Name> | EmptyObject): void
81
56
 
82
57
  // with path:
83
58
  <Name extends string, Path extends string | string[]>(
@@ -92,17 +67,14 @@ export default class RecordHandler<Records = Record<string, unknown>> {
92
67
  update: {
93
68
  <Name extends string>(
94
69
  name: Name,
95
- updater: (data: Lookup<Records, Name>, version: string) => Lookup<Records, Name>,
70
+ updater: (data: Lookup<Records, Name>) => Lookup<Records, Name> | EmptyObject,
96
71
  options?: UpdateOptions,
97
72
  ): Promise<void>
98
73
 
99
74
  <Name extends string, Path extends string | string[]>(
100
75
  name: Name,
101
76
  path: Path,
102
- updater: (
103
- data: Get<Lookup<Records, Name>, Path>,
104
- version: string,
105
- ) => Get<Lookup<Records, Name>, Path>,
77
+ updater: (data: Get<Lookup<Records, Name>, Path>) => Get<Lookup<Records, Name>, Path>,
106
78
  options?: UpdateOptions,
107
79
  ): Promise<void>
108
80
  }
@@ -141,73 +113,6 @@ export default class RecordHandler<Records = Record<string, unknown>> {
141
113
  ): Observable<Get<Lookup<Records, Name>, Path>>
142
114
  }
143
115
 
144
- observe2: {
145
- <Name extends string>(
146
- name: Name,
147
- options: ObserveOptions,
148
- ): Observable<{
149
- name: string
150
- version: string
151
- state: number
152
- data: Lookup<Records, Name>
153
- }>
154
-
155
- <Name extends string, Path extends string | string[]>(
156
- name: Name,
157
- options: ObserveOptionsWithPath<Path>,
158
- ): Observable<{
159
- name: string
160
- version: string
161
- state: number
162
- data: Get<Lookup<Records, Name>, Path>
163
- }>
164
-
165
- <Name extends string>(
166
- name: Name,
167
- state?: number,
168
- options?: ObserveOptions,
169
- ): Observable<{
170
- name: string
171
- version: string
172
- state: number
173
- data: Lookup<Records, Name>
174
- }>
175
-
176
- <Name extends string, Path extends string | string[]>(
177
- name: Name,
178
- state?: number,
179
- options?: ObserveOptionsWithPath<Path>,
180
- ): Observable<{
181
- name: string
182
- version: string
183
- state: number
184
- data: Get<Lookup<Records, Name>, Path>
185
- }>
186
-
187
- <Name extends string, Path extends string | string[]>(
188
- name: Name,
189
- path: Path,
190
- options?: ObserveOptionsWithPath<Path>,
191
- ): Observable<{
192
- name: string
193
- version: string
194
- state: number
195
- data: Get<Lookup<Records, Name>, Path>
196
- }>
197
-
198
- <Name extends string, Path extends string | string[]>(
199
- name: Name,
200
- path: Path,
201
- state?: number,
202
- options?: ObserveOptionsWithPath<Path>,
203
- ): Observable<{
204
- name: string
205
- version: string
206
- state: number
207
- data: Get<Lookup<Records, Name>, Path>
208
- }>
209
- }
210
-
211
116
  get: {
212
117
  <Name extends string>(name: Name, options: ObserveOptions): Promise<Lookup<Records, Name>>
213
118
 
@@ -242,11 +147,11 @@ export default class RecordHandler<Records = Record<string, unknown>> {
242
147
  ): Promise<Get<Lookup<Records, Name>, Path>>
243
148
  }
244
149
 
245
- get2: {
150
+ observe2: {
246
151
  <Name extends string>(
247
152
  name: Name,
248
153
  options: ObserveOptions,
249
- ): Promise<{
154
+ ): Observable<{
250
155
  name: string
251
156
  version: string
252
157
  state: number
@@ -256,7 +161,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
256
161
  <Name extends string, Path extends string | string[]>(
257
162
  name: Name,
258
163
  options: ObserveOptionsWithPath<Path>,
259
- ): Promise<{
164
+ ): Observable<{
260
165
  name: string
261
166
  version: string
262
167
  state: number
@@ -267,7 +172,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
267
172
  name: Name,
268
173
  state?: number,
269
174
  options?: ObserveOptions,
270
- ): Promise<{
175
+ ): Observable<{
271
176
  name: string
272
177
  version: string
273
178
  state: number
@@ -278,7 +183,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
278
183
  name: Name,
279
184
  state?: number,
280
185
  options?: ObserveOptionsWithPath<Path>,
281
- ): Promise<{
186
+ ): Observable<{
282
187
  name: string
283
188
  version: string
284
189
  state: number
@@ -289,7 +194,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
289
194
  name: Name,
290
195
  path: Path,
291
196
  options?: ObserveOptionsWithPath<Path>,
292
- ): Promise<{
197
+ ): Observable<{
293
198
  name: string
294
199
  version: string
295
200
  state: number
@@ -301,7 +206,7 @@ export default class RecordHandler<Records = Record<string, unknown>> {
301
206
  path: Path,
302
207
  state?: number,
303
208
  options?: ObserveOptionsWithPath<Path>,
304
- ): Promise<{
209
+ ): Observable<{
305
210
  name: string
306
211
  version: string
307
212
  state: number
@@ -318,13 +223,12 @@ export interface RecordStats {
318
223
  pruning: number
319
224
  patching: number
320
225
  subscriptions: number
321
- listeners: number
322
226
  }
323
227
 
324
228
  export interface ProvideOptions {
325
229
  recursive?: boolean
326
230
  stringify?: ((input: unknown) => string) | null
327
- mode?: null | 'unicast' | (string & {})
231
+ mode: undefined | null | 'unicast'
328
232
  }
329
233
 
330
234
  export interface SyncOptions {
@@ -1,15 +1,36 @@
1
1
  import type RecordHandler from './record-handler.js'
2
- import type { Get as _Get, AllUnionFields } from 'type-fest'
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
- // HACK: Wrap type-fest's Get to get rid of EmptyObject from union
6
- type RemoveSymbolKeys<T> = {
7
- [K in keyof T as K extends symbol ? never : K]: T[K]
8
- }
9
- export type Get<BaseType, Path extends string | readonly string[]> = _Get<
10
- RemoveSymbolKeys<AllUnionFields<BaseType>>,
11
- Path
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,21 +67,12 @@ export default class Record<Data = unknown> {
47
67
 
48
68
  ref(): Record<Data>
49
69
  unref(): Record<Data>
50
- [Symbol.dispose](): void
51
- subscribe(
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
62
75
  <P extends string | string[]>(path: P): Get<Data, P>
63
- // with function mapper
64
- <R>(fn: (data: Data) => R): R
65
76
  // without path
66
77
  (): Data
67
78
  (path: undefined | string | string[]): unknown
@@ -74,7 +85,7 @@ export default class Record<Data = unknown> {
74
85
  dataAtPath: unknown extends Get<Data, P> ? never : Get<Data, P>,
75
86
  ): void
76
87
  // without path
77
- (data: Data): void
88
+ (data: SettablePossibleEmpty<Data>): void
78
89
  }
79
90
 
80
91
  when: {
@@ -86,13 +97,13 @@ export default class Record<Data = unknown> {
86
97
  update: {
87
98
  // without path
88
99
  (
89
- updater: (data: Readonly<Data>, version: string) => Data,
100
+ updater: (data: Readonly<Data>) => SettablePossibleEmpty<Data>,
90
101
  options?: UpdateOptions,
91
102
  ): Promise<void>
92
103
  // with path
93
104
  <P extends string | string[]>(
94
105
  path: P,
95
- updater: (dataAtPath: Readonly<Get<Data, P>>, version: string) => Get<Data, P>,
106
+ updater: (dataAtPath: Readonly<Get<Data, P>>) => Get<Data, P>,
96
107
  options?: UpdateOptions,
97
108
  ): Promise<void>
98
109
  }
@@ -10,11 +10,8 @@ export default class RpcHandler<
10
10
 
11
11
  provide: <Name extends keyof Methods>(
12
12
  name: Name,
13
- callback: (
14
- args: Methods[Name][0],
15
- response: RpcResponse<Methods[Name][1]>,
16
- ) => Methods[Name][1] | Promise<Methods[Name][1]> | Promise<void> | 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?: 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 | undefined,
33
+ args: Args,
37
34
  callback: (error: unknown, response: ReturnValue) => void,
38
35
  ): void
39
36
  }
@@ -1,5 +1,4 @@
1
1
  export default class RpcResponse<Data> {
2
- completed: boolean
3
2
  reject: () => void
4
3
  error: (error: Error | string) => void
5
4
  send: (data: Data) => void
@@ -83,6 +83,14 @@ export function setTimeout(callback, timeoutDuration) {
83
83
  }
84
84
  }
85
85
 
86
+ export function setInterval(callback, intervalDuration) {
87
+ if (intervalDuration !== null) {
88
+ return setInterval(callback, intervalDuration)
89
+ } else {
90
+ return -1
91
+ }
92
+ }
93
+
86
94
  export function compareRev(a, b) {
87
95
  if (!a) {
88
96
  return b ? -1 : 0
@@ -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
- }