@nxtedition/lib 17.2.23 → 18.0.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 (2) hide show
  1. package/couch.js +60 -103
  2. package/package.json +1 -1
package/couch.js CHANGED
@@ -5,6 +5,7 @@ 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'
8
9
 
9
10
  // https://github.com/fastify/fastify/blob/main/lib/reqIdGenFactory.js
10
11
  // 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
@@ -225,16 +226,21 @@ export function makeCouch(opts) {
225
226
  }
226
227
  }
227
228
 
228
- async function* continuous() {
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
+
229
242
  const req = {
230
- path:
231
- dbPathname +
232
- '/_changes' +
233
- `?${new URLSearchParams({
234
- ...params,
235
- ...options.query,
236
- feed: 'continuous',
237
- })}`,
243
+ path: `${dbPathname}/_changes?${new URLSearchParams(params2)}`,
238
244
  idempotent: false,
239
245
  blocking: true,
240
246
  method,
@@ -250,34 +256,63 @@ export function makeCouch(opts) {
250
256
  bodyTimeout: 2 * (params.heartbeat || 60e3),
251
257
  }
252
258
 
259
+ const HEAD = '{"results":['
260
+ const TAIL = '],'
261
+
253
262
  try {
254
263
  const res = await client.request(req)
255
264
 
256
265
  retryCount = 0
257
266
 
258
267
  let str = ''
268
+ let state = 0
259
269
  for await (const chunk of res.body) {
260
270
  const lines = (str + chunk).split('\n')
261
271
  str = lines.pop() ?? ''
262
272
 
263
- const results = batched ? [] : null
273
+ const changes = []
264
274
  for (const line of lines) {
265
- if (line) {
266
- const change = JSON.parse(line)
267
- if (change.seq) {
268
- params.since = change.seq
275
+ if (live) {
276
+ const data = JSON.parse(line)
277
+ if (data.last_seq) {
278
+ params.since = data.last_seq
279
+ assert(params.since, 'invalid last_seq: ' + params.since)
280
+ } else {
281
+ params.since = data.seq || params.since
282
+ changes.push(data)
269
283
  }
270
- if (results) {
271
- results.push(change)
284
+ } else {
285
+ // NOTE: This makes some assumptions about the format of the JSON.
286
+ if (state === 0) {
287
+ if (line === HEAD) {
288
+ state = 1
289
+ } else {
290
+ assert(line.length < HEAD.length, 'invalid line: ' + line)
291
+ }
292
+ } else if (state === 1) {
293
+ if (line === TAIL) {
294
+ state = 2
295
+ } else {
296
+ const idx = line.lastIndexOf('}') + 1
297
+ assert(idx > 0, 'invalid line; ' + line)
298
+ const data = JSON.parse(line.slice(0, idx))
299
+ params.since = data.seq || params.since
300
+ changes.push(data)
301
+ }
302
+ } else if (state === 2) {
303
+ state = 3
304
+ params.since = JSON.parse('{' + line).last_seq
305
+ assert(params.since, 'invalid last_seq: ' + params.since)
272
306
  } else {
273
- yield change
307
+ assert(false, 'invalid state: ' + state)
274
308
  }
275
309
  }
276
310
  }
277
311
 
278
- if (results?.length) {
279
- yield results
280
- }
312
+ yield changes
313
+
314
+ remaining -= changes.length
315
+ assert(remaining >= 0, 'invalid remaining: ' + remaining)
281
316
  }
282
317
  } catch (err) {
283
318
  Object.assign(err, { data: req })
@@ -285,93 +320,15 @@ export function makeCouch(opts) {
285
320
  }
286
321
  }
287
322
 
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
-
368
323
  try {
369
324
  while (true) {
370
325
  try {
371
- if (live && !options.batchSize && !options.batch_size) {
372
- yield* continuous()
326
+ if (batched) {
327
+ yield* parse(live)
373
328
  } else {
374
- yield* normal()
329
+ for await (const changes of parse(live)) {
330
+ yield* changes
331
+ }
375
332
  }
376
333
  return
377
334
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "17.2.23",
3
+ "version": "18.0.0",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",