hide-a-bed 5.2.7 → 6.0.0-beta.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 (363) hide show
  1. package/.prettierrc +7 -0
  2. package/README.md +270 -218
  3. package/dist/cjs/index.cjs +1952 -0
  4. package/dist/esm/index.mjs +1898 -0
  5. package/docs/.nojekyll +1 -0
  6. package/docs/assets/hierarchy.js +1 -0
  7. package/docs/assets/highlight.css +113 -0
  8. package/docs/assets/icons.js +18 -0
  9. package/docs/assets/icons.svg +1 -0
  10. package/docs/assets/main.js +60 -0
  11. package/docs/assets/navigation.js +1 -0
  12. package/docs/assets/search.js +1 -0
  13. package/docs/assets/style.css +1633 -0
  14. package/docs/classes/QueryBuilder.html +42 -0
  15. package/docs/functions/bindConfig.html +4 -0
  16. package/docs/functions/bulkGet.html +14 -0
  17. package/docs/functions/bulkGetDictionary.html +10 -0
  18. package/docs/functions/bulkRemove.html +12 -0
  19. package/docs/functions/bulkRemoveMap.html +11 -0
  20. package/docs/functions/bulkSave.html +10 -0
  21. package/docs/functions/bulkSaveTransaction.html +23 -0
  22. package/docs/functions/createLock.html +7 -0
  23. package/docs/functions/createQuery.html +1 -0
  24. package/docs/functions/get.html +1 -0
  25. package/docs/functions/getAtRev.html +1 -0
  26. package/docs/functions/getDBInfo.html +10 -0
  27. package/docs/functions/patch.html +8 -0
  28. package/docs/functions/patchDangerously.html +9 -0
  29. package/docs/functions/put.html +1 -0
  30. package/docs/functions/query.html +15 -0
  31. package/docs/functions/queryStream.html +6 -0
  32. package/docs/functions/remove.html +1 -0
  33. package/docs/functions/removeLock.html +6 -0
  34. package/docs/functions/watchDocs.html +9 -0
  35. package/docs/functions/withRetry.html +6 -0
  36. package/docs/hierarchy.html +1 -0
  37. package/docs/index.html +483 -0
  38. package/docs/interfaces/NetworkError.html +6 -0
  39. package/docs/interfaces/NotFoundError.html +10 -0
  40. package/docs/interfaces/RetryOptions.html +10 -0
  41. package/docs/interfaces/RetryableError.html +10 -0
  42. package/docs/interfaces/StandardSchemaV1.FailureResult.html +4 -0
  43. package/docs/interfaces/StandardSchemaV1.Issue.html +6 -0
  44. package/docs/interfaces/StandardSchemaV1.Options.html +3 -0
  45. package/docs/interfaces/StandardSchemaV1.PathSegment.html +4 -0
  46. package/docs/interfaces/StandardSchemaV1.Props.html +10 -0
  47. package/docs/interfaces/StandardSchemaV1.SuccessResult.html +6 -0
  48. package/docs/interfaces/StandardSchemaV1.Types.html +6 -0
  49. package/docs/interfaces/StandardSchemaV1.html +4 -0
  50. package/docs/modules/StandardSchemaV1.html +1 -0
  51. package/docs/modules.html +1 -0
  52. package/docs/types/BoundInstance.html +1 -0
  53. package/docs/types/BulkGetBound.html +2 -0
  54. package/docs/types/BulkGetDictionaryBound.html +1 -0
  55. package/docs/types/BulkGetDictionaryOptions.html +2 -0
  56. package/docs/types/BulkGetDictionaryResult.html +3 -0
  57. package/docs/types/BulkGetOptions.html +3 -0
  58. package/docs/types/BulkGetResponse.html +1 -0
  59. package/docs/types/CouchConfig-1.html +1 -0
  60. package/docs/types/CouchConfig.html +1 -0
  61. package/docs/types/CouchConfigInput.html +1 -0
  62. package/docs/types/CouchDoc-1.html +1 -0
  63. package/docs/types/CouchDoc.html +2 -0
  64. package/docs/types/CouchDocInput.html +2 -0
  65. package/docs/types/GetAtRevBound.html +1 -0
  66. package/docs/types/GetBound.html +1 -0
  67. package/docs/types/GetOptions.html +2 -0
  68. package/docs/types/LockDoc-1.html +1 -0
  69. package/docs/types/LockDoc.html +1 -0
  70. package/docs/types/LockOptions-1.html +1 -0
  71. package/docs/types/LockOptions.html +1 -0
  72. package/docs/types/LockOptionsInput.html +1 -0
  73. package/docs/types/OnInvalidDocAction.html +1 -0
  74. package/docs/types/OnRow.html +1 -0
  75. package/docs/types/QueryBound.html +1 -0
  76. package/docs/types/SimpleViewOptions-1.html +1 -0
  77. package/docs/types/SimpleViewOptions.html +1 -0
  78. package/docs/types/StandardSchemaV1.InferInput.html +2 -0
  79. package/docs/types/StandardSchemaV1.InferOutput.html +2 -0
  80. package/docs/types/StandardSchemaV1.Result.html +2 -0
  81. package/docs/types/ViewQueryResponse-1.html +1 -0
  82. package/docs/types/ViewQueryResponse.html +2 -0
  83. package/docs/types/ViewQueryResponseValidated.html +2 -0
  84. package/docs/types/ViewRow-1.html +1 -0
  85. package/docs/types/ViewRow.html +2 -0
  86. package/docs/types/ViewRowValidated.html +7 -0
  87. package/docs/types/ViewString.html +1 -0
  88. package/docs/types/WatchOptionsInput.html +1 -0
  89. package/docs/types/WatchOptionsSchema-1.html +1 -0
  90. package/docs/types/WatchOptionsSchema.html +1 -0
  91. package/eslint.config.js +15 -0
  92. package/impl/bindConfig.mts +140 -0
  93. package/impl/bulkGet.mts +256 -0
  94. package/impl/bulkGet.test.mts +159 -0
  95. package/impl/bulkRemove.mts +98 -0
  96. package/impl/bulkRemove.test.mts +102 -0
  97. package/impl/bulkSave.mts +286 -0
  98. package/impl/bulkSave.test.mts +319 -0
  99. package/impl/get.mts +137 -0
  100. package/impl/get.test.mts +114 -0
  101. package/impl/getDBInfo.mts +67 -0
  102. package/impl/getDBInfo.test.mts +62 -0
  103. package/impl/patch.mts +134 -0
  104. package/impl/patch.test.mts +142 -0
  105. package/impl/put.mts +56 -0
  106. package/impl/put.test.mts +114 -0
  107. package/impl/query.mts +224 -0
  108. package/impl/query.test.mts +280 -0
  109. package/impl/remove.mts +65 -0
  110. package/impl/remove.test.mts +82 -0
  111. package/impl/retry.mts +66 -0
  112. package/impl/retry.test.mts +77 -0
  113. package/impl/stream.mts +143 -0
  114. package/impl/stream.test.mts +205 -0
  115. package/impl/sugar/lock.mts +103 -0
  116. package/impl/sugar/lock.test.mts +113 -0
  117. package/impl/sugar/{watch.mjs → watch.mts} +56 -22
  118. package/impl/sugar/watch.test.mts +155 -0
  119. package/impl/utils/errors.mts +130 -0
  120. package/impl/utils/errors.test.mts +58 -0
  121. package/impl/utils/logger.mts +62 -0
  122. package/impl/utils/logger.test.mts +129 -0
  123. package/impl/utils/mergeNeedleOpts.mts +16 -0
  124. package/impl/utils/parseRows.mts +117 -0
  125. package/impl/utils/parseRows.test.mts +183 -0
  126. package/impl/utils/queryBuilder.mts +173 -0
  127. package/impl/utils/queryBuilder.test.mts +83 -0
  128. package/impl/utils/queryString.mts +44 -0
  129. package/impl/utils/queryString.test.mts +53 -0
  130. package/impl/{trackedEmitter.mjs → utils/trackedEmitter.mts} +9 -7
  131. package/impl/utils/transactionErrors.mts +71 -0
  132. package/index.mts +82 -0
  133. package/index.test.mts +415 -0
  134. package/package.json +45 -31
  135. package/schema/config.mts +81 -0
  136. package/schema/couch/couch.input.schema.ts +43 -0
  137. package/schema/couch/couch.output.schema.ts +169 -0
  138. package/schema/sugar/lock.mts +18 -0
  139. package/schema/sugar/watch.mts +14 -0
  140. package/schema/util.mts +8 -0
  141. package/tsconfig.json +10 -4
  142. package/tsdown.config.ts +16 -0
  143. package/typedoc.json +4 -0
  144. package/types/output/eslint.config.d.ts +3 -0
  145. package/types/output/eslint.config.d.ts.map +1 -0
  146. package/types/output/impl/bindConfig.d.mts +174 -0
  147. package/types/output/impl/bindConfig.d.mts.map +1 -0
  148. package/types/output/impl/bulkGet.d.mts +75 -0
  149. package/types/output/impl/bulkGet.d.mts.map +1 -0
  150. package/types/output/impl/bulkGet.test.d.mts +2 -0
  151. package/types/output/impl/bulkGet.test.d.mts.map +1 -0
  152. package/types/output/impl/bulkRemove.d.mts +63 -0
  153. package/types/output/impl/bulkRemove.d.mts.map +1 -0
  154. package/types/output/impl/bulkRemove.test.d.mts +2 -0
  155. package/types/output/impl/bulkRemove.test.d.mts.map +1 -0
  156. package/types/output/impl/bulkSave.d.mts +64 -0
  157. package/types/output/impl/bulkSave.d.mts.map +1 -0
  158. package/types/output/impl/bulkSave.test.d.mts +2 -0
  159. package/types/output/impl/bulkSave.test.d.mts.map +1 -0
  160. package/types/output/impl/get.d.mts +20 -0
  161. package/types/output/impl/get.d.mts.map +1 -0
  162. package/types/output/impl/get.test.d.mts +2 -0
  163. package/types/output/impl/get.test.d.mts.map +1 -0
  164. package/types/output/impl/getDBInfo.d.mts +52 -0
  165. package/types/output/impl/getDBInfo.d.mts.map +1 -0
  166. package/types/output/impl/getDBInfo.test.d.mts +2 -0
  167. package/types/output/impl/getDBInfo.test.d.mts.map +1 -0
  168. package/types/output/impl/patch.d.mts +45 -0
  169. package/types/output/impl/patch.d.mts.map +1 -0
  170. package/types/output/impl/patch.test.d.mts +2 -0
  171. package/types/output/impl/patch.test.d.mts.map +1 -0
  172. package/types/output/impl/put.d.mts +5 -0
  173. package/types/output/impl/put.d.mts.map +1 -0
  174. package/types/output/impl/put.test.d.mts +2 -0
  175. package/types/output/impl/put.test.d.mts.map +1 -0
  176. package/types/output/impl/query.d.mts +47 -0
  177. package/types/output/impl/query.d.mts.map +1 -0
  178. package/types/output/impl/query.test.d.mts +2 -0
  179. package/types/output/impl/query.test.d.mts.map +1 -0
  180. package/types/output/impl/remove.d.mts +9 -0
  181. package/types/output/impl/remove.d.mts.map +1 -0
  182. package/types/output/impl/remove.test.d.mts +2 -0
  183. package/types/output/impl/remove.test.d.mts.map +1 -0
  184. package/types/output/impl/retry.d.mts +32 -0
  185. package/types/output/impl/retry.d.mts.map +1 -0
  186. package/types/output/impl/retry.test.d.mts +2 -0
  187. package/types/output/impl/retry.test.d.mts.map +1 -0
  188. package/types/output/impl/stream.d.mts +13 -0
  189. package/types/output/impl/stream.d.mts.map +1 -0
  190. package/types/output/impl/stream.test.d.mts +2 -0
  191. package/types/output/impl/stream.test.d.mts.map +1 -0
  192. package/types/output/impl/sugar/lock.d.mts +24 -0
  193. package/types/output/impl/sugar/lock.d.mts.map +1 -0
  194. package/types/output/impl/sugar/lock.test.d.mts +2 -0
  195. package/types/output/impl/sugar/lock.test.d.mts.map +1 -0
  196. package/types/output/impl/sugar/watch.d.mts +21 -0
  197. package/types/output/impl/sugar/watch.d.mts.map +1 -0
  198. package/types/output/impl/sugar/watch.test.d.mts +2 -0
  199. package/types/output/impl/sugar/watch.test.d.mts.map +1 -0
  200. package/types/output/impl/utils/errors.d.mts +78 -0
  201. package/types/output/impl/utils/errors.d.mts.map +1 -0
  202. package/types/output/impl/utils/errors.test.d.mts +2 -0
  203. package/types/output/impl/utils/errors.test.d.mts.map +1 -0
  204. package/types/output/impl/utils/logger.d.mts +11 -0
  205. package/types/output/impl/utils/logger.d.mts.map +1 -0
  206. package/types/output/impl/utils/logger.test.d.mts +2 -0
  207. package/types/output/impl/utils/logger.test.d.mts.map +1 -0
  208. package/types/output/impl/utils/mergeNeedleOpts.d.mts +53 -0
  209. package/types/output/impl/utils/mergeNeedleOpts.d.mts.map +1 -0
  210. package/types/output/impl/utils/parseRows.d.mts +15 -0
  211. package/types/output/impl/utils/parseRows.d.mts.map +1 -0
  212. package/types/output/impl/utils/parseRows.test.d.mts +2 -0
  213. package/types/output/impl/utils/parseRows.test.d.mts.map +1 -0
  214. package/types/output/impl/utils/queryBuilder.d.mts +68 -0
  215. package/types/output/impl/utils/queryBuilder.d.mts.map +1 -0
  216. package/types/output/impl/utils/queryBuilder.test.d.mts +2 -0
  217. package/types/output/impl/utils/queryBuilder.test.d.mts.map +1 -0
  218. package/types/output/impl/utils/queryString.d.mts +9 -0
  219. package/types/output/impl/utils/queryString.d.mts.map +1 -0
  220. package/types/output/impl/utils/queryString.test.d.mts +2 -0
  221. package/types/output/impl/utils/queryString.test.d.mts.map +1 -0
  222. package/types/output/impl/utils/trackedEmitter.d.mts +7 -0
  223. package/types/output/impl/utils/trackedEmitter.d.mts.map +1 -0
  224. package/{impl → types/output/impl/utils}/transactionErrors.d.mts +16 -31
  225. package/types/output/impl/utils/transactionErrors.d.mts.map +1 -0
  226. package/types/output/index.d.mts +32 -0
  227. package/types/output/index.d.mts.map +1 -0
  228. package/types/output/index.test.d.mts +2 -0
  229. package/types/output/index.test.d.mts.map +1 -0
  230. package/types/output/schema/config.d.mts +90 -0
  231. package/types/output/schema/config.d.mts.map +1 -0
  232. package/types/output/schema/couch/couch.input.schema.d.ts +29 -0
  233. package/types/output/schema/couch/couch.input.schema.d.ts.map +1 -0
  234. package/types/output/schema/couch/couch.output.schema.d.ts +113 -0
  235. package/types/output/schema/couch/couch.output.schema.d.ts.map +1 -0
  236. package/types/output/schema/sugar/lock.d.mts +19 -0
  237. package/types/output/schema/sugar/lock.d.mts.map +1 -0
  238. package/types/output/schema/sugar/watch.d.mts +11 -0
  239. package/types/output/schema/sugar/watch.d.mts.map +1 -0
  240. package/types/output/schema/util.d.mts +85 -0
  241. package/types/output/schema/util.d.mts.map +1 -0
  242. package/types/output/tsdown.config.d.ts +3 -0
  243. package/types/output/tsdown.config.d.ts.map +1 -0
  244. package/types/output/types/standard-schema.d.ts +60 -0
  245. package/types/output/types/standard-schema.d.ts.map +1 -0
  246. package/types/standard-schema.ts +76 -0
  247. package/types/utils.d.ts +1 -0
  248. package/cjs/impl/bulk.cjs +0 -275
  249. package/cjs/impl/changes.cjs +0 -67
  250. package/cjs/impl/crud.cjs +0 -127
  251. package/cjs/impl/errors.cjs +0 -75
  252. package/cjs/impl/logger.cjs +0 -70
  253. package/cjs/impl/patch.cjs +0 -95
  254. package/cjs/impl/query.cjs +0 -116
  255. package/cjs/impl/queryBuilder.cjs +0 -163
  256. package/cjs/impl/retry.cjs +0 -55
  257. package/cjs/impl/stream.cjs +0 -121
  258. package/cjs/impl/sugar/lock.cjs +0 -81
  259. package/cjs/impl/sugar/watch.cjs +0 -159
  260. package/cjs/impl/trackedEmitter.cjs +0 -54
  261. package/cjs/impl/transactionErrors.cjs +0 -70
  262. package/cjs/impl/util.cjs +0 -64
  263. package/cjs/index.cjs +0 -132
  264. package/cjs/integration/changes.cjs +0 -76
  265. package/cjs/integration/disconnect-watch.cjs +0 -52
  266. package/cjs/integration/watch.cjs +0 -59
  267. package/cjs/schema/bind.cjs +0 -59
  268. package/cjs/schema/bulk.cjs +0 -92
  269. package/cjs/schema/changes.cjs +0 -68
  270. package/cjs/schema/config.cjs +0 -48
  271. package/cjs/schema/crud.cjs +0 -77
  272. package/cjs/schema/patch.cjs +0 -53
  273. package/cjs/schema/query.cjs +0 -62
  274. package/cjs/schema/stream.cjs +0 -42
  275. package/cjs/schema/sugar/lock.cjs +0 -59
  276. package/cjs/schema/sugar/watch.cjs +0 -42
  277. package/cjs/schema/util.cjs +0 -39
  278. package/config.json +0 -5
  279. package/docs/compiler.png +0 -0
  280. package/dualmode.config.json +0 -11
  281. package/impl/bulk.d.mts +0 -11
  282. package/impl/bulk.d.mts.map +0 -1
  283. package/impl/bulk.mjs +0 -291
  284. package/impl/changes.d.mts +0 -12
  285. package/impl/changes.d.mts.map +0 -1
  286. package/impl/changes.mjs +0 -53
  287. package/impl/crud.d.mts +0 -7
  288. package/impl/crud.d.mts.map +0 -1
  289. package/impl/crud.mjs +0 -108
  290. package/impl/errors.d.mts +0 -43
  291. package/impl/errors.d.mts.map +0 -1
  292. package/impl/errors.mjs +0 -65
  293. package/impl/logger.d.mts +0 -32
  294. package/impl/logger.d.mts.map +0 -1
  295. package/impl/logger.mjs +0 -59
  296. package/impl/patch.d.mts +0 -6
  297. package/impl/patch.d.mts.map +0 -1
  298. package/impl/patch.mjs +0 -88
  299. package/impl/query.d.mts +0 -195
  300. package/impl/query.d.mts.map +0 -1
  301. package/impl/query.mjs +0 -122
  302. package/impl/queryBuilder.d.mts +0 -154
  303. package/impl/queryBuilder.d.mts.map +0 -1
  304. package/impl/queryBuilder.mjs +0 -175
  305. package/impl/retry.d.mts +0 -2
  306. package/impl/retry.d.mts.map +0 -1
  307. package/impl/retry.mjs +0 -39
  308. package/impl/stream.d.mts +0 -3
  309. package/impl/stream.d.mts.map +0 -1
  310. package/impl/stream.mjs +0 -98
  311. package/impl/sugar/lock.d.mts +0 -5
  312. package/impl/sugar/lock.d.mts.map +0 -1
  313. package/impl/sugar/lock.mjs +0 -70
  314. package/impl/sugar/watch.d.mts +0 -34
  315. package/impl/sugar/watch.d.mts.map +0 -1
  316. package/impl/trackedEmitter.d.mts +0 -8
  317. package/impl/trackedEmitter.d.mts.map +0 -1
  318. package/impl/transactionErrors.d.mts.map +0 -1
  319. package/impl/transactionErrors.mjs +0 -47
  320. package/impl/util.d.mts +0 -3
  321. package/impl/util.d.mts.map +0 -1
  322. package/impl/util.mjs +0 -35
  323. package/index.d.mts +0 -80
  324. package/index.d.mts.map +0 -1
  325. package/index.mjs +0 -141
  326. package/integration/changes.mjs +0 -60
  327. package/integration/disconnect-watch.mjs +0 -36
  328. package/integration/watch.mjs +0 -40
  329. package/log.txt +0 -580
  330. package/schema/bind.d.mts +0 -5461
  331. package/schema/bind.d.mts.map +0 -1
  332. package/schema/bind.mjs +0 -43
  333. package/schema/bulk.d.mts +0 -923
  334. package/schema/bulk.d.mts.map +0 -1
  335. package/schema/bulk.mjs +0 -83
  336. package/schema/changes.d.mts +0 -191
  337. package/schema/changes.d.mts.map +0 -1
  338. package/schema/changes.mjs +0 -59
  339. package/schema/config.d.mts +0 -79
  340. package/schema/config.d.mts.map +0 -1
  341. package/schema/config.mjs +0 -26
  342. package/schema/crud.d.mts +0 -491
  343. package/schema/crud.d.mts.map +0 -1
  344. package/schema/crud.mjs +0 -64
  345. package/schema/patch.d.mts +0 -255
  346. package/schema/patch.d.mts.map +0 -1
  347. package/schema/patch.mjs +0 -42
  348. package/schema/query.d.mts +0 -406
  349. package/schema/query.d.mts.map +0 -1
  350. package/schema/query.mjs +0 -45
  351. package/schema/stream.d.mts +0 -211
  352. package/schema/stream.d.mts.map +0 -1
  353. package/schema/stream.mjs +0 -23
  354. package/schema/sugar/lock.d.mts +0 -238
  355. package/schema/sugar/lock.d.mts.map +0 -1
  356. package/schema/sugar/lock.mjs +0 -50
  357. package/schema/sugar/watch.d.mts +0 -127
  358. package/schema/sugar/watch.d.mts.map +0 -1
  359. package/schema/sugar/watch.mjs +0 -29
  360. package/schema/util.d.mts +0 -160
  361. package/schema/util.d.mts.map +0 -1
  362. package/schema/util.mjs +0 -35
  363. package/types/changes-stream.d.ts +0 -11
