@logux/server 0.12.3 → 0.12.4

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.
@@ -1,4 +1,5 @@
1
1
  import type { ServerConnection, ServerNode } from '@logux/core'
2
+
2
3
  import type { BaseServer } from '../base-server/index.js'
3
4
 
4
5
  /**
@@ -9,24 +10,11 @@ import type { BaseServer } from '../base-server/index.js'
9
10
  * ```
10
11
  */
11
12
  export class ServerClient {
12
- /**
13
- * @param app The server.
14
- * @param connection The Logux connection.
15
- * @param key Client number used as `app.connected` key.
16
- */
17
- constructor(app: BaseServer, connection: ServerConnection, key: number)
18
-
19
13
  /**
20
14
  * Server, which received client.
21
15
  */
22
16
  app: BaseServer
23
17
 
24
- /**
25
- * User ID. It will be filled from client’s node ID.
26
- * It will be undefined before correct authentication.
27
- */
28
- userId?: string
29
-
30
18
  /**
31
19
  * Unique persistence machine ID.
32
20
  * It will be undefined before correct authentication.
@@ -34,28 +22,22 @@ export class ServerClient {
34
22
  clientId?: string
35
23
 
36
24
  /**
37
- * Unique node ID.
38
- * It will be undefined before correct authentication.
39
- */
40
- nodeId?: string
41
-
42
- /**
43
- * Does server process some action from client.
25
+ * The Logux wrapper to WebSocket connection.
44
26
  *
45
27
  * ```js
46
- * console.log('Clients in processing:', clients.map(i => i.processing))
28
+ * console.log(client.connection.ws.upgradeReq.headers)
47
29
  * ```
48
30
  */
49
- processing: boolean
31
+ connection: ServerConnection
50
32
 
51
33
  /**
52
- * The Logux wrapper to WebSocket connection.
34
+ * HTTP headers of WS connection.
53
35
  *
54
36
  * ```js
55
- * console.log(client.connection.ws.upgradeReq.headers)
37
+ * client.httpHeaders['User-Agent']
56
38
  * ```
57
39
  */
58
- connection: ServerConnection
40
+ httpHeaders: { [name: string]: string }
59
41
 
60
42
  /**
61
43
  * Client number used as `app.connected` key.
@@ -69,31 +51,55 @@ export class ServerClient {
69
51
  key: string
70
52
 
71
53
  /**
72
- * Client IP address.
54
+ * Node instance to synchronize logs.
73
55
  *
74
56
  * ```js
75
- * const clientCity = detectLocation(client.remoteAddress)
57
+ * if (client.node.state === 'synchronized')
76
58
  * ```
77
59
  */
78
- remoteAddress: string
60
+ node: ServerNode
79
61
 
80
62
  /**
81
- * HTTP headers of WS connection.
63
+ * Unique node ID.
64
+ * It will be undefined before correct authentication.
65
+ */
66
+ nodeId?: string
67
+
68
+ /**
69
+ * Does server process some action from client.
82
70
  *
83
71
  * ```js
84
- * client.httpHeaders['User-Agent']
72
+ * console.log('Clients in processing:', clients.map(i => i.processing))
85
73
  * ```
86
74
  */
87
- httpHeaders: { [name: string]: string }
75
+ processing: boolean
88
76
 
89
77
  /**
90
- * Node instance to synchronize logs.
78
+ * Client IP address.
91
79
  *
92
80
  * ```js
93
- * if (client.node.state === 'synchronized')
81
+ * const clientCity = detectLocation(client.remoteAddress)
94
82
  * ```
95
83
  */
96
- node: ServerNode
84
+ remoteAddress: string
85
+
86
+ /**
87
+ * User ID. It will be filled from client’s node ID.
88
+ * It will be undefined before correct authentication.
89
+ */
90
+ userId?: string
91
+
92
+ /**
93
+ * @param app The server.
94
+ * @param connection The Logux connection.
95
+ * @param key Client number used as `app.connected` key.
96
+ */
97
+ constructor(app: BaseServer, connection: ServerConnection, key: number)
98
+
99
+ /**
100
+ * Disconnect client.
101
+ */
102
+ destroy(): void
97
103
 
98
104
  /**
99
105
  * Check client subprotocol version. It uses `semver` npm package
@@ -109,9 +115,4 @@ export class ServerClient {
109
115
  * @returns Is version satisfies requirements.
110
116
  */
111
117
  isSubprotocol(range: string): boolean
112
-
113
- /**
114
- * Disconnect client.
115
- */
116
- destroy(): void
117
118
  }
