hide-a-bed 4.2.0 → 5.0.2
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/README.md +175 -14
- package/impl/changes.mjs +53 -0
- package/impl/errors.mjs +1 -1
- package/impl/query.mjs +14 -6
- package/impl/stream.mjs +2 -2
- package/impl/sugar/lock.mjs +70 -0
- package/impl/sugar/watch.mjs +154 -0
- package/index.mjs +23 -3
- package/integration/changes.mjs +60 -0
- package/integration/disconnect-watch.mjs +36 -0
- package/integration/watch.mjs +40 -0
- package/log.txt +84 -0
- package/package.json +3 -2
- package/schema/bind.mjs +8 -1
- package/schema/changes.mjs +59 -0
- package/schema/sugar/lock.mjs +50 -0
- package/schema/sugar/watch.mjs +29 -0
- package/types/changes-stream.d.ts +11 -0
- package/cjs/impl/bulk.cjs +0 -267
- package/cjs/impl/crud.cjs +0 -121
- package/cjs/impl/errors.cjs +0 -75
- package/cjs/impl/logger.cjs +0 -70
- package/cjs/impl/patch.cjs +0 -95
- package/cjs/impl/query.cjs +0 -110
- package/cjs/impl/queryBuilder.cjs +0 -99
- package/cjs/impl/retry.cjs +0 -54
- package/cjs/impl/stream.cjs +0 -121
- package/cjs/impl/trackedEmitter.cjs +0 -54
- package/cjs/impl/transactionErrors.cjs +0 -70
- package/cjs/index.cjs +0 -95
- package/cjs/schema/bind.cjs +0 -44
- package/cjs/schema/bulk.cjs +0 -88
- package/cjs/schema/config.cjs +0 -48
- package/cjs/schema/crud.cjs +0 -77
- package/cjs/schema/patch.cjs +0 -53
- package/cjs/schema/query.cjs +0 -62
- package/cjs/schema/stream.cjs +0 -42
- package/impl/bulk.d.mts +0 -11
- package/impl/bulk.d.mts.map +0 -1
- package/impl/crud.d.mts +0 -7
- package/impl/crud.d.mts.map +0 -1
- package/impl/errors.d.mts +0 -43
- package/impl/errors.d.mts.map +0 -1
- package/impl/logger.d.mts +0 -32
- package/impl/logger.d.mts.map +0 -1
- package/impl/patch.d.mts +0 -6
- package/impl/patch.d.mts.map +0 -1
- package/impl/query.d.mts +0 -195
- package/impl/query.d.mts.map +0 -1
- package/impl/queryBuilder.d.mts +0 -94
- package/impl/queryBuilder.d.mts.map +0 -1
- package/impl/retry.d.mts +0 -2
- package/impl/retry.d.mts.map +0 -1
- package/impl/stream.d.mts +0 -3
- package/impl/stream.d.mts.map +0 -1
- package/impl/trackedEmitter.d.mts +0 -8
- package/impl/trackedEmitter.d.mts.map +0 -1
- package/impl/transactionErrors.d.mts +0 -57
- package/impl/transactionErrors.d.mts.map +0 -1
- package/index.d.mts +0 -56
- package/index.d.mts.map +0 -1
- package/schema/bind.d.mts +0 -820
- package/schema/bind.d.mts.map +0 -1
- package/schema/bulk.d.mts +0 -910
- package/schema/bulk.d.mts.map +0 -1
- package/schema/config.d.mts +0 -79
- package/schema/config.d.mts.map +0 -1
- package/schema/crud.d.mts +0 -491
- package/schema/crud.d.mts.map +0 -1
- package/schema/patch.d.mts +0 -255
- package/schema/patch.d.mts.map +0 -1
- package/schema/query.d.mts +0 -406
- package/schema/query.d.mts.map +0 -1
- package/schema/stream.d.mts +0 -211
- package/schema/stream.d.mts.map +0 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { watchDocs } from "../impl/sugar/watch.mjs";
|
|
2
|
+
import { bindConfig } from '../index.mjs'
|
|
3
|
+
import needle from 'needle'
|
|
4
|
+
|
|
5
|
+
let DB_URL = `https://admin:iEZCEQhVR9PVCmYuZ5Bv@redman-couchdb.staging.brivity.io/testdb`
|
|
6
|
+
|
|
7
|
+
const config = {
|
|
8
|
+
couch: DB_URL,
|
|
9
|
+
bindWithRetry: true,
|
|
10
|
+
logger: (level, ...args) => {
|
|
11
|
+
console.log(`[${level.toUpperCase()}]`, ...args)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const run = async () => {
|
|
16
|
+
await needle('put', DB_URL)
|
|
17
|
+
const db = bindConfig(config)
|
|
18
|
+
const onChange = (change) => {
|
|
19
|
+
console.log(change)
|
|
20
|
+
}
|
|
21
|
+
const feed = watchDocs(config, 'doc-a-1', onChange, {include_docs: true})
|
|
22
|
+
feed.on('end', ({lastSeq}) => {
|
|
23
|
+
console.log('ending')
|
|
24
|
+
console.log('lastSeq', lastSeq)
|
|
25
|
+
})
|
|
26
|
+
// wait a while, and then end the watcher
|
|
27
|
+
setTimeout(() => {
|
|
28
|
+
console.log('stopping...')
|
|
29
|
+
feed.stop()
|
|
30
|
+
console.log('stopped')
|
|
31
|
+
}, 190000)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
run()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { watchDocs } from "../impl/sugar/watch.mjs";
|
|
2
|
+
import test from 'tap'
|
|
3
|
+
import { bindConfig } from '../index.mjs'
|
|
4
|
+
import needle from 'needle'
|
|
5
|
+
|
|
6
|
+
let DB_URL = `https://admin:iEZCEQhVR9PVCmYuZ5Bv@redman-couchdb.staging.brivity.io/testdb`
|
|
7
|
+
|
|
8
|
+
const config = {
|
|
9
|
+
couch: DB_URL,
|
|
10
|
+
bindWithRetry: true,
|
|
11
|
+
logger: (level, ...args) => {
|
|
12
|
+
console.log(`[${level.toUpperCase()}]`, ...args)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
test.test('changes tests', async t => {
|
|
16
|
+
await needle('put', DB_URL)
|
|
17
|
+
t.teardown(async () => {
|
|
18
|
+
await needle('delete', DB_URL)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const db = bindConfig(config)
|
|
22
|
+
|
|
23
|
+
t.test('watch a doc', t => new Promise(async (resolve) => {
|
|
24
|
+
let feed = null
|
|
25
|
+
const onChange = (change) => {
|
|
26
|
+
t.equal(change.id, 'doc-a-1')
|
|
27
|
+
t.equal(change.doc._id, 'doc-a-1')
|
|
28
|
+
t.equal(change.doc.data, 'test')
|
|
29
|
+
feed.stop()
|
|
30
|
+
resolve()
|
|
31
|
+
}
|
|
32
|
+
feed = db.watchDocs('doc-a-1', onChange, {include_docs: true})
|
|
33
|
+
feed.on('end', ({lastSeq}) => {
|
|
34
|
+
console.log('ending')
|
|
35
|
+
console.log('lastSeq', lastSeq)
|
|
36
|
+
})
|
|
37
|
+
await db.put({ _id: 'doc-a-fake-out', data: 'test' }) // we should not see this, because we did not watch it
|
|
38
|
+
await db.put({ _id: 'doc-a-1', data: 'test' })
|
|
39
|
+
}))
|
|
40
|
+
})
|
package/log.txt
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
[Tue, 25 Feb 2025 02:45:58 GMT] [info] [<0.000.0>] pouchdb-server has started on http://127.0.0.1:8985/
|
|
2
|
+
[Tue, 25 Feb 2025 02:45:58 GMT] [info] [<0.000.0>] database is in-memory; no changes will be saved.
|
|
3
|
+
[Tue, 25 Feb 2025 02:45:58 GMT] [info] [<0.000.0>] navigate to http://127.0.0.1:8985/_utils for the Fauxton UI.
|
|
4
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb 201
|
|
5
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/testdoc 201
|
|
6
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/testdoc 200
|
|
7
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/testdoc-not-there 404
|
|
8
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/testdoc-not-there 404
|
|
9
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/notThereDoc 409
|
|
10
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
11
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda 201
|
|
12
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
13
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_bulk_docs 201
|
|
14
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda 201
|
|
15
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-1 201
|
|
16
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
17
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-2 201
|
|
18
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
19
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_bulk_docs 201
|
|
20
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-2 201
|
|
21
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-3 201
|
|
22
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
23
|
+
[Tue, 25 Feb 2025 02:45:59 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/a 201
|
|
24
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_bulk_docs 201
|
|
25
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_bulk_docs 201
|
|
26
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-3 201
|
|
27
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
28
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/conflict-test 201
|
|
29
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:conflict-error 201
|
|
30
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
31
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:bulk-error 201
|
|
32
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
33
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/lock-doc-to-lock 201
|
|
34
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 200
|
|
35
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/lock-doc-to-lock 409
|
|
36
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 200
|
|
37
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/lock-doc-to-lock 201
|
|
38
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 404
|
|
39
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/lock-doc-to-lock 201
|
|
40
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 200
|
|
41
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 200
|
|
42
|
+
[Tue, 25 Feb 2025 02:46:00 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock-2 404
|
|
43
|
+
[Tue, 25 Feb 2025 02:48:53 GMT] [info] [<0.000.0>] pouchdb-server has started on http://127.0.0.1:8985/
|
|
44
|
+
[Tue, 25 Feb 2025 02:48:53 GMT] [info] [<0.000.0>] database is in-memory; no changes will be saved.
|
|
45
|
+
[Tue, 25 Feb 2025 02:48:53 GMT] [info] [<0.000.0>] navigate to http://127.0.0.1:8985/_utils for the Fauxton UI.
|
|
46
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb 201
|
|
47
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/testdoc 201
|
|
48
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/testdoc 200
|
|
49
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/testdoc-not-there 404
|
|
50
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/testdoc-not-there 404
|
|
51
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/notThereDoc 409
|
|
52
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
53
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda 201
|
|
54
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
55
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_bulk_docs 201
|
|
56
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda 201
|
|
57
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-1 201
|
|
58
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
59
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-2 201
|
|
60
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
61
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_bulk_docs 201
|
|
62
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-2 201
|
|
63
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-3 201
|
|
64
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
65
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/a 201
|
|
66
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_bulk_docs 201
|
|
67
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_bulk_docs 201
|
|
68
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:fsda-3 201
|
|
69
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
70
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/conflict-test 201
|
|
71
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:conflict-error 201
|
|
72
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
73
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/txn:bulk-error 201
|
|
74
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - POST /testdb/_all_docs?include_docs=true 200
|
|
75
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/lock-doc-to-lock 201
|
|
76
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 200
|
|
77
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/lock-doc-to-lock 409
|
|
78
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 200
|
|
79
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/lock-doc-to-lock 201
|
|
80
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 404
|
|
81
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - PUT /testdb/lock-doc-to-lock 201
|
|
82
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 200
|
|
83
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock 200
|
|
84
|
+
[Tue, 25 Feb 2025 02:48:54 GMT] [info] [<0.000.0>] 127.0.0.1 - - GET /testdb/lock-doc-to-lock-2 404
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hide-a-bed",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "An abstraction over couchdb calls that includes easy mock/stubs with pouchdb",
|
|
5
5
|
"module": "index.mjs",
|
|
6
6
|
"main": "cjs/index.cjs",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"clean": "rm -rf cjs && (find . -name \"*.mts\" -type f -delete || true) && (find . -name \"*.map\" -type f -delete || true) && (find . -name \"log.txt\" -type f -delete || true)",
|
|
15
15
|
"build": "npm run clean && tsc && npx -p dualmode@latest build",
|
|
16
16
|
"build:cjs": "rm -rf cjs && node build/build.mjs",
|
|
17
|
-
"test": "node tests
|
|
17
|
+
"test": "node tests/*.mjs",
|
|
18
18
|
"lint:fix": "standard --fix",
|
|
19
19
|
"prepublish": "npm run build",
|
|
20
20
|
"full": "npm run lint:fix && npm run build && npm run clean"
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://github.com/ryanramage/hide-a-bed#readme",
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"changes-stream": "^2.2.0",
|
|
37
38
|
"JSONStream": "^1.3.5",
|
|
38
39
|
"lodash": "^4.17.21",
|
|
39
40
|
"needle": "^3.2.0",
|
package/schema/bind.mjs
CHANGED
|
@@ -6,6 +6,9 @@ import { CouchGetBound, CouchPutBound, CouchGetAtRevBound } from './crud.mjs'
|
|
|
6
6
|
import { PatchBound } from './patch.mjs'
|
|
7
7
|
import { SimpleViewQueryBound } from './query.mjs'
|
|
8
8
|
import { SimpleViewQueryStreamBound } from './stream.mjs'
|
|
9
|
+
import { CreateLockBound, RemoveLockBound } from './sugar/lock.mjs'
|
|
10
|
+
import { ChangesBound } from './changes.mjs'
|
|
11
|
+
import { WatchDocsBound } from './sugar/watch.mjs'
|
|
9
12
|
|
|
10
13
|
const BindReturns = z.object({
|
|
11
14
|
bulkGet: BulkGetBound,
|
|
@@ -18,7 +21,11 @@ const BindReturns = z.object({
|
|
|
18
21
|
put: CouchPutBound,
|
|
19
22
|
patch: PatchBound,
|
|
20
23
|
query: SimpleViewQueryBound,
|
|
21
|
-
queryStream: SimpleViewQueryStreamBound
|
|
24
|
+
queryStream: SimpleViewQueryStreamBound,
|
|
25
|
+
createLock: CreateLockBound,
|
|
26
|
+
removeLock: RemoveLockBound,
|
|
27
|
+
changes: ChangesBound,
|
|
28
|
+
watchDocs: WatchDocsBound
|
|
22
29
|
})
|
|
23
30
|
|
|
24
31
|
export const Bind = z.function().args(CouchConfig).returns(BindReturns)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { CouchConfig } from './config.mjs'
|
|
3
|
+
import { CouchDoc } from './crud.mjs'
|
|
4
|
+
|
|
5
|
+
export const ChangesOptions = z.object({
|
|
6
|
+
feed: z.enum(['continuous', 'longpoll']).default('continuous'),
|
|
7
|
+
filter: z.any(), // z.union([z.string(), z.array()]).optional(),
|
|
8
|
+
inactivity_ms: z.number().default(60 * 60 * 1000),
|
|
9
|
+
/** @type {number} */
|
|
10
|
+
timeout: z.number().optional(),
|
|
11
|
+
requestTimeout: z.number().default(2 * 60 * 1000),
|
|
12
|
+
since: z.union([z.number(), z.literal('now')]).default(0),
|
|
13
|
+
heartbeat: z.number().default(30 * 1000),
|
|
14
|
+
style: z.enum(['main_only', 'all_docs']).default('main_only'),
|
|
15
|
+
include_docs: z.boolean().default(false),
|
|
16
|
+
query_params: z.record(z.any()).default({}),
|
|
17
|
+
use_post: z.boolean().default(false)
|
|
18
|
+
}).partial()
|
|
19
|
+
/** @typedef { z.infer<typeof ChangesOptions> } ChangesOptionsSchema */
|
|
20
|
+
|
|
21
|
+
export const ChangesResponse = z.object({
|
|
22
|
+
id: z.string(),
|
|
23
|
+
seq: z.number(),
|
|
24
|
+
changes: z.array(z.object({
|
|
25
|
+
rev: z.string()
|
|
26
|
+
})),
|
|
27
|
+
doc: CouchDoc.nullish().optional(),
|
|
28
|
+
deleted: z.boolean().optional()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export const ChangesEmitter = z.object({
|
|
32
|
+
on: z.function()
|
|
33
|
+
.args(z.string(), z.function().args(z.any()).returns(z.void()))
|
|
34
|
+
.returns(z.any()),
|
|
35
|
+
removeListener: z.function()
|
|
36
|
+
.args(z.string(), z.function().args(z.any()).returns(z.void()))
|
|
37
|
+
.returns(z.any()),
|
|
38
|
+
stop: z.function().returns(z.void())
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export const Changes = z.function()
|
|
42
|
+
.args(
|
|
43
|
+
CouchConfig,
|
|
44
|
+
z.function().args(z.any()).returns(z.void()),
|
|
45
|
+
ChangesOptions
|
|
46
|
+
)
|
|
47
|
+
.returns(z.promise(ChangesEmitter))
|
|
48
|
+
|
|
49
|
+
/** @typedef { z.infer<typeof Changes> } ChangesSchema */
|
|
50
|
+
/** @typedef { z.infer<typeof ChangesEmitter> } ChangesEmitterSchema */
|
|
51
|
+
|
|
52
|
+
export const ChangesBound = z.function()
|
|
53
|
+
.args(
|
|
54
|
+
z.function().args(z.any()).returns(z.void()),
|
|
55
|
+
ChangesOptions
|
|
56
|
+
)
|
|
57
|
+
.returns(z.promise(ChangesEmitter))
|
|
58
|
+
|
|
59
|
+
/** @typedef { z.infer<typeof ChangesBound> } ChangesBoundSchema */
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { CouchConfig } from '../config.mjs'
|
|
3
|
+
import { CouchDoc } from '../crud.mjs'
|
|
4
|
+
|
|
5
|
+
export const Lock = CouchDoc.extend({
|
|
6
|
+
type: z.literal('lock'),
|
|
7
|
+
locks: z.string().describe('the document ID being locked'),
|
|
8
|
+
lockedAt: z.string().describe('ISO timestamp when lock was created'),
|
|
9
|
+
lockedBy: z.string().describe('username of who created the lock')
|
|
10
|
+
})
|
|
11
|
+
/** @typedef { z.infer<typeof Lock> } LockSchema */
|
|
12
|
+
|
|
13
|
+
export const LockOptions = z.object({
|
|
14
|
+
enableLocking: z.boolean().default(true).describe('whether locking is enabled'),
|
|
15
|
+
username: z.string().describe('username to attribute locks to')
|
|
16
|
+
})
|
|
17
|
+
/** @typedef { z.infer<typeof LockOptions> } LockOptionsSchema */
|
|
18
|
+
|
|
19
|
+
export const CreateLock = z.function()
|
|
20
|
+
.args(
|
|
21
|
+
CouchConfig,
|
|
22
|
+
z.string().describe('document ID to lock'),
|
|
23
|
+
LockOptions
|
|
24
|
+
)
|
|
25
|
+
.returns(z.promise(z.boolean()))
|
|
26
|
+
/** @typedef { z.infer<typeof CreateLock> } CreateLockSchema */
|
|
27
|
+
export const CreateLockBound = z.function()
|
|
28
|
+
.args(
|
|
29
|
+
z.string().describe('document ID to lock'),
|
|
30
|
+
LockOptions
|
|
31
|
+
)
|
|
32
|
+
.returns(z.promise(z.boolean()))
|
|
33
|
+
/** @typedef { z.infer<typeof CreateLockBound> } CreateLockBoundSchema */
|
|
34
|
+
|
|
35
|
+
export const RemoveLock = z.function()
|
|
36
|
+
.args(
|
|
37
|
+
CouchConfig,
|
|
38
|
+
z.string().describe('document ID to unlock'),
|
|
39
|
+
LockOptions
|
|
40
|
+
)
|
|
41
|
+
.returns(z.promise(z.void()))
|
|
42
|
+
/** @typedef { z.infer<typeof RemoveLock> } RemoveLockSchema */
|
|
43
|
+
|
|
44
|
+
export const RemoveLockBound = z.function()
|
|
45
|
+
.args(
|
|
46
|
+
z.string().describe('document ID to unlock'),
|
|
47
|
+
LockOptions
|
|
48
|
+
)
|
|
49
|
+
.returns(z.promise(z.void()))
|
|
50
|
+
/** @typedef { z.infer<typeof RemoveLockBound> } RemoveLockBoundSchema */
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { CouchConfig } from '../config.mjs'
|
|
3
|
+
import { ChangesEmitter } from '../changes.mjs'
|
|
4
|
+
|
|
5
|
+
export const WatchOptions = z.object({
|
|
6
|
+
include_docs: z.boolean().default(false)
|
|
7
|
+
}).partial()
|
|
8
|
+
|
|
9
|
+
export const WatchDocs = z.function()
|
|
10
|
+
.args(
|
|
11
|
+
CouchConfig,
|
|
12
|
+
z.union([z.string(), z.array(z.string())]),
|
|
13
|
+
z.function().args(z.any()).returns(z.void()),
|
|
14
|
+
WatchOptions
|
|
15
|
+
)
|
|
16
|
+
.returns(ChangesEmitter)
|
|
17
|
+
|
|
18
|
+
/** @typedef { z.infer<typeof WatchOptions> } WatchOptionsSchema */
|
|
19
|
+
/** @typedef { z.infer<typeof WatchDocs> } WatchDocsSchema */
|
|
20
|
+
|
|
21
|
+
export const WatchDocsBound = z.function()
|
|
22
|
+
.args(
|
|
23
|
+
z.union([z.string(), z.array(z.string())]),
|
|
24
|
+
z.function().args(z.any()).returns(z.void()),
|
|
25
|
+
WatchOptions
|
|
26
|
+
)
|
|
27
|
+
.returns(ChangesEmitter)
|
|
28
|
+
|
|
29
|
+
/** @typedef { z.infer<typeof WatchDocsBound> } WatchDocsBoundSchema */
|
package/cjs/impl/bulk.cjs
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
var bulk_exports = {};
|
|
30
|
-
__export(bulk_exports, {
|
|
31
|
-
bulkGet: () => bulkGet,
|
|
32
|
-
bulkGetDictionary: () => bulkGetDictionary,
|
|
33
|
-
bulkRemove: () => bulkRemove,
|
|
34
|
-
bulkSave: () => bulkSave,
|
|
35
|
-
bulkSaveTransaction: () => bulkSaveTransaction
|
|
36
|
-
});
|
|
37
|
-
module.exports = __toCommonJS(bulk_exports);
|
|
38
|
-
var import_needle = __toESM(require("needle"), 1);
|
|
39
|
-
var import_bulk = require("../schema/bulk.cjs");
|
|
40
|
-
var import_retry = require("./retry.cjs");
|
|
41
|
-
var import_crud = require("./crud.cjs");
|
|
42
|
-
var import_errors = require("./errors.cjs");
|
|
43
|
-
var import_transactionErrors = require("./transactionErrors.cjs");
|
|
44
|
-
var import_logger = require("./logger.cjs");
|
|
45
|
-
var import_crud2 = require("../schema/crud.cjs");
|
|
46
|
-
var import_trackedEmitter = require("./trackedEmitter.cjs");
|
|
47
|
-
const opts = {
|
|
48
|
-
json: true,
|
|
49
|
-
headers: {
|
|
50
|
-
"Content-Type": "application/json"
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
const bulkSave = import_bulk.BulkSave.implement(async (config, docs) => {
|
|
54
|
-
const logger = (0, import_logger.createLogger)(config);
|
|
55
|
-
if (!docs) {
|
|
56
|
-
logger.warn("bulkSave called with no docs");
|
|
57
|
-
return { ok: false, error: "noDocs", reason: "no docs provided" };
|
|
58
|
-
}
|
|
59
|
-
if (!docs.length) {
|
|
60
|
-
logger.warn("bulkSave called with empty docs array");
|
|
61
|
-
return { ok: false, error: "noDocs", reason: "no docs provided" };
|
|
62
|
-
}
|
|
63
|
-
logger.info(`Starting bulk save of ${docs.length} documents`);
|
|
64
|
-
const url = `${config.couch}/_bulk_docs`;
|
|
65
|
-
const body = { docs };
|
|
66
|
-
let resp;
|
|
67
|
-
try {
|
|
68
|
-
resp = await (0, import_needle.default)("post", url, body, opts);
|
|
69
|
-
} catch (err) {
|
|
70
|
-
logger.error("Network error during bulk save:", err);
|
|
71
|
-
import_errors.RetryableError.handleNetworkError(err);
|
|
72
|
-
}
|
|
73
|
-
if (!resp) {
|
|
74
|
-
logger.error("No response received from bulk save request");
|
|
75
|
-
throw new import_errors.RetryableError("no response", 503);
|
|
76
|
-
}
|
|
77
|
-
if (import_errors.RetryableError.isRetryableStatusCode(resp.statusCode)) {
|
|
78
|
-
logger.warn(`Retryable status code received: ${resp.statusCode}`);
|
|
79
|
-
throw new import_errors.RetryableError("retryable error during bulk save", resp.statusCode);
|
|
80
|
-
}
|
|
81
|
-
if (resp.statusCode !== 201) {
|
|
82
|
-
logger.error(`Unexpected status code: ${resp.statusCode}`);
|
|
83
|
-
throw new Error("could not save");
|
|
84
|
-
}
|
|
85
|
-
const results = resp?.body || [];
|
|
86
|
-
return results;
|
|
87
|
-
});
|
|
88
|
-
const bulkGet = import_bulk.BulkGet.implement(async (config, ids) => {
|
|
89
|
-
const logger = (0, import_logger.createLogger)(config);
|
|
90
|
-
const keys = ids;
|
|
91
|
-
logger.info(`Starting bulk get for ${keys.length} documents`);
|
|
92
|
-
const url = `${config.couch}/_all_docs?include_docs=true`;
|
|
93
|
-
const payload = { keys };
|
|
94
|
-
let resp;
|
|
95
|
-
try {
|
|
96
|
-
resp = await (0, import_needle.default)("post", url, payload, opts);
|
|
97
|
-
} catch (err) {
|
|
98
|
-
logger.error("Network error during bulk get:", err);
|
|
99
|
-
import_errors.RetryableError.handleNetworkError(err);
|
|
100
|
-
}
|
|
101
|
-
if (!resp) {
|
|
102
|
-
logger.error("No response received from bulk get request");
|
|
103
|
-
throw new import_errors.RetryableError("no response", 503);
|
|
104
|
-
}
|
|
105
|
-
if (import_errors.RetryableError.isRetryableStatusCode(resp.statusCode)) {
|
|
106
|
-
logger.warn(`Retryable status code received: ${resp.statusCode}`);
|
|
107
|
-
throw new import_errors.RetryableError("retryable error during bulk get", resp.statusCode);
|
|
108
|
-
}
|
|
109
|
-
if (resp.statusCode !== 200) {
|
|
110
|
-
logger.error(`Unexpected status code: ${resp.statusCode}`);
|
|
111
|
-
throw new Error("could not fetch");
|
|
112
|
-
}
|
|
113
|
-
const body = resp.body;
|
|
114
|
-
return body;
|
|
115
|
-
});
|
|
116
|
-
const bulkRemove = import_bulk.BulkRemove.implement(async (config, ids) => {
|
|
117
|
-
const logger = (0, import_logger.createLogger)(config);
|
|
118
|
-
logger.info(`Starting bulk remove for ${ids.length} documents`);
|
|
119
|
-
const resp = await bulkGet(config, ids);
|
|
120
|
-
const toRemove = [];
|
|
121
|
-
resp.rows.forEach((row) => {
|
|
122
|
-
if (!row.doc) return;
|
|
123
|
-
try {
|
|
124
|
-
const d = import_crud2.CouchDoc.parse(row.doc);
|
|
125
|
-
d._deleted = true;
|
|
126
|
-
toRemove.push(d);
|
|
127
|
-
} catch (e) {
|
|
128
|
-
logger.warn(`Invalid document structure in bulk remove: ${row.id}`, e);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
return bulkSave(config, toRemove);
|
|
132
|
-
});
|
|
133
|
-
const bulkGetDictionary = import_bulk.BulkGetDictionary.implement(async (config, ids) => {
|
|
134
|
-
const resp = await bulkGet(config, ids);
|
|
135
|
-
const results = { found: {}, notFound: {} };
|
|
136
|
-
resp.rows.forEach(
|
|
137
|
-
/** @param { import('../schema/query.mjs').ViewRowSchema } row */
|
|
138
|
-
(row) => {
|
|
139
|
-
if (!row.key) return;
|
|
140
|
-
if (row.error) {
|
|
141
|
-
results.notFound[row.key] = row;
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
try {
|
|
145
|
-
const doc = import_crud2.CouchDoc.parse(row.doc);
|
|
146
|
-
results.found[doc._id] = doc;
|
|
147
|
-
} catch (e) {
|
|
148
|
-
results.notFound[row.key] = row;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
);
|
|
152
|
-
return results;
|
|
153
|
-
});
|
|
154
|
-
const bulkSaveTransaction = import_bulk.BulkSaveTransaction.implement(async (config, transactionId, docs) => {
|
|
155
|
-
const emitter = (0, import_trackedEmitter.setupEmitter)(config);
|
|
156
|
-
const logger = (0, import_logger.createLogger)(config);
|
|
157
|
-
const retryOptions = {
|
|
158
|
-
maxRetries: config.maxRetries ?? 10,
|
|
159
|
-
initialDelay: config.initialDelay ?? 1e3,
|
|
160
|
-
backoffFactor: config.backoffFactor ?? 2
|
|
161
|
-
};
|
|
162
|
-
const _put = config.bindWithRetry ? (0, import_retry.withRetry)(import_crud.put.bind(null, config), retryOptions) : import_crud.put.bind(null, config);
|
|
163
|
-
logger.info(`Starting bulk save transaction ${transactionId} for ${docs.length} documents`);
|
|
164
|
-
const txnDoc = {
|
|
165
|
-
_id: `txn:${transactionId}`,
|
|
166
|
-
_rev: null,
|
|
167
|
-
type: "transaction",
|
|
168
|
-
status: "pending",
|
|
169
|
-
changes: docs,
|
|
170
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
171
|
-
};
|
|
172
|
-
let txnresp = await _put(txnDoc);
|
|
173
|
-
logger.debug("Transaction document created:", txnDoc, txnresp);
|
|
174
|
-
await emitter.emit("transaction-created", { txnresp, txnDoc });
|
|
175
|
-
if (txnresp.error) {
|
|
176
|
-
throw new import_transactionErrors.TransactionSetupError("Failed to create transaction document", {
|
|
177
|
-
error: txnresp.error,
|
|
178
|
-
response: txnresp.body
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
const existingDocs = await bulkGetDictionary(config, docs.map((d) => d._id));
|
|
182
|
-
logger.debug("Fetched current revisions of documents:", existingDocs);
|
|
183
|
-
await emitter.emit("transaction-revs-fetched", existingDocs);
|
|
184
|
-
const revErrors = [];
|
|
185
|
-
docs.forEach((d) => {
|
|
186
|
-
if (existingDocs.found[d._id] && existingDocs.found[d._id]._rev !== d._rev) revErrors.push(d._id);
|
|
187
|
-
if (existingDocs.notFound[d._id] && d._rev) revErrors.push(d._id);
|
|
188
|
-
});
|
|
189
|
-
if (revErrors.length > 0) {
|
|
190
|
-
throw new import_transactionErrors.TransactionVersionConflictError(revErrors);
|
|
191
|
-
}
|
|
192
|
-
logger.debug("Checked document revisions:", existingDocs);
|
|
193
|
-
await emitter.emit("transaction-revs-checked", existingDocs);
|
|
194
|
-
const providedDocsById = {};
|
|
195
|
-
docs.forEach((d) => {
|
|
196
|
-
if (!d._id) return;
|
|
197
|
-
providedDocsById[d._id] = d;
|
|
198
|
-
});
|
|
199
|
-
const newDocsToRollback = [];
|
|
200
|
-
const potentialExistingDocsToRollack = [];
|
|
201
|
-
const failedDocs = [];
|
|
202
|
-
try {
|
|
203
|
-
logger.info("Transaction started:", txnDoc);
|
|
204
|
-
await emitter.emit("transaction-started", txnDoc);
|
|
205
|
-
const results = await bulkSave(config, docs);
|
|
206
|
-
logger.info("Transaction updates applied:", results);
|
|
207
|
-
await emitter.emit("transaction-updates-applied", results);
|
|
208
|
-
results.forEach((r) => {
|
|
209
|
-
if (!r.id) return;
|
|
210
|
-
if (!r.error) {
|
|
211
|
-
if (existingDocs.notFound[r.id]) newDocsToRollback.push(r);
|
|
212
|
-
if (existingDocs.found[r.id]) potentialExistingDocsToRollack.push(r);
|
|
213
|
-
} else {
|
|
214
|
-
failedDocs.push(r);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
if (failedDocs.length > 0) {
|
|
218
|
-
throw new import_transactionErrors.TransactionBulkOperationError(failedDocs);
|
|
219
|
-
}
|
|
220
|
-
txnDoc.status = "completed";
|
|
221
|
-
txnDoc._rev = txnresp.rev;
|
|
222
|
-
txnresp = await _put(txnDoc);
|
|
223
|
-
logger.info("Transaction completed:", txnDoc);
|
|
224
|
-
await emitter.emit("transaction-completed", { txnresp, txnDoc });
|
|
225
|
-
if (txnresp.statusCode !== 201) {
|
|
226
|
-
logger.error("Failed to update transaction status to completed");
|
|
227
|
-
}
|
|
228
|
-
return results;
|
|
229
|
-
} catch (error) {
|
|
230
|
-
logger.error("Transaction failed, attempting rollback:", error);
|
|
231
|
-
const toRollback = [];
|
|
232
|
-
potentialExistingDocsToRollack.forEach((row) => {
|
|
233
|
-
if (!row.id || !row.rev) return;
|
|
234
|
-
const doc = existingDocs.found[row.id];
|
|
235
|
-
doc._rev = row.rev;
|
|
236
|
-
toRollback.push(doc);
|
|
237
|
-
});
|
|
238
|
-
newDocsToRollback.forEach((d) => {
|
|
239
|
-
if (!d.id || !d.rev) return;
|
|
240
|
-
const before = structuredClone(providedDocsById[d.id]);
|
|
241
|
-
before._rev = d.rev;
|
|
242
|
-
before._deleted = true;
|
|
243
|
-
toRollback.push(before);
|
|
244
|
-
});
|
|
245
|
-
const bulkRollbackResult = await bulkSave(config, toRollback);
|
|
246
|
-
let status = "rolled_back";
|
|
247
|
-
bulkRollbackResult.forEach((r) => {
|
|
248
|
-
if (r.error) status = "rollback_failed";
|
|
249
|
-
});
|
|
250
|
-
logger.warn("Transaction rolled back:", { bulkRollbackResult, status });
|
|
251
|
-
await emitter.emit("transaction-rolled-back", { bulkRollbackResult, status });
|
|
252
|
-
txnDoc.status = status;
|
|
253
|
-
txnDoc._rev = txnresp.rev;
|
|
254
|
-
txnresp = await _put(txnDoc);
|
|
255
|
-
logger.warn("Transaction rollback status updated:", txnDoc);
|
|
256
|
-
await emitter.emit("transaction-rolled-back-status", { txnresp, txnDoc });
|
|
257
|
-
if (txnresp.statusCode !== 201) {
|
|
258
|
-
logger.error("Failed to update transaction status to rolled_back");
|
|
259
|
-
}
|
|
260
|
-
throw new import_transactionErrors.TransactionRollbackError(
|
|
261
|
-
"Transaction failed and rollback was unsuccessful",
|
|
262
|
-
/** @type {Error} */
|
|
263
|
-
error,
|
|
264
|
-
bulkRollbackResult
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
});
|