@@ -0,0 +1,183 @@
1
+ import assert from 'node:assert/strict'
2
+ import test, { suite } from 'node:test'
3
+ import { z } from 'zod'
4
+ import { parseRows } from './parseRows.mts'
5
+
6
+ const docSchema = z.looseObject({
7
+ _id: z.string(),
8
+ count: z.number()
9
+ })
10
+
11
+ const keySchema = z.number()
12
+
13
+ const valueSchema = z.looseObject({
14
+ rev: z.string()
15
+ })
16
+
17
+ suite('parseRows', () => {
18
+ test('throws when rows is not an array', async () => {
19
+ await assert.rejects(
20
+ () =>
21
+ parseRows(
22
+ {},
23
+ {
24
+ docSchema,
25
+ keySchema,
26
+ valueSchema,
27
+ onInvalidDoc: 'throw'
28
+ }
29
+ ),
30
+ err => err instanceof Error && err.message === 'invalid rows format'
31
+ )
32
+ })
33
+
34
+ test('parses rows and validates documents', async () => {
35
+ const rows = [
36
+ {
37
+ id: 'doc-valid',
38
+ key: 1,
39
+ value: { rev: '1-abc' },
40
+ doc: {
41
+ _id: 'doc-valid',
42
+ count: 42
43
+ }
44
+ },
45
+ {
46
+ id: 'doc-missing',
47
+ key: 2,
48
+ error: 'not_found',
49
+ doc: null,
50
+ value: { rev: '1-missing' }
51
+ }
52
+ ]
53
+
54
+ const result = await parseRows(rows, {
55
+ docSchema,
56
+ keySchema,
57
+ valueSchema,
58
+ onInvalidDoc: 'throw'
59
+ })
60
+
61
+ assert.strictEqual(result.length, 2)
62
+ const [valid, missing] = result
63
+ assert.strictEqual(valid?.id, 'doc-valid')
64
+ assert.strictEqual(valid?.doc?.count, 42)
65
+ assert.strictEqual(valid?.key, 1)
66
+ assert.strictEqual(valid?.value?.rev, '1-abc')
67
+ assert.strictEqual(missing?.id, 'doc-missing')
68
+ assert.strictEqual(missing?.error, 'not_found')
69
+ assert.strictEqual(missing?.key, 2)
70
+ assert.strictEqual(missing?.value?.rev, '1-missing')
71
+ assert.strictEqual(missing?.doc, null)
72
+ })
73
+
74
+ test('skips invalid documents when requested', async () => {
75
+ const rows = [
76
+ {
77
+ id: 'doc-valid',
78
+ key: 1,
79
+ value: { rev: '1-valid' },
80
+ doc: {
81
+ _id: 'doc-valid',
82
+ count: 7
83
+ }
84
+ },
85
+ {
86
+ id: 'doc-invalid',
87
+ key: 2,
88
+ value: { rev: '1-invalid' },
89
+ doc: {
90
+ _id: 'doc-invalid',
91
+ count: 'nope'
92
+ }
93
+ }
94
+ ]
95
+
96
+ const result = await parseRows(rows, {
97
+ docSchema,
98
+ keySchema,
99
+ valueSchema,
100
+ onInvalidDoc: 'skip'
101
+ })
102
+
103
+ assert.strictEqual(result.length, 1)
104
+ assert.strictEqual(result[0]?.id, 'doc-valid')
105
+ assert.strictEqual(result[0]?.doc?.count, 7)
106
+ assert.strictEqual(result[0]?.key, 1)
107
+ })
108
+
109
+ test('throws when a document fails validation with onInvalidDoc=throw', async () => {
110
+ const rows = [
111
+ {
112
+ id: 'doc-invalid',
113
+ key: 1,
114
+ value: { rev: '1-invalid' },
115
+ doc: {
116
+ _id: 'doc-invalid',
117
+ count: 'nope'
118
+ }
119
+ }
120
+ ]
121
+
122
+ await assert.rejects(
123
+ () =>
124
+ parseRows(rows, {
125
+ docSchema,
126
+ keySchema,
127
+ valueSchema,
128
+ onInvalidDoc: 'throw'
129
+ }),
130
+ err => Array.isArray(err)
131
+ )
132
+ })
133
+
134
+ test('throws when key fails validation', async () => {
135
+ const rows = [
136
+ {
137
+ id: 'doc-valid',
138
+ key: 'invalid',
139
+ value: { rev: '1-valid' },
140
+ doc: {
141
+ _id: 'doc-valid',
142
+ count: 7
143
+ }
144
+ }
145
+ ]
146
+
147
+ await assert.rejects(
148
+ () =>
149
+ parseRows(rows, {
150
+ docSchema,
151
+ keySchema,
152
+ valueSchema,
153
+ onInvalidDoc: 'throw'
154
+ }),
155
+ err => Array.isArray(err)
156
+ )
157
+ })
158
+
159
+ test('throws when value fails validation', async () => {
160
+ const rows = [
161
+ {
162
+ id: 'doc-valid',
163
+ key: 1,
164
+ value: { rev: 123 },
165
+ doc: {
166
+ _id: 'doc-valid',
167
+ count: 7
168
+ }
169
+ }
170
+ ]
171
+
172
+ await assert.rejects(
173
+ () =>
174
+ parseRows(rows, {
175
+ docSchema,
176
+ keySchema,
177
+ valueSchema,
178
+ onInvalidDoc: 'throw'
179
+ }),
180
+ err => Array.isArray(err)
181
+ )
182
+ })
183
+ })
@@ -0,0 +1,173 @@
1
+ import type { ViewOptions } from '../../schema/couch/couch.input.schema.ts'
2
+
3
+ /**
4
+ * A builder class for constructing CouchDB view query options.
5
+ * Provides a fluent API for setting various query parameters.
6
+ * @example
7
+ * const queryOptions = new QueryBuilder()
8
+ * .limit(10)
9
+ * .include_docs()
10
+ * .startKey('someKey')
11
+ * .build();
12
+ * @see SimpleViewOptions for the full list of options.
13
+ *
14
+ * @remarks
15
+ * Each method corresponds to a CouchDB view option and returns the builder instance for chaining.
16
+ *
17
+ * @returns The constructed SimpleViewOptions object.
18
+ */
19
+ export class QueryBuilder {
20
+ #options: ViewOptions = {}
21
+
22
+ descending(descending = true): this {
23
+ this.#options.descending = descending
24
+ return this
25
+ }
26
+
27
+ endkey_docid(endkeyDocId: NonNullable<ViewOptions['endkey_docid']>): this {
28
+ this.#options.endkey_docid = endkeyDocId
29
+ return this
30
+ }
31
+
32
+ /**
33
+ * Alias for endkey_docid
34
+ */
35
+ end_key_doc_id(endkeyDocId: NonNullable<ViewOptions['endkey_docid']>): this {
36
+ this.#options.endkey_docid = endkeyDocId
37
+ return this
38
+ }
39
+
40
+ endkey(endkey: ViewOptions['endkey']): this {
41
+ this.#options.endkey = endkey
42
+ return this
43
+ }
44
+
45
+ /**
46
+ * Alias for endkey
47
+ */
48
+ endKey(endkey: ViewOptions['endkey']): this {
49
+ this.#options.endkey = endkey
50
+ return this
51
+ }
52
+
53
+ /**
54
+ * Alias for endkey
55
+ */
56
+ end_key(endkey: ViewOptions['endkey']): this {
57
+ this.#options.endkey = endkey
58
+ return this
59
+ }
60
+
61
+ group(group = true): this {
62
+ this.#options.group = group
63
+ return this
64
+ }
65
+
66
+ group_level(level: NonNullable<ViewOptions['group_level']>): this {
67
+ this.#options.group_level = level
68
+ return this
69
+ }
70
+
71
+ include_docs(includeDocs = true): this {
72
+ this.#options.include_docs = includeDocs
73
+ return this
74
+ }
75
+
76
+ inclusive_end(inclusiveEnd = true): this {
77
+ this.#options.inclusive_end = inclusiveEnd
78
+ return this
79
+ }
80
+
81
+ key(key: ViewOptions['key']): this {
82
+ this.#options.key = key
83
+ return this
84
+ }
85
+
86
+ keys(keys: NonNullable<ViewOptions['keys']>): this {
87
+ this.#options.keys = keys
88
+ return this
89
+ }
90
+
91
+ limit(limit: NonNullable<ViewOptions['limit']>): this {
92
+ this.#options.limit = limit
93
+ return this
94
+ }
95
+
96
+ reduce(reduce = true): this {
97
+ this.#options.reduce = reduce
98
+ return this
99
+ }
100
+
101
+ skip(skip: NonNullable<ViewOptions['skip']>): this {
102
+ this.#options.skip = skip
103
+ return this
104
+ }
105
+
106
+ sorted(sorted = true): this {
107
+ this.#options.sorted = sorted
108
+ return this
109
+ }
110
+
111
+ stable(stable = true): this {
112
+ this.#options.stable = stable
113
+ return this
114
+ }
115
+
116
+ startkey(startkey: ViewOptions['startkey']): this {
117
+ this.#options.startkey = startkey
118
+ return this
119
+ }
120
+
121
+ /**
122
+ * Alias for startkey
123
+ */
124
+ startKey(startkey: ViewOptions['startkey']): this {
125
+ this.#options.startkey = startkey
126
+ return this
127
+ }
128
+
129
+ /**
130
+ * Alias for startkey
131
+ */
132
+ start_key(startkey: ViewOptions['startkey']): this {
133
+ this.#options.startkey = startkey
134
+ return this
135
+ }
136
+
137
+ startkey_docid(startkeyDocId: NonNullable<ViewOptions['startkey_docid']>): this {
138
+ this.#options.startkey_docid = startkeyDocId
139
+ return this
140
+ }
141
+
142
+ /**
143
+ * Alias for startkey_docid
144
+ */
145
+ start_key_doc_id(startkeyDocId: NonNullable<ViewOptions['startkey_docid']>): this {
146
+ this.#options.startkey_docid = startkeyDocId
147
+ return this
148
+ }
149
+
150
+ update(update: NonNullable<ViewOptions['update']>): this {
151
+ this.#options.update = update
152
+ return this
153
+ }
154
+
155
+ update_seq(updateSeq = true): this {
156
+ this.#options.update_seq = updateSeq
157
+ return this
158
+ }
159
+
160
+ /**
161
+ * Builds and returns the ViewOptions object.
162
+ */
163
+ build(): ViewOptions {
164
+ return { ...this.#options }
165
+ }
166
+ }
167
+
168
+ type AssertViewOptionsCovered =
169
+ Exclude<keyof ViewOptions, keyof QueryBuilder> extends never ? true : never
170
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
171
+ const _assertViewOptionsCovered: AssertViewOptionsCovered = true
172
+
173
+ export const createQuery = (): QueryBuilder => new QueryBuilder()
@@ -0,0 +1,83 @@
1
+ import assert from 'node:assert/strict'
2
+ import test, { suite } from 'node:test'
3
+ import { QueryBuilder, createQuery } from './queryBuilder.mts'
4
+
5
+ suite('QueryBuilder', () => {
6
+ test('build returns populated options', () => {
7
+ const options = new QueryBuilder()
8
+ .descending(false)
9
+ .endkey_docid('end-doc')
10
+ .endkey(['range-end'])
11
+ .group(false)
12
+ .group_level(2)
13
+ .include_docs(true)
14
+ .inclusive_end(false)
15
+ .key('primary')
16
+ .keys(['k1', { foo: 'bar' }])
17
+ .limit(10)
18
+ .reduce(false)
19
+ .skip(5)
20
+ .sorted(false)
21
+ .stable(true)
22
+ .startkey(['range-start'])
23
+ .startkey_docid('start-doc')
24
+ .update('lazy')
25
+ .update_seq(true)
26
+ .build()
27
+
28
+ assert.deepStrictEqual(options, {
29
+ descending: false,
30
+ endkey_docid: 'end-doc',
31
+ endkey: ['range-end'],
32
+ group: false,
33
+ group_level: 2,
34
+ include_docs: true,
35
+ inclusive_end: false,
36
+ key: 'primary',
37
+ keys: ['k1', { foo: 'bar' }],
38
+ limit: 10,
39
+ reduce: false,
40
+ skip: 5,
41
+ sorted: false,
42
+ stable: true,
43
+ startkey: ['range-start'],
44
+ startkey_docid: 'start-doc',
45
+ update: 'lazy',
46
+ update_seq: true
47
+ })
48
+ })
49
+
50
+ test('alias methods map to canonical fields', () => {
51
+ const options = new QueryBuilder()
52
+ .endKey(['alias-end'])
53
+ .startKey(['alias-start'])
54
+ .end_key_doc_id('alias-end-doc')
55
+ .start_key_doc_id('alias-start-doc')
56
+ .end_key(['duplicate-end'])
57
+ .start_key(['duplicate-start'])
58
+ .build()
59
+
60
+ assert.deepStrictEqual(options, {
61
+ endkey: ['duplicate-end'],
62
+ startkey: ['duplicate-start'],
63
+ endkey_docid: 'alias-end-doc',
64
+ startkey_docid: 'alias-start-doc'
65
+ })
66
+ })
67
+
68
+ test('build returns a defensive copy', () => {
69
+ const builder = new QueryBuilder().limit(5)
70
+ const first = builder.build()
71
+ first.limit = 42
72
+
73
+ const second = builder.build()
74
+
75
+ assert.notStrictEqual(first, second)
76
+ assert.strictEqual(second.limit, 5)
77
+ })
78
+
79
+ test('createQuery returns a builder instance', () => {
80
+ const builder = createQuery()
81
+ assert.ok(builder instanceof QueryBuilder)
82
+ })
83
+ })
@@ -0,0 +1,44 @@
1
+ import { ViewOptions } from '../../schema/couch/couch.input.schema.ts'
2
+
3
+ const KEYS_TO_QUOTE: (keyof ViewOptions)[] = [
4
+ 'endkey_docid',
5
+ 'endkey',
6
+ 'key',
7
+ 'keys',
8
+ 'startkey',
9
+ 'startkey_docid',
10
+ 'update'
11
+ ]
12
+
13
+ /**
14
+ * Serialize CouchDB view options into a URL-safe query string, quoting values CouchDB expects as JSON.
15
+ * @param options The view options to serialize
16
+ * @param params The list of option keys that require JSON quoting
17
+ * @returns The serialized query string
18
+ */
19
+ export function queryString(options: ViewOptions = {}): string {
20
+ const searchParams = new URLSearchParams()
21
+ const parsedOptions = ViewOptions.parse(options)
22
+ Object.entries(parsedOptions).forEach(([key, rawValue]) => {
23
+ let value = rawValue
24
+ if (KEYS_TO_QUOTE.includes(key as keyof ViewOptions)) {
25
+ if (typeof value === 'string') value = `"${value}"`
26
+ if (Array.isArray(value)) {
27
+ value =
28
+ '[' +
29
+ value
30
+ .map(i => {
31
+ if (i === null) return 'null'
32
+ if (typeof i === 'string') return `"${i}"`
33
+ if (typeof i === 'object' && Object.keys(i).length === 0) return '{}'
34
+ if (typeof i === 'object') return JSON.stringify(i)
35
+ return i
36
+ })
37
+ .join(',') +
38
+ ']'
39
+ }
40
+ }
41
+ searchParams.set(key, String(value))
42
+ })
43
+ return searchParams.toString()
44
+ }
@@ -0,0 +1,53 @@
1
+ import assert from 'node:assert/strict'
2
+ import test, { suite } from 'node:test'
3
+ import { queryString } from './queryString.mts'
4
+ import type { ViewOptions } from '../../schema/couch/couch.input.schema.ts'
5
+
6
+ const keysToQuote: Array<keyof ViewOptions> = [
7
+ 'endkey_docid',
8
+ 'endkey',
9
+ 'key',
10
+ 'keys',
11
+ 'startkey',
12
+ 'startkey_docid',
13
+ 'update'
14
+ ]
15
+
16
+ suite('queryString', () => {
17
+ test('queryString quotes string values for KEYS_TO_QUOTE except keys array', () => {
18
+ const encodedByKey = new Map<string, string | null>()
19
+
20
+ for (const key of keysToQuote) {
21
+ if (key === 'keys') continue
22
+ const optionValue: ViewOptions = {
23
+ [key]: key === 'update' ? 'lazy' : 'banana'
24
+ } as ViewOptions
25
+ const encoded = queryString(optionValue)
26
+ const params = new URLSearchParams(encoded)
27
+ encodedByKey.set(key as string, params.get(key as string))
28
+ }
29
+
30
+ assert.deepEqual(Object.fromEntries(encodedByKey), {
31
+ endkey_docid: '"banana"',
32
+ endkey: '"banana"',
33
+ key: '"banana"',
34
+ startkey: '"banana"',
35
+ startkey_docid: '"banana"',
36
+ update: '"lazy"'
37
+ })
38
+ })
39
+
40
+ test('queryString leaves primitive options outside KEYS_TO_QUOTE unchanged', () => {
41
+ const encoded = queryString({ descending: true })
42
+ const params = new URLSearchParams(encoded)
43
+ assert.equal(params.get('descending'), 'true')
44
+ })
45
+
46
+ test('queryString stringifies array values for keys option', () => {
47
+ const encoded = queryString({
48
+ keys: ['alpha', null, 42, {}, { foo: 'bar' }]
49
+ })
50
+ const params = new URLSearchParams(encoded)
51
+ assert.equal(params.get('keys'), '["alpha",null,42,{}, {"foo":"bar"}]'.replace(' ', ''))
52
+ })
53
+ })
@@ -1,21 +1,23 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
1
3
  import { EventEmitter } from 'events'
2
4
 
3
5
  export class TrackedEmitter extends EventEmitter {
4
6
  // create a constructor with some options
5
- constructor (options) {
7
+ constructor(options) {
6
8
  super(options)
7
9
  if (options.delay) this.delay = options.delay
8
10
  }
9
11
 
10
- emit (event, ...args) {
12
+ emit(event, ...args) {
11
13
  const listeners = this.listeners(event)
12
14
  let completed = 0
13
15
 
14
- return new Promise((resolve) => {
16
+ return new Promise(resolve => {
15
17
  if (!listeners || listeners.length === 0) {
16
18
  return resolve() // no listeners? no delay
17
19
  }
18
- listeners.forEach((listener) => {
20
+ listeners.forEach(listener => {
19
21
  listener(...args)
20
22
  completed++
21
23
  if (completed === listeners.length) {
@@ -27,7 +29,7 @@ export class TrackedEmitter extends EventEmitter {
27
29
  }
28
30
  }
29
31
 
30
- export const setupEmitter = (config) => {
31
- if (!config._emitter) return ({ emit: async () => {} })
32
- return config._emitter
32
+ export const setupEmitter = config => {
33
+ if (!config['~emitter']) return { emit: async () => {} }
34
+ return config['~emitter']
33
35
  }
@@ -0,0 +1,71 @@
1
+ export class TransactionSetupError extends Error {
2
+ details: Record<string, unknown>
3
+
4
+ constructor(message: string, details: Record<string, unknown> = {}) {
5
+ super(message)
6
+ this.name = 'TransactionSetupError'
7
+ this.details = details
8
+ }
9
+ }
10
+
11
+ export class TransactionVersionConflictError extends Error {
12
+ conflictingIds: string[]
13
+
14
+ constructor(conflictingIds: string[]) {
15
+ super(`Revision mismatch for documents: ${conflictingIds.join(', ')}`)
16
+ this.name = 'TransactionVersionConflictError'
17
+ this.conflictingIds = conflictingIds
18
+ }
19
+ }
20
+
21
+ export class TransactionBulkOperationError extends Error {
22
+ failedDocs: {
23
+ ok?: boolean | null
24
+ id?: string | null
25
+ rev?: string | null
26
+ error?: string | null
27
+ reason?: string | null
28
+ }[]
29
+
30
+ constructor(
31
+ failedDocs: Array<{
32
+ ok?: boolean | null
33
+ id?: string | null
34
+ rev?: string | null
35
+ error?: string | null
36
+ reason?: string | null
37
+ }>
38
+ ) {
39
+ super(`Failed to save documents: ${failedDocs.map(d => d.id).join(', ')}`)
40
+ this.name = 'TransactionBulkOperationError'
41
+ this.failedDocs = failedDocs
42
+ }
43
+ }
44
+
45
+ export class TransactionRollbackError extends Error {
46
+ originalError: Error
47
+ rollbackResults: {
48
+ ok?: boolean | null
49
+ id?: string | null
50
+ rev?: string | null
51
+ error?: string | null
52
+ reason?: string | null
53
+ }[]
54
+
55
+ constructor(
56
+ message: string,
57
+ originalError: Error,
58
+ rollbackResults: Array<{
59
+ ok?: boolean | null
60
+ id?: string | null
61
+ rev?: string | null
62
+ error?: string | null
63
+ reason?: string | null
64
+ }>
65
+ ) {
66
+ super(message)
67
+ this.name = 'TransactionRollbackError'
68
+ this.originalError = originalError
69
+ this.rollbackResults = rollbackResults
70
+ }
71
+ }