@logux/server 0.14.0 → 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.
@@ -5,17 +5,35 @@ import { nanoid } from 'nanoid'
5
5
  import { readFile } from 'node:fs/promises'
6
6
  import { dirname, join } from 'node:path'
7
7
  import { fileURLToPath } from 'node:url'
8
- import UrlPattern from 'url-pattern'
9
8
  import { WebSocketServer } from 'ws'
10
9
 
11
10
  import { addHttpPages } from '../add-http-pages/index.js'
12
11
  import { Context } from '../context/index.js'
13
12
  import { createHttpServer } from '../create-http-server/index.js'
14
13
  import { ServerClient } from '../server-client/index.js'
14
+ import { createPattern } from '../url-pattern/index.js'
15
15
 
16
16
  const SKIP_PROCESS = Symbol('skipProcess')
17
17
  const RESEND_META = ['channels', 'users', 'clients', 'nodes']
18
18
 
19
+ function addTarget(targets, client, action, meta) {
20
+ let entries = targets.get(client)
21
+ if (entries) {
22
+ entries.push([action, meta])
23
+ } else {
24
+ targets.set(client, [[action, meta]])
25
+ }
26
+ }
27
+
28
+ function sendTargets(targets) {
29
+ for (let [client, entries] of targets) {
30
+ let sending = client.node.onAdd(entries)
31
+ // Only `ServerClient` tracks sending for `Server#drain()`
32
+ if (client.track) client.track(sending)
33
+ }
34
+ targets.clear()
35
+ }
36
+
19
37
  function optionError(msg) {
20
38
  let error = new Error(msg)
21
39
  error.logux = true
@@ -110,7 +128,7 @@ export class BaseServer {
110
128
  if (!this.options.host) this.options.host = '127.0.0.1'
111
129
  }
112
130
 
113
- this.nodeId = `server:${this.options.id || nanoid(8)}`
131
+ this.nodeId = `server:${this.options.id || nanoid(6)}`
114
132
 
115
133
  if (this.options.fileUrl) {
116
134
  this.options.root = dirname(fileURLToPath(this.options.fileUrl))
@@ -159,89 +177,64 @@ export class BaseServer {
159
177
  }
160
178
  this.replaceResendShortcuts(meta)
161
179
  })
