@logux/server 0.13.1 → 0.15.0
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/LICENSE +1 -1
- package/README.md +18 -41
- package/{base-server → add-http-pages}/hello.html +34 -26
- package/add-http-pages/index.js +24 -0
- package/add-sync-map/index.d.ts +1 -3
- package/add-sync-map/index.js +4 -10
- package/base-server/index.d.ts +175 -119
- package/base-server/index.js +272 -184
- package/context/index.d.ts +70 -61
- package/context/index.js +33 -20
- package/create-reporter/index.js +80 -93
- package/human-formatter/index.js +60 -85
- package/human-formatter/utils.js +14 -0
- package/index.d.ts +1 -1
- package/index.js +1 -9
- package/options-loader/index.js +46 -14
- package/options-loader/index.test.js +3 -3
- package/options-loader/test.env +2 -0
- package/package.json +21 -32
- package/request/index.d.ts +1 -99
- package/request/index.js +2 -40
- package/server/index.d.ts +31 -40
- package/server/index.js +26 -22
- package/server-client/index.d.ts +21 -12
- package/server-client/index.js +56 -11
- package/test-client/index.d.ts +24 -7
- package/test-client/index.js +8 -8
- package/test-server/index.d.ts +27 -14
- package/test-server/index.js +25 -5
- package/url-pattern/index.js +28 -0
- package/bind-backend-proxy/index.js +0 -325
- package/bind-control-server/index.js +0 -137
- package/request/test.test.js +0 -134
package/request/index.js
CHANGED
|
@@ -1,45 +1,7 @@
|
|
|
1
|
-
import fetch from 'node-fetch'
|
|
2
|
-
|
|
3
|
-
function argsToString(url, opts, response) {
|
|
4
|
-
let { method, ...other } = opts
|
|
5
|
-
return `${method || 'GET'} ${url} ${JSON.stringify(other)} ${response}`
|
|
6
|
-
}
|
|
7
|
-
|
|
8
1
|
export class ResponseError extends Error {
|
|
9
|
-
constructor(statusCode, url
|
|
10
|
-
super(`${statusCode} response on ${
|
|
2
|
+
constructor(statusCode, url) {
|
|
3
|
+
super(`${statusCode} response on ${url}`)
|
|
11
4
|
this.name = 'ResponseError'
|
|
12
5
|
this.statusCode = statusCode
|
|
13
6
|
}
|
|
14
7
|
}
|
|
15
|
-
|
|
16
|
-
export async function request(url, opts = {}, fetcher = fetch) {
|
|
17
|
-
let response = await fetcher(url, opts)
|
|
18
|
-
if (response.status === 204) {
|
|
19
|
-
return undefined
|
|
20
|
-
} else if (response.status >= 200 && response.status <= 299) {
|
|
21
|
-
return response.json()
|
|
22
|
-
} else {
|
|
23
|
-
throw new ResponseError(response.status, url, opts, await response.text())
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function get(url, opts = {}, fetcher = fetch) {
|
|
28
|
-
return request(url, { ...opts, method: 'GET' }, fetcher)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function post(url, opts = {}, fetcher = fetch) {
|
|
32
|
-
return request(url, { ...opts, method: 'POST' }, fetcher)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function put(url, opts = {}, fetcher = fetch) {
|
|
36
|
-
return request(url, { ...opts, method: 'PUT' }, fetcher)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function patch(url, opts = {}, fetcher = fetch) {
|
|
40
|
-
return request(url, { ...opts, method: 'PATCH' }, fetcher)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function del(url, opts = {}, fetcher = fetch) {
|
|
44
|
-
return request(url, { ...opts, method: 'DELETE' }, fetcher)
|
|
45
|
-
}
|
package/server/index.d.ts
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Log } from '@logux/core'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
2
4
|
BaseServer,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
BaseServerOptions,
|
|
6
|
+
Logger,
|
|
7
|
+
ServerMeta
|
|
5
8
|
} from '../base-server/index.js'
|
|
6
9
|
|
|
10
|
+
export interface LogStream {
|
|
11
|
+
/**
|
|
12
|
+
* Used to synchronously write log messages on application failure.
|
|
13
|
+
*/
|
|
14
|
+
flushSync?(): void
|
|
15
|
+
|
|
16
|
+
write(str: string): void
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
export interface LoggerOptions {
|
|
8
20
|
/**
|
|
9
|
-
*
|
|
21
|
+
* Use color for human output.
|
|
10
22
|
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Used by pino to synchronously write log messages on application failure.
|
|
14
|
-
*/
|
|
15
|
-
flushSync?(): void
|
|
23
|
+
color?: boolean
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Stream to be used by logger to write log.
|
|
27
|
+
*/
|
|
28
|
+
stream?: LogStream
|
|
19
29
|
|
|
20
30
|
/**
|
|
21
31
|
* Logger message format.
|
|
@@ -28,28 +38,6 @@ export interface ServerOptions extends BaseServerOptions {
|
|
|
28
38
|
* Logger with custom settings.
|
|
29
39
|
*
|
|
30
40
|
* You can either configure built-in logger or provide your own.
|
|
31
|
-
* Custom logger example: pino logger that streams logs to
|
|
32
|
-
* elasticsearch
|
|
33
|
-
*
|
|
34
|
-
* ```js
|
|
35
|
-
* import pino from 'pino'
|
|
36
|
-
* import pinoElastic from 'pino-elasticsearch'
|
|
37
|
-
*
|
|
38
|
-
* const streamToElastic = pinoElastic({
|
|
39
|
-
* consistency: 'one',
|
|
40
|
-
* node: 'http://localhost:9200',
|
|
41
|
-
* ecs: true
|
|
42
|
-
* })
|
|
43
|
-
*
|
|
44
|
-
* const server = new Server(
|
|
45
|
-
* Server.loadOptions(process, {
|
|
46
|
-
* …,
|
|
47
|
-
* logger: pino({ level: 'info' }, streamToElastic)
|
|
48
|
-
* })
|
|
49
|
-
* )
|
|
50
|
-
* ```
|
|
51
|
-
*
|
|
52
|
-
* Other logger examples can be found here http://getpino.io/#/docs/ecosystem
|
|
53
41
|
*/
|
|
54
42
|
logger?: Logger | LoggerOptions
|
|
55
43
|
}
|
|
@@ -68,15 +56,18 @@ export interface ServerOptions extends BaseServerOptions {
|
|
|
68
56
|
* }
|
|
69
57
|
*
|
|
70
58
|
* const server = new Server(Object.assign({
|
|
71
|
-
* subprotocol:
|
|
72
|
-
*
|
|
73
|
-
*
|
|
59
|
+
* subprotocol: 1,
|
|
60
|
+
* minSubprotocol: 1,
|
|
61
|
+
* root: import.meta.dirname
|
|
74
62
|
* }, envOptions))
|
|
75
63
|
*
|
|
76
64
|
* server.listen()
|
|
77
65
|
* ```
|
|
78
66
|
*/
|
|
79
|
-
export class Server<
|
|
67
|
+
export class Server<
|
|
68
|
+
Headers extends object = unknown,
|
|
69
|
+
ClientData extends object = unknown
|
|
70
|
+
> extends BaseServer<Headers, Log<ServerMeta>, ClientData> {
|
|
80
71
|
/**
|
|
81
72
|
* Server options.
|
|
82
73
|
*
|
|
@@ -96,9 +87,9 @@ export class Server<Headers extends object = {}> extends BaseServer<Headers> {
|
|
|
96
87
|
*
|
|
97
88
|
* ```js
|
|
98
89
|
* const server = new Server(Server.loadOptions(process, {
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
90
|
+
* minSubprotocol: 1,
|
|
91
|
+
* subprotocol: 1,
|
|
92
|
+
* root: import.meta.dirname,
|
|
102
93
|
* port: 31337
|
|
103
94
|
* }))
|
|
104
95
|
* ```
|
package/server/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import glob from '
|
|
2
|
-
import { join, relative } from 'node:path'
|
|
3
|
-
import
|
|
1
|
+
import { glob } from 'node:fs/promises'
|
|
2
|
+
import { join, matchesGlob, relative } from 'node:path'
|
|
3
|
+
import { styleText } from 'node:util'
|
|
4
4
|
|
|
5
5
|
import { BaseServer } from '../base-server/index.js'
|
|
6
6
|
import { createReporter } from '../create-reporter/index.js'
|
|
@@ -13,18 +13,9 @@ let cliOptionsSpec = {
|
|
|
13
13
|
'LOGUX_PORT=1337 LOGUX_HOST=127.0.0.1 $0'
|
|
14
14
|
],
|
|
15
15
|
options: {
|
|
16
|
-
backend: {
|
|
17
|
-
description: 'Backend to process actions'
|
|
18
|
-
},
|
|
19
16
|
cert: {
|
|
20
17
|
description: 'Path to SSL certificate'
|
|
21
18
|
},
|
|
22
|
-
controlMask: {
|
|
23
|
-
description: 'CIDR masks of control servers'
|
|
24
|
-
},
|
|
25
|
-
controlSecret: {
|
|
26
|
-
description: 'Secret to control Logux server'
|
|
27
|
-
},
|
|
28
19
|
host: {
|
|
29
20
|
alias: 'h',
|
|
30
21
|
description: 'Host to bind server'
|
|
@@ -37,6 +28,9 @@ let cliOptionsSpec = {
|
|
|
37
28
|
description: 'Logger type',
|
|
38
29
|
parse: value => oneOf(['human', 'json'], value)
|
|
39
30
|
},
|
|
31
|
+
minSubprotocol: {
|
|
32
|
+
description: 'Check supported client subprotocols'
|
|
33
|
+
},
|
|
40
34
|
port: {
|
|
41
35
|
alias: 'p',
|
|
42
36
|
description: 'Port to bind server',
|
|
@@ -47,9 +41,6 @@ let cliOptionsSpec = {
|
|
|
47
41
|
},
|
|
48
42
|
subprotocol: {
|
|
49
43
|
description: 'Server subprotocol'
|
|
50
|
-
},
|
|
51
|
-
supports: {
|
|
52
|
-
description: 'Range of supported client subprotocols'
|
|
53
44
|
}
|
|
54
45
|
}
|
|
55
46
|
}
|
|
@@ -106,7 +97,7 @@ export class Server extends BaseServer {
|
|
|
106
97
|
let [help, options] = loadOptions(
|
|
107
98
|
cliOptionsSpec,
|
|
108
99
|
process,
|
|
109
|
-
defaults.root ?
|
|
100
|
+
defaults.root ? join(defaults.root, '.env') : undefined
|
|
110
101
|
)
|
|
111
102
|
if (help) {
|
|
112
103
|
process.stdout.write(help + '\n')
|
|
@@ -116,7 +107,10 @@ export class Server extends BaseServer {
|
|
|
116
107
|
return Object.assign(defaults, options)
|
|
117
108
|
} catch (e) {
|
|
118
109
|
process.stderr.write(
|
|
119
|
-
|
|
110
|
+
styleText(
|
|
111
|
+
'red',
|
|
112
|
+
styleText('bgRed', styleText('black', ' FATAL ')) + `${e.message}\n`
|
|
113
|
+
)
|
|
120
114
|
)
|
|
121
115
|
return process.exit(1)
|
|
122
116
|
}
|
|
@@ -125,14 +119,24 @@ export class Server extends BaseServer {
|
|
|
125
119
|
async autoloadModules(
|
|
126
120
|
files = ['modules/*/index.js', 'modules/*.js', '!**/*.{test,spec}.js']
|
|
127
121
|
) {
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
if (!Array.isArray(files)) files = [files]
|
|
123
|
+
let ignore = files.filter(i => i.startsWith('!')).map(i => i.slice(1))
|
|
124
|
+
let patterns = files.filter(i => !i.startsWith('!'))
|
|
125
|
+
|
|
126
|
+
let matches = new Set()
|
|
127
|
+
for await (let entry of glob(patterns, {
|
|
130
128
|
cwd: this.options.root,
|
|
131
|
-
|
|
132
|
-
})
|
|
129
|
+
withFileTypes: true
|
|
130
|
+
})) {
|
|
131
|
+
if (!entry.isFile()) continue
|
|
132
|
+
let file = join(entry.parentPath, entry.name)
|
|
133
|
+
let path = relative(this.options.root, file)
|
|
134
|
+
if (ignore.some(i => matchesGlob(path, i))) continue
|
|
135
|
+
matches.add(file)
|
|
136
|
+
}
|
|
133
137
|
|
|
134
138
|
await Promise.all(
|
|
135
|
-
matches.map(async file => {
|
|
139
|
+
[...matches].map(async file => {
|
|
136
140
|
let serverModule = (await import(file)).default
|
|
137
141
|
if (typeof serverModule === 'function') {
|
|
138
142
|
await serverModule(this)
|
package/server-client/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { BaseServer } from '../base-server/index.js'
|
|
|
9
9
|
* const client = server.connected.get(0)
|
|
10
10
|
* ```
|
|
11
11
|
*/
|
|
12
|
-
export class ServerClient {
|
|
12
|
+
export class ServerClient<ClientData extends object = unknown> {
|
|
13
13
|
/**
|
|
14
14
|
* Server, which received client.
|
|
15
15
|
*/
|
|
@@ -30,6 +30,23 @@ export class ServerClient {
|
|
|
30
30
|
*/
|
|
31
31
|
connection: ServerConnection
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Open structure to save data for the whole connection.
|
|
35
|
+
*
|
|
36
|
+
* ```js
|
|
37
|
+
* server.auth(async ({ client, userId, token }) => {
|
|
38
|
+
* let session = await findSession(token)
|
|
39
|
+
* if (!session) return false
|
|
40
|
+
* client.data.sessionId = session.id
|
|
41
|
+
* return true
|
|
42
|
+
* })
|
|
43
|
+
* server.on('disconnected', client => {
|
|
44
|
+
* touchSession(client.data.sessionId)
|
|
45
|
+
* })
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
data: ClientData
|
|
49
|
+
|
|
33
50
|
/**
|
|
34
51
|
* HTTP headers of WS connection.
|
|
35
52
|
*
|
|
@@ -102,17 +119,9 @@ export class ServerClient {
|
|
|
102
119
|
destroy(): void
|
|
103
120
|
|
|
104
121
|
/**
|
|
105
|
-
*
|
|
106
|
-
* to parse requirements.
|
|
107
|
-
*
|
|
108
|
-
* ```js
|
|
109
|
-
* if (client.isSubprotocol('4.x')) {
|
|
110
|
-
* useOldAPI()
|
|
111
|
-
* }
|
|
112
|
-
* ```
|
|
122
|
+
* Wait until the client will confirm all sent actions.
|
|
113
123
|
*
|
|
114
|
-
* @
|
|
115
|
-
* @returns Is version satisfies requirements.
|
|
124
|
+
* @returns Promise with `false` if the client was disconnected.
|
|
116
125
|
*/
|
|
117
|
-
|
|
126
|
+
drain(): Promise<boolean>
|
|
118
127
|
}
|
package/server-client/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { LoguxError, parseId } from '@logux/core'
|
|
2
|
-
import
|
|
2
|
+
import { parseCookie } from 'cookie'
|
|
3
3
|
import fastq from 'fastq'
|
|
4
|
-
import semver from 'semver'
|
|
5
4
|
|
|
6
5
|
import { ALLOWED_META } from '../allowed-meta/index.js'
|
|
6
|
+
import { Context } from '../context/index.js'
|
|
7
7
|
import { filterMeta } from '../filter-meta/index.js'
|
|
8
8
|
import { FilteredNode } from '../filtered-node/index.js'
|
|
9
9
|
|
|
@@ -11,6 +11,8 @@ async function onSend(action, meta) {
|
|
|
11
11
|
return [action, filterMeta(meta)]
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
function ignore() {}
|
|
15
|
+
|
|
14
16
|
function reportDetails(client) {
|
|
15
17
|
return {
|
|
16
18
|
connectionId: client.key,
|
|
@@ -70,7 +72,9 @@ export class ServerClient {
|
|
|
70
72
|
this.userId = undefined
|
|
71
73
|
this.clientId = undefined
|
|
72
74
|
this.nodeId = undefined
|
|
75
|
+
this.data = {}
|
|
73
76
|
this.processing = false
|
|
77
|
+
this.sending = new Set()
|
|
74
78
|
this.connection = connection
|
|
75
79
|
this.key = key.toString()
|
|
76
80
|
if (connection.ws) {
|
|
@@ -81,18 +85,35 @@ export class ServerClient {
|
|
|
81
85
|
this.httpHeaders = {}
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
let Node = app.options.Node || FilteredNode
|
|
89
|
+
|
|
90
|
+
this.node = new Node(this, app.nodeId, app.log, connection, {
|
|
85
91
|
auth: this.auth.bind(this),
|
|
86
92
|
onReceive: this.onReceive.bind(this),
|
|
87
93
|
onSend,
|
|
88
94
|
ping: app.options.ping,
|
|
89
95
|
subprotocol: app.options.subprotocol,
|
|
96
|
+
syncBatch: app.options.syncBatch,
|
|
90
97
|
timeout: app.options.timeout
|
|
91
98
|
})
|
|
92
99
|
if (this.app.env === 'development') {
|
|
93
100
|
this.node.setLocalHeaders({ env: 'development' })
|
|
94
101
|
}
|
|
95
102
|
|
|
103
|
+
if (app.connectLoader) {
|
|
104
|
+
this.node.syncSinceQuery = async lastSynced => {
|
|
105
|
+
let context = new Context(app, this)
|
|
106
|
+
let entries = await app.connectLoader(context, lastSynced)
|
|
107
|
+
let added = 0
|
|
108
|
+
for (let entry of entries) {
|
|
109
|
+
// `added` should be taken before `filterMeta()` removes it
|
|
110
|
+
if (entry[1].added > added) added = entry[1].added
|
|
111
|
+
entry[1] = filterMeta(entry[1])
|
|
112
|
+
}
|
|
113
|
+
return { added, entries }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
96
117
|
this.node.catch(err => {
|
|
97
118
|
err.connectionId = this.key
|
|
98
119
|
this.app.emitter.emit('error', err)
|
|
@@ -116,10 +137,10 @@ export class ServerClient {
|
|
|
116
137
|
this.clientId = clientId
|
|
117
138
|
this.userId = userId
|
|
118
139
|
|
|
119
|
-
if (this.app.options.
|
|
120
|
-
if (
|
|
140
|
+
if (this.app.options.minSubprotocol) {
|
|
141
|
+
if (this.node.remoteSubprotocol < this.app.options.minSubprotocol) {
|
|
121
142
|
throw new LoguxError('wrong-subprotocol', {
|
|
122
|
-
supported: this.app.options.
|
|
143
|
+
supported: this.app.options.minSubprotocol,
|
|
123
144
|
used: this.node.remoteSubprotocol
|
|
124
145
|
})
|
|
125
146
|
}
|
|
@@ -142,13 +163,14 @@ export class ServerClient {
|
|
|
142
163
|
try {
|
|
143
164
|
result = await this.app.authenticator({
|
|
144
165
|
client: this,
|
|
145
|
-
cookie:
|
|
166
|
+
cookie: parseCookie(headers.cookie || ''),
|
|
146
167
|
headers: this.node.remoteHeaders,
|
|
147
168
|
token,
|
|
148
169
|
userId: this.userId
|
|
149
170
|
})
|
|
150
171
|
} catch (e) {
|
|
151
172
|
if (e.name === 'LoguxError') {
|
|
173
|
+
/* c8 ignore next 1 */
|
|
152
174
|
throw e
|
|
153
175
|
} else {
|
|
154
176
|
e.nodeId = nodeId
|
|
@@ -223,8 +245,21 @@ export class ServerClient {
|
|
|
223
245
|
this.app.connected.delete(this.key)
|
|
224
246
|
}
|
|
225
247
|
|
|
226
|
-
|
|
227
|
-
|
|
248
|
+
async drain() {
|
|
249
|
+
if (this.sending.size > 0) await Promise.all(this.sending)
|
|
250
|
+
if (this.destroyed || !this.node.connected) return false
|
|
251
|
+
if (this.node.syncing === 0) return true
|
|
252
|
+
return new Promise(resolve => {
|
|
253
|
+
let unbind = this.node.on('state', () => {
|
|
254
|
+
if (this.node.state === 'synchronized') {
|
|
255
|
+
unbind()
|
|
256
|
+
resolve(true)
|
|
257
|
+
} else if (!this.node.connected) {
|
|
258
|
+
unbind()
|
|
259
|
+
resolve(false)
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
})
|
|
228
263
|
}
|
|
229
264
|
|
|
230
265
|
onReceive(action, meta) {
|
|
@@ -251,8 +286,10 @@ export class ServerClient {
|
|
|
251
286
|
|
|
252
287
|
if (isChannel) {
|
|
253
288
|
for (let channel of this.app.channels) {
|
|
254
|
-
let
|
|
255
|
-
|
|
289
|
+
let match = channel.regexp
|
|
290
|
+
? action.channel.match(channel.regexp)
|
|
291
|
+
: channel.pattern(action.channel)
|
|
292
|
+
if (match) {
|
|
256
293
|
queueName = channel.queue
|
|
257
294
|
break
|
|
258
295
|
}
|
|
@@ -287,4 +324,12 @@ export class ServerClient {
|
|
|
287
324
|
})
|
|
288
325
|
})
|
|
289
326
|
}
|
|
327
|
+
|
|
328
|
+
track(sending) {
|
|
329
|
+
let done = Promise.resolve(sending).then(ignore, ignore)
|
|
330
|
+
this.sending.add(done)
|
|
331
|
+
void done.then(() => {
|
|
332
|
+
this.sending.delete(done)
|
|
333
|
+
})
|
|
334
|
+
}
|
|
290
335
|
}
|
package/test-client/index.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
LoguxSubscribeAction,
|
|
3
|
+
LoguxUnsubscribeAction
|
|
4
|
+
} from '@logux/actions'
|
|
5
|
+
import type {
|
|
6
|
+
Action,
|
|
7
|
+
AnyAction,
|
|
8
|
+
ClientNode,
|
|
9
|
+
TestLog,
|
|
10
|
+
TestPair
|
|
11
|
+
} from '@logux/core'
|
|
3
12
|
|
|
4
13
|
import type { ServerMeta } from '../base-server/index.js'
|
|
5
14
|
import type { TestServer } from '../test-server/index.js'
|
|
@@ -12,7 +21,7 @@ export interface TestClientOptions {
|
|
|
12
21
|
cookie?: object
|
|
13
22
|
headers?: object
|
|
14
23
|
httpHeaders?: { [key: string]: string }
|
|
15
|
-
subprotocol?:
|
|
24
|
+
subprotocol?: number
|
|
16
25
|
token?: string
|
|
17
26
|
}
|
|
18
27
|
|
|
@@ -77,6 +86,11 @@ export class TestClient {
|
|
|
77
86
|
*/
|
|
78
87
|
log: TestLog
|
|
79
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Logux node.
|
|
91
|
+
*/
|
|
92
|
+
node: ClientNode<object, TestLog<ServerMeta>>
|
|
93
|
+
|
|
80
94
|
/**
|
|
81
95
|
* Client’s node ID.
|
|
82
96
|
*
|
|
@@ -127,7 +141,7 @@ export class TestClient {
|
|
|
127
141
|
* @param test Function, where do you expect action will be received
|
|
128
142
|
* @returns Promise with all received actions
|
|
129
143
|
*/
|
|
130
|
-
collect(test: () => Promise<
|
|
144
|
+
collect(test: () => Promise<unknown>): Promise<Action[]>
|
|
131
145
|
|
|
132
146
|
/**
|
|
133
147
|
* Connect to test server.
|
|
@@ -166,7 +180,10 @@ export class TestClient {
|
|
|
166
180
|
* @param meta Optional action’s meta.
|
|
167
181
|
* @returns Promise until `logux/processed` answer.
|
|
168
182
|
*/
|
|
169
|
-
process
|
|
183
|
+
process<TypeAction extends Action = AnyAction>(
|
|
184
|
+
action: TypeAction,
|
|
185
|
+
meta?: Partial<ServerMeta>
|
|
186
|
+
): Promise<Action[]>
|
|
170
187
|
|
|
171
188
|
/**
|
|
172
189
|
* Collect actions received from server during the `test` call.
|
|
@@ -181,10 +198,10 @@ export class TestClient {
|
|
|
181
198
|
* @param test Function, where do you expect action will be received
|
|
182
199
|
* @returns Promise with all received actions
|
|
183
200
|
*/
|
|
184
|
-
received(test: () =>
|
|
201
|
+
received(test: () => unknown): Promise<Action[]>
|
|
185
202
|
|
|
186
203
|
/**
|
|
187
|
-
* Subscribe to the channel and collect all actions
|
|
204
|
+
* Subscribe to the channel and collect all actions during the subscription.
|
|
188
205
|
*
|
|
189
206
|
* ```js
|
|
190
207
|
* let posts = await client.subscribe('posts')
|
package/test-client/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ClientNode, TestPair } from '@logux/core'
|
|
2
|
-
import
|
|
2
|
+
import { stringifyCookie } from 'cookie'
|
|
3
3
|
import { setTimeout } from 'node:timers/promises'
|
|
4
4
|
|
|
5
5
|
import { filterMeta } from '../filter-meta/index.js'
|
|
@@ -34,9 +34,9 @@ export class TestClient {
|
|
|
34
34
|
this.node.setLocalHeaders(opts.headers)
|
|
35
35
|
}
|
|
36
36
|
if (opts.cookie) {
|
|
37
|
-
this.pair.right.ws.upgradeReq.headers.cookie =
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
this.pair.right.ws.upgradeReq.headers.cookie = stringifyCookie(
|
|
38
|
+
opts.cookie
|
|
39
|
+
)
|
|
40
40
|
}
|
|
41
41
|
server.unbind.push(() => {
|
|
42
42
|
this.node.destroy()
|
|
@@ -46,7 +46,7 @@ export class TestClient {
|
|
|
46
46
|
async collect(test) {
|
|
47
47
|
let added = []
|
|
48
48
|
let unbind = this.node.log.on('add', (action, meta) => {
|
|
49
|
-
if (
|
|
49
|
+
if (meta.id.split(' ')[1] !== this.nodeId) {
|
|
50
50
|
added.push(action)
|
|
51
51
|
}
|
|
52
52
|
})
|
|
@@ -67,8 +67,8 @@ export class TestClient {
|
|
|
67
67
|
})
|
|
68
68
|
|
|
69
69
|
this.server.addClient(this.pair.right)
|
|
70
|
-
this.node.connection.connect()
|
|
71
|
-
this.node.waitFor('synchronized').then(() => {
|
|
70
|
+
void this.node.connection.connect()
|
|
71
|
+
void this.node.waitFor('synchronized').then(() => {
|
|
72
72
|
this.node.throwsError = true
|
|
73
73
|
unbind()
|
|
74
74
|
resolve()
|
|
@@ -136,7 +136,7 @@ export class TestClient {
|
|
|
136
136
|
async received(test) {
|
|
137
137
|
let actions = []
|
|
138
138
|
let unbind = this.log.on('add', (action, meta) => {
|
|
139
|
-
if (
|
|
139
|
+
if (meta.id.split(' ')[1] !== this.nodeId) {
|
|
140
140
|
actions.push(action)
|
|
141
141
|
}
|
|
142
142
|
})
|
package/test-server/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { TestLog, TestTime } from '@logux/core'
|
|
1
|
+
import type { Log, TestLog, TestTime } from '@logux/core'
|
|
2
2
|
|
|
3
|
-
import { BaseServer } from '../base-server/index.js'
|
|
4
3
|
import type {
|
|
4
|
+
BaseServer,
|
|
5
5
|
BaseServerOptions,
|
|
6
6
|
Logger,
|
|
7
7
|
ServerMeta
|
|
@@ -9,8 +9,10 @@ import type {
|
|
|
9
9
|
import type { LoggerOptions } from '../server/index.js'
|
|
10
10
|
import type { TestClient, TestClientOptions } from '../test-client/index.js'
|
|
11
11
|
|
|
12
|
-
export interface TestServerOptions
|
|
13
|
-
|
|
12
|
+
export interface TestServerOptions extends Omit<
|
|
13
|
+
BaseServerOptions,
|
|
14
|
+
'minSubprotocol' | 'subprotocol'
|
|
15
|
+
> {
|
|
14
16
|
/**
|
|
15
17
|
* Disable built-in auth.
|
|
16
18
|
*/
|
|
@@ -21,9 +23,9 @@ export interface TestServerOptions
|
|
|
21
23
|
*/
|
|
22
24
|
logger?: Logger | LoggerOptions
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
minSubprotocol?: number
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
subprotocol?: number
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
/**
|
|
@@ -46,8 +48,22 @@ export interface TestServerOptions
|
|
|
46
48
|
* ```
|
|
47
49
|
*/
|
|
48
50
|
export class TestServer<
|
|
49
|
-
Headers extends object =
|
|
50
|
-
|
|
51
|
+
Headers extends object = unknown,
|
|
52
|
+
ClientData extends object = unknown
|
|
53
|
+
> extends BaseServer<Headers, Log<ServerMeta>, ClientData> {
|
|
54
|
+
/**
|
|
55
|
+
* fetch() compatible API to test HTTP endpoints.
|
|
56
|
+
*
|
|
57
|
+
* ```js
|
|
58
|
+
* server.http('GET', '/version', (req, res) => {
|
|
59
|
+
* res.end('1.0.0')
|
|
60
|
+
* })
|
|
61
|
+
* let res = await server.fetch()
|
|
62
|
+
* expect(await res.text()).toEqual('1.0.0')
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
fetch: typeof fetch
|
|
66
|
+
|
|
51
67
|
/**
|
|
52
68
|
* Server actions log, with methods to check actions inside.
|
|
53
69
|
*
|
|
@@ -93,7 +109,7 @@ export class TestServer<
|
|
|
93
109
|
*
|
|
94
110
|
* @param test Callback with subscripting or action sending.
|
|
95
111
|
*/
|
|
96
|
-
expectDenied(test: () =>
|
|
112
|
+
expectDenied(test: () => unknown): Promise<void>
|
|
97
113
|
|
|
98
114
|
/**
|
|
99
115
|
* Call callback and throw an error if there was no error during
|
|
@@ -102,10 +118,7 @@ export class TestServer<
|
|
|
102
118
|
* @param text RegExp or string of error message.
|
|
103
119
|
* @param test Callback with subscripting or action sending.
|
|
104
120
|
*/
|
|
105
|
-
expectError(
|
|
106
|
-
text: RegExp | string,
|
|
107
|
-
test: () => any | Promise<any>
|
|
108
|
-
): Promise<void>
|
|
121
|
+
expectError(text: RegExp | string, test: () => unknown): Promise<void>
|
|
109
122
|
|
|
110
123
|
/**
|
|
111
124
|
* Call callback and throw an error if there was no `logux/undo` in return
|
|
@@ -120,7 +133,7 @@ export class TestServer<
|
|
|
120
133
|
* @param reason The reason in undo action.
|
|
121
134
|
* @param test Callback with subscripting or action sending.
|
|
122
135
|
*/
|
|
123
|
-
expectUndo(reason: string, test: () =>
|
|
136
|
+
expectUndo(reason: string, test: () => unknown): Promise<void>
|
|
124
137
|
|
|
125
138
|
/**
|
|
126
139
|
* Try to connect client and throw an error is client didn’t received
|