@@ -2,15 +2,15 @@ import { LoguxError, parseId } from '@logux/core'
2
2
  import cookie from 'cookie'
3
3
  import semver from 'semver'
4
4
 
5
- import { FilteredNode } from '../filtered-node/index.js'
6
5
  import { ALLOWED_META } from '../allowed-meta/index.js'
7
6
  import { filterMeta } from '../filter-meta/index.js'
7
+ import { FilteredNode } from '../filtered-node/index.js'
8
8
 
9
9
  function reportDetails(client) {
10
10
  return {
11
11
  connectionId: client.key,
12
- subprotocol: client.node.remoteSubprotocol,
13
- nodeId: client.nodeId
12
+ nodeId: client.nodeId,
13
+ subprotocol: client.node.remoteSubprotocol
14
14
  }
15
15
  }
16
16
 
@@ -32,13 +32,13 @@ export class ServerClient {
32
32
  }
33
33
 
34
34
  this.node = new FilteredNode(this, app.nodeId, app.log, connection, {
35
- subprotocol: app.options.subprotocol,
35
+ auth: this.auth.bind(this),
36
36
  inFilter: this.filter.bind(this),
37
- timeout: app.options.timeout,
38
- outMap: this.outMap.bind(this),
39
37
  inMap: this.inMap.bind(this),
38
+ outMap: this.outMap.bind(this),
40
39
  ping: app.options.ping,
41
- auth: this.auth.bind(this)
40
+ subprotocol: app.options.subprotocol,
41
+ timeout: app.options.timeout
42
42
  })
