@nxtedition/deepstream.io-client-js 28.1.22 → 28.1.24

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm run test:types:*)"
5
+ ],
6
+ "deny": [],
7
+ "ask": []
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/deepstream.io-client-js",
3
- "version": "28.1.22",
3
+ "version": "28.1.24",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "type": "module",
@@ -21,7 +21,8 @@
21
21
  "scripts": {
22
22
  "prepublishOnly": "pinst --disable",
23
23
  "postpublish": "pinst --enable",
24
- "prepare": "husky"
24
+ "prepare": "husky",
25
+ "test:types": "tsd"
25
26
  },
26
27
  "lint-staged": {
27
28
  "*.{js,jsx,md,ts}": [
@@ -60,6 +61,7 @@
60
61
  "pinst": "^3.0.0",
61
62
  "prettier": "^3.3.3",
62
63
  "rxjs": "^7.8.1",
64
+ "tsd": "^0.33.0",
63
65
  "type-fest": "^4.33.0",
64
66
  "typescript": "^5.6.3",
65
67
  "typescript-eslint": "^8.12.2"
package/src/client.d.ts CHANGED
@@ -1,14 +1,28 @@
1
- import type Record from './record/record.js'
2
- import type RecordHandler, { RecordStats } from './record/record-handler.js'
3
- import type EventHandler, { EventStats } from './event/event-handler.js'
4
- import type RpcHandler, { RpcStats, RpcMethodDef } from './rpc/rpc-handler.js'
1
+ import type DsRecord from './record/record.js'
2
+ import type { Paths, Get } from './record/record.js'
3
+ import type RecordHandler from './record/record-handler.js'
4
+ import type { RecordStats, ProvideOptions, SyncOptions } from './record/record-handler.js'
5
+ import type EventHandler from './event/event-handler.js'
6
+ import type { EventStats } from './event/event-handler.js'
7
+ import type RpcHandler from './rpc/rpc-handler.js'
8
+ import type { RpcStats, RpcMethodDef } from './rpc/rpc-handler.js'
5
9
 
6
- export default function <Records, Methods>(
7
- url: string,
8
- options?: unknown,
9
- ): DeepstreamClient<Records, Methods>
10
+ export default function <
11
+ Records extends Record<string, unknown> = Record<string, unknown>,
12
+ Methods extends Record<string, RpcMethodDef> = Record<string, RpcMethodDef>,
13
+ >(url: string, options?: unknown): DeepstreamClient<Records, Methods>
10
14
 
11
- export type { Record, RecordHandler, EventHandler, RpcHandler }
15
+ export type {
16
+ DsRecord,
17
+ RecordHandler,
18
+ EventHandler,
19
+ RpcHandler,
20
+ RpcMethodDef,
21
+ ProvideOptions,
22
+ SyncOptions,
23
+ Paths,
24
+ Get,
25
+ }
12
26
 
13
27
  type RecordStateConstants = Readonly<{
14
28
  VOID: 0
@@ -28,8 +42,8 @@ type ConnectionStateConstants = Readonly<{
28
42
  ERROR: 'ERROR'
29
43
  RECONNECTING: 'RECONNECTING'
30
44
  }>
31
- type ConnectionStateKey = keyof typeof ConnectionStateConstants
32
- type ConnectionStateName = (typeof ConnectionStateConstants)[ConnectionStateKey]
45
+ type ConnectionStateKey = keyof ConnectionStateConstants
46
+ type ConnectionStateName = ConnectionStateConstants[ConnectionStateKey]
33
47
 
34
48
  type EventConstants = Readonly<{
35
49
  CONNECTION_ERROR: 'connectionError'
@@ -61,12 +75,12 @@ type EventConstants = Readonly<{
61
75
  RECORD_NOT_FOUND: 'RECORD_NOT_FOUND'
62
76
  NOT_SUBSCRIBED: 'NOT_SUBSCRIBED'
63
77
  }>
64
- type EventKey = keyof typeof EventConstants
65
- type EventName = (typeof EventConstants)[EventKey]
78
+ type EventKey = keyof EventConstants
79
+ type EventName = EventConstants[EventKey]
66
80
 
67
81
  export interface DeepstreamClient<
68
- Records = Record<string, unknown>,
69
- Methods = Record<string, RpcMethodDef>,
82
+ Records extends Record<string, unknown> = Record<string, unknown>,
83
+ Methods extends Record<string, RpcMethodDef> = Record<string, RpcMethodDef>,
70
84
  > {
71
85
  nuid: () => string
72
86
  event: EventHandler
@@ -92,13 +106,3 @@ export interface DeepstreamClient<
92
106
  EVENT: EventConstants
93
107
  }
94
108
  }
95
-
96
- export interface ProvideOptions {
97
- recursive?: boolean
98
- stringify?: ((input: unknown) => string) | null
99
- }
100
-
101
- export interface SyncOptions {
102
- signal?: AbortSignal
103
- timeout?: number
104
- }
@@ -0,0 +1,105 @@
1
+ import make from './client.js'
2
+ import { expectAssignable, expectError } from 'tsd'
3
+
4
+ interface Records extends Record<string, unknown> {
5
+ o: {
6
+ o0?: {
7
+ o1?: {
8
+ o2?: {
9
+ o3?: string
10
+ }
11
+ }
12
+ }
13
+ }
14
+ n: {
15
+ n0: {
16
+ n1: {
17
+ n2: {
18
+ n3: string
19
+ }
20
+ }
21
+ }
22
+ }
23
+ c: Circular
24
+ m: {
25
+ m1: string
26
+ m2: string
27
+ m3?: string
28
+ }
29
+ p: {
30
+ p1: string
31
+ p2?: string
32
+ p3: { p4: string }
33
+ }
34
+ [x: `${string}:domain`]: {
35
+ d1: string
36
+ d2: {
37
+ d3: string
38
+ }
39
+ }
40
+ }
41
+
42
+ interface Circular {
43
+ a: {
44
+ b0: Circular
45
+ b1: string
46
+ }
47
+ }
48
+
49
+ const ds = make<Records>('')
50
+
51
+ expectAssignable<{ n0?: { n1: { n2: { n3: string } } } } | undefined>(await ds.record.get('n'))
52
+ expectAssignable<{ n1: { n2: { n3: string } } } | undefined>(await ds.record.get('n', 'n0'))
53
+
54
+ // set withouth path
55
+ ds.record.set('n', {}) // empty should always work
56
+ ds.record.set('n', { n0: { n1: { n2: { n3: 'test' } } } })
57
+ expectError(ds.record.set('n', { n0: {} })) // nested props are required
58
+
59
+ // set with path
60
+ ds.record.set('n', 'n0.n1', { n2: { n3: 'test' } })
61
+ ds.record.set('n', 'n0', { n1: { n2: { n3: 'test' } } })
62
+ ds.record.set('n', 'n0.n1', { n2: { n3: 'test' } })
63
+ ds.record.set('n', 'n0.n1.n2', { n3: 'test' })
64
+ ds.record.set('n', 'n0.n1.n2.n3', 'test')
65
+ ds.record.set('o', 'o0.o1.o2.o3', 'test')
66
+ ds.record.set('o', 'o0', {})
67
+ ds.record.set('o', 'o0.o1', {})
68
+ ds.record.set('o', 'o0.o1.o2', {})
69
+ ds.record.set('o', 'o0.o1', { o2: {} })
70
+ ds.record.set('o', 'o0.o1', { o2: { o3: 'test' } })
71
+ ds.record.set('c', 'a.b1', 'test')
72
+ ds.record.set('x:domain', 'd1', 'test')
73
+ const id = 'id'
74
+ ds.record.set(`${id}:domain`, 'd2.d3', 'test')
75
+
76
+ expectAssignable<string>(await ds.record.get(`${id}:domain`, 'd2.d3'))
77
+
78
+ // errors
79
+ expectError(ds.record.set('o', 'o0.o1', { o2: { o3: 0 } }))
80
+ expectError(ds.record.set('o', 'o0.o1', { o3: 0 }))
81
+ expectError(ds.record.set('n', 'x1', {}))
82
+ expectError(ds.record.set('n', 'n0.x2', 22))
83
+ expectError(ds.record.set('n', 'n1.x2', {}))
84
+ expectError(ds.record.set('n', 'n1.n2.n3', { n4: 22 }))
85
+
86
+ expectAssignable<string>(await ds.record.get('p', 'p1'))
87
+ expectAssignable<string | undefined>(await ds.record.get('p', 'p2'))
88
+ expectAssignable<unknown>(await ds.record.get('p', 'x1'))
89
+
90
+ // Circular
91
+ expectAssignable<string | undefined>(await ds.record.get('c', 'a.b1'))
92
+
93
+ // ============
94
+ //
95
+
96
+ // getRecord
97
+ const daRec = ds.record.getRecord('o')
98
+ daRec.set({ o0: {} })
99
+
100
+ daRec.update('o0', (x) => ({ ...x, o1: {} }))
101
+ expectError(daRec.update((x) => 'x'))
102
+ expectError(daRec.update('o0', (x) => ({ ...x, o1: '22' })))
103
+
104
+ ds.record.set('foo', { num: [22, true] })
105
+ ds.record.set('foo', { num: ['22'] })
@@ -1,10 +1,10 @@
1
1
  import type { Observable } from 'rxjs'
2
- import type Record, { EmptyObject, GettablePossibleEmpty, SettablePossibleEmpty } from './record.js'
2
+ import type DsRecord from './record.js'
3
+ import type { EmptyObject, Get, Paths } from './record.js'
3
4
 
4
- type Paths<T> = keyof T
5
- type Get<Data, Path extends string> = Path extends keyof Data ? Data[Path] : unknown
6
-
7
- export default class RecordHandler<Records> {
5
+ export default class RecordHandler<
6
+ Lookup extends Record<string, unknown> = Record<string, unknown>,
7
+ > {
8
8
  VOID: 0
9
9
  CLIENT: 1
10
10
  SERVER: 2
@@ -20,13 +20,13 @@ export default class RecordHandler<Records> {
20
20
  connected: boolean
21
21
  stats: RecordStats
22
22
 
23
- getRecord: <Name extends keyof Records, Data extends Records[Name] = Records[Name]>(
23
+ getRecord<Name extends string, Data = Name extends keyof Lookup ? Lookup[Name] : unknown>(
24
24
  name: Name,
25
- ) => Record<Data>
25
+ ): DsRecord<Data>
26
26
 
27
- provide: <Data>(
27
+ provide: (
28
28
  pattern: string,
29
- callback: (key: string) => Data,
29
+ callback: (key: string) => unknown,
30
30
  optionsOrRecursive?: ProvideOptions | boolean,
31
31
  ) => void | (() => void)
32
32
 
@@ -34,115 +34,107 @@ export default class RecordHandler<Records> {
34
34
 
35
35
  set: {
36
36
  // without path:
37
- <Name extends keyof Records>(name: Name, data: SettablePossibleEmpty<Records[Name]>): void
37
+ <Name extends string>(name: Name, data: Lookup[Name] | EmptyObject): void
38
38
 
39
39
  // with path:
40
- <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data>>(
40
+ <Name extends string, Path extends string | string[]>(
41
41
  name: Name,
42
42
  path: Path,
43
- data: Get<Data, Path>,
43
+ data: Path extends Paths<Lookup[Name]> ? Get<Lookup[Name], Path> : never,
44
44
  ): void
45
45
  }
46
46
 
47
47
  update: {
48
48
  // without path:
49
- <Name extends keyof Records, Data extends Records[Name]>(
49
+ <Name extends string>(
50
50
  name: Name,
51
- updater: (data: Readonly<GettablePossibleEmpty<Data>>) => SettablePossibleEmpty<Data>,
51
+ updater: (data: Lookup[Name]) => Lookup[Name] | EmptyObject,
52
52
  ): Promise<void>
53
53
 
54
54
  // with path:
55
- <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data>>(
55
+ <Name extends string, Path extends string | string[]>(
56
56
  name: Name,
57
57
  path: Path,
58
- updater: (data: Readonly<Get<Data, Path>> | undefined) => Get<Data, Path>,
58
+ updater: (data: Get<Lookup[Name], Path>) => Get<Lookup[Name], Path>,
59
59
  ): Promise<void>
60
60
  }
61
61
 
62
62
  observe: {
63
63
  // without path:
64
- <Name extends keyof Records, Data extends Records[Name]>(
65
- name: Name,
66
- ): Observable<GettablePossibleEmpty<Data>>
64
+ <Name extends string>(name: Name): Observable<Lookup[Name]>
67
65
 
68
66
  // with path:
69
- <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data> & string>(
67
+ <Name extends string, Path extends string | string[]>(
70
68
  name: Name,
71
69
  path: Path,
72
- ): Observable<Get<Data, Path> | undefined>
70
+ ): Observable<Get<Lookup[Name], Path>>
73
71
 
74
72
  // with state:
75
- <Name extends keyof Records, Data extends Records[Name]>(
76
- name: Name,
77
- state: number,
78
- ): Observable<GettablePossibleEmpty<Data>>
73
+ <Name extends string>(name: Name, state: number): Observable<Lookup[Name]>
79
74
 
80
75
  // with path and state:
81
- <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data> & string>(
76
+ <Name extends string, Path extends string | string[]>(
82
77
  name: Name,
83
78
  path: Path,
84
79
  state: number,
85
- ): Observable<Get<Data, Path> | undefined>
80
+ ): Observable<Get<Lookup[Name], Path>>
86
81
  }
87
82
 
88
83
  get: {
89
84
  // without path:
90
- <Name extends keyof Records, Data extends Records[Name]>(
91
- name: Name,
92
- state?: number,
93
- ): Promise<GettablePossibleEmpty<Data>>
85
+ <Name extends string>(name: Name, state?: number): Promise<Lookup[Name]>
94
86
 
95
87
  // with path:
96
- <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data> & string>(
88
+ <Name extends string, Path extends string | string[]>(
97
89
  name: Name,
98
- path?: Path,
90
+ path: Path,
99
91
  state?: number,
100
- ): Promise<Get<Data, Path> | undefined>
92
+ ): Promise<Get<Lookup[Name], Path>>
101
93
  }
102
94
 
103
95
  observe2: {
104
96
  // without path:
105
- <Name extends keyof Records, Data extends Records[Name]>(
97
+ <Name extends string>(
106
98
  name: Name,
107
99
  ): Observable<{
108
- name: Name
100
+ name: string
109
101
  version: string
110
102
  state: number
111
- data: GettablePossibleEmpty<Data>
103
+ data: Lookup[Name]
112
104
  }>
113
105
 
114
106
  // with path:
115
- <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data> & string>(
107
+ <Name extends string, Path extends string | string[]>(
116
108
  name: Name,
117
109
  path: Path,
118
110
  ): Observable<{
119
- name: Name
111
+ name: string
120
112
  version: string
121
113
  state: number
122
- data: Get<Data, Path> | undefined
114
+ data: Get<Lookup[Name], Path>
123
115
  }>
124
116
 
125
117
  // with state:
126
- <Name extends keyof Records, Data extends Records[Name]>(
118
+ <Name extends string>(
127
119
  name: Name,
128
120
  state: number,
129
121
  ): Observable<{
130
- name: Name
122
+ name: string
131
123
  version: string
132
124
  state: number
133
- data: GettablePossibleEmpty<Data>
125
+ data: Lookup[Name]
134
126
  }>
135
127
 
136
128
  // with path and state:
137
- <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data> & string>(
129
+ <Name extends string, Path extends string | string[]>(
138
130
  name: Name,
139
131
  path: Path,
140
132
  state: number,
141
133
  ): Observable<{
142
- name: Name
134
+ name: string
143
135
  version: string
144
136
  state: number
145
- data: Get<Data, Path> | undefined
137
+ data: Get<Lookup[Name], Path>
146
138
  }>
147
139
  }
148
140
  }
@@ -156,3 +148,13 @@ export interface RecordStats {
156
148
  patching: number
157
149
  subscriptions: number
158
150
  }
151
+
152
+ export interface ProvideOptions {
153
+ recursive?: boolean
154
+ stringify?: ((input: unknown) => string) | null
155
+ }
156
+
157
+ export interface SyncOptions {
158
+ signal?: AbortSignal
159
+ timeout?: number
160
+ }
@@ -163,13 +163,16 @@ class RecordHandler {
163
163
  }
164
164
 
165
165
  _onUpdating(rec, value) {
166
+ const callbacks = this._updating.get(rec)
167
+
166
168
  if (value) {
169
+ invariant(!callbacks, 'updating callbacks must not exist')
167
170
  this._stats.updating += 1
168
171
  this._updating.set(rec, [])
169
172
  } else {
170
- this._stats.updating -= 1
173
+ invariant(callbacks, 'updating callbacks must exist')
171
174
 
172
- const callbacks = this._updating.get(rec)
175
+ this._stats.updating -= 1
173
176
  this._updating.delete(rec)
174
177
  for (const callback of callbacks) {
175
178
  callback()
@@ -273,63 +276,31 @@ class RecordHandler {
273
276
  // TODO (perf): Slow implementation...
274
277
 
275
278
  const signal = opts?.signal
276
- const timeout = opts?.timeout ?? 2 * 60e3
279
+ const timeout = opts?.timeout
277
280
 
278
- const disposers = []
281
+ let disposers
279
282
  try {
280
283
  const signalPromise = signal
281
284
  ? new Promise((resolve, reject) => {
282
285
  const onAbort = () => reject(signal.reason ?? new utils.AbortError())
283
286
  signal.addEventListener('abort', onAbort)
287
+ disposers ??= []
284
288
  disposers.push(() => signal.removeEventListener('abort', onAbort))
285
289
  })
286
290
  : null
287
291
 
288
292
  signalPromise?.catch(noop)
289
293
 
290
- if (this._putting.size) {
291
- const promises = []
292
-
293
- {
294
- const puttingPromises = []
295
- for (const callbacks of this._putting.values()) {
296
- puttingPromises.push(new Promise((resolve) => callbacks.push(resolve)))
297
- }
298
- promises.push(puttingPromises)
299
- }
300
-
301
- if (timeout) {
302
- promises.push(
303
- new Promise((resolve) => {
304
- const patchingTimeout = timers.setTimeout(() => {
305
- this._client._$onError(
306
- C.TOPIC.RECORD,
307
- C.EVENT.TIMEOUT,
308
- new Error('sync putting timeout'),
309
- )
310
- resolve(null)
311
- }, timeout)
312
- disposers.push(() => timers.clearTimeout(patchingTimeout))
313
- }),
314
- )
315
- }
316
-
317
- if (signalPromise) {
318
- promises.push(signalPromise)
319
- }
320
-
321
- await Promise.race(promises)
322
- }
323
-
324
294
  if (this._patching.size) {
325
- const promises = []
295
+ let promises
326
296
 
327
297
  {
328
298
  const patchingPromises = []
329
299
  for (const callbacks of this._patching.values()) {
330
300
  patchingPromises.push(new Promise((resolve) => callbacks.push(resolve)))
331
301
  }
332
- promises.push(patchingPromises)
302
+ promises ??= []
303
+ promises.push(Promise.all(patchingPromises))
333
304
  }
334
305
 
335
306
  if (timeout) {
@@ -343,30 +314,36 @@ class RecordHandler {
343
314
  )
344
315
  resolve(null)
345
316
  }, timeout)
317
+ disposers ??= []
346
318
  disposers.push(() => timers.clearTimeout(patchingTimeout))
347
319
  }),
348
320
  )
349
321
  }
350
322
 
351
323
  if (signalPromise) {
324
+ promises ??= []
352
325
  promises.push(signalPromise)
353
326
  }
354
327
 
355
- await Promise.race(promises)
328
+ if (promises) {
329
+ await Promise.race(promises)
330
+ }
356
331
  }
357
332
 
358
333
  if (this._updating.size) {
359
- const promises = []
334
+ let promises
360
335
 
361
336
  {
362
337
  const updatingPromises = []
363
338
  for (const callbacks of this._updating.values()) {
364
339
  updatingPromises.push(new Promise((resolve) => callbacks.push(resolve)))
365
340
  }
366
- promises.push(updatingPromises)
341
+ promises ??= []
342
+ promises.push(Promise.all(updatingPromises))
367
343
  }
368
344
 
369
345
  if (timeout) {
346
+ promises ??= []
370
347
  promises.push(
371
348
  new Promise((resolve) => {
372
349
  const updatingTimeout = timers.setTimeout(() => {
@@ -377,50 +354,57 @@ class RecordHandler {
377
354
  )
378
355
  resolve(null)
379
356
  }, timeout)
357
+ disposers ??= []
380
358
  disposers.push(() => timers.clearTimeout(updatingTimeout))
381
359
  }),
382
360
  )
383
361
  }
384
362
 
385
- await Promise.race(promises)
363
+ if (promises) {
364
+ await Promise.race(promises)
365
+ }
386
366
  }
387
367
 
388
368
  {
389
- const promises = []
369
+ const syncPromise = new Promise((resolve) => this._sync(resolve))
390
370
 
391
- promises.push(
392
- new Promise((resolve) => {
393
- const token = xuid()
394
- this._syncEmitter.once(token, resolve)
395
- this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SYNC, [token])
396
- }),
397
- )
371
+ let promises
398
372
 
399
373
  if (timeout) {
374
+ promises ??= []
400
375
  promises.push(
401
376
  new Promise((resolve, reject) => {
402
377
  const serverTimeout = timers.setTimeout(() => {
403
378
  reject(new Error('sync server timeout'))
404
379
  }, timeout)
380
+ disposers ??= []
405
381
  disposers.push(() => timers.clearTimeout(serverTimeout))
406
382
  }),
407
383
  )
408
384
  }
409
385
 
410
386
  if (signalPromise) {
387
+ promises ??= []
411
388
  promises.push(signalPromise)
412
389
  }
413
390
 
414
- await Promise.race(promises)
391
+ if (promises) {
392
+ promises.push(syncPromise)
393
+ await Promise.race(promises)
394
+ } else {
395
+ await syncPromise
396
+ }
415
397
  }
416
398
  } finally {
417
- for (const disposer of disposers) {
418
- disposer()
399
+ if (disposers) {
400
+ for (const disposer of disposers) {
401
+ disposer()
402
+ }
419
403
  }
420
404
  }
421
405
  }
422
406
 
423
- _sync(callback, opaque) {
407
+ _sync(callback, type, opaque) {
424
408
  this._syncQueue.push(callback, opaque)
425
409
 
426
410
  if (this._syncQueue.length > 2) {
@@ -437,7 +421,7 @@ class RecordHandler {
437
421
  queue[n](queue[n + 1])
438
422
  }
439
423
  })
440
- this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SYNC, [token, 'WEAK'])
424
+ this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SYNC, type ? [token, type] : [token])
441
425
  }, 1)
442
426
  }
443
427
 
@@ -476,9 +460,7 @@ class RecordHandler {
476
460
  this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.PUT, update)
477
461
 
478
462
  this._putting.set(update, [])
479
- this._sync((update) => {
480
- this._putting.delete(update)
481
- }, update)
463
+ this._sync((update) => this._putting.delete(update), 'WEAK', update)
482
464
  }
483
465
 
484
466
  /**
@@ -545,7 +527,7 @@ class RecordHandler {
545
527
  rec.subscribe(onUpdateFast, opaque)
546
528
 
547
529
  if (!opaque.synced) {
548
- this._sync(onSyncFast, opaque)
530
+ this._sync(onSyncFast, 'WEAK', opaque)
549
531
  }
550
532
  }
551
533
  })
@@ -736,13 +718,14 @@ class RecordHandler {
736
718
 
737
719
  if (connected) {
738
720
  this._connected = Date.now()
739
- for (const token of this._syncEmitter.eventNames()) {
740
- this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SYNC, [token])
741
- }
742
721
 
743
722
  for (const update of this._putting.keys()) {
744
723
  this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.PUT, update)
745
724
  }
725
+
726
+ for (const token of this._syncEmitter.eventNames()) {
727
+ this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SYNC, [token])
728
+ }
746
729
  } else {
747
730
  this._connected = 0
748
731
  }
@@ -1,10 +1,6 @@
1
1
  import type RecordHandler from './record-handler.js'
2
- import type { EmptyObject, SingleKeyObject } from 'type-fest'
3
-
4
- type Paths<T> = keyof T
5
- type Get<Data, Path extends string> = Path extends keyof Data ? Data[Path] : unknown
6
-
7
- export type { EmptyObject } from 'type-fest'
2
+ import type { Get, EmptyObject, SingleKeyObject } from 'type-fest'
3
+ export type { Get, Paths, EmptyObject } from 'type-fest'
8
4
 
9
5
  // When getting, for convenience, we say the data might be partial under some
10
6
  // circumstances.
@@ -45,12 +41,12 @@ export interface UpdateOptions {
45
41
  signal?: AbortSignal
46
42
  }
47
43
 
48
- export default class Record<Data> {
44
+ export default class Record<Data = unknown> {
49
45
  constructor(name: string, handler: RecordHandler)
50
46
 
51
47
  readonly name: string
52
48
  readonly version: string
53
- readonly data: GettablePossibleEmpty<Data>
49
+ readonly data: Data
54
50
  readonly state: number
55
51
  readonly refs: number
56
52
 
@@ -61,29 +57,16 @@ export default class Record<Data> {
61
57
 
62
58
  get: {
63
59
  // with path
64
- <Path extends Paths<Data>, DataAtPath extends Get<Data, Path> = Get<Data, Path>>(
65
- path: Path,
66
- ): DataAtPath | undefined
60
+ <P extends string | string[]>(path: P): Get<Data, P>
67
61
  // without path
68
- (): GettablePossibleEmpty<Data>
69
- // implementation
70
- <Path extends Paths<Data>, DataAtPath extends Get<Data, Path> = Get<Data, Path>>(
71
- path?: Path,
72
- ): Path extends undefined ? GettablePossibleEmpty<Data> : DataAtPath | undefined
62
+ (): Data
73
63
  }
74
64
 
75
65
  set: {
76
66
  // with path
77
- <Path extends Paths<Data>, DataAtPath extends Get<Data, Path>>(
78
- path: Path,
79
- dataAtPath: DataAtPath,
80
- ): void
67
+ <P extends string | string[]>(path: P, dataAtPath: Get<Data, P>): void
81
68
  // without path
82
69
  (data: SettablePossibleEmpty<Data>): void
83
- // implementation
84
- <Path extends Paths<Data>, DataAtPath extends Get<Data, Path>>(
85
- ...args: [pathOrData: Path | SettablePossibleEmpty<Data>, value?: DataAtPath]
86
- ): void
87
70
  }
88
71
 
89
72
  when: {
@@ -93,18 +76,17 @@ export default class Record<Data> {
93
76
  (state: number, options: WhenOptions): Promise<Record<Data>>
94
77
  }
95
78
 
96
- update<
97
- Path extends Paths<Data>,
98
- PathOrUpdater extends
99
- | Path
100
- | ((data: Readonly<GettablePossibleEmpty<Data>>) => SettablePossibleEmpty<Data>),
101
- >(
102
- ...args: PathOrUpdater extends Path
103
- ? [
104
- path: Path,
105
- updater: (dataAtPath: Readonly<Get<Data, Path>> | undefined) => Get<Data, Path>,
106
- options?: UpdateOptions,
107
- ]
108
- : [updater: PathOrUpdater, options?: UpdateOptions]
109
- ): Promise<void>
79
+ update: {
80
+ // without path
81
+ (
82
+ updater: (data: Readonly<Data>) => SettablePossibleEmpty<Data>,
83
+ options?: UpdateOptions,
84
+ ): Promise<void>
85
+ // with path
86
+ <P extends string | string[]>(
87
+ path: P,
88
+ updater: (dataAtPath: Readonly<Get<Data, P>>) => Get<Data, P>,
89
+ options?: UpdateOptions,
90
+ ): Promise<void>
91
+ }
110
92
  }