@nxtedition/deepstream.io-client-js 28.1.5 → 28.1.7

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": "28.1.5",
3
+ "version": "28.1.7",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "type": "module",
@@ -60,6 +60,7 @@
60
60
  "pinst": "^3.0.0",
61
61
  "prettier": "^3.3.3",
62
62
  "rxjs": "^7.8.1",
63
+ "type-fest": "^4.33.0",
63
64
  "typescript": "^5.6.3",
64
65
  "typescript-eslint": "^8.12.2"
65
66
  },
package/src/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import Record from './record/record.js'
2
- import RecordHandler, { RecordStats } from './record/record-handler.js'
3
- import EventHandler, { EventStats } from './event/event-handler.js'
4
- import RpcHandler, { RpcStats, RpcMethodDef } from './rpc/rpc-handler.js'
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'
5
5
 
6
6
  export default function <Records, Methods>(
7
7
  url: string,
@@ -9,7 +9,7 @@ export default class EventHandler {
9
9
  once: (name: string, callback: () => void) => this
10
10
  off: (name: string, callback: () => void) => this
11
11
  observe: <Data>(name: string) => Observable<Data>
12
- emit: <Data>(name: string, data: Data) => void
12
+ emit: <Data>(name: string, data?: Data) => void
13
13
  provide: (pattern: string, callback: (name: string) => void, options: unknown) => () => void
14
14
  }
15
15
 
@@ -1,5 +1,5 @@
1
1
  import type { Observable } from 'rxjs'
2
- import Record from './record.js'
2
+ import type Record, { EmptyObject, GettablePossibleEmpty, SettablePossibleEmpty } from './record.js'
3
3
 
4
4
  type Paths<T> = keyof T
5
5
  type Get<Data, Path extends string> = Path extends keyof Data ? Data[Path] : unknown
@@ -12,15 +12,14 @@ export default class RecordHandler<Records> {
12
12
  PROVIDER: 4
13
13
 
14
14
  JSON: {
15
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
16
- EMPTY: Readonly<{}>
17
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
18
- EMPTY_OBJ: Readonly<{}>
15
+ EMPTY: EmptyObject
16
+ EMPTY_OBJ: EmptyObject
19
17
  EMPTY_ARR: Readonly<unknown[]>
20
18
  }
21
19
 
22
20
  connected: boolean
23
21
  stats: RecordStats
22
+
24
23
  getRecord: <Name extends keyof Records, Data extends Records[Name] = Records[Name]>(
25
24
  name: Name,
26
25
  ) => Record<Data>
@@ -35,7 +34,7 @@ export default class RecordHandler<Records> {
35
34
 
36
35
  set: {
37
36
  // without path:
38
- <Name extends keyof Records>(name: Name, data: Records[Name]): void
37
+ <Name extends keyof Records>(name: Name, data: SettablePossibleEmpty<Records[Name]>): void
39
38
 
40
39
  // with path:
41
40
  <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data>>(
@@ -49,39 +48,41 @@ export default class RecordHandler<Records> {
49
48
  // without path:
50
49
  <Name extends keyof Records, Data extends Records[Name]>(
51
50
  name: Name,
52
- updater: (data: Data) => Data,
51
+ updater: (data: Readonly<GettablePossibleEmpty<Data>>) => SettablePossibleEmpty<Data>,
53
52
  ): Promise<void>
54
53
 
55
54
  // with path:
56
55
  <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data>>(
57
56
  name: Name,
58
57
  path: Path,
59
- updater: (data: Get<Data, Path>) => Get<Data, Path>,
58
+ updater: (data: Readonly<Get<Data, Path>> | undefined) => Get<Data, Path>,
60
59
  ): Promise<void>
61
60
  }
62
61
 
63
62
  observe: {
64
63
  // without path:
65
- <Name extends keyof Records, Data extends Records[Name]>(name: Name): Observable<Data>
64
+ <Name extends keyof Records, Data extends Records[Name]>(
65
+ name: Name,
66
+ ): Observable<GettablePossibleEmpty<Data>>
66
67
 
67
68
  // with path:
68
69
  <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data> & string>(
69
70
  name: Name,
70
71
  path: Path,
71
- ): Observable<Get<Data, Path>>
72
+ ): Observable<Get<Data, Path> | undefined>
72
73
 
73
74
  // with state:
74
75
  <Name extends keyof Records, Data extends Records[Name]>(
75
76
  name: Name,
76
77
  state: number,
77
- ): Observable<Data>
78
+ ): Observable<GettablePossibleEmpty<Data>>
78
79
 
79
80
  // with path and state:
80
81
  <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data> & string>(
81
82
  name: Name,
82
83
  path: Path,
83
84
  state: number,
84
- ): Observable<Get<Data, Path>>
85
+ ): Observable<Get<Data, Path> | undefined>
85
86
  }
86
87
 
87
88
  get: {
@@ -89,14 +90,14 @@ export default class RecordHandler<Records> {
89
90
  <Name extends keyof Records, Data extends Records[Name]>(
90
91
  name: Name,
91
92
  state?: number,
92
- ): Promise<Data>
93
+ ): Promise<GettablePossibleEmpty<Data>>
93
94
 
94
95
  // with path:
95
96
  <Name extends keyof Records, Data extends Records[Name], Path extends Paths<Data> & string>(
96
97
  name: Name,
97
98
  path?: Path,
98
99
  state?: number,
99
- ): Promise<Get<Data, Path>>
100
+ ): Promise<Get<Data, Path> | undefined>
100
101
  }
101
102
 
102
103
  observe2: {
@@ -107,7 +108,7 @@ export default class RecordHandler<Records> {
107
108
  name: Name
108
109
  version: string
109
110
  state: number
110
- data: Data
111
+ data: GettablePossibleEmpty<Data>
111
112
  }>
112
113
 
113
114
  // with path:
@@ -116,9 +117,9 @@ export default class RecordHandler<Records> {
116
117
  path: Path,
117
118
  ): Observable<{
118
119
  name: Name
119
- version: Get<Data, Path>
120
+ version: string
120
121
  state: number
121
- data: Data
122
+ data: Get<Data, Path> | undefined
122
123
  }>
123
124
 
124
125
  // with state:
@@ -127,9 +128,9 @@ export default class RecordHandler<Records> {
127
128
  state: number,
128
129
  ): Observable<{
129
130
  name: Name
130
- version: Get<Data, Path>
131
+ version: string
131
132
  state: number
132
- data: Data
133
+ data: GettablePossibleEmpty<Data>
133
134
  }>
134
135
 
135
136
  // with path and state:
@@ -139,9 +140,9 @@ export default class RecordHandler<Records> {
139
140
  state: number,
140
141
  ): Observable<{
141
142
  name: Name
142
- version: Get<Data, Path>
143
+ version: string
143
144
  state: number
144
- data: Get<Data, Path>
145
+ data: Get<Data, Path> | undefined
145
146
  }>
146
147
  }
147
148
  }
