@nxtedition/lib 19.8.13 → 19.8.15
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/couch.js +173 -148
- package/package.json +7 -7
package/couch.js
CHANGED
|
@@ -7,7 +7,13 @@ import tp from 'timers/promises'
|
|
|
7
7
|
import { defaultDelay as delay } from './http.js'
|
|
8
8
|
import urljoin from 'url-join'
|
|
9
9
|
import { AbortError } from './errors.js'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
dispatch,
|
|
12
|
+
Agent,
|
|
13
|
+
Pool,
|
|
14
|
+
parseHeaders,
|
|
15
|
+
request as undiciRequest,
|
|
16
|
+
} from '@nxtedition/nxt-undici'
|
|
11
17
|
|
|
12
18
|
// https://github.com/fastify/fastify/blob/main/lib/reqIdGenFactory.js
|
|
13
19
|
// 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
|
|
@@ -191,12 +197,6 @@ export function makeCouch(opts) {
|
|
|
191
197
|
throw new Error('invalid pathname')
|
|
192
198
|
}
|
|
193
199
|
|
|
194
|
-
const batched = options.batched || false
|
|
195
|
-
const live = options.live == null || !!options.live
|
|
196
|
-
const retry = options.retry
|
|
197
|
-
|
|
198
|
-
let retryCount = 0
|
|
199
|
-
|
|
200
200
|
const ac = new AbortController()
|
|
201
201
|
const onAbort = () => {
|
|
202
202
|
ac.abort()
|
|
@@ -214,169 +214,192 @@ export function makeCouch(opts) {
|
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
const batched = options.batched || false
|
|
218
|
+
const live = options.live == null || !!options.live
|
|
219
|
+
const retry = options.retry
|
|
220
|
+
const limit = options.limit
|
|
221
|
+
|
|
218
222
|
try {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
+
yield* _changes({
|
|
224
|
+
batched,
|
|
225
|
+
live,
|
|
226
|
+
retry,
|
|
227
|
+
limit,
|
|
228
|
+
params,
|
|
229
|
+
method,
|
|
230
|
+
body,
|
|
231
|
+
signal: ac.signal,
|
|
232
|
+
client,
|
|
233
|
+
})
|
|
234
|
+
} finally {
|
|
235
|
+
ac.abort()
|
|
236
|
+
if (signal) {
|
|
237
|
+
if (signal.off) {
|
|
238
|
+
signal.off('abort', onAbort)
|
|
239
|
+
} else if (signal.removeEventListener) {
|
|
240
|
+
signal.removeEventListener('abort', onAbort)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
223
245
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
246
|
+
async function* _changes({ batched, live, retry, limit, params, method, body, signal, client }) {
|
|
247
|
+
let retryCount = 0
|
|
248
|
+
let remaining = Number(limit) || Infinity
|
|
249
|
+
while (true) {
|
|
250
|
+
let src
|
|
251
|
+
try {
|
|
252
|
+
const query = { ...params, feed: live ? 'continuous' : 'normal' }
|
|
227
253
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
idempotent: false,
|
|
232
|
-
blocking: true,
|
|
233
|
-
method,
|
|
234
|
-
body: JSON.stringify(body),
|
|
235
|
-
signal: ac.signal,
|
|
236
|
-
headers: {
|
|
237
|
-
'user-agent': userAgent,
|
|
238
|
-
'request-id': genReqId(),
|
|
239
|
-
...(body ? { 'content-type': 'application/json' } : {}),
|
|
240
|
-
},
|
|
241
|
-
bodyTimeout: 2 * (params.heartbeat || 60e3),
|
|
242
|
-
}
|
|
254
|
+
if (Number.isFinite(remaining)) {
|
|
255
|
+
query.limit = remaining
|
|
256
|
+
}
|
|
243
257
|
|
|
244
|
-
|
|
258
|
+
const ureq = {
|
|
259
|
+
path: `${dbPathname}/_changes`,
|
|
260
|
+
origin: dbOrigin,
|
|
261
|
+
query,
|
|
262
|
+
idempotent: false,
|
|
263
|
+
blocking: true,
|
|
264
|
+
method,
|
|
265
|
+
body: JSON.stringify(body),
|
|
266
|
+
signal,
|
|
267
|
+
headers: {
|
|
268
|
+
'user-agent': userAgent,
|
|
269
|
+
'request-id': genReqId(),
|
|
270
|
+
...(body ? { 'content-type': 'application/json' } : {}),
|
|
271
|
+
},
|
|
272
|
+
bodyTimeout: 2 * (params.heartbeat || 60e3),
|
|
273
|
+
highWaterMark: 256 * 1024,
|
|
274
|
+
dispatcher: client,
|
|
275
|
+
}
|
|
245
276
|
|
|
246
|
-
|
|
247
|
-
throw makeError(ureq, {
|
|
248
|
-
status: ures.statusCode,
|
|
249
|
-
headers: ures.headers,
|
|
250
|
-
data: await ures.body.text(),
|
|
251
|
-
})
|
|
252
|
-
}
|
|
277
|
+
const ures = await undiciRequest(ureq)
|
|
253
278
|
|
|
254
|
-
|
|
279
|
+
if (ures.statusCode < 200 || ures.statusCode >= 300) {
|
|
280
|
+
throw makeError(ureq, {
|
|
281
|
+
status: ures.statusCode,
|
|
282
|
+
headers: ures.headers,
|
|
283
|
+
data: await ures.body.text(),
|
|
284
|
+
})
|
|
285
|
+
}
|
|
255
286
|
|
|
256
|
-
|
|
287
|
+
src = ures.body
|
|
257
288
|
|
|
258
|
-
|
|
259
|
-
let error = null
|
|
260
|
-
let ended = false
|
|
261
|
-
let state = 0
|
|
289
|
+
const changes = []
|
|
262
290
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
291
|
+
let resume = null
|
|
292
|
+
let error = null
|
|
293
|
+
let ended = false
|
|
294
|
+
let state = 0
|
|
295
|
+
|
|
296
|
+
function maybeResume() {
|
|
297
|
+
if (resume) {
|
|
298
|
+
resume()
|
|
299
|
+
resume = null
|
|
268
300
|
}
|
|
301
|
+
}
|
|
269
302
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
303
|
+
src
|
|
304
|
+
.on('readable', maybeResume)
|
|
305
|
+
.on('error', (err) => {
|
|
306
|
+
error = err
|
|
307
|
+
maybeResume()
|
|
308
|
+
})
|
|
309
|
+
.on('end', () => {
|
|
310
|
+
ended = true
|
|
311
|
+
maybeResume()
|
|
312
|
+
})
|
|
313
|
+
.on('close', maybeResume)
|
|
314
|
+
|
|
315
|
+
let str = ''
|
|
316
|
+
while (true) {
|
|
317
|
+
const chunk = src.read()
|
|
318
|
+
|
|
319
|
+
if (chunk !== null) {
|
|
320
|
+
const lines = (str + chunk).split(/\r?\n/)
|
|
321
|
+
str = lines.pop() ?? ''
|
|
322
|
+
|
|
323
|
+
for (const line of lines) {
|
|
324
|
+
if (line === '') {
|
|
325
|
+
// hearbeat
|
|
326
|
+
yield batched ? [] : null
|
|
327
|
+
} else if (line === ',') {
|
|
328
|
+
// Do nothing. Couch sometimes insert new line between
|
|
329
|
+
// json body and comma.
|
|
330
|
+
} else if (live) {
|
|
331
|
+
const data = JSON.parse(line)
|
|
332
|
+
if (data.last_seq) {
|
|
333
|
+
params.since = data.last_seq
|
|
334
|
+
} else {
|
|
335
|
+
params.since = data.seq || params.since
|
|
336
|
+
changes.push(data)
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
// NOTE: This makes some assumptions about the format of the JSON.
|
|
340
|
+
if (state === 0) {
|
|
341
|
+
if (line.endsWith('[')) {
|
|
342
|
+
state = 1
|
|
301
343
|
} else {
|
|
302
|
-
|
|
303
|
-
changes.push(data)
|
|
344
|
+
assert(false, 'invalid head: ' + line)
|
|
304
345
|
}
|
|
305
|
-
} else {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
assert(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const idx = line.lastIndexOf('}') + 1
|
|
318
|
-
try {
|
|
319
|
-
assert(idx >= 0, 'invalid row: ' + idx + ' ' + line)
|
|
320
|
-
const change = JSON.parse(line.slice(0, idx))
|
|
321
|
-
params.since = change.seq || params.since
|
|
322
|
-
changes.push(change)
|
|
323
|
-
} catch (err) {
|
|
324
|
-
throw Object.assign(err, { data: line })
|
|
325
|
-
}
|
|
346
|
+
} else if (state === 1) {
|
|
347
|
+
if (line.startsWith(']')) {
|
|
348
|
+
state = 2
|
|
349
|
+
} else {
|
|
350
|
+
const idx = line.lastIndexOf('}') + 1
|
|
351
|
+
try {
|
|
352
|
+
assert(idx >= 0, 'invalid row: ' + idx + ' ' + line)
|
|
353
|
+
const change = JSON.parse(line.slice(0, idx))
|
|
354
|
+
params.since = change.seq || params.since
|
|
355
|
+
changes.push(change)
|
|
356
|
+
} catch (err) {
|
|
357
|
+
throw Object.assign(err, { data: line })
|
|
326
358
|
}
|
|
327
|
-
} else if (state === 2) {
|
|
328
|
-
state = 3
|
|
329
|
-
params.since = JSON.parse('{' + line).last_seq
|
|
330
|
-
assert(params.since, 'invalid trailer: ' + line)
|
|
331
359
|
}
|
|
360
|
+
} else if (state === 2) {
|
|
361
|
+
state = 3
|
|
362
|
+
params.since = JSON.parse('{' + line).last_seq
|
|
363
|
+
assert(params.since, 'invalid trailer: ' + line)
|
|
332
364
|
}
|
|
333
365
|
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
|
|
366
|
+
}
|
|
367
|
+
} else if (changes.length) {
|
|
368
|
+
remaining -= changes.length
|
|
369
|
+
assert(remaining >= 0, 'invalid remaining: ' + remaining)
|
|
337
370
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
} else {
|
|
341
|
-
yield* changes.splice(0)
|
|
342
|
-
}
|
|
343
|
-
} else if (error) {
|
|
344
|
-
throw error
|
|
345
|
-
} else if (!ended) {
|
|
346
|
-
await new Promise((resolve) => {
|
|
347
|
-
resume = resolve
|
|
348
|
-
})
|
|
371
|
+
if (batched) {
|
|
372
|
+
yield changes.splice(0)
|
|
349
373
|
} else {
|
|
350
|
-
|
|
374
|
+
yield* changes.splice(0)
|
|
351
375
|
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
Object.assign(
|
|
359
|
-
retryState,
|
|
360
|
-
await retry(err, retryCount++, retryState, { signal }, () =>
|
|
361
|
-
delay(err, retryCount, { signal }),
|
|
362
|
-
),
|
|
363
|
-
)
|
|
364
|
-
params.since = retryState.since ?? 0
|
|
376
|
+
} else if (error) {
|
|
377
|
+
throw error
|
|
378
|
+
} else if (!ended) {
|
|
379
|
+
await new Promise((resolve) => {
|
|
380
|
+
resume = resolve
|
|
381
|
+
})
|
|
365
382
|
} else {
|
|
366
|
-
|
|
383
|
+
return
|
|
367
384
|
}
|
|
368
|
-
} finally {
|
|
369
|
-
src?.on('error', () => {}).destroy()
|
|
370
385
|
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
386
|
+
} catch (err) {
|
|
387
|
+
if (err.name === 'AbortError') {
|
|
388
|
+
throw err
|
|
389
|
+
} else if (typeof retry === 'function') {
|
|
390
|
+
const retryState = { since: params.since }
|
|
391
|
+
Object.assign(
|
|
392
|
+
retryState,
|
|
393
|
+
await retry(err, retryCount++, retryState, { signal }, () =>
|
|
394
|
+
delay(err, retryCount, { signal }),
|
|
395
|
+
),
|
|
396
|
+
)
|
|
397
|
+
params.since = retryState.since ?? 0
|
|
398
|
+
} else {
|
|
399
|
+
await delay(err, retryCount, { signal })
|
|
379
400
|
}
|
|
401
|
+
} finally {
|
|
402
|
+
src?.on('error', () => {}).destroy()
|
|
380
403
|
}
|
|
381
404
|
}
|
|
382
405
|
}
|
|
@@ -451,10 +474,11 @@ export function makeCouch(opts) {
|
|
|
451
474
|
method,
|
|
452
475
|
body: typeof body === 'object' && body ? JSON.stringify(body) : body,
|
|
453
476
|
headers,
|
|
477
|
+
dispatcher: client,
|
|
454
478
|
}
|
|
455
479
|
|
|
456
480
|
return new Promise((resolve, reject) =>
|
|
457
|
-
|
|
481
|
+
dispatch(req, {
|
|
458
482
|
resolve,
|
|
459
483
|
reject,
|
|
460
484
|
signal,
|
|
@@ -702,11 +726,12 @@ export function makeCouch(opts) {
|
|
|
702
726
|
}
|
|
703
727
|
|
|
704
728
|
async function up(params, body, { client = defaultClient, signal = null } = {}) {
|
|
705
|
-
const res = await
|
|
729
|
+
const res = await request({
|
|
706
730
|
path: '/_up',
|
|
707
731
|
method: 'GET',
|
|
708
732
|
signal,
|
|
709
733
|
throwOnError: true,
|
|
734
|
+
dispatcher: client,
|
|
710
735
|
})
|
|
711
736
|
return await res.body.json()
|
|
712
737
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "19.8.
|
|
3
|
+
"version": "19.8.15",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"type": "module",
|
|
@@ -79,10 +79,10 @@
|
|
|
79
79
|
"**/*.d.ts"
|
|
80
80
|
],
|
|
81
81
|
"dependencies": {
|
|
82
|
-
"@aws-sdk/client-s3": "^3.
|
|
82
|
+
"@aws-sdk/client-s3": "^3.614.0",
|
|
83
83
|
"@elastic/elasticsearch": "^8.14.0",
|
|
84
|
-
"@elastic/transport": "^8.
|
|
85
|
-
"@nxtedition/nxt-undici": "^4.
|
|
84
|
+
"@elastic/transport": "^8.7.0",
|
|
85
|
+
"@nxtedition/nxt-undici": "^4.1.3",
|
|
86
86
|
"content-type": "^1.0.5",
|
|
87
87
|
"date-fns": "^3.6.0",
|
|
88
88
|
"fast-querystring": "^1.1.1",
|
|
@@ -91,14 +91,14 @@
|
|
|
91
91
|
"json5": "^2.2.3",
|
|
92
92
|
"koa-compose": "^4.1.0",
|
|
93
93
|
"lodash": "^4.17.21",
|
|
94
|
-
"mime": "^4.0.
|
|
94
|
+
"mime": "^4.0.4",
|
|
95
95
|
"moment-timezone": "^0.5.45",
|
|
96
96
|
"nconf": "^0.12.1",
|
|
97
97
|
"nested-error-stacks": "^2.1.1",
|
|
98
98
|
"object-hash": "^3.0.0",
|
|
99
99
|
"p-queue": "^8.0.1",
|
|
100
100
|
"pino": "^9.2.0",
|
|
101
|
-
"qs": "^6.12.
|
|
101
|
+
"qs": "^6.12.3",
|
|
102
102
|
"request-target": "^1.0.2",
|
|
103
103
|
"smpte-timecode": "^1.3.6",
|
|
104
104
|
"split-string": "^6.0.0",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"devDependencies": {
|
|
110
110
|
"@nxtedition/deepstream.io-client-js": ">=24.2.4",
|
|
111
111
|
"@types/lodash": "^4.17.6",
|
|
112
|
-
"@types/node": "^20.14.
|
|
112
|
+
"@types/node": "^20.14.10",
|
|
113
113
|
"eslint": "^8.0.0",
|
|
114
114
|
"eslint-config-prettier": "^9.1.0",
|
|
115
115
|
"eslint-config-standard": "^17.0.0",
|