@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/context/index.d.ts
CHANGED
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
import type { AnyAction } from '@logux/core'
|
|
1
|
+
import type { Action, AnyAction } from '@logux/core'
|
|
2
2
|
|
|
3
3
|
import type { ServerMeta } from '../base-server/index.js'
|
|
4
|
+
import type { ServerClient } from '../server-client/index.js'
|
|
4
5
|
import type { Server } from '../server/index.js'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
* Action context.
|
|
8
|
-
*
|
|
9
|
-
* ```js
|
|
10
|
-
* server.type('FOO', {
|
|
11
|
-
* access (ctx, action, meta) {
|
|
12
|
-
* return ctx.isSubprotocol('3.x') ? check3(action) : check4(action)
|
|
13
|
-
* }
|
|
14
|
-
* })
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
export class Context<Data extends object = {}, Headers extends object = {}> {
|
|
7
|
+
export class ConnectContext<Headers extends object = unknown> {
|
|
18
8
|
/**
|
|
19
9
|
* Unique persistence client ID.
|
|
20
10
|
*
|
|
@@ -24,23 +14,6 @@ export class Context<Data extends object = {}, Headers extends object = {}> {
|
|
|
24
14
|
*/
|
|
25
15
|
clientId: string
|
|
26
16
|
|
|
27
|
-
/**
|
|
28
|
-
* Open structure to save some data between different steps of processing.
|
|
29
|
-
*
|
|
30
|
-
* ```js
|
|
31
|
-
* server.type('RENAME', {
|
|
32
|
-
* access (ctx, action, meta) {
|
|
33
|
-
* ctx.data.user = findUser(ctx.userId)
|
|
34
|
-
* return ctx.data.user.hasAccess(action.projectId)
|
|
35
|
-
* }
|
|
36
|
-
* process (ctx, action, meta) {
|
|
37
|
-
* return ctx.data.user.rename(action.projectId, action.name)
|
|
38
|
-
* }
|
|
39
|
-
* })
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
data: Data
|
|
43
|
-
|
|
44
17
|
/**
|
|
45
18
|
* Client’s headers.
|
|
46
19
|
*
|
|
@@ -53,15 +26,6 @@ export class Context<Data extends object = {}, Headers extends object = {}> {
|
|
|
53
26
|
*/
|
|
54
27
|
headers: Headers
|
|
55
28
|
|
|
56
|
-
/**
|
|
57
|
-
* Was action created by Logux server.
|
|
58
|
-
*
|
|
59
|
-
* ```js
|
|
60
|
-
* access: (ctx, action, meta) => ctx.isServer
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
|
-
isServer: boolean
|
|
64
|
-
|
|
65
29
|
/**
|
|
66
30
|
* Unique node ID.
|
|
67
31
|
*
|
|
@@ -77,10 +41,9 @@ export class Context<Data extends object = {}, Headers extends object = {}> {
|
|
|
77
41
|
server: Server
|
|
78
42
|
|
|
79
43
|
/**
|
|
80
|
-
* Action creator application subprotocol version
|
|
81
|
-
* Use {@link Context#isSubprotocol} to check it.
|
|
44
|
+
* Action creator application subprotocol version.
|
|
82
45
|
*/
|
|
83
|
-
subprotocol:
|
|
46
|
+
subprotocol: number
|
|
84
47
|
|
|
85
48
|
/**
|
|
86
49
|
* User ID taken node ID.
|
|
@@ -92,32 +55,27 @@ export class Context<Data extends object = {}, Headers extends object = {}> {
|
|
|
92
55
|
* }
|
|
93
56
|
* ```
|
|
94
57
|
*/
|
|
95
|
-
userId:
|
|
58
|
+
userId: string
|
|
96
59
|
|
|
97
|
-
|
|
98
|
-
* @param nodeId Unique node ID.
|
|
99
|
-
* @param clientId Unique persistence client ID.
|
|
100
|
-
* @param userId User ID taken node ID.
|
|
101
|
-
* @param subprotocol Action creator application subprotocol version
|
|
102
|
-
* in SemVer format.
|
|
103
|
-
* @param server Logux server
|
|
104
|
-
*/
|
|
105
|
-
constructor(server: Server, meta: ServerMeta)
|
|
60
|
+
constructor(server: Server, client: ServerClient)
|
|
106
61
|
|
|
107
62
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
63
|
+
* Wait until the client will confirm all actions, which were sent to it.
|
|
64
|
+
*
|
|
65
|
+
* Use it to send a long history without loading it all into the memory:
|
|
66
|
+
* the client’s speed will limit how fast you read the database.
|
|
110
67
|
*
|
|
111
68
|
* ```js
|
|
112
|
-
*
|
|
113
|
-
*
|
|
69
|
+
* while (await ctx.drain()) {
|
|
70
|
+
* let page = await cursor.next(100)
|
|
71
|
+
* if (!page.length) break
|
|
72
|
+
* ctx.sendBack(page.map(i => i.action))
|
|
114
73
|
* }
|
|
115
74
|
* ```
|
|
116
75
|
*
|
|
117
|
-
* @
|
|
118
|
-
* @returns Is version satisfies requirements.
|
|
76
|
+
* @returns Promise with `false` if the client was disconnected.
|
|
119
77
|
*/
|
|
120
|
-
|
|
78
|
+
drain(): Promise<boolean>
|
|
121
79
|
|
|
122
80
|
/**
|
|
123
81
|
* Send action back to the client.
|
|
@@ -126,13 +84,64 @@ export class Context<Data extends object = {}, Headers extends object = {}> {
|
|
|
126
84
|
* ctx.sendBack({ type: 'login/success', token })
|
|
127
85
|
* ```
|
|
128
86
|
*
|
|
87
|
+
* An array of actions will be sent in a single message. Use it to send
|
|
88
|
+
* a big history page by page instead of a message per action.
|
|
89
|
+
*
|
|
90
|
+
* ```js
|
|
91
|
+
* ctx.sendBack(page.map(i => i.action))
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* Every action in the array can have own meta as `[action, meta]`.
|
|
95
|
+
*
|
|
129
96
|
* Action will not be processed by server’s callbacks from `Server#type`.
|
|
130
97
|
*
|
|
131
|
-
* @param action The action.
|
|
98
|
+
* @param action The action or the array of actions.
|
|
132
99
|
* @param meta Action’s meta.
|
|
133
100
|
* @returns Promise until action was added to the server log.
|
|
134
101
|
*/
|
|
135
|
-
sendBack
|
|
102
|
+
sendBack<TypeAction extends Action = AnyAction>(
|
|
103
|
+
action:
|
|
104
|
+
| (TypeAction | [TypeAction, Partial<ServerMeta>])[]
|
|
105
|
+
| TypeAction,
|
|
106
|
+
meta?: Partial<ServerMeta>
|
|
107
|
+
): Promise<void>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Action context.
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export class Context<
|
|
115
|
+
Data extends object = unknown,
|
|
116
|
+
Headers extends object = unknown
|
|
117
|
+
> extends ConnectContext<Headers> {
|
|
118
|
+
/**
|
|
119
|
+
* Open structure to save some data between different steps of processing.
|
|
120
|
+
*
|
|
121
|
+
* ```js
|
|
122
|
+
* server.type('RENAME', {
|
|
123
|
+
* access (ctx, action, meta) {
|
|
124
|
+
* ctx.data.user = findUser(ctx.userId)
|
|
125
|
+
* return ctx.data.user.hasAccess(action.projectId)
|
|
126
|
+
* }
|
|
127
|
+
* process (ctx, action, meta) {
|
|
128
|
+
* return ctx.data.user.rename(action.projectId, action.name)
|
|
129
|
+
* }
|
|
130
|
+
* })
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
data: Data
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Was action created by Logux server.
|
|
137
|
+
*
|
|
138
|
+
* ```js
|
|
139
|
+
* access: (ctx, action, meta) => ctx.isServer
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
isServer: boolean
|
|
143
|
+
|
|
144
|
+
constructor(server: Server, meta: ServerMeta)
|
|
136
145
|
}
|
|
137
146
|
|
|
138
147
|
/**
|
package/context/index.js
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
import { parseId } from '@logux/core'
|
|
2
|
-
import semver from 'semver'
|
|
3
2
|
|
|
4
3
|
export class Context {
|
|
5
4
|
constructor(server, meta) {
|
|
6
5
|
this.server = server
|
|
7
6
|
this.data = {}
|
|
8
7
|
|
|
9
|
-
let
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let client = server.clientIds.get(this.clientId)
|
|
16
|
-
|
|
17
|
-
if (meta.subprotocol) {
|
|
18
|
-
this.subprotocol = meta.subprotocol
|
|
19
|
-
} else if (client) {
|
|
8
|
+
let client
|
|
9
|
+
if (meta.node) {
|
|
10
|
+
client = meta
|
|
11
|
+
this.nodeId = client.nodeId
|
|
12
|
+
this.userId = client.userId
|
|
13
|
+
this.clientId = client.clientId
|
|
20
14
|
this.subprotocol = client.node.remoteSubprotocol
|
|
15
|
+
} else {
|
|
16
|
+
let parsed = parseId(meta.id)
|
|
17
|
+
this.nodeId = parsed.nodeId
|
|
18
|
+
this.userId = parsed.userId
|
|
19
|
+
this.clientId = parsed.clientId
|
|
20
|
+
this.isServer = this.userId === 'server'
|
|
21
|
+
client = server.clientIds.get(this.clientId)
|
|
22
|
+
if (meta.subprotocol) {
|
|
23
|
+
this.subprotocol = meta.subprotocol
|
|
24
|
+
} else if (client) {
|
|
25
|
+
this.subprotocol = client.node.remoteSubprotocol
|
|
26
|
+
}
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
if (client) {
|
|
@@ -27,15 +33,22 @@ export class Context {
|
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
return
|
|
36
|
+
drain() {
|
|
37
|
+
return this.server.drain(this.clientId)
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
sendBack(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
sendBack(actions, meta = {}) {
|
|
41
|
+
let common = { clients: [this.clientId], status: 'processed', ...meta }
|
|
42
|
+
if (Array.isArray(actions)) {
|
|
43
|
+
return this.server.log.add(
|
|
44
|
+
actions.map(item => {
|
|
45
|
+
// Every action can have own meta as `[action, meta]`
|
|
46
|
+
return Array.isArray(item)
|
|
47
|
+
? [item[0], { ...common, ...item[1] }]
|
|
48
|
+
: [item, { ...common }]
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
return this.server.log.add(actions, common)
|
|
40
53
|
}
|
|
41
54
|
}
|
package/create-reporter/index.js
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
import os from 'node:os'
|
|
2
|
-
import {
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import pico from 'picocolors'
|
|
5
|
-
import pino from 'pino'
|
|
2
|
+
import { sep } from 'node:path'
|
|
6
3
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
__dirname,
|
|
10
|
-
'../human-formatter/index.js'
|
|
11
|
-
)
|
|
4
|
+
import humanFormatter from '../human-formatter/index.js'
|
|
5
|
+
import { BINARY, isBinary } from '../human-formatter/utils.js'
|
|
12
6
|
|
|
13
7
|
const ERROR_CODES = {
|
|
14
8
|
EACCES: (e, environment) => {
|
|
15
9
|
let wayToFix = {
|
|
16
|
-
development:
|
|
17
|
-
|
|
18
|
-
production: '$ su - `<username>`\n' + `$ npm start -p ${e.port}`
|
|
10
|
+
development: 'In dev mode it can be done with sudo:\n$ sudo npm start',
|
|
11
|
+
production: `$ su - \`<username>\`\n$ npm start -p ${e.port}`
|
|
19
12
|
}
|
|
20
13
|
|
|
21
14
|
return {
|
|
@@ -27,13 +20,14 @@ const ERROR_CODES = {
|
|
|
27
20
|
}
|
|
28
21
|
},
|
|
29
22
|
EADDRINUSE: e => {
|
|
23
|
+
let port = String(e.port)
|
|
30
24
|
let wayToFix = {
|
|
31
|
-
darwin: `$ sudo lsof -i ${e.port}\n
|
|
25
|
+
darwin: `$ sudo lsof -i ${e.port}\n$ sudo kill -9 \`<processid>\``,
|
|
32
26
|
linux:
|
|
33
27
|
'$ su - root\n' +
|
|
34
28
|
`# netstat -nlp | grep ${e.port}\n` +
|
|
35
29
|
'Proto Local Address State PID/Program name\n' +
|
|
36
|
-
`tcp 0.0.0.0:${
|
|
30
|
+
`tcp 0.0.0.0:${port.padEnd(8)}LISTEN \`777\`/node\n` +
|
|
37
31
|
'# sudo kill -9 `777`',
|
|
38
32
|
win32:
|
|
39
33
|
'Run `cmd.exe` as an administrator\n' +
|
|
@@ -49,13 +43,7 @@ const ERROR_CODES = {
|
|
|
49
43
|
'or previous version of this server was not killed.\n\n' +
|
|
50
44
|
(wayToFix[os.platform()] || '')
|
|
51
45
|
}
|
|
52
|
-
}
|
|
53
|
-
LOGUX_NO_CONTROL_SECRET: e => ({
|
|
54
|
-
msg: e.message,
|
|
55
|
-
note:
|
|
56
|
-
'Call `npx nanoid` and set result as `controlSecret` ' +
|
|
57
|
-
'or `LOGUX_CONTROL_SECRET` environment variable'
|
|
58
|
-
})
|
|
46
|
+
}
|
|
59
47
|
}
|
|
60
48
|
|
|
61
49
|
const REPORTERS = {
|
|
@@ -134,9 +122,9 @@ const REPORTERS = {
|
|
|
134
122
|
let details = {
|
|
135
123
|
environment: r.environment,
|
|
136
124
|
loguxServer: r.loguxServer,
|
|
125
|
+
minSubprotocol: r.minSubprotocol,
|
|
137
126
|
nodeId: r.nodeId,
|
|
138
|
-
subprotocol: r.subprotocol
|
|
139
|
-
supports: r.supports
|
|
127
|
+
subprotocol: r.subprotocol
|
|
140
128
|
}
|
|
141
129
|
|
|
142
130
|
if (r.environment === 'development') {
|
|
@@ -155,15 +143,6 @@ const REPORTERS = {
|
|
|
155
143
|
details.healthCheck = `${httpProtocol}${r.host}:${r.port}/health`
|
|
156
144
|
}
|
|
157
145
|
|
|
158
|
-
if (r.controlSecret) {
|
|
159
|
-
details.controlListen = `http://${r.host}:${r.port}/`
|
|
160
|
-
details.controlMask = r.controlMask
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (r.backend) {
|
|
164
|
-
details.backendSend = r.backend
|
|
165
|
-
}
|
|
166
|
-
|
|
167
146
|
if (r.redis) {
|
|
168
147
|
details.redis = r.redis
|
|
169
148
|
}
|
|
@@ -191,73 +170,52 @@ const REPORTERS = {
|
|
|
191
170
|
msg: 'Wrong channel name'
|
|
192
171
|
}),
|
|
193
172
|
|
|
194
|
-
wrongControlIp: () => ({
|
|
195
|
-
level: 'warn',
|
|
196
|
-
msg: 'IP address of control request do not pass the mask'
|
|
197
|
-
}),
|
|
198
|
-
|
|
199
|
-
wrongControlSecret: () => ({
|
|
200
|
-
level: 'warn',
|
|
201
|
-
msg: 'Wrong secret in control request'
|
|
202
|
-
}),
|
|
203
|
-
|
|
204
173
|
zombie: () => ({ level: 'warn', msg: 'Zombie client was disconnected' })
|
|
205
174
|
}
|
|
206
175
|
|
|
207
|
-
function createLogger(options) {
|
|
208
|
-
if (options.logger === 'human' || options.logger.type === 'human') {
|
|
209
|
-
let env = options.env || process.env.NODE_ENV || 'development'
|
|
210
|
-
let color =
|
|
211
|
-
env !== 'development' ? false : pico.createColors().isColorSupported
|
|
212
|
-
let basepath = options.root || process.cwd()
|
|
213
|
-
if (basepath.slice(-1) !== sep) basepath += sep
|
|
214
|
-
|
|
215
|
-
let logger = pino(
|
|
216
|
-
pino.transport({
|
|
217
|
-
options: {
|
|
218
|
-
basepath,
|
|
219
|
-
color,
|
|
220
|
-
destination: options.logger.destination
|
|
221
|
-
},
|
|
222
|
-
target: PATH_TO_PRETTIFYING_PINO_TRANSPORT
|
|
223
|
-
})
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
// NOTE: needed only for tests
|
|
227
|
-
logger._basepath = basepath
|
|
228
|
-
logger._color = color
|
|
229
|
-
|
|
230
|
-
return logger
|
|
231
|
-
}
|
|
232
|
-
return pino(
|
|
233
|
-
{
|
|
234
|
-
name: 'logux-server',
|
|
235
|
-
timestamp: pino.stdTimeFunctions.isoTime
|
|
236
|
-
},
|
|
237
|
-
options.logger.stream || pino.destination()
|
|
238
|
-
)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
176
|
function cleanFromKeys(obj, regexp, seen) {
|
|
242
177
|
let result = {}
|
|
243
178
|
for (let key in obj) {
|
|
244
|
-
|
|
245
|
-
if (typeof v === 'string') {
|
|
246
|
-
result[key] = v.replace(regexp, '[SECRET]')
|
|
247
|
-
} else if (typeof v === 'object' && !Array.isArray(v) && v !== null) {
|
|
248
|
-
if (seen.includes(v)) {
|
|
249
|
-
throw new Error('Circular reference in action')
|
|
250
|
-
}
|
|
251
|
-
seen.push(v)
|
|
252
|
-
result[key] = cleanFromKeys(v, regexp, seen)
|
|
253
|
-
seen.pop()
|
|
254
|
-
} else {
|
|
255
|
-
result[key] = v
|
|
256
|
-
}
|
|
179
|
+
result[key] = cleanValue(obj[key], regexp, seen)
|
|
257
180
|
}
|
|
258
181
|
return result
|
|
259
182
|
}
|
|
260
183
|
|
|
184
|
+
function cleanValue(value, regexp, seen) {
|
|
185
|
+
if (typeof value === 'string') {
|
|
186
|
+
return value.replace(regexp, '[SECRET]')
|
|
187
|
+
} else if (isBinary(value)) {
|
|
188
|
+
return BINARY
|
|
189
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
190
|
+
if (seen.includes(value)) {
|
|
191
|
+
throw new Error('Circular reference in action')
|
|
192
|
+
}
|
|
193
|
+
seen.push(value)
|
|
194
|
+
let cleaned = Array.isArray(value)
|
|
195
|
+
? value.map(i => cleanValue(i, regexp, seen))
|
|
196
|
+
: cleanFromKeys(value, regexp, seen)
|
|
197
|
+
seen.pop()
|
|
198
|
+
return cleaned
|
|
199
|
+
} else {
|
|
200
|
+
return value
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function createRecord(level, details, msg) {
|
|
205
|
+
/* c8 ignore next 4 */
|
|
206
|
+
if (typeof details === 'string') {
|
|
207
|
+
msg = details
|
|
208
|
+
details = {}
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
level,
|
|
212
|
+
time: new Date().toISOString(),
|
|
213
|
+
pid: process.pid,
|
|
214
|
+
...details,
|
|
215
|
+
msg
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
261
219
|
export function createReporter(options) {
|
|
262
220
|
let cleanFromLog = options.cleanFromLog || /Bearer [^\s"]+/g
|
|
263
221
|
function reporter(type, details) {
|
|
@@ -270,12 +228,41 @@ export function createReporter(options) {
|
|
|
270
228
|
)
|
|
271
229
|
}
|
|
272
230
|
|
|
273
|
-
|
|
274
|
-
typeof options.logger !== 'string' && 'info' in options.logger
|
|
275
|
-
if (customLoggerProvided) {
|
|
231
|
+
if (typeof options.logger !== 'string' && 'info' in options.logger) {
|
|
276
232
|
reporter.logger = options.logger
|
|
277
233
|
} else {
|
|
278
|
-
|
|
234
|
+
let format
|
|
235
|
+
if (options.logger === 'human' || options.logger.type === 'human') {
|
|
236
|
+
let basepath = options.root || process.cwd()
|
|
237
|
+
if (basepath.slice(-1) !== sep) basepath += sep
|
|
238
|
+
format = humanFormatter({ basepath })
|
|
239
|
+
} else {
|
|
240
|
+
format = record => JSON.stringify(record) + '\n'
|
|
241
|
+
}
|
|
242
|
+
let stream = options.logger?.stream ?? process.stderr
|
|
243
|
+
|
|
244
|
+
reporter.logger = {
|
|
245
|
+
/* c8 ignore next 3 */
|
|
246
|
+
debug(details, msg) {
|
|
247
|
+
stream.write(format(createRecord(20, details, msg)))
|
|
248
|
+
},
|
|
249
|
+
info(details, msg) {
|
|
250
|
+
stream.write(format(createRecord(30, details, msg)))
|
|
251
|
+
},
|
|
252
|
+
warn(details, msg) {
|
|
253
|
+
stream.write(format(createRecord(40, details, msg)))
|
|
254
|
+
},
|
|
255
|
+
error(details, msg) {
|
|
256
|
+
stream.write(format(createRecord(50, details, msg)))
|
|
257
|
+
},
|
|
258
|
+
fatal(details, msg) {
|
|
259
|
+
if (stream.flushSync) {
|
|
260
|
+
stream.flushSync(format(createRecord(60, details, msg)))
|
|
261
|
+
} else {
|
|
262
|
+
stream.write(format(createRecord(60, details, msg)))
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
279
266
|
}
|
|
280
267
|
return reporter
|
|
281
268
|
}
|