@logux/server 0.13.1 → 0.14.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/README.md +3 -23
- package/add-http-pages/index.js +24 -0
- package/base-server/index.d.ts +93 -95
- package/base-server/index.js +78 -70
- package/context/index.d.ts +43 -66
- package/context/index.js +18 -16
- package/create-reporter/index.js +55 -76
- package/human-formatter/index.js +52 -82
- 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 +11 -20
- package/request/index.d.ts +1 -99
- package/request/index.js +2 -40
- package/server/index.d.ts +24 -37
- package/server/index.js +11 -16
- package/server-client/index.d.ts +0 -15
- package/server-client/index.js +21 -9
- package/test-client/index.d.ts +19 -5
- package/test-server/index.d.ts +20 -10
- package/test-server/index.js +21 -2
- package/bind-backend-proxy/index.js +0 -325
- package/bind-control-server/index.js +0 -137
- package/request/test.test.js +0 -134
- /package/{base-server → add-http-pages}/hello.html +0 -0
package/server/index.d.ts
CHANGED
|
@@ -4,18 +4,25 @@ import {
|
|
|
4
4
|
type Logger
|
|
5
5
|
} from '../base-server/index.js'
|
|
6
6
|
|
|
7
|
+
export interface LogStream {
|
|
8
|
+
/**
|
|
9
|
+
* Used to synchronously write log messages on application failure.
|
|
10
|
+
*/
|
|
11
|
+
flushSync?(): void
|
|
12
|
+
|
|
13
|
+
write(str: string): void
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
export interface LoggerOptions {
|
|
8
17
|
/**
|
|
9
|
-
*
|
|
18
|
+
* Use color for human output.
|
|
10
19
|
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Used by pino to synchronously write log messages on application failure.
|
|
14
|
-
*/
|
|
15
|
-
flushSync?(): void
|
|
20
|
+
color?: boolean
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Stream to be used by logger to write log.
|
|
24
|
+
*/
|
|
25
|
+
stream?: LogStream
|
|
19
26
|
|
|
20
27
|
/**
|
|
21
28
|
* Logger message format.
|
|
@@ -28,28 +35,6 @@ export interface ServerOptions extends BaseServerOptions {
|
|
|
28
35
|
* Logger with custom settings.
|
|
29
36
|
*
|
|
30
37
|
* 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
38
|
*/
|
|
54
39
|
logger?: Logger | LoggerOptions
|
|
55
40
|
}
|
|
@@ -68,15 +53,17 @@ export interface ServerOptions extends BaseServerOptions {
|
|
|
68
53
|
* }
|
|
69
54
|
*
|
|
70
55
|
* const server = new Server(Object.assign({
|
|
71
|
-
* subprotocol:
|
|
72
|
-
*
|
|
73
|
-
*
|
|
56
|
+
* subprotocol: 1,
|
|
57
|
+
* minSubprotocol: 1,
|
|
58
|
+
* root: import.meta.dirname
|
|
74
59
|
* }, envOptions))
|
|
75
60
|
*
|
|
76
61
|
* server.listen()
|
|
77
62
|
* ```
|
|
78
63
|
*/
|
|
79
|
-
export class Server<
|
|
64
|
+
export class Server<
|
|
65
|
+
Headers extends object = unknown
|
|
66
|
+
> extends BaseServer<Headers> {
|
|
80
67
|
/**
|
|
81
68
|
* Server options.
|
|
82
69
|
*
|
|
@@ -96,9 +83,9 @@ export class Server<Headers extends object = {}> extends BaseServer<Headers> {
|
|
|
96
83
|
*
|
|
97
84
|
* ```js
|
|
98
85
|
* const server = new Server(Server.loadOptions(process, {
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
86
|
+
* minSubprotocol: 1,
|
|
87
|
+
* subprotocol: 1,
|
|
88
|
+
* root: import.meta.dirname,
|
|
102
89
|
* port: 31337
|
|
103
90
|
* }))
|
|
104
91
|
* ```
|
package/server/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import glob from 'fast-glob'
|
|
2
1
|
import { join, relative } from 'node:path'
|
|
3
|
-
import
|
|
2
|
+
import { styleText } from 'node:util'
|
|
3
|
+
import { glob } from 'tinyglobby'
|
|
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,6 +119,7 @@ export class Server extends BaseServer {
|
|
|
125
119
|
async autoloadModules(
|
|
126
120
|
files = ['modules/*/index.js', 'modules/*.js', '!**/*.{test,spec}.js']
|
|
127
121
|
) {
|
|
122
|
+
if (!Array.isArray(files)) files = [files]
|
|
128
123
|
let matches = await glob(files, {
|
|
129
124
|
absolute: true,
|
|
130
125
|
cwd: this.options.root,
|
package/server-client/index.d.ts
CHANGED
|
@@ -100,19 +100,4 @@ export class ServerClient {
|
|
|
100
100
|
* Disconnect client.
|
|
101
101
|
*/
|
|
102
102
|
destroy(): void
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Check client subprotocol version. It uses `semver` npm package
|
|
106
|
-
* to parse requirements.
|
|
107
|
-
*
|
|
108
|
-
* ```js
|
|
109
|
-
* if (client.isSubprotocol('4.x')) {
|
|
110
|
-
* useOldAPI()
|
|
111
|
-
* }
|
|
112
|
-
* ```
|
|
113
|
-
*
|
|
114
|
-
* @param range npm’s version requirements.
|
|
115
|
-
* @returns Is version satisfies requirements.
|
|
116
|
-
*/
|
|
117
|
-
isSubprotocol(range: string): boolean
|
|
118
103
|
}
|
package/server-client/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { LoguxError, parseId } from '@logux/core'
|
|
2
2
|
import cookie 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
|
|
|
@@ -81,7 +81,9 @@ export class ServerClient {
|
|
|
81
81
|
this.httpHeaders = {}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
let Node = app.options.Node || FilteredNode
|
|
85
|
+
|
|
86
|
+
this.node = new Node(this, app.nodeId, app.log, connection, {
|
|
85
87
|
auth: this.auth.bind(this),
|
|
86
88
|
onReceive: this.onReceive.bind(this),
|
|
87
89
|
onSend,
|
|
@@ -93,6 +95,19 @@ export class ServerClient {
|
|
|
93
95
|
this.node.setLocalHeaders({ env: 'development' })
|
|
94
96
|
}
|
|
95
97
|
|
|
98
|
+
if (app.connectLoader) {
|
|
99
|
+
this.node.syncSinceQuery = async lastSynced => {
|
|
100
|
+
let context = new Context(app, this)
|
|
101
|
+
let entries = await app.connectLoader(context, lastSynced)
|
|
102
|
+
let added = entries.reduce((max, entry) => {
|
|
103
|
+
let meta = filterMeta(entry[1])
|
|
104
|
+
entry[1] = meta
|
|
105
|
+
return meta.added > max ? meta.added : max
|
|
106
|
+
}, 0)
|
|
107
|
+
return { added, entries }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
96
111
|
this.node.catch(err => {
|
|
97
112
|
err.connectionId = this.key
|
|
98
113
|
this.app.emitter.emit('error', err)
|
|
@@ -116,10 +131,10 @@ export class ServerClient {
|
|
|
116
131
|
this.clientId = clientId
|
|
117
132
|
this.userId = userId
|
|
118
133
|
|
|
119
|
-
if (this.app.options.
|
|
120
|
-
if (
|
|
134
|
+
if (this.app.options.minSubprotocol) {
|
|
135
|
+
if (this.node.remoteSubprotocol < this.app.options.minSubprotocol) {
|
|
121
136
|
throw new LoguxError('wrong-subprotocol', {
|
|
122
|
-
supported: this.app.options.
|
|
137
|
+
supported: this.app.options.minSubprotocol,
|
|
123
138
|
used: this.node.remoteSubprotocol
|
|
124
139
|
})
|
|
125
140
|
}
|
|
@@ -149,6 +164,7 @@ export class ServerClient {
|
|
|
149
164
|
})
|
|
150
165
|
} catch (e) {
|
|
151
166
|
if (e.name === 'LoguxError') {
|
|
167
|
+
/* c8 ignore next 1 */
|
|
152
168
|
throw e
|
|
153
169
|
} else {
|
|
154
170
|
e.nodeId = nodeId
|
|
@@ -223,10 +239,6 @@ export class ServerClient {
|
|
|
223
239
|
this.app.connected.delete(this.key)
|
|
224
240
|
}
|
|
225
241
|
|
|
226
|
-
isSubprotocol(range) {
|
|
227
|
-
return semver.satisfies(this.node.remoteSubprotocol, range)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
242
|
onReceive(action, meta) {
|
|
231
243
|
if (this.app.actionToQueue.has(meta.id)) {
|
|
232
244
|
return Promise.resolve(false)
|
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
|
*
|
|
@@ -181,10 +195,10 @@ export class TestClient {
|
|
|
181
195
|
* @param test Function, where do you expect action will be received
|
|
182
196
|
* @returns Promise with all received actions
|
|
183
197
|
*/
|
|
184
|
-
received(test: () =>
|
|
198
|
+
received(test: () => unknown): Promise<Action[]>
|
|
185
199
|
|
|
186
200
|
/**
|
|
187
|
-
* Subscribe to the channel and collect all actions
|
|
201
|
+
* Subscribe to the channel and collect all actions during the subscription.
|
|
188
202
|
*
|
|
189
203
|
* ```js
|
|
190
204
|
* let posts = await client.subscribe('posts')
|
package/test-server/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { LoggerOptions } from '../server/index.js'
|
|
|
10
10
|
import type { TestClient, TestClientOptions } from '../test-client/index.js'
|
|
11
11
|
|
|
12
12
|
export interface TestServerOptions
|
|
13
|
-
extends Omit<BaseServerOptions, '
|
|
13
|
+
extends Omit<BaseServerOptions, 'minSubprotocol' | 'subprotocol'> {
|
|
14
14
|
/**
|
|
15
15
|
* Disable built-in auth.
|
|
16
16
|
*/
|
|
@@ -21,9 +21,9 @@ export interface TestServerOptions
|
|
|
21
21
|
*/
|
|
22
22
|
logger?: Logger | LoggerOptions
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
minSubprotocol?: number
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
subprotocol?: number
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -46,8 +46,21 @@ export interface TestServerOptions
|
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
48
|
export class TestServer<
|
|
49
|
-
Headers extends object =
|
|
49
|
+
Headers extends object = unknown
|
|
50
50
|
> extends BaseServer<Headers> {
|
|
51
|
+
/**
|
|
52
|
+
* fetch() compatible API to test HTTP endpoints.
|
|
53
|
+
*
|
|
54
|
+
* ```js
|
|
55
|
+
* server.http('GET', '/version', (req, res) => {
|
|
56
|
+
* res.end('1.0.0')
|
|
57
|
+
* })
|
|
58
|
+
* let res = await server.fetch()
|
|
59
|
+
* expect(await res.text()).toEqual('1.0.0')
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
fetch: typeof fetch
|
|
63
|
+
|
|
51
64
|
/**
|
|
52
65
|
* Server actions log, with methods to check actions inside.
|
|
53
66
|
*
|
|
@@ -93,7 +106,7 @@ export class TestServer<
|
|
|
93
106
|
*
|
|
94
107
|
* @param test Callback with subscripting or action sending.
|
|
95
108
|
*/
|
|
96
|
-
expectDenied(test: () =>
|
|
109
|
+
expectDenied(test: () => unknown): Promise<void>
|
|
97
110
|
|
|
98
111
|
/**
|
|
99
112
|
* Call callback and throw an error if there was no error during
|
|
@@ -102,10 +115,7 @@ export class TestServer<
|
|
|
102
115
|
* @param text RegExp or string of error message.
|
|
103
116
|
* @param test Callback with subscripting or action sending.
|
|
104
117
|
*/
|
|
105
|
-
expectError(
|
|
106
|
-
text: RegExp | string,
|
|
107
|
-
test: () => any | Promise<any>
|
|
108
|
-
): Promise<void>
|
|
118
|
+
expectError(text: RegExp | string, test: () => unknown): Promise<void>
|
|
109
119
|
|
|
110
120
|
/**
|
|
111
121
|
* Call callback and throw an error if there was no `logux/undo` in return
|
|
@@ -120,7 +130,7 @@ export class TestServer<
|
|
|
120
130
|
* @param reason The reason in undo action.
|
|
121
131
|
* @param test Callback with subscripting or action sending.
|
|
122
132
|
*/
|
|
123
|
-
expectUndo(reason: string, test: () =>
|
|
133
|
+
expectUndo(reason: string, test: () => unknown): Promise<void>
|
|
124
134
|
|
|
125
135
|
/**
|
|
126
136
|
* Try to connect client and throw an error is client didn’t received
|
package/test-server/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TestTime } from '@logux/core'
|
|
2
|
+
import { createServer } from 'node:http'
|
|
2
3
|
|
|
3
4
|
import { BaseServer } from '../base-server/index.js'
|
|
4
5
|
import { createReporter } from '../create-reporter/index.js'
|
|
@@ -13,8 +14,8 @@ export class TestServer extends BaseServer {
|
|
|
13
14
|
opts.time.lastId += 1
|
|
14
15
|
super({
|
|
15
16
|
id: `${opts.time.lastId}`,
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
minSubprotocol: 0,
|
|
18
|
+
subprotocol: 0,
|
|
18
19
|
...opts
|
|
19
20
|
})
|
|
20
21
|
if (opts.logger) {
|
|
@@ -31,6 +32,8 @@ export class TestServer extends BaseServer {
|
|
|
31
32
|
if (opts.auth !== false) this.auth(() => true)
|
|
32
33
|
this.testUsers = {}
|
|
33
34
|
|
|
35
|
+
this.fetch = this.fetch.bind(this)
|
|
36
|
+
|
|
34
37
|
this.on('fatal', () => {
|
|
35
38
|
this.destroy()
|
|
36
39
|
})
|
|
@@ -88,4 +91,20 @@ export class TestServer extends BaseServer {
|
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
}
|
|
94
|
+
|
|
95
|
+
async fetch(path, init) {
|
|
96
|
+
let server = createServer(async (req, res) => {
|
|
97
|
+
await this.processHttp(req, res)
|
|
98
|
+
server.close()
|
|
99
|
+
})
|
|
100
|
+
await new Promise(resolve => {
|
|
101
|
+
server.listen(0, resolve)
|
|
102
|
+
})
|
|
103
|
+
let { port } = server.address()
|
|
104
|
+
return fetch(`http://localhost:${port}${path}`, init)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
isBruteforce() {
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
91
110
|
}
|