@nxtedition/lib 18.0.1 → 18.0.3

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 (2) hide show
  1. package/couch.js +103 -63
  2. package/package.json +1 -1
package/couch.js CHANGED
@@ -5,7 +5,6 @@ import { defaultDelay as delay } from './http.js'
5
5
  import querystring from 'querystring'
6
6
  import urljoin from 'url-join'
7
7
  import undici from 'undici'
8
- import assert from 'node:assert'
9
8
 
10
9
  // https://github.com/fastify/fastify/blob/main/lib/reqIdGenFactory.js
11
10
  // 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
@@ -226,21 +225,16 @@ export function makeCouch(opts) {
226
225
  }
227
226
  }
228
227
 
229
- async function* parse(live) {
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
-
228
+ async function* continuous() {
242
229
  const req = {
243
- path: `${dbPathname}/_changes?${new URLSearchParams(params2)}`,
230
+ path:
231
+ dbPathname +
232
+ '/_changes' +
233
+ `?${new URLSearchParams({
234
+ ...params,
235
+ ...options.query,
236
+ feed: 'continuous',
237
+ })}`,
244
238
  idempotent: false,
245
239
  blocking: true,
246
240
  method,
@@ -256,66 +250,34 @@ export function makeCouch(opts) {
256
250
  bodyTimeout: 2 * (params.heartbeat || 60e3),
257
251
  }
258
252
 
259
- const HEAD = '{"results":['
260
- const TAIL = '],'
261
-
262
253
  try {
263
254
  const res = await client.request(req)
264
255
 
265
256
  retryCount = 0
266
257
 
267
258
  let str = ''
268
- let state = 0
269
259
  for await (const chunk of res.body) {
270
260
  const lines = (str + chunk).split('\n')
271
261
  str = lines.pop() ?? ''
272
262
 
273
- const changes = []
263
+ const results = batched ? [] : null
274
264
  for (const line of lines) {
275
- if (line === '') {
276
- continue
277
- }
278
- if (live) {
279
- const data = JSON.parse(line)
280
- if (data.last_seq) {
281
- params.since = data.last_seq
282
- assert(params.since, 'invalid last_seq: ' + params.since)
283
- } else {
284
- params.since = data.seq || params.since
285
- changes.push(data)
265
+ if (line) {
266
+ const change = JSON.parse(line)
267
+ if (change.seq) {
268
+ params.since = change.seq
286
269
  }
287
- } else {
288
- // NOTE: This makes some assumptions about the format of the JSON.
289
- if (state === 0) {
290
- if (line === HEAD) {
291
- state = 1
292
- } else {
293
- assert(line.length < HEAD.length, 'invalid line: ' + line)
294
- }
295
- } else if (state === 1) {
296
- if (line === TAIL) {
297
- state = 2
298
- } else {
299
- const idx = line.lastIndexOf('}') + 1
300
- assert(idx > 0, 'invalid line; ' + line)
301
- const data = JSON.parse(line.slice(0, idx))
302
- params.since = data.seq || params.since
303
- changes.push(data)
304
- }
305
- } else if (state === 2) {
306
- state = 3
307
- params.since = JSON.parse('{' + line).last_seq
308
- assert(params.since, 'invalid last_seq: ' + params.since)
270
+ if (results) {
271
+ results.push(change)
309
272
  } else {
310
- assert(false, 'invalid state: ' + state)
273
+ yield change
311
274
  }
312
275
  }
313
276
  }
314
277
 
315
- yield changes
316
-
317
- remaining -= changes.length
318
- assert(remaining >= 0, 'invalid remaining: ' + remaining)
278
+ if (results?.length) {
279
+ yield results
280
+ }
319
281
  }
320
282
  } catch (err) {
321
283
  Object.assign(err, { data: req })
@@ -323,15 +285,93 @@ export function makeCouch(opts) {
323
285
  }
324
286
  }
325
287
 
288
+ async function* normal() {
289
+ const batchSize =
290
+ options.batch_size ?? options.batchSize ?? (params.include_docs ? 512 : 4096)
291
+ let remaining = parseInt(options.limit) || Infinity
292
+
293
+ const next = async () => {
294
+ const req = {
295
+ path:
296
+ dbPathname +
297
+ '/_changes' +
298
+ `?${new URLSearchParams({
299
+ ...params,
300
+ ...options.query,
301
+ limit: Math.min(remaining, batchSize),
302
+ feed: live ? 'longpoll' : 'normal',
303
+ })}`,
304
+ idempotent: true,
305
+ blocking: live,
306
+ method,
307
+ body: JSON.stringify(body),
308
+ signal: ac.signal,
309
+ headers: {
310
+ 'user-agent': userAgent,
311
+ 'request-id': genReqId(),
312
+ ...(body ? { 'content-type': 'application/json' } : {}),
313
+ },
314
+ }
315
+
316
+ try {
317
+ const res = await client.request(req)
318
+
319
+ if (res.statusCode < 200 || res.statusCode >= 300) {
320
+ throw makeError(req, {
321
+ status: res.statusCode,
322
+ headers: res.headers,
323
+ data: await res.body.text(),
324
+ })
325
+ }
326
+
327
+ return await res.body.json()
328
+ } catch (err) {
329
+ Object.assign(err, { data: req })
330
+ return { err }
331
+ }
332
+ }
333
+
334
+ let promise
335
+ while (remaining) {
336
+ const { last_seq: seq, results, err } = await (promise ?? next())
337
+ promise = null
338
+
339
+ if (err) {
340
+ throw err
341
+ }
342
+
343
+ retryCount = 0
344
+
345
+ if (seq) {
346
+ params.since = seq
347
+ if (results.length > 0 && !results.at(-1)?.seq) {
348
+ results.at(-1).seq = seq
349
+ }
350
+ }
351
+
352
+ remaining -= results.length
353
+
354
+ if (!live && results.length === 0) {
355
+ return
356
+ }
357
+
358
+ promise = next()
359
+
360
+ if (batched) {
361
+ yield results
362
+ } else {
363
+ yield* results
364
+ }
365
+ }
366
+ }
367
+
326
368
  try {
327
369
  while (true) {
328
370
  try {
329
- if (batched) {
330
- yield* parse(live)
371
+ if (live && !options.batchSize && !options.batch_size) {
372
+ yield* continuous()
331
373
  } else {
332
- for await (const changes of parse(live)) {
333
- yield* changes
334
- }
374
+ yield* normal()
335
375
  }
336
376
  return
337
377
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "18.0.1",
3
+ "version": "18.0.3",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",