@nxtedition/lib 21.2.1 → 21.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/couch.js +5 -1
  2. package/http.js +53 -48
  3. package/package.json +1 -2
package/couch.js CHANGED
@@ -117,6 +117,7 @@ export function makeCouch(opts) {
117
117
  * @param {number} [options.heartbeat=60000] - The interval at which to send a heartbeat.
118
118
  * @param {function} [options.retry=null] - The function to retry the request on error.
119
119
  * @param {string} [options.since=null] - The sequence number to start from.
120
+ * @param {string} [options.highWaterMark=128 * 1024] - Buffering.
120
121
  * @yields {Array<{ id: string, seq?: string, doc?: Object, deleted?: boolean, changes: Array<{ rev: string }> }>}
121
122
  */
122
123
  async function* changes({ client = defaultClient, signal = null, logger, ...options } = {}) {
@@ -207,6 +208,7 @@ export function makeCouch(opts) {
207
208
  }
208
209
  }
209
210
 
211
+ const highWaterMark = options.highWaterMark
210
212
  const live = options.live == null || !!options.live
211
213
  const retry =
212
214
  options.retry ??
@@ -220,6 +222,7 @@ export function makeCouch(opts) {
220
222
  method,
221
223
  live,
222
224
  retry,
225
+ highWaterMark,
223
226
  params,
224
227
  body,
225
228
  signal: ac.signal,
@@ -245,6 +248,7 @@ export function makeCouch(opts) {
245
248
  body,
246
249
  signal,
247
250
  client,
251
+ highWaterMark = 128 * 1024,
248
252
  blocking = live || !params.limit || params.limit > 256,
249
253
  }) {
250
254
  let retryCount = 0
@@ -270,7 +274,7 @@ export function makeCouch(opts) {
270
274
  signal,
271
275
  headersTimeout: 2 * 60e3,
272
276
  bodyTimeout: 2 * (params.heartbeat || 60e3),
273
- highWaterMark: 128 * 1024,
277
+ highWaterMark,
274
278
  dispatcher: client,
275
279
  }
276
280
 
package/http.js CHANGED
@@ -7,10 +7,9 @@ import querystring from 'fast-querystring'
7
7
  import compose from 'koa-compose'
8
8
  import http from 'http'
9
9
  import fp from 'lodash/fp.js'
10
- import cookie from 'cookie'
11
10
  import tp from 'timers/promises'
12
11
 
13
- const kAbortController = Symbol('abortController')
12
+ export const kAbortController = Symbol('abortController')
14
13
 
15
14
  const ERR_HEADER_EXPR =
16
15
  /^(content-length|content-type|te|host|upgrade|trailers|connection|keep-alive|http2-settings|transfer-encoding|proxy-connection|proxy-authenticate|proxy-authorization)$/i
@@ -41,9 +40,7 @@ export class Context {
41
40
  #ac
42
41
  #url
43
42
  #logger
44
- #headers
45
43
  #query
46
- #cookies
47
44
 
48
45
  constructor(req, res, logger) {
49
46
  assert(req)
@@ -54,17 +51,10 @@ export class Context {
54
51
  this.#req = req
55
52
  this.#res = res
56
53
  this.#logger = logger.child({ reqId: this.#id, url: req.url })
57
- this.#headers = {
58
- 'request-id': this.#id,
59
- }
60
- }
61
-
62
- setHeader(name, value) {
63
- this.#headers[name.toLowerCase()] = value
64
54
  }
65
55
 
66
- removeHeader(name) {
67
- delete this.#headers[name.toLowerCase()]
56
+ get id() {
57
+ return this.#id
68
58
  }
69
59
 
70
60
  get [kAbortController]() {
@@ -82,8 +72,13 @@ export class Context {
82
72
  }
83
73
 
84
74
  set url(value) {
75
+ if (typeof value !== 'string') {
76
+ throw new Error('url must be a string')
77
+ }
78
+
85
79
  this.#req.url = value
86
- this.#url = null
80
+ this.#url = undefined
81
+ this.#query = undefined
87
82
  }
88
83
 
89
84
  get query() {
@@ -108,18 +103,6 @@ export class Context {
108
103
  get method() {
109
104
  return this.#req.method
110
105
  }
111
-
112
- get headers() {
113
- return this.#headers
114
- }
115
-
116
- get cookies() {
117
- if (this.#cookies === undefined) {
118
- this.#cookies = this.req.headers.cookie ? cookie.parse(this.req.headers.cookie) : null
119
- }
120
-
121
- return this.#cookies
122
- }
123
106
  }
124
107
 
125
108
  export async function request2(ctx, next) {
@@ -133,7 +116,9 @@ export async function request2(ctx, next) {
133
116
 
134
117
  const isHealthcheck = req.url === '/healthcheck' || req.url === '/_up'
135
118
 
136
- if (!isHealthcheck) {
119
+ if (isHealthcheck) {
120
+ // Do nothing...
121
+ } else {
137
122
  logger.debug({ req }, 'request started')
138
123
  }
139
124
 
@@ -160,12 +145,12 @@ export async function request2(ctx, next) {
160
145
  ctx[kAbortController]?.abort(err)
161
146
 
162
147
  const statusCode = err.statusCode || err.$metadata?.httpStatusCode || 500
163
- const responseTime = Math.ceil(performance.now() - startTime)
148
+ const elapsedTime = Math.ceil(performance.now() - startTime)
164
149
 
165
150
  if (!res.headersSent && !res.destroyed) {
166
151
  res.statusCode = statusCode
167
152
 
168
- let reqId = req?.id || err.id
153
+ let reqId = ctx.id || req?.id || err.id
169
154
  for (const name of res.getHeaderNames()) {
170
155
  if (!reqId && name === 'request-id') {
171
156
  reqId = res.getHeader(name)
@@ -177,8 +162,20 @@ export async function request2(ctx, next) {
177
162
  res.setHeader('request-id', reqId)
178
163
  }
179
164
 
180
- if (fp.isPlainObject(err.headers)) {
181
- for (const [key, val] of Object.entries(err.headers)) {
165
+ const { headers } = err
166
+
167
+ if (fp.isPlainObject(headers)) {
168
+ for (const [key, val] of Object.entries(headers)) {
169
+ if (!ERR_HEADER_EXPR.test(key)) {
170
+ res.setHeader(key, val)
171
+ }
172
+ }
173
+ } else if (Array.isArray(err.headers)) {
174
+ assert(headers.length % 2 === 0)
175
+ assert(headers.length === 0 || typeof headers[0] === 'string')
176
+ for (let n = 0; n < headers.length; n += 2) {
177
+ const key = headers[n + 0]
178
+ const val = headers[n + 1]
182
179
  if (!ERR_HEADER_EXPR.test(key)) {
183
180
  res.setHeader(key, val)
184
181
  }
@@ -187,23 +184,27 @@ export async function request2(ctx, next) {
187
184
 
188
185
  if (fp.isPlainObject(err.body)) {
189
186
  res.setHeader('content-type', 'application/json')
190
- res.write(JSON.stringify(err.body))
187
+ res.end(JSON.stringify(err.body))
188
+ } else if (typeof err.body === 'string') {
189
+ res.end(err.body)
190
+ } else if (Buffer.isBuffer(err.body)) {
191
+ res.end(err.body)
192
+ } else {
193
+ res.end()
191
194
  }
192
195
 
193
196
  if (statusCode < 500) {
194
- logger.warn({ req, res, err, responseTime }, 'request failed')
197
+ logger.warn({ req, res, err, elapsedTime }, 'request failed')
195
198
  } else {
196
- logger.error({ req, res, err, responseTime }, 'request error')
199
+ logger.error({ req, res, err, elapsedTime }, 'request error')
197
200
  }
198
-
199
- res.end()
200
201
  } else {
201
202
  if (req.aborted || !res.writableEnded || err.name === 'AbortError') {
202
- logger.debug({ req, res, err, responseTime }, 'request aborted')
203
+ logger.debug({ req, res, err, elapsedTime }, 'request aborted')
203
204
  } else if (statusCode < 500) {
204
- logger.warn({ req, res, err, responseTime }, 'request failed')
205
+ logger.warn({ req, res, err, elapsedTime }, 'request failed')
205
206
  } else {
206
- logger.error({ req, res, err, responseTime }, 'request error')
207
+ logger.error({ req, res, err, elapsedTime }, 'request error')
207
208
  }
208
209
 
209
210
  if (!res.writableEnded) {
@@ -276,7 +277,7 @@ export async function request(ctx, next) {
276
277
  ac?.abort(err)
277
278
 
278
279
  const statusCode = err.statusCode || err.$metadata?.httpStatusCode || 500
279
- const responseTime = Math.ceil(performance.now() - startTime)
280
+ const elapsedTime = Math.ceil(performance.now() - startTime)
280
281
 
281
282
  if (!res.headersSent && !res.destroyed) {
282
283
  res.statusCode = statusCode
@@ -303,23 +304,27 @@ export async function request(ctx, next) {
303
304
 
304
305
  if (fp.isPlainObject(err.body)) {
305
306
  res.setHeader('content-type', 'application/json')
306
- res.write(JSON.stringify(err.body))
307
+ res.end(JSON.stringify(err.body))
308
+ } else if (typeof err.body === 'string') {
309
+ res.end(err.body)
310
+ } else if (Buffer.isBuffer(err.body)) {
311
+ res.end(err.body)
312
+ } else {
313
+ res.end()
307
314
  }
308
315
 
309
316
  if (statusCode < 500) {
310
- reqLogger.warn({ res, err, responseTime }, 'request failed')
317
+ reqLogger.warn({ res, err, elapsedTime }, 'request failed')
311
318
  } else {
312
- reqLogger.error({ res, err, responseTime }, 'request error')
319
+ reqLogger.error({ res, err, elapsedTime }, 'request error')
313
320
  }
314
-
315
- res.end()
316
321
  } else {
317
322
  if (req.aborted || !res.writableEnded || err.name === 'AbortError') {
318
- reqLogger.debug({ res, err, responseTime }, 'request aborted')
323
+ reqLogger.debug({ res, err, elapsedTime }, 'request aborted')
319
324
  } else if (statusCode < 500) {
320
- reqLogger.warn({ res, err, responseTime }, 'request failed')
325
+ reqLogger.warn({ res, err, elapsedTime }, 'request failed')
321
326
  } else {
322
- reqLogger.error({ res, err, responseTime }, 'request error')
327
+ reqLogger.error({ res, err, elapsedTime }, 'request error')
323
328
  }
324
329
 
325
330
  if (!res.writableEnded) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "21.2.1",
3
+ "version": "21.3.0",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",
@@ -59,7 +59,6 @@
59
59
  "@elastic/transport": "^8.7.1",
60
60
  "@nxtedition/nxt-undici": "^4.2.13",
61
61
  "content-type": "^1.0.5",
62
- "cookie": "^0.7.2",
63
62
  "date-fns": "^3.6.0",
64
63
  "fast-querystring": "^1.1.1",
65
64
  "hasha": "^6.0.0",