@nxtedition/lib 19.1.6 → 19.3.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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/s3.js +85 -0
  3. package/serializers.js +34 -20
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "19.1.6",
3
+ "version": "19.3.0",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",
package/s3.js CHANGED
@@ -3,10 +3,73 @@ import stream from 'node:stream'
3
3
  import tp from 'node:timers/promises'
4
4
  import AWS from '@aws-sdk/client-s3'
5
5
  import PQueue from 'p-queue'
6
+ import { NodeHttpHandler } from '@smithy/node-http-handler'
7
+ import undici from 'undici'
6
8
 
7
9
  const QUEUE = new PQueue({ concurrency: 8 })
8
10
  const MD5_HEX_EXPR = /^[A-F0-9]{32}$/i
9
11
 
12
+ /**
13
+ * @typedef {import('undici').Dispatcher} Dispatcher
14
+ * @typedef {import('@aws-sdk/client-s3').S3ClientConfig & {dispatcher?: Dispatcher}} S3ClientConfig
15
+ */
16
+
17
+ export class S3Client extends AWS.S3Client {
18
+ /**
19
+ * @param {S3ClientConfig} config
20
+ */
21
+ constructor(config) {
22
+ const { dispatcher, ...options } = config
23
+ super({
24
+ requestHandler: new UndiciRequestHandler(dispatcher),
25
+ ...options,
26
+ })
27
+ }
28
+ }
29
+
30
+ class UndiciRequestHandler extends NodeHttpHandler {
31
+ #dispatcher
32
+
33
+ /**
34
+ * @param {Dispatcher=} dispatcher
35
+ */
36
+ constructor(dispatcher) {
37
+ super()
38
+ this.#dispatcher = dispatcher
39
+ }
40
+
41
+ /**
42
+ * @param {import('@smithy/protocol-http').HttpRequest} request
43
+ * @param {import('@smithy/types').HttpHandlerOptions=} options
44
+ */
45
+ async handle(request, options) {
46
+ const abortSignal = options?.abortSignal
47
+
48
+ const { protocol, hostname, port, path, ...requestOptions } = request
49
+ // NOTE: Expect header is not supported by undici
50
+ const { Expect, ...headers } = request.headers
51
+ const typedMethod = /** @type {import('undici').Dispatcher.HttpMethod} */ (request.method)
52
+
53
+ const url = `${request.protocol}//${request.hostname}${request.port ? `:${request.port}` : ''}${request.path}`
54
+
55
+ const response = await undici.request(url, {
56
+ ...requestOptions,
57
+ method: typedMethod,
58
+ signal: abortSignal,
59
+ dispatcher: this.#dispatcher,
60
+ headers,
61
+ body: request.body,
62
+ })
63
+
64
+ return {
65
+ response: {
66
+ ...response,
67
+ headers: getTransformedHeaders(response.headers),
68
+ },
69
+ }
70
+ }
71
+ }
72
+
10
73
  /**
11
74
  * Uploads a file to S3 using multipart upload.
12
75
  *
@@ -241,3 +304,25 @@ export async function upload({
241
304
  signal?.removeEventListener('abort', onAbort)
242
305
  }
243
306
  }
307
+
308
+ /**
309
+ * @see https://github.com/smithy-lang/smithy-typescript/blob/main/packages/node-http-handler/src/get-transformed-headers.ts
310
+ *
311
+ * @param {import('http2').IncomingHttpHeaders} headers
312
+ * @returns {import('@smithy/types').HeaderBag}
313
+ */
314
+ function getTransformedHeaders(headers) {
315
+ /**
316
+ * @type {import('@smithy/types').HeaderBag}
317
+ */
318
+ const transformedHeaders = {}
319
+
320
+ for (const name of Object.keys(headers)) {
321
+ const headerValues = headers[name]
322
+ transformedHeaders[name] = Array.isArray(headerValues)
323
+ ? headerValues.join(',')
324
+ : headerValues ?? ''
325
+ }
326
+
327
+ return transformedHeaders
328
+ }
package/serializers.js CHANGED
@@ -90,6 +90,10 @@ export default {
90
90
  },
91
91
  }
92
92
 
93
+ // TODO (fix): Merge with errros/serializeError.
94
+ // Note that 'data' here should be string while in "serializeError" it should be object.
95
+ // Maybe we should rename serializeError to makeErrorObject or somehting??
96
+
93
97
  // Based on: https://github.com/pinojs/pino-std-serializers
94
98
 
95
99
  const seen = Symbol('circular-ref-tag')
@@ -136,17 +140,13 @@ Object.defineProperty(pinoErrProto, rawSymbol, {
136
140
 
137
141
  function errSerializer(err) {
138
142
  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
143
+ return err.length === 0 ? undefined : errSerializer({ message: '', errors: err })
144
+ } else if (!isErrorLike(err)) {
145
+ return undefined
146
+ } else if (err.message === '' && Array.isArray(err.errors) && err.errors.length === 0) {
147
+ return undefined
148
+ } else if (err.message === '' && Array.isArray(err.errors) && err.errors.length === 1) {
149
+ return errSerializer(err.errors[0])
150
150
  }
151
151
 
152
152
  err[seen] = undefined // tag to prevent re-looking at this
@@ -171,20 +171,34 @@ function errSerializer(err) {
171
171
  if (!Object.prototype.hasOwnProperty.call(val, seen)) {
172
172
  _err[key] = errSerializer(val)
173
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 {
174
+ } else if (val != null) {
183
175
  _err[key] = val
184
176
  }
185
177
  }
186
178
  }
187
179
 
180
+ if (_err.data != null && typeof _err.data !== 'string') {
181
+ _err.data = JSON.stringify(_err.data, undefined, 2)
182
+ }
183
+
184
+ if (_err.code != null && typeof _err.code !== 'string') {
185
+ _err.code = String(_err.code)
186
+ }
187
+
188
+ if (_err.statusCode == null && _err.status != null) {
189
+ _err.statusCode = Number(_err.status) ?? undefined
190
+ }
191
+
192
+ if (_err.signalCode == null && _err.signal != null) {
193
+ _err.signalCode = SIGNALS[_err._signal] ?? _err._signal
194
+ } else if (_err.signalCode != null) {
195
+ _err.signalCode = SIGNALS[_err.signalCode] ?? _err.signalCode
196
+ }
197
+
198
+ if (_err.exitCode == null && _err.code != null && /^([A-Z]+|[a-z]+|[0-9]+)$/.test(_err.code)) {
199
+ _err.exitCode = String(_err.code)
200
+ }
201
+
188
202
  delete err[seen] // clean up tag in case err is serialized again later
189
203
  _err.raw = err
190
204
  return _err