162
- this.on('add', async (action, meta) => {
163
- let start = Date.now()
180
+ // Reports are per action, `clean` for a reason-less action is emitted
181
+ // by the log before `batch` event
182
+ this.on('add', (action, meta) => {
164
183
  if (meta.reasons.length === 0) {
165
184
  cleaned[meta.id] = true
166
185
  this.emitter.emit('report', 'addClean', { action, meta })
167
186
  } else {
168
187
  this.emitter.emit('report', 'add', { action, meta })
169
188
  }
189
+ })
190
+ this.on('batch', entries => {
191
+ let start = Date.now()
192
+ let ready = []
170
193
 
171
- if (this.destroying && !this.actionToQueue.has(meta.id)) {
172
- return
173
- }
174
-
175
- if (action.type === 'logux/subscribe') {
176
- if (meta.server === this.nodeId) {
177
- this.subscribeAction(action, meta, start)
194
+ for (let [action, meta] of entries) {
195
+ if (this.destroying && !this.actionToQueue.has(meta.id)) {
196
+ continue
178
197
  }
179
- return
180
- }
181
198
 
182
- if (action.type === 'logux/unsubscribe') {
183
- if (meta.server === this.nodeId) {
184
- this.unsubscribeAction(action, meta)
199
+ if (action.type === 'logux/subscribe') {
200
+ if (meta.server === this.nodeId) {
201
+ void this.subscribeAction(action, meta, start)
202
+ }
203
+ continue
185
204
  }
186
- return
187
- }
188
205
 
189
- let processor = this.getProcessor(action.type)
190
- if (processor && processor.resend && meta.status === 'waiting') {
191
- let ctx = this.createContext(action, meta)
192
- let resend
193
- try {
194
- resend = await processor.resend(ctx, action, meta)
195
- } catch (e) {
196
- this.undo(action, meta, 'error')
197
- this.emitter.emit('error', e, action, meta)
198
- this.finally(processor, ctx, action, meta)
199
- return
200
- }
201
- if (resend) {
202
- if (typeof resend === 'string') {
203
- resend = { channels: [resend] }
204
- } else if (Array.isArray(resend)) {
205
- resend = { channels: resend }
206
- } else {
207
- this.replaceResendShortcuts(resend)
208
- }
209
- let diff = {}
210
- for (let i of RESEND_META) {
211
- if (resend[i]) diff[i] = resend[i]
206
+ if (action.type === 'logux/unsubscribe') {
207
+ if (meta.server === this.nodeId) {
208
+ this.unsubscribeAction(action, meta)
212
209
  }
213
- await this.log.changeMeta(meta.id, diff)
214
- meta = { ...meta, ...diff }
210
+ continue
215
211
  }
216
- }
217
212
 
218
- if (this.isUseless(action, meta)) {
219
- this.emitter.emit('report', 'useless', { action, meta })
220
- }
221
-
222
- await this.sendAction(action, meta)
213
+ let processor = this.getProcessor(action.type)
214
+ if (processor && processor.resend && meta.status === 'waiting') {
215
+ // Resending is asynchronous, so the action can’t wait for others
216
+ void this.resendAction(action, meta, processor, start)
217
+ continue
218
+ }
223
219
 
224
- if (meta.status === 'waiting') {
225
- if (!processor) {
226
- this.internalUnknownType(action, meta)
227
- return
220
+ if (this.isUseless(action, meta)) {
221
+ this.emitter.emit('report', 'useless', { action, meta })
228
222
  }
229
- if (processor.process) {
230
- this.processAction(processor, action, meta, start)
231
- } else {
232
- this.emitter.emit('processed', action, meta, 0)
233
- this.finally(
234
- processor,
235
- this.createContext(action, meta),
236
- action,
237
- meta
238
- )
239
- this.markAsProcessed(meta)
223
+ ready.push([action, meta, processor])
224
+ }
225
+
226
+ if (ready.length === 0) return
227
+ let runProcessors = () => {
228
+ for (let [action, meta, processor] of ready) {
229
+ this.runProcessor(action, meta, processor, start)
240
230
  }
241
- } else {
242
- this.emitter.emit('processed', action, meta, 0)
243
- this.finally(processor, this.createContext(action, meta), action, meta)
244
231
  }
232
+ // Actions are processed only after they were sent to the clients
233
+ void Promise.resolve(this.sendBatch(ready)).then(runProcessors, error => {
234
+ // Do not stop action processing on a broken channel filter
235
+ runProcessors()
236
+ throw error
237
+ })
245
238
  })
246
239
  this.on('clean', (action, meta) => {
247
240
  if (cleaned[meta.id]) {
@@ -445,9 +438,7 @@ export class BaseServer {
445
438
  normalizeChannelCallbacks(`Channel ${pattern}`, callbacks)
446
439
  let channel = Object.assign({}, callbacks)
447
440
  if (typeof pattern === 'string') {
448
- channel.pattern = new UrlPattern(pattern, {
449
- segmentValueCharset: '^/'
450
- })
441
+ channel.pattern = createPattern(pattern)
451
442
  } else {
452
443
  channel.regexp = pattern
453
444
  }
@@ -496,6 +487,12 @@ export class BaseServer {
496
487
  return Promise.all(this.unbind.map(i => i()))
497
488
  }
498
489
 
490
+ drain(clientId) {
491
+ let client = this.clientIds.get(clientId)
492
+ if (!client) return Promise.resolve(false)
493
+ return client.drain()
494
+ }
495
+
499
496
  finally(processor, ctx, action, meta) {
500
497
  this.contexts.delete(action)
501
498
  if (processor && processor.finally) {
@@ -522,6 +519,11 @@ export class BaseServer {
522
519
  return undefined
523
520
  }
524
521
 
522
+ handleClient(ws, req) {
523
+ ws.upgradeReq = req
524
+ this.addClient(new ServerConnection(ws))
525
+ }
526
+
525
527
  http(method, url, listener) {
526
528
  if (this.options.disableHttpServer) {
527
529
  throw new Error(
@@ -624,10 +626,7 @@ export class BaseServer {
624
626
  await readFile(join(import.meta.dirname, '..', 'package.json'))
625
627
  )
626
628
 
627
- this.ws.on('connection', (ws, req) => {
628
- ws.upgradeReq = req
629
- this.addClient(new ServerConnection(ws))
630
- })
629
+ this.ws.on('connection', (ws, req) => this.handleClient(ws, req))
631
630
  this.emitter.emit('report', 'listen', {
632
631
  cert: !!this.options.cert,
633
632
  environment: this.env,
@@ -655,7 +654,12 @@ export class BaseServer {
655
654
  }
656
655
 
657
656
  on(event, listener) {
658
- if (event === 'preadd' || event === 'add' || event === 'clean') {
657
+ if (
658
+ event === 'preadd' ||
659
+ event === 'add' ||
660
+ event === 'batch' ||
661
+ event === 'clean'
662
+ ) {
659
663
  return this.log.emitter.on(event, listener)
660
664
  } else {
661
665
  return this.emitter.on(event, listener)
@@ -668,11 +672,7 @@ export class BaseServer {
668
672
  throw new Error('Callbacks for unknown channel are already defined')
669
673
  }
670
674
  let channel = Object.assign({}, callbacks)
671
- channel.pattern = {
672
- match(name) {
673
- return [name]
674
- }
675
- }
675
+ channel.pattern = name => [name]
676
676
  this.otherSubscriber = channel
677
677
  }
678
678
 
@@ -685,6 +685,7 @@ export class BaseServer {
685
685
  }
686
686
 
687
687
  performUnsubscribe(clientNodeId, action, meta) {
688
+ if (action.channel === '__proto__' || clientNodeId === '__proto__') return
688
689
  if (this.subscribers[action.channel]) {
689
690
  let subscriber = this.subscribers[action.channel][clientNodeId]
690
691
  if (subscriber) {
@@ -806,7 +807,42 @@ export class BaseServer {
806
807
  }
807
808
  }
808
809
 
809
- async sendAction(action, meta) {
810
+ async resendAction(action, meta, processor, start) {
811
+ let ctx = this.createContext(action, meta)
812
+ let resend
813
+ try {
814
+ resend = await processor.resend(ctx, action, meta)
815
+ } catch (e) {
816
+ this.undo(action, meta, 'error')
817
+ this.emitter.emit('error', e, action, meta)
818
+ this.finally(processor, ctx, action, meta)
819
+ return
820
+ }
821
+ if (resend) {
822
+ if (typeof resend === 'string') {
823
+ resend = { channels: [resend] }
824
+ } else if (Array.isArray(resend)) {
825
+ resend = { channels: resend }
826
+ } else {
827
+ this.replaceResendShortcuts(resend)
828
+ }
829
+ let diff = {}
830
+ for (let i of RESEND_META) {
831
+ if (resend[i]) diff[i] = resend[i]
832
+ }
833
+ await this.log.changeMeta(meta.id, diff)
834
+ meta = { ...meta, ...diff }
835
+ }
836
+
837
+ if (this.isUseless(action, meta)) {
838
+ this.emitter.emit('report', 'useless', { action, meta })
839
+ }
840
+
841
+ await this.sendAction(action, meta)
842
+ this.runProcessor(action, meta, processor, start)
843
+ }
844
+
845
+ resolveTargets(action, meta, targets) {
810
846
  let from = parseId(meta.id).clientId
811
847
  let ignoreClients = new Set(meta.excludeClients || [])
812
848
  ignoreClients.add(from)
@@ -816,7 +852,7 @@ export class BaseServer {
816
852
  let client = this.nodeIds.get(id)
817
853
  if (client) {
818
854
  ignoreClients.add(client.clientId)
819
- client.node.onAdd(action, meta)
855
+ addTarget(targets, client, action, meta)
820
856
  }
821
857
  }
822
858
  }
@@ -826,7 +862,7 @@ export class BaseServer {
826
862
  if (this.clientIds.has(id)) {
827
863
  let client = this.clientIds.get(id)
828
864
  ignoreClients.add(client.clientId)
829
- client.node.onAdd(action, meta)
865
+ addTarget(targets, client, action, meta)
830
866
  }
831
867
  }
832
868
  }
@@ -838,32 +874,38 @@ export class BaseServer {
838
874
  for (let client of users) {
839
875
  if (!ignoreClients.has(client.clientId)) {
840
876
  ignoreClients.add(client.clientId)
841
- client.node.onAdd(action, meta)
877
+ addTarget(targets, client, action, meta)
842
878
  }
843
879
  }
844
880
  }
845
881
  }
846
882
  }
847
883
 
848
- if (meta.channels) {
849
- for (let channel of meta.channels) {
850
- if (this.subscribers[channel]) {
851
- for (let nodeId in this.subscribers[channel]) {
852
- let clientId = parseId(nodeId).clientId
853
- if (!ignoreClients.has(clientId)) {
854
- let subscriber = this.subscribers[channel][nodeId]
855
- if (subscriber) {
856
- let ctx = this.createContext(action, meta)
857
- let client = this.clientIds.get(clientId)
858
- for (let filter of Object.values(subscriber.filters)) {
859
- filter =
860
- typeof filter === 'function'
861
- ? await filter(ctx, action, meta)
862
- : filter
863
- if (filter && client) {
864
- ignoreClients.add(clientId)
865
- client.node.onAdd(action, meta)
866
- }
884
+ if (!meta.channels) return undefined
885
+
886
+ let waiting = []
887
+ for (let channel of meta.channels) {
888
+ if (this.subscribers[channel]) {
889
+ for (let nodeId in this.subscribers[channel]) {
890
+ let clientId = parseId(nodeId).clientId
891
+ if (!ignoreClients.has(clientId)) {
892
+ let subscriber = this.subscribers[channel][nodeId]
893
+ if (subscriber) {
894
+ let ctx = this.createContext(action, meta)
895
+ let client = this.clientIds.get(clientId)
896
+ for (let filter of Object.values(subscriber.filters)) {
897
+ if (typeof filter === 'function') {
898
+ waiting.push(
899
+ Promise.resolve(filter(ctx, action, meta)).then(result => {
900
+ if (result && client && !ignoreClients.has(clientId)) {
901
+ ignoreClients.add(clientId)
902
+ addTarget(targets, client, action, meta)
903
+ }
904
+ })
905
+ )
906
+ } else if (filter && client) {
907
+ ignoreClients.add(clientId)
908
+ addTarget(targets, client, action, meta)
867
909
  }
868
910
  }
869
911
  }
@@ -871,6 +913,49 @@ export class BaseServer {
871
913
  }
872
914
  }
873
915
  }
916
+ return waiting.length > 0 ? Promise.all(waiting) : undefined
917
+ }
918
+
919
+ runProcessor(action, meta, processor, start) {
920
+ if (meta.status === 'waiting') {
921
+ if (!processor) {
922
+ this.internalUnknownType(action, meta)
923
+ return
924
+ }
925
+ if (processor.process) {
926
+ void this.processAction(processor, action, meta, start)
927
+ } else {
928
+ this.emitter.emit('processed', action, meta, 0)
929
+ this.finally(processor, this.createContext(action, meta), action, meta)
930
+ this.markAsProcessed(meta)
931
+ }
932
+ } else {
933
+ this.emitter.emit('processed', action, meta, 0)
934
+ this.finally(processor, this.createContext(action, meta), action, meta)
935
+ }
936
+ }
937
+
938
+ async sendAction(action, meta) {
939
+ let targets = new Map()
940
+ let waiting = this.resolveTargets(action, meta, targets)
941
+ if (waiting) await waiting
942
+ sendTargets(targets)
943
+ }
944
+
945
+ sendBatch(entries) {
946
+ let targets = new Map()
947
+ let waiting = []
948
+ for (let [action, meta] of entries) {
949
+ let promise = this.resolveTargets(action, meta, targets)
950
+ if (promise) waiting.push(promise)
951
+ }
952
+ if (waiting.length === 0) {
953
+ sendTargets(targets)
954
+ return undefined
955
+ }
956
+ return Promise.all(waiting).then(() => {
957
+ sendTargets(targets)
958
+ })
874
959
  }
875
960
 
876
961
  sendOnConnect(loader) {
@@ -887,6 +972,7 @@ export class BaseServer {
887
972
  }
888
973
 
889
974
  subscribe(nodeId, channel) {
975
+ if (channel === '__proto__' || nodeId === '__proto__') return
890
976
  if (!this.subscribers[channel] || !this.subscribers[channel][nodeId]) {
891
977
  if (!this.subscribers[channel]) {
892
978
  this.subscribers[channel] = {}
@@ -897,7 +983,7 @@ export class BaseServer {
897
983
  }
898
984
 
899
985
  async subscribeAction(action, meta, start) {
900
- if (typeof action.channel !== 'string') {
986
+ if (typeof action.channel !== 'string' || action.channel === '__proto__') {
901
987
  this.wrongChannel(action, meta)
902
988
  return
903
989
  }
@@ -910,7 +996,7 @@ export class BaseServer {
910
996
  let match
911
997
  for (let channel of channels) {
912
998
  if (channel.pattern) {
913
- match = channel.pattern.match(action.channel)
999
+ match = channel.pattern(action.channel)
914
1000
  } else {
915
1001
  match = action.channel.match(channel.regexp)
916
1002
  }
@@ -918,6 +1004,7 @@ export class BaseServer {
918
1004
  let subscribed = false
919
1005
  if (match) {
920
1006
  let ctx = this.createContext(action, meta)
1007
+ if (ctx.nodeId === '__proto__') return
921
1008
  ctx.params = match
922
1009
  try {
923
1010
  let access = await channel.access(ctx, action, meta)
@@ -967,16 +1054,9 @@ export class BaseServer {
967
1054
  subscribed = true
968
1055
 
969
1056
  if (channel.load) {
1057
+ // Actions from a single `load()` are sent in a single message
970
1058
  let sendBack = await channel.load(ctx, action, meta)
971
- if (Array.isArray(sendBack)) {
972
- await Promise.all(
973
- sendBack.map(i => {
974
- return Array.isArray(i) ? ctx.sendBack(...i) : ctx.sendBack(i)
975
- })
976
- )
977
- } else if (sendBack) {
978
- await ctx.sendBack(sendBack)
979
- }
1059
+ if (sendBack) await ctx.sendBack(sendBack)
980
1060
  }
981
1061
  this.emitter.emit('subscribed', action, meta, Date.now() - start)
982
1062
  this.markAsProcessed(meta)
@@ -1,4 +1,4 @@
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
4
  import type { ServerClient } from '../server-client/index.js'
@@ -59,6 +59,24 @@ export class ConnectContext<Headers extends object = unknown> {
59
59
 
60
60
  constructor(server: Server, client: ServerClient)
61
61
 
62
+ /**
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.
67
+ *
68
+ * ```js
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))
73
+ * }
74
+ * ```
75
+ *
76
+ * @returns Promise with `false` if the client was disconnected.
77
+ */
78
+ drain(): Promise<boolean>
79
+
62
80
  /**
63
81
  * Send action back to the client.
64
82
  *
@@ -66,13 +84,27 @@ export class ConnectContext<Headers extends object = unknown> {
66
84
  * ctx.sendBack({ type: 'login/success', token })
67
85
  * ```
68
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
+ *
69
96
  * Action will not be processed by server’s callbacks from `Server#type`.
70
97
  *
71
- * @param action The action.
98
+ * @param action The action or the array of actions.
72
99
  * @param meta Action’s meta.
73
100
  * @returns Promise until action was added to the server log.
74
101
  */
75
- sendBack(action: AnyAction, meta?: Partial<ServerMeta>): Promise<void>
102
+ sendBack<TypeAction extends Action = AnyAction>(
103
+ action:
104
+ | (TypeAction | [TypeAction, Partial<ServerMeta>])[]
105
+ | TypeAction,
106
+ meta?: Partial<ServerMeta>
107
+ ): Promise<void>
76
108
  }
77
109
 
78
110
  /**
package/context/index.js CHANGED
@@ -33,11 +33,22 @@ export class Context {
33
33
  }
34
34
  }
35
35
 
36
- sendBack(action, meta = {}) {
37
- return this.server.log.add(action, {
38
- clients: [this.clientId],
39
- status: 'processed',
40
- ...meta
41
- })
36
+ drain() {
37
+ return this.server.drain(this.clientId)
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)
42
53
  }
43
54
  }
@@ -2,13 +2,13 @@ import os from 'node:os'
2
2
  import { sep } from 'node:path'
3
3
 
4
4
  import humanFormatter from '../human-formatter/index.js'
5
+ import { BINARY, isBinary } from '../human-formatter/utils.js'
5
6
 
6
7
  const ERROR_CODES = {
7
8
  EACCES: (e, environment) => {
8
9
  let wayToFix = {
9
- development:
10
- 'In dev mode it can be done with sudo:\n' + '$ sudo npm start',
11
- 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}`
12
12
  }
13
13
 
14
14
  return {
@@ -22,7 +22,7 @@ const ERROR_CODES = {
22
22
  EADDRINUSE: e => {
23
23
  let port = String(e.port)
24
24
  let wayToFix = {
25
- darwin: `$ sudo lsof -i ${e.port}\n` + '$ sudo kill -9 `<processid>`',
25
+ darwin: `$ sudo lsof -i ${e.port}\n$ sudo kill -9 \`<processid>\``,
26
26
  linux:
27
27
  '$ su - root\n' +
28
28
  `# netstat -nlp | grep ${e.port}\n` +
@@ -176,23 +176,46 @@ const REPORTERS = {
176
176
  function cleanFromKeys(obj, regexp, seen) {
177
177
  let result = {}
178
178
  for (let key in obj) {
179
- let v = obj[key]
180
- if (typeof v === 'string') {
181
- result[key] = v.replace(regexp, '[SECRET]')
182
- } else if (typeof v === 'object' && !Array.isArray(v) && v !== null) {
183
- if (seen.includes(v)) {
184
- throw new Error('Circular reference in action')
185
- }
186
- seen.push(v)
187
- result[key] = cleanFromKeys(v, regexp, seen)
188
- seen.pop()
189
- } else {
190
- result[key] = v
191
- }
179
+ result[key] = cleanValue(obj[key], regexp, seen)
192
180
  }
193
181
  return result
194
182
  }
195
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
+
196
219
  export function createReporter(options) {
197
220
  let cleanFromLog = options.cleanFromLog || /Bearer [^\s"]+/g
198
221
  function reporter(type, details) {
@@ -218,21 +241,6 @@ export function createReporter(options) {
218
241
  }
219
242
  let stream = options.logger?.stream ?? process.stderr
220
243
 
221
- function createRecord(level, details, msg) {
222
- /* c8 ignore next 4 */
223
- if (typeof details === 'string') {
224
- msg = details
225
- details = {}
226
- }
227
- return {
228
- level,
229
- time: new Date().toISOString(),
230
- pid: process.pid,
231
- ...details,
232
- msg
233
- }
234
- }
235
-
236
244
  reporter.logger = {
237
245
  /* c8 ignore next 3 */
238
246
  debug(details, msg) {