@nxtedition/lib 19.0.5 → 19.0.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/couch.js +122 -90
- package/package.json +1 -1
package/couch.js
CHANGED
|
@@ -7,6 +7,8 @@ import querystring from 'querystring'
|
|
|
7
7
|
import urljoin from 'url-join'
|
|
8
8
|
import undici from 'undici'
|
|
9
9
|
import { AbortError } from './errors.js'
|
|
10
|
+
import split2 from 'split2'
|
|
11
|
+
import stream from 'node:stream'
|
|
10
12
|
|
|
11
13
|
// https://github.com/fastify/fastify/blob/main/lib/reqIdGenFactory.js
|
|
12
14
|
// 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
|
|
@@ -226,21 +228,9 @@ export function makeCouch(opts) {
|
|
|
226
228
|
}
|
|
227
229
|
}
|
|
228
230
|
|
|
229
|
-
async function
|
|
230
|
-
let remaining = Number(options.limit) || Infinity
|
|
231
|
-
|
|
232
|
-
const params2 = {
|
|
233
|
-
...params,
|
|
234
|
-
...options.query,
|
|
235
|
-
feed: live ? 'continuous' : 'normal',
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (Number.isFinite(remaining)) {
|
|
239
|
-
params.limit = remaining
|
|
240
|
-
}
|
|
241
|
-
|
|
231
|
+
async function parse(live, params) {
|
|
242
232
|
const req = {
|
|
243
|
-
path: `${dbPathname}/_changes?${new URLSearchParams(
|
|
233
|
+
path: `${dbPathname}/_changes?${new URLSearchParams(params)}`,
|
|
244
234
|
idempotent: false,
|
|
245
235
|
blocking: true,
|
|
246
236
|
method,
|
|
@@ -258,96 +248,138 @@ export function makeCouch(opts) {
|
|
|
258
248
|
const HEAD = '{"results":['
|
|
259
249
|
const TAIL = '],'
|
|
260
250
|
|
|
261
|
-
|
|
262
|
-
const res = await client.request(req)
|
|
263
|
-
|
|
264
|
-
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
265
|
-
throw makeError(req, {
|
|
266
|
-
status: res.statusCode,
|
|
267
|
-
headers: res.headers,
|
|
268
|
-
data: await res.body.text(),
|
|
269
|
-
})
|
|
270
|
-
}
|
|
251
|
+
const res = await client.request(req)
|
|
271
252
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
str += decoder.decode(chunk, { stream: true })
|
|
280
|
-
|
|
281
|
-
const lines = str.split('\n')
|
|
282
|
-
str = lines.pop() ?? ''
|
|
253
|
+
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
254
|
+
throw makeError(req, {
|
|
255
|
+
status: res.statusCode,
|
|
256
|
+
headers: res.headers,
|
|
257
|
+
data: await res.body.text(),
|
|
258
|
+
})
|
|
259
|
+
}
|
|
283
260
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (line === HEAD) {
|
|
302
|
-
state = 1
|
|
261
|
+
return stream.pipeline(
|
|
262
|
+
res.body,
|
|
263
|
+
split2(),
|
|
264
|
+
new stream.Transform({
|
|
265
|
+
objectMode: true,
|
|
266
|
+
construct(callback) {
|
|
267
|
+
this.state = 0
|
|
268
|
+
callback(null)
|
|
269
|
+
},
|
|
270
|
+
transform(line, encoding, callback) {
|
|
271
|
+
assert(typeof line === 'string', 'invalid line: ' + line)
|
|
272
|
+
if (line) {
|
|
273
|
+
if (live) {
|
|
274
|
+
const data = JSON.parse(line)
|
|
275
|
+
if (data.last_seq) {
|
|
276
|
+
assert(data.last_seq, 'invalid last_seq: ' + data.last_seq)
|
|
277
|
+
params.since = data.last_seq
|
|
303
278
|
} else {
|
|
304
|
-
assert(line.length < HEAD.length, 'invalid line: ' + line)
|
|
305
|
-
}
|
|
306
|
-
} else if (state === 1) {
|
|
307
|
-
if (line === TAIL) {
|
|
308
|
-
state = 2
|
|
309
|
-
} else {
|
|
310
|
-
const idx = line.lastIndexOf('}') + 1
|
|
311
|
-
assert(idx > 0, 'invalid line; ' + line)
|
|
312
|
-
const data = JSON.parse(line.slice(0, idx))
|
|
313
279
|
params.since = data.seq || params.since
|
|
314
|
-
|
|
280
|
+
this.push(data)
|
|
315
281
|
}
|
|
316
|
-
} else if (state === 2) {
|
|
317
|
-
state = 3
|
|
318
|
-
params.since = JSON.parse('{' + line).last_seq
|
|
319
|
-
assert(params.since, 'invalid last_seq: ' + params.since)
|
|
320
282
|
} else {
|
|
321
|
-
|
|
283
|
+
// NOTE: This makes some assumptions about the format of the JSON.
|
|
284
|
+
if (this.state === 0) {
|
|
285
|
+
if (line === HEAD) {
|
|
286
|
+
this.state = 1
|
|
287
|
+
} else {
|
|
288
|
+
assert(line.length < HEAD.length, 'invalid line: ' + line)
|
|
289
|
+
}
|
|
290
|
+
} else if (this.state === 1) {
|
|
291
|
+
if (line === TAIL) {
|
|
292
|
+
this.state = 2
|
|
293
|
+
} else {
|
|
294
|
+
const idx = line.lastIndexOf('}') + 1
|
|
295
|
+
assert(idx > 0, 'invalid line; ' + line)
|
|
296
|
+
const data = JSON.parse(line.slice(0, idx))
|
|
297
|
+
params.since = data.seq || params.since
|
|
298
|
+
this.push(data)
|
|
299
|
+
}
|
|
300
|
+
} else if (this.state === 2) {
|
|
301
|
+
this.state = 3
|
|
302
|
+
params.since = JSON.parse('{' + line).last_seq
|
|
303
|
+
assert(params.since, 'invalid last_seq: ' + params.since)
|
|
304
|
+
} else {
|
|
305
|
+
assert(false, 'invalid state: ' + this.state)
|
|
306
|
+
}
|
|
322
307
|
}
|
|
323
308
|
}
|
|
309
|
+
callback(null)
|
|
310
|
+
},
|
|
311
|
+
flush(callback) {
|
|
312
|
+
assert(live || this.state === 3, 'invalid state: ' + this.state)
|
|
313
|
+
callback(null)
|
|
314
|
+
},
|
|
315
|
+
}),
|
|
316
|
+
() => {},
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let remaining = Number(options.limit) || Infinity
|
|
321
|
+
try {
|
|
322
|
+
while (true) {
|
|
323
|
+
try {
|
|
324
|
+
const params2 = {
|
|
325
|
+
...params,
|
|
326
|
+
...options.query,
|
|
327
|
+
feed: live ? 'continuous' : 'normal',
|
|
324
328
|
}
|
|
325
329
|
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
yield changes
|
|
329
|
-
} else {
|
|
330
|
-
yield* changes
|
|
331
|
-
}
|
|
330
|
+
if (Number.isFinite(remaining)) {
|
|
331
|
+
params.limit = remaining
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
334
|
+
const src = await parse(live, params2)
|
|
335
|
+
const changes = []
|
|
337
336
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
Object.assign(err, { data: req })
|
|
342
|
-
throw err
|
|
343
|
-
}
|
|
344
|
-
}
|
|
337
|
+
let resume = null
|
|
338
|
+
let error = null
|
|
339
|
+
let ended = false
|
|
345
340
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
341
|
+
src
|
|
342
|
+
.on('readable', () => {
|
|
343
|
+
if (resume) {
|
|
344
|
+
resume()
|
|
345
|
+
resume = null
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
.on('error', (err) => {
|
|
349
|
+
error = err
|
|
350
|
+
|
|
351
|
+
if (resume) {
|
|
352
|
+
resume()
|
|
353
|
+
resume = null
|
|
354
|
+
}
|
|
355
|
+
})
|
|
356
|
+
.on('end', () => {
|
|
357
|
+
ended = true
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
while (true) {
|
|
361
|
+
const change = src.read()
|
|
362
|
+
if (change) {
|
|
363
|
+
changes.push(change)
|
|
364
|
+
} else if (changes.length) {
|
|
365
|
+
remaining -= changes.length
|
|
366
|
+
assert(remaining >= 0, 'invalid remaining: ' + remaining)
|
|
367
|
+
|
|
368
|
+
if (batched) {
|
|
369
|
+
yield changes.splice(0)
|
|
370
|
+
} else {
|
|
371
|
+
yield* changes.splice(0)
|
|
372
|
+
}
|
|
373
|
+
} else if (error) {
|
|
374
|
+
throw error
|
|
375
|
+
} else if (!ended) {
|
|
376
|
+
await new Promise((resolve) => {
|
|
377
|
+
resume = resolve
|
|
378
|
+
})
|
|
379
|
+
} else {
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
}
|
|
351
383
|
} catch (err) {
|
|
352
384
|
if (err.name === 'AbortError') {
|
|
353
385
|
throw err
|