@@ -1,10 +1,42 @@
1
- import RecordHandler from './record-handler.js'
1
+ import type RecordHandler from './record-handler.js'
2
+ import type { EmptyObject, SingleKeyObject } from 'type-fest'
2
3
 
3
4
  type Paths<T> = keyof T
4
5
  type Get<Data, Path extends string> = Path extends keyof Data ? Data[Path] : unknown
5
6
 
7
+ export type { EmptyObject } from 'type-fest'
8
+
9
+ // When getting, for convenience, we say the data might be partial under some
10
+ // circumstances.
11
+ //
12
+ // When you e.g. do record.get or record.update, there is always a possibility
13
+ // that the data object is empty. The naive correct type for that would be
14
+ // `Data | EmptyObject`. However, that forces the user to always type guard
15
+ // against the empty object case. This type tries to allow the user to skip
16
+ // that check in some cases, where it should be safe to do so.
17
+ export type GettablePossibleEmpty<Data> = keyof Data extends never
18
+ ? EmptyObject // If there are no keys at all
19
+ : Partial<Data> extends Data
20
+ ? // All properties in Data are already optional, so we can safely return it
21
+ // as is. The user just need to check the properties themselves instead.
22
+ Data
23
+ : SingleKeyObject<Data> extends never
24
+ ? // There are more than one property in Data, and some of them are
25
+ // required. That means that the user must always check for the empty
26
+ // object case.
27
+ Data | EmptyObject
28
+ : // There is exactly one property in Data, and it is required. In this
29
+ // particular case, we can safely use Data as the "empty" type, but
30
+ // with the single property turned optional.
31
+ {
32
+ [K in keyof Data]+?: Data[K]
33
+ }
34
+
35
+ // When setting the data must fully adhere to the Data type, or exactly an
36
+ // empty object.
37
+ export type SettablePossibleEmpty<Data> = Data | EmptyObject
38
+
6
39
  export interface WhenOptions {
7
- state?: number
8
40
  timeout?: number
9
41
  signal?: AbortSignal
10
42
  }
