mockaton 10.1.0 → 10.2.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/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/Api.js +1 -1
- package/src/Dashboard.html +1 -1
- package/src/Dashboard.js +51 -56
- package/src/Mockaton.js +16 -9
- package/src/utils/http-request.js +18 -0
- package/src/utils/http-response.js +14 -2
- package/src/utils/logger.js +22 -6
package/index.d.ts
CHANGED
package/package.json
CHANGED
package/src/Api.js
CHANGED
|
@@ -104,7 +104,7 @@ function reinitialize(_, response) {
|
|
|
104
104
|
async function selectCookie(req, response) {
|
|
105
105
|
const error = cookie.setCurrent(await parseJSON(req))
|
|
106
106
|
if (error)
|
|
107
|
-
sendUnprocessableContent(response, error)
|
|
107
|
+
sendUnprocessableContent(response, error?.message || error)
|
|
108
108
|
else
|
|
109
109
|
sendOK(response)
|
|
110
110
|
}
|
package/src/Dashboard.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="stylesheet" href="./mockaton/Dashboard.css">
|
|
6
6
|
<link rel="icon" href="data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m235 33.7v202c0 9.19-5.81 14-17.4 14-11.6 0-17.4-4.83-17.4-14v-151c-0.115-4.49-6.72-5.88-8.46-0.87l-48.3 155c-2.22 7.01-7.72 10.1-16 9.9-3.63-0.191-7.01-1.14-9.66-2.89-2.89-1.72-4.83-4.34-5.57-7.72-11.1-37-22.6-74.3-34.1-111-4.34-14-8.95-31.4-14-48.3-1.82-4.83-8.16-5.32-8.46 1.16v156c0 9.19-5.81 14-17.4 14-11.6 0-17.4-4.83-17.4-14v-207c0-5.74 2.62-13.2 9.39-16.3 7.5-3.14 15-4.05 21.8-3.8 3.14 0 6.03 0.686 8.95 1.46 3.14 0.797 6.03 1.98 8.7 3.63 2.65 1.38 5.32 3.14 7.5 5.57 2.22 2.22 3.87 4.83 5.07 7.72l45.8 157c4.63-15.9 32.4-117 33.3-121 4.12-13.8 7.72-26.5 10.9-38.7 1.16-2.65 2.89-5.32 5.07-7.5 2.15-2.15 4.58-4.12 7.5-5.32 2.65-1.57 5.57-2.89 8.46-3.63 3.14-0.797 9.44-0.988 12.1-0.988 11.6 1.07 29.4 9.14 29.4 27z' fill='%23808080'/%3E%3C/svg%3E">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
-
<meta name="description" content="Mock Server
|
|
8
|
+
<meta name="description" content="HTTP Mock Server">
|
|
9
9
|
<title>Mockaton</title>
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/src/Dashboard.js
CHANGED
|
@@ -78,8 +78,10 @@ for (const k of Object.keys(CSS))
|
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
/** @type {State & {
|
|
81
|
-
* groupByMethod: boolean,
|
|
82
81
|
* canProxy: boolean
|
|
82
|
+
* groupByMethod: boolean
|
|
83
|
+
* toggleGroupByMethod: () => void
|
|
84
|
+
* leftSideWidth?: number
|
|
83
85
|
* }} */
|
|
84
86
|
const state = {
|
|
85
87
|
brokersByMethod: {},
|
|
@@ -87,6 +89,7 @@ const state = {
|
|
|
87
89
|
cookies: [],
|
|
88
90
|
comments: [],
|
|
89
91
|
delay: 0,
|
|
92
|
+
|
|
90
93
|
collectProxied: false,
|
|
91
94
|
proxyFallback: '',
|
|
92
95
|
get canProxy() {
|
|
@@ -127,19 +130,19 @@ const leftSideRef = useRef()
|
|
|
127
130
|
function App() {
|
|
128
131
|
const { leftSideWidth } = state
|
|
129
132
|
return [
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
Header(),
|
|
134
|
+
Menu(),
|
|
132
135
|
r('main', null,
|
|
133
136
|
r('div', {
|
|
134
137
|
ref: leftSideRef,
|
|
135
138
|
style: { width: leftSideWidth + 'px' },
|
|
136
139
|
className: CSS.leftSide
|
|
137
140
|
},
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
MockList(),
|
|
142
|
+
StaticFilesList()),
|
|
140
143
|
r('div', { className: CSS.rightSide },
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
Resizer(),
|
|
145
|
+
PayloadViewer()))
|
|
143
146
|
]
|
|
144
147
|
}
|
|
145
148
|
|
|
@@ -153,15 +156,15 @@ function Header() {
|
|
|
153
156
|
width: 160
|
|
154
157
|
}),
|
|
155
158
|
r('div', null,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
GlobalDelayField(),
|
|
160
|
+
CookieSelector(),
|
|
161
|
+
BulkSelector(),
|
|
162
|
+
ProxyFallbackField(),
|
|
163
|
+
ResetButton()),
|
|
161
164
|
r('button', {
|
|
162
165
|
className: CSS.MenuTrigger,
|
|
163
166
|
popovertarget: 'Menu'
|
|
164
|
-
},
|
|
167
|
+
}, SettingsIcon())
|
|
165
168
|
))
|
|
166
169
|
}
|
|
167
170
|
|
|
@@ -251,7 +254,7 @@ function GlobalDelayField() {
|
|
|
251
254
|
}
|
|
252
255
|
return (
|
|
253
256
|
r('label', className(CSS.Field, CSS.GlobalDelayField),
|
|
254
|
-
r('span', null,
|
|
257
|
+
r('span', null, TimerIcon(), Strings.delay_ms),
|
|
255
258
|
r('input', {
|
|
256
259
|
type: 'number',
|
|
257
260
|
min: 0,
|
|
@@ -263,7 +266,7 @@ function GlobalDelayField() {
|
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
function ProxyFallbackField() {
|
|
266
|
-
const { proxyFallback
|
|
269
|
+
const { proxyFallback } = state
|
|
267
270
|
function onChange() {
|
|
268
271
|
const saveCheckbox = this.closest(`.${CSS.FallbackBackend}`).querySelector('[type=checkbox]')
|
|
269
272
|
saveCheckbox.disabled = !this.validity.valid || !this.value.trim()
|
|
@@ -279,7 +282,7 @@ function ProxyFallbackField() {
|
|
|
279
282
|
return (
|
|
280
283
|
r('div', className(CSS.Field, CSS.FallbackBackend),
|
|
281
284
|
r('label', null,
|
|
282
|
-
r('span', null,
|
|
285
|
+
r('span', null, CloudIcon(), Strings.fallback_server),
|
|
283
286
|
r('input', {
|
|
284
287
|
type: 'url',
|
|
285
288
|
autocomplete: 'none',
|
|
@@ -287,14 +290,11 @@ function ProxyFallbackField() {
|
|
|
287
290
|
value: proxyFallback,
|
|
288
291
|
onChange
|
|
289
292
|
})),
|
|
290
|
-
|
|
291
|
-
collectProxied,
|
|
292
|
-
disabled: !proxyFallback
|
|
293
|
-
})))
|
|
293
|
+
SaveProxiedCheckbox()))
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
function SaveProxiedCheckbox(
|
|
297
|
-
const { collectProxied } = state
|
|
296
|
+
function SaveProxiedCheckbox() {
|
|
297
|
+
const { collectProxied, canProxy } = state
|
|
298
298
|
function onChange() {
|
|
299
299
|
mockaton.setCollectProxied(this.checked)
|
|
300
300
|
.then(parseError)
|
|
@@ -304,7 +304,7 @@ function SaveProxiedCheckbox({ disabled }) {
|
|
|
304
304
|
r('label', className(CSS.SaveProxiedCheckbox),
|
|
305
305
|
r('input', {
|
|
306
306
|
type: 'checkbox',
|
|
307
|
-
disabled,
|
|
307
|
+
disabled: !canProxy,
|
|
308
308
|
checked: collectProxied,
|
|
309
309
|
onChange
|
|
310
310
|
}),
|
|
@@ -330,8 +330,7 @@ function ResetButton() {
|
|
|
330
330
|
/** # MockList */
|
|
331
331
|
|
|
332
332
|
function MockList() {
|
|
333
|
-
const { brokersByMethod, groupByMethod } = state
|
|
334
|
-
const canProxy = state.canProxy
|
|
333
|
+
const { brokersByMethod, groupByMethod, canProxy } = state
|
|
335
334
|
|
|
336
335
|
if (!Object.keys(brokersByMethod).length)
|
|
337
336
|
return (
|
|
@@ -355,14 +354,14 @@ function MockList() {
|
|
|
355
354
|
}
|
|
356
355
|
|
|
357
356
|
function Row({ method, urlMask, urlMaskDittoed, broker }) {
|
|
358
|
-
const canProxy = state
|
|
357
|
+
const { canProxy } = state
|
|
359
358
|
return (
|
|
360
359
|
r('tr', { 'data-method': method, 'data-urlMask': urlMask },
|
|
361
|
-
canProxy && r('td', null,
|
|
362
|
-
r('td', null,
|
|
363
|
-
r('td', null,
|
|
364
|
-
r('td', null,
|
|
365
|
-
r('td', null,
|
|
360
|
+
canProxy && r('td', null, ProxyToggler(broker)),
|
|
361
|
+
r('td', null, DelayRouteToggler(broker)),
|
|
362
|
+
r('td', null, InternalServerErrorToggler(broker)),
|
|
363
|
+
r('td', null, PreviewLink(method, urlMask, urlMaskDittoed)),
|
|
364
|
+
r('td', null, MockSelector(broker))))
|
|
366
365
|
}
|
|
367
366
|
|
|
368
367
|
function rowsFor(targetMethod) {
|
|
@@ -385,7 +384,7 @@ function rowsFor(targetMethod) {
|
|
|
385
384
|
}))
|
|
386
385
|
}
|
|
387
386
|
|
|
388
|
-
function PreviewLink(
|
|
387
|
+
function PreviewLink(method, urlMask, urlMaskDittoed) {
|
|
389
388
|
async function onClick(event) {
|
|
390
389
|
event.preventDefault()
|
|
391
390
|
try {
|
|
@@ -408,8 +407,8 @@ function PreviewLink({ method, urlMask, urlMaskDittoed }) {
|
|
|
408
407
|
: tail))
|
|
409
408
|
}
|
|
410
409
|
|
|
411
|
-
/** @param {
|
|
412
|
-
function MockSelector(
|
|
410
|
+
/** @param {ClientMockBroker} broker */
|
|
411
|
+
function MockSelector(broker) {
|
|
413
412
|
const { groupByMethod } = state
|
|
414
413
|
|
|
415
414
|
function onChange() {
|
|
@@ -457,8 +456,8 @@ function MockSelector({ broker }) {
|
|
|
457
456
|
}, nameFor(file))))))
|
|
458
457
|
}
|
|
459
458
|
|
|
460
|
-
/** @param {
|
|
461
|
-
function DelayRouteToggler(
|
|
459
|
+
/** @param {ClientMockBroker} broker */
|
|
460
|
+
function DelayRouteToggler(broker) {
|
|
462
461
|
function commit(checked) {
|
|
463
462
|
const { method, urlMask } = parseFilename(broker.mocks[0])
|
|
464
463
|
mockaton.setRouteIsDelayed(method, urlMask, checked)
|
|
@@ -472,8 +471,8 @@ function DelayRouteToggler({ broker }) {
|
|
|
472
471
|
}
|
|
473
472
|
|
|
474
473
|
|
|
475
|
-
/** @param {
|
|
476
|
-
function InternalServerErrorToggler(
|
|
474
|
+
/** @param {ClientMockBroker} broker */
|
|
475
|
+
function InternalServerErrorToggler(broker) {
|
|
477
476
|
function onChange() {
|
|
478
477
|
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
479
478
|
mockaton.select(
|
|
@@ -499,8 +498,8 @@ function InternalServerErrorToggler({ broker }) {
|
|
|
499
498
|
r('span', null, '500')))
|
|
500
499
|
}
|
|
501
500
|
|
|
502
|
-
/** @param {
|
|
503
|
-
function ProxyToggler(
|
|
501
|
+
/** @param {ClientMockBroker} broker */
|
|
502
|
+
function ProxyToggler(broker) {
|
|
504
503
|
function onChange() {
|
|
505
504
|
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
506
505
|
mockaton.setRouteIsProxied(method, urlMask, this.checked)
|
|
@@ -519,7 +518,7 @@ function ProxyToggler({ broker }) {
|
|
|
519
518
|
checked: !broker.currentMock.file,
|
|
520
519
|
onChange
|
|
521
520
|
}),
|
|
522
|
-
|
|
521
|
+
CloudIcon()))
|
|
523
522
|
}
|
|
524
523
|
|
|
525
524
|
|
|
@@ -527,8 +526,7 @@ function ProxyToggler({ broker }) {
|
|
|
527
526
|
/** # StaticFilesList */
|
|
528
527
|
|
|
529
528
|
function StaticFilesList() {
|
|
530
|
-
const { staticBrokers } = state
|
|
531
|
-
const canProxy = state.canProxy
|
|
529
|
+
const { staticBrokers, canProxy } = state
|
|
532
530
|
if (!Object.keys(staticBrokers).length)
|
|
533
531
|
return null
|
|
534
532
|
const dp = dittoSplitPaths(Object.keys(staticBrokers)).map(([ditto, tail]) => ditto
|
|
@@ -543,15 +541,15 @@ function StaticFilesList() {
|
|
|
543
541
|
r('tbody', null,
|
|
544
542
|
Object.values(staticBrokers).map((broker, i) =>
|
|
545
543
|
r('tr', null,
|
|
546
|
-
canProxy && r('td', null,
|
|
547
|
-
r('td', null,
|
|
548
|
-
r('td', null,
|
|
544
|
+
canProxy && r('td', null, ProxyStaticToggler()),
|
|
545
|
+
r('td', null, DelayStaticRouteToggler(broker)),
|
|
546
|
+
r('td', null, NotFoundToggler(broker)),
|
|
549
547
|
r('td', null, r('a', { href: broker.route, target: '_blank' }, dp[i]))
|
|
550
548
|
)))))
|
|
551
549
|
}
|
|
552
550
|
|
|
553
|
-
/** @param {
|
|
554
|
-
function DelayStaticRouteToggler(
|
|
551
|
+
/** @param {ClientStaticBroker} broker */
|
|
552
|
+
function DelayStaticRouteToggler(broker) {
|
|
555
553
|
function commit(checked) {
|
|
556
554
|
mockaton.setStaticRouteIsDelayed(broker.route, checked)
|
|
557
555
|
.then(parseError)
|
|
@@ -563,8 +561,8 @@ function DelayStaticRouteToggler({ broker }) {
|
|
|
563
561
|
})
|
|
564
562
|
}
|
|
565
563
|
|
|
566
|
-
/** @param {
|
|
567
|
-
function NotFoundToggler(
|
|
564
|
+
/** @param {ClientStaticBroker} broker */
|
|
565
|
+
function NotFoundToggler(broker) {
|
|
568
566
|
function onChange() {
|
|
569
567
|
mockaton.setStaticRouteStatus(broker.route, this.checked ? 404 : 200)
|
|
570
568
|
.then(parseError)
|
|
@@ -583,7 +581,7 @@ function NotFoundToggler({ broker }) {
|
|
|
583
581
|
r('span', null, '404')))
|
|
584
582
|
}
|
|
585
583
|
|
|
586
|
-
function ProxyStaticToggler(
|
|
584
|
+
function ProxyStaticToggler() { // TODO
|
|
587
585
|
function onChange() {
|
|
588
586
|
}
|
|
589
587
|
return (
|
|
@@ -597,13 +595,13 @@ function ProxyStaticToggler({}) { // TODO
|
|
|
597
595
|
disabled: true,
|
|
598
596
|
onChange
|
|
599
597
|
}),
|
|
600
|
-
|
|
598
|
+
CloudIcon()))
|
|
601
599
|
}
|
|
602
600
|
|
|
603
601
|
|
|
604
602
|
function ClickDragToggler({ checked, commit }) {
|
|
605
603
|
function onPointerEnter(event) {
|
|
606
|
-
if (event.buttons === 1)
|
|
604
|
+
if (event.buttons === 1)
|
|
607
605
|
onPointerDown.call(this)
|
|
608
606
|
}
|
|
609
607
|
function onPointerDown() {
|
|
@@ -881,9 +879,6 @@ function className(...args) {
|
|
|
881
879
|
|
|
882
880
|
|
|
883
881
|
function createElement(tag, props, ...children) {
|
|
884
|
-
if (typeof tag === 'function')
|
|
885
|
-
return tag(props)
|
|
886
|
-
|
|
887
882
|
const node = document.createElement(tag)
|
|
888
883
|
for (const [k, v] of Object.entries(props || {}))
|
|
889
884
|
if (k === 'ref') v.current = node
|
package/src/Mockaton.js
CHANGED
|
@@ -6,12 +6,12 @@ import { config, setup } from './config.js'
|
|
|
6
6
|
import { dispatchMock } from './MockDispatcher.js'
|
|
7
7
|
import { dispatchStatic } from './StaticDispatcher.js'
|
|
8
8
|
import * as staticCollection from './staticCollection.js'
|
|
9
|
-
import { BodyReaderError } from './utils/http-request.js'
|
|
10
9
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
11
10
|
import { setCorsHeaders, isPreflight } from './utils/http-cors.js'
|
|
12
11
|
import { watchMocksDir, watchStaticDir } from './Watcher.js'
|
|
13
12
|
import { apiPatchRequests, apiGetRequests } from './Api.js'
|
|
14
|
-
import {
|
|
13
|
+
import { BodyReaderError, isControlCharFree } from './utils/http-request.js'
|
|
14
|
+
import { sendNoContent, sendInternalServerError, sendUnprocessableContent, sendTooLongURI, sendBadRequest } from './utils/http-response.js'
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
export function Mockaton(options) {
|
|
@@ -39,27 +39,34 @@ export function Mockaton(options) {
|
|
|
39
39
|
|
|
40
40
|
async function onRequest(req, response) {
|
|
41
41
|
response.on('error', logger.warn)
|
|
42
|
+
response.setHeader('Server', 'Mockaton')
|
|
43
|
+
|
|
44
|
+
const url = req.url || ''
|
|
45
|
+
|
|
46
|
+
if (url.length > 2048) {
|
|
47
|
+
sendTooLongURI(response)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!isControlCharFree(url)) {
|
|
52
|
+
sendBadRequest(response)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
42
55
|
|
|
43
56
|
try {
|
|
44
|
-
|
|
57
|
+
const { method } = req
|
|
45
58
|
|
|
46
59
|
if (config.corsAllowed)
|
|
47
60
|
setCorsHeaders(req, response, config)
|
|
48
61
|
|
|
49
|
-
const { url, method } = req
|
|
50
|
-
|
|
51
62
|
if (isPreflight(req))
|
|
52
63
|
sendNoContent(response)
|
|
53
|
-
|
|
54
64
|
else if (method === 'PATCH' && apiPatchRequests.has(url))
|
|
55
65
|
await apiPatchRequests.get(url)(req, response)
|
|
56
|
-
|
|
57
66
|
else if (method === 'GET' && apiGetRequests.has(url))
|
|
58
67
|
apiGetRequests.get(url)(req, response)
|
|
59
|
-
|
|
60
68
|
else if (method === 'GET' && staticCollection.brokerByRoute(url))
|
|
61
69
|
await dispatchStatic(req, response)
|
|
62
|
-
|
|
63
70
|
else
|
|
64
71
|
await dispatchMock(req, response)
|
|
65
72
|
}
|
|
@@ -48,3 +48,21 @@ export function readBody(req, parser = a => a) {
|
|
|
48
48
|
}
|
|
49
49
|
})
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
export const reControlAndDelChars = /[\x00-\x1f\x7f]/
|
|
53
|
+
export function isControlCharFree(url) {
|
|
54
|
+
try {
|
|
55
|
+
const decoded = decode(url)
|
|
56
|
+
return decoded && !reControlAndDelChars.test(decoded)
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function decode(url) {
|
|
64
|
+
const candidate = decodeURIComponent(url)
|
|
65
|
+
return candidate === decodeURIComponent(candidate)
|
|
66
|
+
? candidate
|
|
67
|
+
: '' // reject multiple encodings
|
|
68
|
+
}
|
|
@@ -28,21 +28,33 @@ export function sendNoContent(response) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
export function sendBadRequest(response) {
|
|
32
|
+
response.statusCode = 400
|
|
33
|
+
logger.access(response)
|
|
34
|
+
response.end()
|
|
35
|
+
}
|
|
36
|
+
|
|
31
37
|
export function sendNotFound(response) {
|
|
32
38
|
response.statusCode = 404
|
|
33
39
|
logger.access(response)
|
|
34
40
|
response.end()
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
export function sendTooLongURI(response) {
|
|
44
|
+
response.statusCode = 414
|
|
45
|
+
logger.access(response)
|
|
46
|
+
response.end()
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
export function sendUnprocessableContent(response, error) {
|
|
38
|
-
logger.
|
|
50
|
+
logger.access(response, error)
|
|
39
51
|
response.statusCode = 422
|
|
40
52
|
response.end(error)
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
|
|
44
56
|
export function sendInternalServerError(response, error) {
|
|
45
|
-
logger.error(error?.message || error, error?.stack ||
|
|
57
|
+
logger.error(500, logger.sanitizeURL(response.req.url), error?.message || error, error?.stack || '')
|
|
46
58
|
response.statusCode = 500
|
|
47
59
|
response.end()
|
|
48
60
|
}
|
package/src/utils/logger.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { decode, reControlAndDelChars } from './http-request.js'
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
export const logger = new class {
|
|
2
5
|
#level = 'normal'
|
|
3
6
|
|
|
@@ -12,16 +15,17 @@ export const logger = new class {
|
|
|
12
15
|
|
|
13
16
|
accessMock(url, ...msg) {
|
|
14
17
|
if (this.#level !== 'quiet')
|
|
15
|
-
console.log(this.#msg('MOCK', this
|
|
18
|
+
console.log(this.#msg('MOCK', this.sanitizeURL(url), ...msg))
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
access(response) {
|
|
21
|
+
access(response, error) {
|
|
19
22
|
if (this.#level === 'verbose')
|
|
20
23
|
console.log(this.#msg(
|
|
21
24
|
'ACCESS',
|
|
22
25
|
response.req.method,
|
|
23
26
|
response.statusCode,
|
|
24
|
-
this
|
|
27
|
+
this.sanitizeURL(response.req.url),
|
|
28
|
+
error))
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
warn(...msg) {
|
|
@@ -33,10 +37,22 @@ export const logger = new class {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
#msg(...msg) {
|
|
40
|
+
if (!msg.at(-1))
|
|
41
|
+
msg.pop()
|
|
36
42
|
return [new Date().toISOString(), ...msg].join('::')
|
|
37
43
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
|
|
45
|
+
sanitizeURL(url) {
|
|
46
|
+
try {
|
|
47
|
+
const decoded = decode(url)
|
|
48
|
+
if (!decoded)
|
|
49
|
+
return '__MULTI_ENCODED_URL__'
|
|
50
|
+
return decoded
|
|
51
|
+
.replace(reControlAndDelChars, '')
|
|
52
|
+
.slice(0, 200)
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return '__NON_DECODABLE_URL__'
|
|
56
|
+
}
|
|
41
57
|
}
|
|
42
58
|
}
|