@nxtedition/lib 15.0.45 → 15.0.47
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 +57 -38
- package/deepstream.js +124 -6
- package/package.json +2 -1
package/couch.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
const stream = require('node:stream')
|
|
2
|
+
const tp = require('node:timers/promises')
|
|
1
3
|
const createError = require('http-errors')
|
|
2
4
|
const makeWeak = require('./weakCache')
|
|
3
|
-
const tp = require('timers/promises')
|
|
4
5
|
const { delay } = require('./http')
|
|
6
|
+
const split2 = require('split2')
|
|
5
7
|
|
|
6
8
|
// https://github.com/fastify/fastify/blob/main/lib/reqIdGenFactory.js
|
|
7
9
|
// 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
|
|
@@ -62,7 +64,7 @@ module.exports = function (opts) {
|
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
const { origin: dbOrigin, pathname: dbPathname } = new URL(
|
|
65
|
-
Array.isArray(config.url) ? config.url[0] : config.url
|
|
67
|
+
Array.isArray(config.url) ? config.url[0] : config.url,
|
|
66
68
|
)
|
|
67
69
|
|
|
68
70
|
const defaultClientOpts = {
|
|
@@ -83,7 +85,7 @@ module.exports = function (opts) {
|
|
|
83
85
|
...defaultClientOpts,
|
|
84
86
|
connections: 4, // TODO (fix): Global limit?
|
|
85
87
|
pipelining: 2,
|
|
86
|
-
})
|
|
88
|
+
}),
|
|
87
89
|
)
|
|
88
90
|
|
|
89
91
|
function makeError(req, res) {
|
|
@@ -131,7 +133,7 @@ module.exports = function (opts) {
|
|
|
131
133
|
})
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
async function* changes({ client = defaultClient, signal, ...options } = {}) {
|
|
136
|
+
async function* changes({ client = defaultClient, signal, highWaterMark, ...options } = {}) {
|
|
135
137
|
const params = {}
|
|
136
138
|
|
|
137
139
|
let body
|
|
@@ -242,42 +244,59 @@ module.exports = function (opts) {
|
|
|
242
244
|
...(body ? { 'content-type': 'application/json' } : {}),
|
|
243
245
|
},
|
|
244
246
|
throwOnError: true,
|
|
245
|
-
highWaterMark:
|
|
247
|
+
highWaterMark: highWaterMark ?? 256 * 1024,
|
|
246
248
|
bodyTimeout: 2 * (params.heartbeat || 60e3),
|
|
247
249
|
}
|
|
248
250
|
|
|
249
|
-
|
|
250
|
-
const res = await client.request(req)
|
|
251
|
+
const res = await client.request(req)
|
|
251
252
|
|
|
252
|
-
|
|
253
|
+
retryCount = 0
|
|
253
254
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
}
|
|
255
|
+
const src = stream.pipeline(res.body, split2(), () => {})
|
|
256
|
+
|
|
257
|
+
let error
|
|
258
|
+
let ended = false
|
|
259
|
+
let resume = () => {}
|
|
260
|
+
|
|
261
|
+
src
|
|
262
|
+
.on('error', (err) => {
|
|
263
|
+
error = err
|
|
264
|
+
})
|
|
265
|
+
.on('readable', () => {
|
|
266
|
+
resume()
|
|
267
|
+
})
|
|
268
|
+
.on('end', () => {
|
|
269
|
+
ended = true
|
|
270
|
+
resume()
|
|
271
|
+
})
|
|
273
272
|
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
const batch = batched ? [] : null
|
|
274
|
+
while (true) {
|
|
275
|
+
const line = src.read()
|
|
276
|
+
|
|
277
|
+
if (line === '') {
|
|
278
|
+
continue
|
|
279
|
+
} else if (line !== null) {
|
|
280
|
+
const change = JSON.parse(line)
|
|
281
|
+
if (change.seq) {
|
|
282
|
+
params.since = change.seq
|
|
283
|
+
}
|
|
284
|
+
if (batch) {
|
|
285
|
+
batch.push(change)
|
|
286
|
+
} else {
|
|
287
|
+
yield change
|
|
276
288
|
}
|
|
289
|
+
} else if (batch?.length) {
|
|
290
|
+
yield batch.splice(0)
|
|
291
|
+
} else if (error) {
|
|
292
|
+
throw Object.assign(error, { data: req })
|
|
293
|
+
} else if (ended) {
|
|
294
|
+
return
|
|
295
|
+
} else {
|
|
296
|
+
await new Promise((resolve) => {
|
|
297
|
+
resume = resolve
|
|
298
|
+
})
|
|
277
299
|
}
|
|
278
|
-
} catch (err) {
|
|
279
|
-
Object.assign(err, { data: req })
|
|
280
|
-
throw err
|
|
281
300
|
}
|
|
282
301
|
}
|
|
283
302
|
|
|
@@ -416,7 +435,7 @@ module.exports = function (opts) {
|
|
|
416
435
|
|
|
417
436
|
function _request(
|
|
418
437
|
url,
|
|
419
|
-
{ params, client = defaultClient, idempotent, body, method, headers, signal }
|
|
438
|
+
{ params, client = defaultClient, idempotent, body, method, headers, signal },
|
|
420
439
|
) {
|
|
421
440
|
if (Array.isArray(headers)) {
|
|
422
441
|
// Do nothing...
|
|
@@ -445,7 +464,7 @@ module.exports = function (opts) {
|
|
|
445
464
|
url
|
|
446
465
|
.split('/')
|
|
447
466
|
.map((part) => encodeURIComponent(part))
|
|
448
|
-
.join('/')
|
|
467
|
+
.join('/'),
|
|
449
468
|
)
|
|
450
469
|
}
|
|
451
470
|
|
|
@@ -515,7 +534,7 @@ module.exports = function (opts) {
|
|
|
515
534
|
status: this.status,
|
|
516
535
|
headers: this.headers,
|
|
517
536
|
data: this.data,
|
|
518
|
-
})
|
|
537
|
+
}),
|
|
519
538
|
)
|
|
520
539
|
} else {
|
|
521
540
|
this.resolve({
|
|
@@ -536,7 +555,7 @@ module.exports = function (opts) {
|
|
|
536
555
|
|
|
537
556
|
this.reject(err)
|
|
538
557
|
},
|
|
539
|
-
})
|
|
558
|
+
}),
|
|
540
559
|
)
|
|
541
560
|
}
|
|
542
561
|
|
|
@@ -721,7 +740,7 @@ module.exports = function (opts) {
|
|
|
721
740
|
pathname,
|
|
722
741
|
params,
|
|
723
742
|
body,
|
|
724
|
-
{ client = getClient('_all_docs'), signal, idempotent = true, headers } = {}
|
|
743
|
+
{ client = getClient('_all_docs'), signal, idempotent = true, headers } = {},
|
|
725
744
|
) {
|
|
726
745
|
const req = {
|
|
727
746
|
pathname,
|
|
@@ -746,7 +765,7 @@ module.exports = function (opts) {
|
|
|
746
765
|
pathname,
|
|
747
766
|
params,
|
|
748
767
|
body,
|
|
749
|
-
{ client, signal, idempotent = true, headers } = {}
|
|
768
|
+
{ client, signal, idempotent = true, headers } = {},
|
|
750
769
|
) {
|
|
751
770
|
const req = {
|
|
752
771
|
pathname,
|
package/deepstream.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
const qs = require('qs')
|
|
2
2
|
const cached = require('./util/cached')
|
|
3
|
+
const undici = require('undici')
|
|
4
|
+
const stream = require('node:stream')
|
|
5
|
+
const split2 = require('split2')
|
|
6
|
+
const { delay } = require('./http')
|
|
3
7
|
|
|
4
8
|
function provide(ds, domain, callback, options) {
|
|
5
9
|
if (domain instanceof RegExp) {
|
|
@@ -23,7 +27,7 @@ function provide(ds, domain, callback, options) {
|
|
|
23
27
|
callback = cached(
|
|
24
28
|
callback,
|
|
25
29
|
cachedOptions,
|
|
26
|
-
cachedOptions.keySelector ? cachedOptions.keySelector : (id, options, key) => key
|
|
30
|
+
cachedOptions.keySelector ? cachedOptions.keySelector : (id, options, key) => key,
|
|
27
31
|
)
|
|
28
32
|
} else if (options.minAge) {
|
|
29
33
|
// Backwards compat
|
|
@@ -43,13 +47,13 @@ function provide(ds, domain, callback, options) {
|
|
|
43
47
|
const [id, options] = parseKey(key)
|
|
44
48
|
return callback(id, options, key)
|
|
45
49
|
},
|
|
46
|
-
{ recursive: options.recursive, mode: options.mode, stringify: options.stringify }
|
|
50
|
+
{ recursive: options.recursive, mode: options.mode, stringify: options.stringify },
|
|
47
51
|
)
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
function parseKey(key) {
|
|
51
55
|
const { json, id, query } = key.match(
|
|
52
|
-
/^(?:(?<json>\{.*\}):|(?<id>.*):)?[^?]*(?:\?(?<query>.*))
|
|
56
|
+
/^(?:(?<json>\{.*\}):|(?<id>.*):)?[^?]*(?:\?(?<query>.*))?$/,
|
|
53
57
|
).groups
|
|
54
58
|
return [
|
|
55
59
|
id || '',
|
|
@@ -75,7 +79,7 @@ function observe(ds, name, ...args) {
|
|
|
75
79
|
? `${name.endsWith('?') ? '' : '?'}${qs.stringify(query, { skipNulls: true })}`
|
|
76
80
|
: ''
|
|
77
81
|
}`,
|
|
78
|
-
...args
|
|
82
|
+
...args,
|
|
79
83
|
)
|
|
80
84
|
}
|
|
81
85
|
|
|
@@ -94,7 +98,7 @@ function observe2(ds, name, ...args) {
|
|
|
94
98
|
? `${name.endsWith('?') ? '' : '?'}${qs.stringify(query, { skipNulls: true })}`
|
|
95
99
|
: ''
|
|
96
100
|
}`,
|
|
97
|
-
...args
|
|
101
|
+
...args,
|
|
98
102
|
)
|
|
99
103
|
}
|
|
100
104
|
|
|
@@ -113,7 +117,7 @@ function get(ds, name, ...args) {
|
|
|
113
117
|
? `${name.endsWith('?') ? '' : '?'}${qs.stringify(query, { skipNulls: true })}`
|
|
114
118
|
: ''
|
|
115
119
|
}`,
|
|
116
|
-
...args
|
|
120
|
+
...args,
|
|
117
121
|
)
|
|
118
122
|
}
|
|
119
123
|
|
|
@@ -127,18 +131,132 @@ function init(ds) {
|
|
|
127
131
|
set: (...args) => ds.record.set(...args),
|
|
128
132
|
get: (...args) => get(ds, ...args),
|
|
129
133
|
update: (...args) => ds.record.update(...args),
|
|
134
|
+
changes: (...args) => changes(ds, ...args),
|
|
130
135
|
},
|
|
131
136
|
}
|
|
132
137
|
ds.nxt = nxt
|
|
133
138
|
return nxt
|
|
134
139
|
}
|
|
135
140
|
|
|
141
|
+
async function* changes(
|
|
142
|
+
ds,
|
|
143
|
+
{
|
|
144
|
+
since = 'now',
|
|
145
|
+
live = true,
|
|
146
|
+
batched = false,
|
|
147
|
+
includeDocs = false,
|
|
148
|
+
highWaterMark = 256 * 1024,
|
|
149
|
+
heartbeat = 60e3,
|
|
150
|
+
signal,
|
|
151
|
+
retry,
|
|
152
|
+
},
|
|
153
|
+
) {
|
|
154
|
+
const url = new URL('/_record/changes', ds._url)
|
|
155
|
+
|
|
156
|
+
url.protocol = url.protocol === 'ws:' ? 'http:' : 'https:'
|
|
157
|
+
url.port = '6100'
|
|
158
|
+
url.searchParams.set('since', since || '0')
|
|
159
|
+
url.searchParams.set('live', String(live))
|
|
160
|
+
url.searchParams.set('include_docs', String(includeDocs))
|
|
161
|
+
|
|
162
|
+
let ac
|
|
163
|
+
|
|
164
|
+
const abort = () => {
|
|
165
|
+
ac?.abort(signal.reason)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
signal?.addEventListener('abort', abort)
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
for (let retryCount = 0; retryCount < retry; retryCount++) {
|
|
172
|
+
ac = new AbortController()
|
|
173
|
+
try {
|
|
174
|
+
// TODO (fix): Use nxt-undici
|
|
175
|
+
const res = await undici.request(url, {
|
|
176
|
+
idempotent: false,
|
|
177
|
+
blocking: true,
|
|
178
|
+
method: 'GET',
|
|
179
|
+
signal: ac.signal,
|
|
180
|
+
throwOnError: true,
|
|
181
|
+
highWaterMark,
|
|
182
|
+
bodyTimeout: 2 * heartbeat,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const src = stream.pipeline(res.body, split2(), () => {})
|
|
186
|
+
|
|
187
|
+
let error
|
|
188
|
+
let ended = false
|
|
189
|
+
let resume = () => {}
|
|
190
|
+
|
|
191
|
+
src
|
|
192
|
+
.on('error', (err) => {
|
|
193
|
+
error = err
|
|
194
|
+
})
|
|
195
|
+
.on('readable', () => {
|
|
196
|
+
resume()
|
|
197
|
+
})
|
|
198
|
+
.on('end', () => {
|
|
199
|
+
ended = true
|
|
200
|
+
resume()
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const batch = batched ? [] : null
|
|
204
|
+
while (true) {
|
|
205
|
+
const line = src.read()
|
|
206
|
+
|
|
207
|
+
if (line === '') {
|
|
208
|
+
continue
|
|
209
|
+
} else if (line !== null) {
|
|
210
|
+
const change = JSON.parse(line)
|
|
211
|
+
|
|
212
|
+
retryCount = 0
|
|
213
|
+
|
|
214
|
+
if (change.seq) {
|
|
215
|
+
since = change.seq
|
|
216
|
+
}
|
|
217
|
+
if (batch) {
|
|
218
|
+
batch.push(change)
|
|
219
|
+
} else {
|
|
220
|
+
yield change
|
|
221
|
+
}
|
|
222
|
+
} else if (batch?.length) {
|
|
223
|
+
yield batch.splice(0)
|
|
224
|
+
} else if (error) {
|
|
225
|
+
throw error
|
|
226
|
+
} else if (ended) {
|
|
227
|
+
return
|
|
228
|
+
} else {
|
|
229
|
+
await new Promise((resolve) => {
|
|
230
|
+
resume = resolve
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
} catch (err) {
|
|
235
|
+
if (typeof retry === 'function') {
|
|
236
|
+
const retryState = { since }
|
|
237
|
+
await retry(err, retryCount, retryState, { signal: ac.signal })
|
|
238
|
+
url.searchParams.set('since', since || '0')
|
|
239
|
+
} else {
|
|
240
|
+
await delay(err, retryCount, { signal: ac.signal })
|
|
241
|
+
}
|
|
242
|
+
} finally {
|
|
243
|
+
ac.abort()
|
|
244
|
+
ac = null
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} finally {
|
|
248
|
+
signal?.removeEventListener('abort', abort)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
136
252
|
module.exports = Object.assign(init, {
|
|
253
|
+
changes,
|
|
137
254
|
provide,
|
|
138
255
|
observe,
|
|
139
256
|
observe2,
|
|
140
257
|
get,
|
|
141
258
|
record: {
|
|
259
|
+
changes,
|
|
142
260
|
provide,
|
|
143
261
|
observe,
|
|
144
262
|
observe2,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "15.0.
|
|
3
|
+
"version": "15.0.47",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"files": [
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
"request-target": "^1.0.2",
|
|
90
90
|
"smpte-timecode": "^1.3.3",
|
|
91
91
|
"split-string": "^6.0.0",
|
|
92
|
+
"split2": "^4.2.0",
|
|
92
93
|
"toobusy-js": "^0.5.1",
|
|
93
94
|
"undici": "^5.25.4",
|
|
94
95
|
"url-join": "^4.0.0"
|