@@ -18,7 +50,7 @@ export default class Record<Data> {
18
50
 
19
51
  readonly name: string
20
52
  readonly version: string
21
- readonly data: Data
53
+ readonly data: GettablePossibleEmpty<Data>
22
54
  readonly state: number
23
55
  readonly refs: number
24
56
 
@@ -31,13 +63,13 @@ export default class Record<Data> {
31
63
  // with path
32
64
  <Path extends Paths<Data>, DataAtPath extends Get<Data, Path> = Get<Data, Path>>(
33
65
  path: Path,
34
- ): DataAtPath
66
+ ): DataAtPath | undefined
35
67
  // without path
36
- (): Data
68
+ (): GettablePossibleEmpty<Data>
37
69
  // implementation
38
70
  <Path extends Paths<Data>, DataAtPath extends Get<Data, Path> = Get<Data, Path>>(
39
71
  path?: Path,
40
- ): Path extends undefined ? Data : DataAtPath
72
+ ): Path extends undefined ? GettablePossibleEmpty<Data> : DataAtPath | undefined
41
73
  }
42
74
 
43
75
  set: {
@@ -47,10 +79,10 @@ export default class Record<Data> {
47
79
  dataAtPath: DataAtPath,
48
80
  ): void
49
81
  // without path
50
- (data: Data): void
82
+ (data: SettablePossibleEmpty<Data>): void
51
83
  // implementation
52
84
  <Path extends Paths<Data>, DataAtPath extends Get<Data, Path>>(
53
- ...args: [pathOrData: Path | Data, value?: DataAtPath]
85
+ ...args: [pathOrData: Path | SettablePossibleEmpty<Data>, value?: DataAtPath]
54
86
  ): void
55
87
  }
56
88
 
@@ -61,11 +93,16 @@ export default class Record<Data> {
61
93
  (state: number, options: WhenOptions): Promise<Record<Data>>
62
94
  }
63
95
 
