@nxtedition/lib 19.1.3 → 19.1.6
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/errors.js +14 -4
- package/package.json +1 -2
- package/s3.js +23 -8
- package/serializers.js +105 -36
package/errors.js
CHANGED
|
@@ -42,12 +42,16 @@ export function parseError(error) {
|
|
|
42
42
|
const kSeen = Symbol('kSeen')
|
|
43
43
|
|
|
44
44
|
export function serializeError(error) {
|
|
45
|
+
return _serializeError(error, { depth: 0 })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function _serializeError(error, { depth }) {
|
|
45
49
|
if (!error) {
|
|
46
50
|
return null
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
if (typeof error === 'string') {
|
|
50
|
-
return [
|
|
54
|
+
return [_serializeError({ message: error }, { depth })]
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
if (Buffer.isBuffer(error)) {
|
|
@@ -55,13 +59,17 @@ export function serializeError(error) {
|
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
if (Array.isArray(error)) {
|
|
58
|
-
return error.map(
|
|
62
|
+
return error.map((x) => _serializeError(x, { depth })).filter(Boolean)
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
if (Object.prototype.hasOwnProperty.call(error, kSeen)) {
|
|
62
66
|
return null
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
if (depth > 8) {
|
|
70
|
+
return [{ messages: 'Maximum error serialization depth exceeded' }]
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
error[kSeen] = undefined
|
|
66
74
|
|
|
67
75
|
const type =
|
|
@@ -104,8 +112,10 @@ export function serializeError(error) {
|
|
|
104
112
|
signalCode = SIGNALS[signalCode] ?? signalCode
|
|
105
113
|
}
|
|
106
114
|
|
|
107
|
-
errors = Array.isArray(errors)
|
|
108
|
-
|
|
115
|
+
errors = Array.isArray(errors)
|
|
116
|
+
? errors.map((x) => _serializeError(x, { depth: depth + 1 })).filter(Boolean)
|
|
117
|
+
: undefined
|
|
118
|
+
cause = cause ? _serializeError(cause, { depth: depth + 1 }) : undefined
|
|
109
119
|
|
|
110
120
|
delete error[kSeen]
|
|
111
121
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "19.1.
|
|
3
|
+
"version": "19.1.6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"type": "module",
|
|
@@ -94,7 +94,6 @@
|
|
|
94
94
|
"object-hash": "^3.0.0",
|
|
95
95
|
"p-queue": "^8.0.1",
|
|
96
96
|
"pino": "^8.20.0",
|
|
97
|
-
"pino-std-serializers": "^6.2.2",
|
|
98
97
|
"qs": "^6.12.1",
|
|
99
98
|
"request-target": "^1.0.2",
|
|
100
99
|
"smpte-timecode": "^1.3.5",
|
package/s3.js
CHANGED
|
@@ -4,7 +4,8 @@ import tp from 'node:timers/promises'
|
|
|
4
4
|
import AWS from '@aws-sdk/client-s3'
|
|
5
5
|
import PQueue from 'p-queue'
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const QUEUE = new PQueue({ concurrency: 8 })
|
|
8
|
+
const MD5_HEX_EXPR = /^[A-F0-9]{32}$/i
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Uploads a file to S3 using multipart upload.
|
|
@@ -14,6 +15,7 @@ const queue = new PQueue({ concurrency: 8 })
|
|
|
14
15
|
* @param {AbortSignal} options.signal - The signal to abort the upload.
|
|
15
16
|
* @param {Object} options.logger - The logger to use.
|
|
16
17
|
* @param {number} [options.partSize=16e6] - The size of each part in the multipart upload.
|
|
18
|
+
* @param {PQueue} [options.queue] - The queue to use for part uploads.
|
|
17
19
|
* @param {Object} options.params - The parameters for the upload.
|
|
18
20
|
* @param {Buffer|NodeJS.ReadStream} options.params.Body - The data to upload.
|
|
19
21
|
* @param {string} options.params.Key - The key of the object.
|
|
@@ -22,7 +24,14 @@ const queue = new PQueue({ concurrency: 8 })
|
|
|
22
24
|
* @param {number} [options.params.ContentLength] - The length of the object.
|
|
23
25
|
* @returns {Promise<Object>} The result of the upload.
|
|
24
26
|
*/
|
|
25
|
-
export async function upload({
|
|
27
|
+
export async function upload({
|
|
28
|
+
client: s3,
|
|
29
|
+
queue = QUEUE,
|
|
30
|
+
signal,
|
|
31
|
+
logger,
|
|
32
|
+
partSize = 16e6,
|
|
33
|
+
params,
|
|
34
|
+
}) {
|
|
26
35
|
if (s3 == null) {
|
|
27
36
|
throw new Error('Invalid client')
|
|
28
37
|
}
|
|
@@ -35,9 +44,18 @@ export async function upload({ client: s3, signal, logger, partSize = 16e6, para
|
|
|
35
44
|
throw new Error('Invalid params')
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
const { Body, Key, Bucket, ContentMD5, ContentLength } = params
|
|
47
|
+
const { Body, Key, Bucket, ContentMD5, ContentLength } = params
|
|
39
48
|
|
|
40
|
-
|
|
49
|
+
const size = ContentLength != null ? Number(ContentLength) : null
|
|
50
|
+
|
|
51
|
+
if (size != null && (!Number.isFinite(size) || size < 0)) {
|
|
52
|
+
throw new Error('Invalid params.ContentLength')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const hash = ContentMD5 != null ? Buffer.from(ContentMD5, 'base64').toString('hex') : null
|
|
56
|
+
if (hash != null && !MD5_HEX_EXPR.test(hash)) {
|
|
57
|
+
throw new Error('Invalid params.ContentMD5')
|
|
58
|
+
}
|
|
41
59
|
|
|
42
60
|
const promises = []
|
|
43
61
|
|
|
@@ -123,7 +141,7 @@ export async function upload({ client: s3, signal, logger, partSize = 16e6, para
|
|
|
123
141
|
} catch (err) {
|
|
124
142
|
logger?.warn({ err }, 'part upload failed')
|
|
125
143
|
|
|
126
|
-
if (retryCount <
|
|
144
|
+
if (retryCount < 5) {
|
|
127
145
|
logger?.debug({ retryCount }, 'part upload retry')
|
|
128
146
|
await tp.setTimeout(1e3, undefined, { signal: uploader.signal })
|
|
129
147
|
} else {
|
|
@@ -196,9 +214,6 @@ export async function upload({ client: s3, signal, logger, partSize = 16e6, para
|
|
|
196
214
|
parts,
|
|
197
215
|
}
|
|
198
216
|
|
|
199
|
-
const size = ContentLength != null ? Number(ContentLength) : null
|
|
200
|
-
const hash = ContentMD5 ? Buffer.from(ContentMD5, 'base64').toString('hex') : null
|
|
201
|
-
|
|
202
217
|
if (size != null && size !== result.size) {
|
|
203
218
|
throw new Error(`Expected size ${size} but got ${result.size}`)
|
|
204
219
|
}
|
package/serializers.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import serializers from 'pino-std-serializers'
|
|
2
1
|
import { SIGNALS } from './platform.js'
|
|
3
2
|
import { util } from 'undici'
|
|
4
3
|
|
|
@@ -25,41 +24,7 @@ function getHeaders(obj) {
|
|
|
25
24
|
export default {
|
|
26
25
|
data: (data) =>
|
|
27
26
|
data != null && typeof data === 'object' ? JSON.stringify(data, undefined, 2) : data,
|
|
28
|
-
err: (err) =>
|
|
29
|
-
// TODO (fix): Merge with errors/serializeError?
|
|
30
|
-
|
|
31
|
-
if (Buffer.isBuffer(err)) {
|
|
32
|
-
err = new Error('unexpected buffer error')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (Array.isArray(err)) {
|
|
36
|
-
err = err.length === 1 ? err[0] : new AggregateError(err)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (err == null) {
|
|
40
|
-
return undefined
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const ret = serializers.err(err)
|
|
44
|
-
|
|
45
|
-
if (ret == null) {
|
|
46
|
-
return undefined
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (ret.data !== null && typeof ret.data === 'object') {
|
|
50
|
-
ret.data = JSON.stringify(ret.data)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (typeof ret.signal === 'number') {
|
|
54
|
-
ret.signal = SIGNALS[ret.signal] ?? String(ret.signal)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (typeof ret.code === 'number') {
|
|
58
|
-
ret.code = String(ret.code)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return ret
|
|
62
|
-
},
|
|
27
|
+
err: (err) => errSerializer(err),
|
|
63
28
|
res: (res) =>
|
|
64
29
|
res && {
|
|
65
30
|
id: res.id || res.req?.id || getHeader(res, 'request-id') || getHeader(res.req, 'request-id'),
|
|
@@ -124,3 +89,107 @@ export default {
|
|
|
124
89
|
}
|
|
125
90
|
},
|
|
126
91
|
}
|
|
92
|
+
|
|
93
|
+
// Based on: https://github.com/pinojs/pino-std-serializers
|
|
94
|
+
|
|
95
|
+
const seen = Symbol('circular-ref-tag')
|
|
96
|
+
const rawSymbol = Symbol('pino-raw-err-ref')
|
|
97
|
+
|
|
98
|
+
const pinoErrProto = Object.create(
|
|
99
|
+
{},
|
|
100
|
+
{
|
|
101
|
+
type: {
|
|
102
|
+
enumerable: true,
|
|
103
|
+
writable: true,
|
|
104
|
+
value: undefined,
|
|
105
|
+
},
|
|
106
|
+
message: {
|
|
107
|
+
enumerable: true,
|
|
108
|
+
writable: true,
|
|
109
|
+
value: undefined,
|
|
110
|
+
},
|
|
111
|
+
stack: {
|
|
112
|
+
enumerable: true,
|
|
113
|
+
writable: true,
|
|
114
|
+
value: undefined,
|
|
115
|
+
},
|
|
116
|
+
aggregateErrors: {
|
|
117
|
+
enumerable: true,
|
|
118
|
+
writable: true,
|
|
119
|
+
value: undefined,
|
|
120
|
+
},
|
|
121
|
+
raw: {
|
|
122
|
+
enumerable: false,
|
|
123
|
+
get: function () {
|
|
124
|
+
return this[rawSymbol]
|
|
125
|
+
},
|
|
126
|
+
set: function (val) {
|
|
127
|
+
this[rawSymbol] = val
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
)
|
|
132
|
+
Object.defineProperty(pinoErrProto, rawSymbol, {
|
|
133
|
+
writable: true,
|
|
134
|
+
value: {},
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
function errSerializer(err) {
|
|
138
|
+
if (Array.isArray(err)) {
|
|
139
|
+
if (err.length === 0) {
|
|
140
|
+
return null
|
|
141
|
+
} else if (err.length === 1) {
|
|
142
|
+
return errSerializer(err[0])
|
|
143
|
+
} else {
|
|
144
|
+
return new AggregateError(err.map(errSerializer))
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!isErrorLike(err)) {
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
err[seen] = undefined // tag to prevent re-looking at this
|
|
153
|
+
const _err = Object.create(pinoErrProto)
|
|
154
|
+
_err.type =
|
|
155
|
+
toString.call(err.constructor) === '[object Function]' ? err.constructor.name : err.name
|
|
156
|
+
_err.message = err.message
|
|
157
|
+
_err.stack = err.stack
|
|
158
|
+
|
|
159
|
+
if (Array.isArray(err.errors)) {
|
|
160
|
+
_err.aggregateErrors = err.errors.map((err) => errSerializer(err))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (isErrorLike(err.cause) && !Object.prototype.hasOwnProperty.call(err.cause, seen)) {
|
|
164
|
+
_err.cause = errSerializer(err.cause)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const key in err) {
|
|
168
|
+
if (_err[key] === undefined) {
|
|
169
|
+
const val = err[key]
|
|
170
|
+
if (isErrorLike(val)) {
|
|
171
|
+
if (!Object.prototype.hasOwnProperty.call(val, seen)) {
|
|
172
|
+
_err[key] = errSerializer(val)
|
|
173
|
+
}
|
|
174
|
+
} else if (val == null) {
|
|
175
|
+
// Do nothing...
|
|
176
|
+
} else if (key === 'data' && typeof val === 'object') {
|
|
177
|
+
_err[key] = JSON.stringify(val, undefined, 2)
|
|
178
|
+
} else if (key === 'code' && typeof val === 'number') {
|
|
179
|
+
_err[key] = String(val)
|
|
180
|
+
} else if (key === 'signal' && typeof val === 'number') {
|
|
181
|
+
_err[key] = SIGNALS[val] ?? String(val)
|
|
182
|
+
} else {
|
|
183
|
+
_err[key] = val
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
delete err[seen] // clean up tag in case err is serialized again later
|
|
189
|
+
_err.raw = err
|
|
190
|
+
return _err
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const isErrorLike = (err) => {
|
|
194
|
+
return err && typeof err.message === 'string'
|
|
195
|
+
}
|