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.
Files changed (75) hide show
  1. package/README.md +175 -14
  2. package/impl/changes.mjs +53 -0
  3. package/impl/errors.mjs +1 -1
  4. package/impl/query.mjs +14 -6
  5. package/impl/stream.mjs +2 -2
  6. package/impl/sugar/lock.mjs +70 -0
  7. package/impl/sugar/watch.mjs +154 -0
  8. package/index.mjs +23 -3
  9. package/integration/changes.mjs +60 -0
  10. package/integration/disconnect-watch.mjs +36 -0
  11. package/integration/watch.mjs +40 -0
  12. package/log.txt +84 -0
  13. package/package.json +3 -2
  14. package/schema/bind.mjs +8 -1
  15. package/schema/changes.mjs +59 -0
  16. package/schema/sugar/lock.mjs +50 -0
  17. package/schema/sugar/watch.mjs +29 -0
  18. package/types/changes-stream.d.ts +11 -0
  19. package/cjs/impl/bulk.cjs +0 -267
  20. package/cjs/impl/crud.cjs +0 -121
  21. package/cjs/impl/errors.cjs +0 -75
  22. package/cjs/impl/logger.cjs +0 -70
  23. package/cjs/impl/patch.cjs +0 -95
  24. package/cjs/impl/query.cjs +0 -110
  25. package/cjs/impl/queryBuilder.cjs +0 -99
  26. package/cjs/impl/retry.cjs +0 -54
  27. package/cjs/impl/stream.cjs +0 -121
  28. package/cjs/impl/trackedEmitter.cjs +0 -54
  29. package/cjs/impl/transactionErrors.cjs +0 -70
  30. package/cjs/index.cjs +0 -95
  31. package/cjs/schema/bind.cjs +0 -44
  32. package/cjs/schema/bulk.cjs +0 -88
  33. package/cjs/schema/config.cjs +0 -48
  34. package/cjs/schema/crud.cjs +0 -77
  35. package/cjs/schema/patch.cjs +0 -53
  36. package/cjs/schema/query.cjs +0 -62
  37. package/cjs/schema/stream.cjs +0 -42
  38. package/impl/bulk.d.mts +0 -11
  39. package/impl/bulk.d.mts.map +0 -1
  40. package/impl/crud.d.mts +0 -7
  41. package/impl/crud.d.mts.map +0 -1
  42. package/impl/errors.d.mts +0 -43
  43. package/impl/errors.d.mts.map +0 -1
  44. package/impl/logger.d.mts +0 -32
  45. package/impl/logger.d.mts.map +0 -1
  46. package/impl/patch.d.mts +0 -6
  47. package/impl/patch.d.mts.map +0 -1
  48. package/impl/query.d.mts +0 -195
  49. package/impl/query.d.mts.map +0 -1
  50. package/impl/queryBuilder.d.mts +0 -94
  51. package/impl/queryBuilder.d.mts.map +0 -1
  52. package/impl/retry.d.mts +0 -2
  53. package/impl/retry.d.mts.map +0 -1
  54. package/impl/stream.d.mts +0 -3
  55. package/impl/stream.d.mts.map +0 -1
  56. package/impl/trackedEmitter.d.mts +0 -8
  57. package/impl/trackedEmitter.d.mts.map +0 -1
  58. package/impl/transactionErrors.d.mts +0 -57
  59. package/impl/transactionErrors.d.mts.map +0 -1
  60. package/index.d.mts +0 -56
  61. package/index.d.mts.map +0 -1
  62. package/schema/bind.d.mts +0 -820
  63. package/schema/bind.d.mts.map +0 -1
  64. package/schema/bulk.d.mts +0 -910
  65. package/schema/bulk.d.mts.map +0 -1
  66. package/schema/config.d.mts +0 -79
  67. package/schema/config.d.mts.map +0 -1
  68. package/schema/crud.d.mts +0 -491
  69. package/schema/crud.d.mts.map +0 -1
  70. package/schema/patch.d.mts +0 -255
  71. package/schema/patch.d.mts.map +0 -1
  72. package/schema/query.d.mts +0 -406
  73. package/schema/query.d.mts.map +0 -1
  74. package/schema/stream.d.mts +0 -211
  75. 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": "4.2.0",
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/test.mjs",
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 */
@@ -0,0 +1,11 @@
1
+ declare module 'changes-stream' {
2
+ import { EventEmitter } from 'events'
3
+
4
+ class ChangesStream extends EventEmitter {
5
+ constructor(options: any)
6
+ read(): any
7
+ destroy(): void
8
+ }
9
+
10
+ export = ChangesStream
11
+ }
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
- });