64
- update<Path extends Paths<Data>, PathOrUpdater extends Path | ((data: Data) => Data)>(
96
+ update<
97
+ Path extends Paths<Data>,
98
+ PathOrUpdater extends
99
+ | Path
100
+ | ((data: Readonly<GettablePossibleEmpty<Data>>) => SettablePossibleEmpty<Data>),
101
+ >(
65
102
  ...args: PathOrUpdater extends Path
66
103
  ? [
67
104
  path: Path,
68
- updater: (dataAtPath: Get<Data, Path>) => Get<Data, Path>,
105
+ updater: (dataAtPath: Readonly<Get<Data, Path>> | undefined) => Get<Data, Path>,
69
106
  options?: UpdateOptions,
70
107
  ]
71
108
  : [updater: PathOrUpdater, options?: UpdateOptions]
package/src/test.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  import make from './client.js'
2
+ import {
3
+ EmptyObject,
4
+ GettablePossibleEmpty,
5
+ PossiblyEmptyData,
6
+ SettablePossibleEmpty,
7
+ } from './record/record.js'
8
+ import { assertNonEmpty } from './record/record-handler.js'
2
9
 
3
10
  interface Records {
4
11
  test: {
@@ -8,6 +15,9 @@ interface Records {
8
15
  foo: {
9
16
  bar: number
10
17
  }
18
+ tjena: {
19
+ optional?: string
20
+ }
11
21
  num: number
12
22
  str: string
13
23
  }
@@ -18,6 +28,93 @@ interface Methods {
18
28
 
19
29
  const ds = make<Records, Methods>('http://localhost:3000')
20
30
 
31
+ const y = await ds.record.get('test')
32
+ if ('age' in y) {
33
+ console.log(y.name)
34
+ }
35
+
36
+ const q: PossiblyEmptyData<{ test: number }> = {}
37
+ console.log(q.test)
38
+ if ('test' in q) {
39
+ console.log(q.test)
40
+ }
41
+
42
+ const w: PossiblyEmptyData<{ test: number; haj: number }> = { test: 22, haj: 22 }
43
+
44
+ function test<D>(cb: (data: GettablePossibleEmpty<D>) => SettablePossibleEmpty<D>) {
45
+ // cb({})
46
+ }
47
+
48
+ test<{ foo: number; bar: number }>((data) => {
49
+ return data
50
+ })
51
+
52
+ test<{ foo?: number; bar: number }>(() => {
53
+ return {
54
+ foo: 22,
55
+ }
56
+ })
57
+
58
+ test<{ foo?: number; bar: number }>((data) => {
59
+ // return data
60
+ const a= {
61
+ ...data,
62
+ foo: 22,
63
+ }
64
+ return a
65
+ })
66
+
67
+ test<{ foo?: number; bar?: number }>((data) => {
68
+ return {
69
+ ...data,
70
+ foo: 22,
71
+ }
72
+ })
73
+
74
+ const y = await ds.record.get('test')
75
+ if ('age' in y) {
76
+ console.log(y.name)
77
+
78
+ test<{ foo?: number }>((data) => {
79
+ return data
80
+ })
81
+
82
+ test<{ foo: number }>((data) => {
83
+ return {
84
+ ...data,
85
+ foo: 22,
86
+ }
87
+ })
88
+
89
+ if ('test' in w) {
90
+ console.log(w.haj)
91
+ }
92
+ console.log(w.test)
93
+ console.log(w.test)
94
+
95
+ const m: PossiblyEmptyData<{ test?: number }> = {}
96
+ console.log(m.test)
97
+
98
+ assertNonEmpty(q)
99
+ q.test
100
+ // q.test
101
+
102
+ // q
103
+
104
+ ds.record.set('test', ds.record.JSON.EMPTY_OBJ)
105
+
106
+ ds.record.update('test', (data) => {
107
+ const a = {
108
+ // ...data,
109
+ age: 22,
110
+ }
111
+ console.log(data.name)
112
+ return {
113
+ ...data,
114
+ age: 22,
115
+ }
116
+ })
117
+
21
118
  ds.record.provide('test', () => {})
22
119
 
23
120
  ds.record.set('test', {
@@ -25,6 +122,8 @@ ds.record.set('test', {
25
122
  })
26
123
 
27
124
  ds.record.set('foo', { value: 'namn' })
125
+ ds.record.set('foo', { bar: 22 })
126
+ ds.record.set('foo', {})
28
127
 
29
128
  ds.record.set('test', 'name', '23')
30
129
 
@@ -55,6 +154,8 @@ r.set('name', 'name')
55
154
  r.set('age', true)
56
155
  r.set('age', 22)
57
156
 
157
+ ds.record.set('test', {})
158
+
58
159
  const a1 = r.get()
59
160
  const a2 = r.get('age')
60
161
  const a3 = r.get('name')
@@ -66,8 +167,7 @@ const s = Object.freeze({})
66
167
 
67
168
  ds.record.JSON.EMPTY
68
169
 
69
- ds.event.provide('test', (what) => {
70
- }, {})
170
+ ds.event.provide('test', (what) => {}, {})
71
171
 
72
172
  const a = ds.rpc.provide('test', (args, response) => {
73
173
  console.log(args.name)
@@ -75,4 +175,3 @@ const a = ds.rpc.provide('test', (args, response) => {
75
175
  })
76
176
 
77
177
  a()
78
-