@nxtedition/deepstream.io-client-js 28.1.23 → 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.23",
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
+ }
@@ -276,14 +276,15 @@ class RecordHandler {
276
276
  // TODO (perf): Slow implementation...
277
277
 
278
278
  const signal = opts?.signal
279
- const timeout = opts?.timeout ?? 2 * 60e3
279
+ const timeout = opts?.timeout
280
280
 
281
- const disposers = []
281
+ let disposers
282
282
  try {
283
283
  const signalPromise = signal
284
284
  ? new Promise((resolve, reject) => {
285
285
  const onAbort = () => reject(signal.reason ?? new utils.AbortError())
286
286
  signal.addEventListener('abort', onAbort)
287
+ disposers ??= []
287
288
  disposers.push(() => signal.removeEventListener('abort', onAbort))
288
289
  })
289
290
  : null
@@ -291,13 +292,14 @@ class RecordHandler {
291
292
  signalPromise?.catch(noop)
292
293
 
293
294
  if (this._patching.size) {
294
- const promises = []
295
+ let promises
295
296
 
296
297
  {
297
298
  const patchingPromises = []
298
299
  for (const callbacks of this._patching.values()) {
299
300
  patchingPromises.push(new Promise((resolve) => callbacks.push(resolve)))
300
301
  }
302
+ promises ??= []
301
303
  promises.push(Promise.all(patchingPromises))
302
304
  }
303
305
 
@@ -312,30 +314,36 @@ class RecordHandler {
312
314
  )
313
315
  resolve(null)
314
316
  }, timeout)
317
+ disposers ??= []
315
318
  disposers.push(() => timers.clearTimeout(patchingTimeout))
316
319
  }),
317
320
  )
318
321
  }
319
322
 
320
323
  if (signalPromise) {
324
+ promises ??= []
321
325
  promises.push(signalPromise)
322
326
  }
323
327
 
324
- await Promise.race(promises)
328
+ if (promises) {
329
+ await Promise.race(promises)
330
+ }
325
331
  }
326
332
 
327
333
  if (this._updating.size) {
328
- const promises = []
334
+ let promises
329
335
 
330
336
  {
331
337
  const updatingPromises = []
332
338
  for (const callbacks of this._updating.values()) {
333
339
  updatingPromises.push(new Promise((resolve) => callbacks.push(resolve)))
334
340
  }
341
+ promises ??= []
335
342
  promises.push(Promise.all(updatingPromises))
336
343
  }
337
344
 
338
345
  if (timeout) {
346
+ promises ??= []
339
347
  promises.push(
340
348
  new Promise((resolve) => {
341
349
  const updatingTimeout = timers.setTimeout(() => {
@@ -346,39 +354,52 @@ class RecordHandler {
346
354
  )
347
355
  resolve(null)
348
356
  }, timeout)
357
+ disposers ??= []
349
358
  disposers.push(() => timers.clearTimeout(updatingTimeout))
350
359
  }),
351
360
  )
352
361
  }
353
362
 
354
- await Promise.race(promises)
363
+ if (promises) {
364
+ await Promise.race(promises)
365
+ }
355
366
  }
356
367
 
357
368
  {
358
- const promises = []
369
+ const syncPromise = new Promise((resolve) => this._sync(resolve))
359
370
 
360
- promises.push(new Promise((resolve) => this._sync(resolve)))
371
+ let promises
361
372
 
362
373
  if (timeout) {
374
+ promises ??= []
363
375
  promises.push(
364
376
  new Promise((resolve, reject) => {
365
377
  const serverTimeout = timers.setTimeout(() => {
366
378
  reject(new Error('sync server timeout'))
367
379
  }, timeout)
380
+ disposers ??= []
368
381
  disposers.push(() => timers.clearTimeout(serverTimeout))
369
382
  }),
370
383
  )
371
384
  }
372
385
 
373
386
  if (signalPromise) {
387
+ promises ??= []
374
388
  promises.push(signalPromise)
375
389
  }
376
390
 
377
- await Promise.race(promises)
391
+ if (promises) {
392
+ promises.push(syncPromise)
393
+ await Promise.race(promises)
394
+ } else {
395
+ await syncPromise
396
+ }
378
397
  }
379
398
  } finally {
380
- for (const disposer of disposers) {
381
- disposer()
399
+ if (disposers) {
400
+ for (const disposer of disposers) {
401
+ disposer()
402
+ }
382
403
  }
383
404
  }
384
405
  }
@@ -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
  }