43
43
  if (this.app.env === 'development') {
44
44
  this.node.setLocalHeaders({ env: 'development' })
@@ -61,44 +61,6 @@ export class ServerClient {
61
61
  this.app.emitter.emit('connected', this)
62
62
  }
63
63
 
64
- isSubprotocol(range) {
65
- return semver.satisfies(this.node.remoteSubprotocol, range)
66
- }
67
-
68
- destroy() {
69
- this.destroyed = true
70
- this.node.destroy()
71
- if (this.userId) {
72
- let users = this.app.userIds.get(this.userId)
73
- if (users) {
74
- users = users.filter(i => i !== this)
75
- if (users.length === 0) {
76
- this.app.userIds.delete(this.userId)
77
- } else {
78
- this.app.userIds.set(this.userId, users)
79
- }
80
- }
81
- }
82
- if (this.clientId) {
83
- this.app.clientIds.delete(this.clientId)
84
- this.app.nodeIds.delete(this.nodeId)
85
-
86
- for (let channel in this.app.subscribers) {
87
- let subscriber = this.app.subscribers[channel][this.nodeId]
88
- if (subscriber) {
89
- let action = { type: 'logux/unsubscribe', channel }
90
- let actionId = this.app.log.generateId()
91
- let meta = { id: actionId, time: parseInt(actionId), reasons: [] }
92
- this.app.performUnsubscribe(this.nodeId, action, meta)
93
- }
94
- }
95
- }
96
- if (!this.app.destroying) {
97
- this.app.emitter.emit('disconnected', this)
98
- }
99
- this.app.connected.delete(this.key)
100
- }
101
-
102
64
  async auth(nodeId, token) {
103
65
  this.nodeId = nodeId
104
66
  let { clientId, userId } = parseId(nodeId)
@@ -129,11 +91,11 @@ export class ServerClient {
129
91
  let result
130
92
  try {
131
93
  result = await this.app.authenticator({
132
- headers: this.node.remoteHeaders,
133
- cookie: cookie.parse(headers.cookie || ''),
134
- userId: this.userId,
135
94
  client: this,
136
- token
95
+ cookie: cookie.parse(headers.cookie || ''),
96
+ headers: this.node.remoteHeaders,
97
+ token,
98
+ userId: this.userId
137
99
  })
138
100
  } catch (e) {
139
101
  if (e.name === 'LoguxError') {
@@ -177,15 +139,46 @@ export class ServerClient {
177
139
  return result
178
140
  }
179
141
 
180
- async outMap(action, meta) {
181
- return [action, filterMeta(meta)]
142
+ denyBack(action, meta) {
143
+ this.app.emitter.emit('report', 'denied', { actionId: meta.id })
144
+ let [undoAction, undoMeta] = this.app.buildUndo(action, meta, 'denied')
145
+ undoMeta.clients = (undoMeta.clients || []).concat([this.clientId])
146
+ this.app.log.add(undoAction, undoMeta)
147
+ this.app.debugActionError(meta, `Action "${meta.id}" was denied`)
182
148
  }
183
149
 
184
- async inMap(action, meta) {
185
- if (!meta.subprotocol) {
186
- meta.subprotocol = this.node.remoteSubprotocol
150
+ destroy() {
151
+ this.destroyed = true
152
+ this.node.destroy()
153
+ if (this.userId) {
154
+ let users = this.app.userIds.get(this.userId)
155
+ if (users) {
156
+ users = users.filter(i => i !== this)
157
+ if (users.length === 0) {
158
+ this.app.userIds.delete(this.userId)
159
+ } else {
160
+ this.app.userIds.set(this.userId, users)
161
+ }
162
+ }
187
163
  }
188
- return [action, meta]
164
+ if (this.clientId) {
165
+ this.app.clientIds.delete(this.clientId)
166
+ this.app.nodeIds.delete(this.nodeId)
167
+
168
+ for (let channel in this.app.subscribers) {
169
+ let subscriber = this.app.subscribers[channel][this.nodeId]
170
+ if (subscriber) {
171
+ let action = { channel, type: 'logux/unsubscribe' }
172
+ let actionId = this.app.log.generateId()
173
+ let meta = { id: actionId, reasons: [], time: parseInt(actionId) }
174
+ this.app.performUnsubscribe(this.nodeId, action, meta)
175
+ }
176
+ }
177
+ }
178
+ if (!this.app.destroying) {
179
+ this.app.emitter.emit('disconnected', this)
180
+ }
181
+ this.app.connected.delete(this.key)
189
182
  }
190
183
 
191
184
  async filter(action, meta) {
@@ -231,11 +224,18 @@ export class ServerClient {
231
224
  }
232
225
  }
233
226
 
234
- denyBack(action, meta) {
235
- this.app.emitter.emit('report', 'denied', { actionId: meta.id })
236
- let [undoAction, undoMeta] = this.app.buildUndo(action, meta, 'denied')
237
- undoMeta.clients = (undoMeta.clients || []).concat([this.clientId])
238
- this.app.log.add(undoAction, undoMeta)
239
- this.app.debugActionError(meta, `Action "${meta.id}" was denied`)
227
+ async inMap(action, meta) {
228
+ if (!meta.subprotocol) {
229
+ meta.subprotocol = this.node.remoteSubprotocol
230
+ }
231
+ return [action, meta]
232
+ }
233
+
234
+ isSubprotocol(range) {
235
+ return semver.satisfies(this.node.remoteSubprotocol, range)
236
+ }
237
+
238
+ async outMap(action, meta) {
239
+ return [action, filterMeta(meta)]
240
240
  }
241
241
  }
@@ -1,5 +1,6 @@
1
1
  import type { LoguxSubscribeAction, LoguxUnsubscribeAction } from '@logux/actions'
2
2
  import type { Action, AnyAction, TestLog, TestPair } from '@logux/core'
3
+
3
4
  import type { ServerMeta } from '../base-server/index.js'
4
5
  import type { TestServer } from '../test-server/index.js'
5
6
 
@@ -8,10 +9,10 @@ export class LoguxActionError extends Error {
8
9
  }
9
10
 
10
11
  export interface TestClientOptions {
12
+ cookie?: object
13
+ headers?: object
11
14
  httpHeaders?: { [key: string]: string }
12
15
  subprotocol?: string
13
- headers?: object
14
- cookie?: object
15
16
  token?: string
16
17
  }
17
18
 
@@ -58,11 +59,14 @@ export interface TestClientOptions {
58
59
  */
59
60
  export class TestClient {
60
61
  /**
61
- * @param server Test server.
62
- * @param userId User ID.
63
- * @param opts Other options.
62
+ * Client’s ID.
63
+ *
64
+ * ```js
65
+ * let client = new TestClient(server, '10')
66
+ * client.clientId //=> '10:1'
67
+ * ```
64
68
  */
65
- constructor(server: TestServer, userId: string, opts?: TestClientOptions)
69
+ clientId: string
66
70
 
67
71
  /**
68
72
  * Client’s log with extra methods to check actions inside.
@@ -84,14 +88,13 @@ export class TestClient {
84
88
  nodeId: string
85
89
 
86
90
  /**
87
- * Client’s ID.
91
+ * Connection channel between client and server to track sent messages.
88
92
  *
89
93
  * ```js
90
- * let client = new TestClient(server, '10')
91
- * client.clientId //=> '10:1'
94
+ * console.log(client.pair.leftSent)
92
95
  * ```
93
96
  */
94
- clientId: string
97
+ pair: TestPair
95
98
 
96
99
  /**
97
100
  * User ID.
@@ -104,13 +107,27 @@ export class TestClient {
104
107
  userId: string
105
108
 
106
109
  /**
107
- * Connection channel between client and server to track sent messages.
110
+ * @param server Test server.
111
+ * @param userId User ID.
112
+ * @param opts Other options.
113
+ */
114
+ constructor(server: TestServer, userId: string, opts?: TestClientOptions)
115
+
116
+ /**
117
+ * Collect actions added by server and other clients during the `test` call.
108
118
  *
109
119
  * ```js
110
- * console.log(client.pair.leftSent)
120
+ * let answers = await client.collect(async () => {
121
+ * client.log.add({ type: 'pay' })
122
+ * await delay(10)
123
+ * })
124
+ * expect(actions).toEqual([{ type: 'paid' }])
111
125
  * ```
126
+ *
127
+ * @param test Function, where do you expect action will be received
128
+ * @returns Promise with all received actions
112
129
  */
113
- pair: TestPair
130
+ collect(test: () => Promise<any>): Promise<Action[]>
114
131
 
115
132
  /**
116
133
  * Connect to test server.
@@ -136,22 +153,6 @@ export class TestClient {
136
153
  */
137
154
  disconnect(): Promise<void>
138
155
 
139
- /**
140
- * Collect actions added by server and other clients during the `test` call.
141
- *
142
- * ```js
143
- * let answers = await client.collect(async () => {
144
- * client.log.add({ type: 'pay' })
145
- * await delay(10)
146
- * })
147
- * expect(actions).toEqual([{ type: 'paid' }])
148
- * ```
149
- *
150
- * @param test Function, where do you expect action will be received
151
- * @returns Promise with all received actions
152
- */
153
- collect(test: () => Promise<any>): Promise<Action[]>
154
-
155
156
  /**
156
157
  * Send action to the sever and collect all response actions.
157
158
  *
@@ -167,6 +168,21 @@ export class TestClient {
167
168
  */
168
169
  process(action: AnyAction, meta?: Partial<ServerMeta>): Promise<Action[]>
169
170
 
171
+ /**
172
+ * Collect actions received from server during the `test` call.
173
+ *
174
+ * ```js
175
+ * let answers = await client1.received(async () => {
176
+ * await client2.process({ type: 'resend' })
177
+ * })
178
+ * expect(actions).toEqual([{ type: 'local' }])
179
+ * ```
180
+ *
181
+ * @param test Function, where do you expect action will be received
182
+ * @returns Promise with all received actions
183
+ */
184
+ received(test: () => any | Promise<any>): Promise<Action[]>
185
+
170
186
  /**
171
187
  * Subscribe to the channel and collect all actions dunring the subscription.
172
188
  *
@@ -183,7 +199,7 @@ export class TestClient {
183
199
  * @returns Promise with all actions from the server.
184
200
  */
185
201
  subscribe(
186
- channel: string | LoguxSubscribeAction,
202
+ channel: LoguxSubscribeAction | string,
187
203
  filter?: object,
188
204
  since?: { id: string; time: number }
189
205
  ): Promise<Action[]>
@@ -200,22 +216,7 @@ export class TestClient {
200
216
  * @returns Promise until server will remove client from subscribers.
201
217
  */
202
218
  unsubscribe(
203
- channel: string | LoguxUnsubscribeAction,
219
+ channel: LoguxUnsubscribeAction | string,
204
220
  filter?: object
205
221
  ): Promise<Action[]>
206
-
207
- /**
208
- * Collect actions received from server during the `test` call.
209
- *
210
- * ```js
211
- * let answers = await client1.received(async () => {
212
- * await client2.process({ type: 'resend' })
213
- * })
214
- * expect(actions).toEqual([{ type: 'local' }])
215
- * ```
216
- *
217
- * @param test Function, where do you expect action will be received
218
- * @returns Promise with all received actions
219
- */
220
- received(test: () => Promise<any> | any): Promise<Action[]>
221
222
  }
@@ -1,6 +1,6 @@
1
1
  import { ClientNode, TestPair } from '@logux/core'
2
- import { delay } from 'nanodelay'
3
2
  import cookie from 'cookie'
3
+ import { delay } from 'nanodelay'
4
4
 
5
5
  import { filterMeta } from '../filter-meta/index.js'
6
6
 
@@ -43,6 +43,18 @@ export class TestClient {
43
43
  })
44
44
  }
45
45
 
46
+ async collect(test) {
47
+ let added = []
48
+ let unbind = this.node.log.on('add', (action, meta) => {
49
+ if (!meta.id.includes(` ${this.nodeId} `)) {
50
+ added.push(action)
51
+ }
52
+ })
53
+ await test()
54
+ unbind()
55
+ return added
56
+ }
57
+
46
58
  connect() {
47
59
  return new Promise((resolve, reject) => {
48
60
  this.node.throwsError = false
@@ -69,18 +81,6 @@ export class TestClient {
69
81
  return this.pair.wait('right')
70
82
  }
71
83
 
72
- async collect(test) {
73
- let added = []
74
- let unbind = this.node.log.on('add', (action, meta) => {
75
- if (!meta.id.includes(` ${this.nodeId} `)) {
76
- added.push(action)
77
- }
78
- })
79
- await test()
80
- unbind()
81
- return added
82
- }
83
-
84
84
  process(action, meta) {
85
85
  return this.collect(async () => {
86
86
  return new Promise((resolve, reject) => {
@@ -133,10 +133,23 @@ export class TestClient {
133
133
  })
134
134
  }
135
135
 
136
+ async received(test) {
137
+ let actions = []
138
+ let unbind = this.log.on('add', (action, meta) => {
139
+ if (!meta.id.includes(` ${this.nodeId} `)) {
140
+ actions.push(action)
141
+ }
142
+ })
143
+ await test()
144
+ await delay(1)
145
+ unbind()
146
+ return actions
147
+ }
148
+
136
149
  async subscribe(channel, filter, since) {
137
150
  let action = channel
138
151
  if (typeof channel === 'string') {
139
- action = { type: 'logux/subscribe', channel }
152
+ action = { channel, type: 'logux/subscribe' }
140
153
  }
141
154
  if (filter) {
142
155
  action.filter = filter
@@ -151,24 +164,11 @@ export class TestClient {
151
164
  unsubscribe(channel, filter) {
152
165
  let action = channel
153
166
  if (typeof channel === 'string') {
154
- action = { type: 'logux/unsubscribe', channel }
167
+ action = { channel, type: 'logux/unsubscribe' }
155
168
  }
156
169
  if (filter) {
157
170
  action.filter = filter
158
171
  }
159
172
  return this.process(action)
160
173
  }
161
-
162
- async received(test) {
163
- let actions = []
164
- let unbind = this.log.on('add', (action, meta) => {
165
- if (!meta.id.includes(` ${this.nodeId} `)) {
166
- actions.push(action)
167
- }
168
- })
169
- await test()
170
- await delay(1)
171
- unbind()
172
- return actions
173
- }
174
174
  }