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
package/impl/patch.mts ADDED
@@ -0,0 +1,134 @@
1
+ import { put } from './put.mts'
2
+ import { get } from './get.mts'
3
+ import { createLogger } from './utils/logger.mts'
4
+ import { setTimeout } from 'node:timers/promises'
5
+ import { CouchConfig, type CouchConfigInput } from '../schema/config.mts'
6
+ import { z } from 'zod'
7
+
8
+ const PatchProperties = z
9
+ .looseObject({
10
+ _rev: z.string('_rev is required for patch operations')
11
+ })
12
+ .describe('Patch payload with _rev')
13
+
14
+ /**
15
+ * Patch a CouchDB document by merging provided properties.
16
+ * Validates that the _rev matches before applying the patch.
17
+ *
18
+ * @param configInput - CouchDB configuration
19
+ * @param id - Document ID to patch
20
+ * @param _properties - Properties to merge into the document (must include _rev)
21
+ * @returns The result of the put operation
22
+ *
23
+ * @throws Error if the _rev does not match or other errors occur
24
+ */
25
+ export const patch = async (
26
+ configInput: CouchConfigInput,
27
+ id: string,
28
+ _properties: z.infer<typeof PatchProperties>
29
+ ) => {
30
+ const config = CouchConfig.parse(configInput)
31
+ const properties = PatchProperties.parse(_properties)
32
+ const logger = createLogger(configInput)
33
+
34
+ logger.info(`Starting patch operation for document ${id}`)
35
+ logger.debug('Patch properties:', properties)
36
+ const doc = await get(config, id)
37
+ if (doc?._rev !== properties._rev) {
38
+ return {
39
+ statusCode: 409,
40
+ ok: false,
41
+ error: 'conflict'
42
+ }
43
+ }
44
+
45
+ const updatedDoc = { ...doc, ...properties }
46
+ logger.debug('Merged document:', updatedDoc)
47
+ const result = await put(config, updatedDoc)
48
+ logger.info(`Successfully patched document ${id}, rev: ${result.rev}`)
49
+ return result
50
+ }
51
+
52
+ /**
53
+ * Patch a CouchDB document by merging provided properties.
54
+ * This function will retry on conflicts using an exponential backoff strategy.
55
+ *
56
+ * @remarks patchDangerously can clobber data. It will retry even if a conflict happens. There are some use cases for this, but you have been warned, hence the name.
57
+ *
58
+ * @param configInput - CouchDB configuration
59
+ * @param id - Document ID to patch
60
+ * @param properties - Properties to merge into the document
61
+ * @returns The result of the put operation or an error if max retries are exceeded
62
+ *
63
+ * @throws Error if max retries are exceeded or other errors occur
64
+ */
65
+ export const patchDangerously = async (
66
+ configInput: CouchConfigInput,
67
+ id: string,
68
+ properties: Record<string, unknown>
69
+ ) => {
70
+ const config = CouchConfig.parse(configInput)
71
+ const logger = createLogger(config)
72
+ const maxRetries = config.maxRetries || 5
73
+ let delay = config.initialDelay || 1000
74
+ let attempts = 0
75
+
76
+ logger.info(`Starting patch operation for document ${id}`)
77
+ logger.debug('Patch properties:', properties)
78
+
79
+ while (attempts <= maxRetries) {
80
+ logger.debug(`Attempt ${attempts + 1} of ${maxRetries + 1}`)
81
+ try {
82
+ const doc = await get(config, id)
83
+ if (!doc) {
84
+ logger.warn(`Document ${id} not found`)
85
+ return { ok: false, statusCode: 404, error: 'not_found' }
86
+ }
87
+
88
+ const updatedDoc = { ...doc, ...properties }
89
+ logger.debug('Merged document:', updatedDoc)
90
+
91
+ const result = await put(config, updatedDoc)
92
+
93
+ // Check if the response indicates a conflict
94
+ if (result.ok) {
95
+ logger.info(`Successfully patched document ${id}, rev: ${result.rev}`)
96
+ return result
97
+ }
98
+
99
+ // If not ok, treat as conflict and retry
100
+ attempts++
101
+ if (attempts > maxRetries) {
102
+ logger.error(`Failed to patch ${id} after ${maxRetries} attempts`)
103
+ throw new Error(`Failed to patch after ${maxRetries} attempts`)
104
+ }
105
+
106
+ logger.warn(`Conflict detected for ${id}, retrying (attempt ${attempts})`)
107
+ await setTimeout(delay)
108
+ delay *= config.backoffFactor || 2
109
+ logger.debug(`Next retry delay: ${delay}ms`)
110
+ } catch (err) {
111
+ if (
112
+ typeof err === 'object' &&
113
+ err !== null &&
114
+ 'message' in err &&
115
+ err.message === 'not_found'
116
+ ) {
117
+ logger.warn(`Document ${id} not found during patch operation`)
118
+ return { ok: false, statusCode: 404, error: 'not_found' }
119
+ }
120
+
121
+ // Handle other errors (network, etc)
122
+ attempts++
123
+ if (attempts > maxRetries) {
124
+ const error = `Failed to patch after ${maxRetries} attempts: ${err}`
125
+ logger.error(error)
126
+ return { ok: false, statusCode: 500, error }
127
+ }
128
+
129
+ logger.warn(`Error during patch attempt ${attempts}: ${err}`)
130
+ await setTimeout(delay)
131
+ logger.debug(`Retrying after ${delay}ms`)
132
+ }
133
+ }
134
+ }
@@ -0,0 +1,142 @@
1
+ import assert from 'node:assert/strict'
2
+ import test, { suite } from 'node:test'
3
+ import needle from 'needle'
4
+ import type { CouchConfigInput } from '../schema/config.mts'
5
+ import { get } from './get.mts'
6
+ import { patch, patchDangerously } from './patch.mts'
7
+ import { TEST_DB_URL } from '../test/setup-db.mts'
8
+
9
+ const baseConfig: CouchConfigInput = {
10
+ couch: TEST_DB_URL,
11
+ initialDelay: 10,
12
+ maxRetries: 2,
13
+ backoffFactor: 1.2
14
+ }
15
+
16
+ type DocBody = Record<string, unknown>
17
+
18
+ async function saveDoc(id: string, body: DocBody) {
19
+ const response = await needle(
20
+ 'put',
21
+ `${TEST_DB_URL}/${id}`,
22
+ {
23
+ _id: id,
24
+ ...body
25
+ },
26
+ { json: true }
27
+ )
28
+
29
+ if (response.statusCode !== 201 && response.statusCode !== 200) {
30
+ throw new Error(`Failed to save document ${id}: ${response.statusCode}`)
31
+ }
32
+
33
+ return response.body as { rev: string }
34
+ }
35
+
36
+ suite('patch', () => {
37
+ test('it will throw if provided config is invalid', async () => {
38
+ await assert.rejects(async () => {
39
+ // @ts-expect-error testing invalid config
40
+ await patch({ notAnOption: true, couch: DB_URL, useConsoleLogger: true }, 'doc1', {
41
+ foo: 'bar'
42
+ })
43
+ })
44
+
45
+ await assert.rejects(async () => {
46
+ await patchDangerously(
47
+ // @ts-expect-error testing invalid config
48
+ { anotherBadOption: 123, couch: DB_URL, useConsoleLogger: true },
49
+ 'doc1',
50
+ { foo: 'bar' }
51
+ )
52
+ })
53
+ })
54
+
55
+ test('patch should throw if document revision is not provided', async () => {
56
+ await assert.rejects(
57
+ async () => {
58
+ // @ts-expect-error testing missing _rev
59
+ await patch(baseConfig, 'doc-no-rev', { foo: 'bar' })
60
+ },
61
+ {
62
+ message: /_rev is required for patch operations/
63
+ }
64
+ )
65
+ })
66
+
67
+ test('integration with pouchdb-server', async t => {
68
+ const patch_doc_id = `patch-doc-${Date.now()}`
69
+ const initial = await saveDoc(patch_doc_id, { message: 'original' })
70
+
71
+ await t.test('patch updates document when revision matches', async () => {
72
+ const result = await patch(baseConfig, patch_doc_id, {
73
+ _rev: initial.rev,
74
+ message: 'patched',
75
+ updated: true
76
+ })
77
+ assert.ok(result.ok)
78
+ assert.ok(result.rev)
79
+
80
+ const doc = await get(baseConfig, patch_doc_id)
81
+ assert.strictEqual(doc?.message, 'patched')
82
+ assert.strictEqual(doc?.updated, true)
83
+ })
84
+
85
+ await t.test('patch returns conflict on stale revision', async () => {
86
+ const current = await get(baseConfig, patch_doc_id)
87
+ const staleRev = initial.rev
88
+
89
+ const conflict = await patch(baseConfig, patch_doc_id, {
90
+ _rev: staleRev,
91
+ message: 'should-fail'
92
+ })
93
+ assert.strictEqual(conflict.ok, false)
94
+ assert.strictEqual(conflict.statusCode, 409)
95
+ assert.strictEqual(conflict.error, 'conflict')
96
+
97
+ const doc = await get(baseConfig, patch_doc_id)
98
+ assert.strictEqual(doc?.message, current?.message)
99
+ })
100
+
101
+ await t.test('patchDangerously merges properties without revision', async () => {
102
+ const result = await patchDangerously(baseConfig, patch_doc_id, {
103
+ description: 'dangerously updated'
104
+ })
105
+ assert.ok(result?.ok)
106
+
107
+ const doc = await get(baseConfig, patch_doc_id)
108
+ assert.strictEqual(doc?.description, 'dangerously updated')
109
+ })
110
+
111
+ await t.test('patchDangerously returns not_found when document missing', async () => {
112
+ const response = await patchDangerously(baseConfig, 'missing-doc', {
113
+ message: 'noop'
114
+ })
115
+ assert.strictEqual(response?.ok, false)
116
+ assert.strictEqual(response?.statusCode, 404)
117
+ assert.strictEqual(response?.error, 'not_found')
118
+ })
119
+
120
+ await t.test('patchDangerously reports failure after exhausting retries', async () => {
121
+ const doc = await get(baseConfig, patch_doc_id)
122
+ const conflictConfig: CouchConfigInput = {
123
+ ...baseConfig,
124
+ maxRetries: 1,
125
+ initialDelay: 1,
126
+ backoffFactor: 1
127
+ }
128
+
129
+ const response = await patchDangerously(conflictConfig, patch_doc_id, {
130
+ _rev: initial.rev,
131
+ conflicted: true
132
+ })
133
+ assert.strictEqual(response?.ok, false)
134
+ assert.strictEqual(response?.statusCode, 500)
135
+ assert.match(response?.error ?? '', /Failed to patch after 1 attempts/)
136
+
137
+ const current = await get(baseConfig, patch_doc_id)
138
+ assert.strictEqual(current?.conflicted, undefined)
139
+ assert.strictEqual(current?._rev, doc?._rev)
140
+ })
141
+ })
142
+ })
package/impl/put.mts ADDED
@@ -0,0 +1,56 @@
1
+ import needle from 'needle'
2
+ import { createLogger } from './utils/logger.mts'
3
+ import { mergeNeedleOpts } from './utils/mergeNeedleOpts.mts'
4
+ import { RetryableError } from './utils/errors.mts'
5
+ import { CouchConfig, type CouchConfigInput } from '../schema/config.mts'
6
+ import { CouchPutResponse, type CouchDoc } from '../schema/couch/couch.output.schema.ts'
7
+ import { z } from 'zod'
8
+
9
+ export const put = async (
10
+ configInput: CouchConfigInput,
11
+ doc: CouchDoc
12
+ ): Promise<z.infer<typeof CouchPutResponse>> => {
13
+ const config = CouchConfig.parse(configInput)
14
+ const logger = createLogger(config)
15
+ const url = `${config.couch}/${doc._id}`
16
+ const body = doc
17
+ const opts = {
18
+ json: true,
19
+ headers: {
20
+ 'Content-Type': 'application/json'
21
+ }
22
+ }
23
+ const mergedOpts = mergeNeedleOpts(config, opts)
24
+
25
+ logger.info(`Putting document with id: ${doc._id}`)
26
+ let resp
27
+ try {
28
+ resp = await needle('put', url, body, mergedOpts)
29
+ } catch (err) {
30
+ logger.error('Error during put operation:', err)
31
+ RetryableError.handleNetworkError(err)
32
+ }
33
+
34
+ if (!resp) {
35
+ logger.error('No response received from put request')
36
+ throw new RetryableError('no response', 503)
37
+ }
38
+
39
+ const result = resp?.body || {}
40
+ result.statusCode = resp.statusCode
41
+
42
+ if (resp.statusCode === 409) {
43
+ logger.warn(`Conflict detected for document: ${doc._id}`)
44
+ result.ok = false
45
+ result.error = 'conflict'
46
+ return CouchPutResponse.parse(result)
47
+ }
48
+
49
+ if (RetryableError.isRetryableStatusCode(resp.statusCode)) {
50
+ logger.warn(`Retryable status code received: ${resp.statusCode}`)
51
+ throw new RetryableError(result.reason || 'retryable error', resp.statusCode)
52
+ }
53
+
54
+ logger.info(`Successfully saved document: ${doc._id}`)
55
+ return CouchPutResponse.parse(result)
56
+ }
@@ -0,0 +1,114 @@
1
+ import assert from 'node:assert/strict'
2
+ import test, { suite } from 'node:test'
3
+ import needle from 'needle'
4
+ import type { CouchConfigInput } from '../schema/config.mts'
5
+ import { put } from './put.mts'
6
+ import { RetryableError } from './utils/errors.mts'
7
+ import { TEST_DB_URL } from '../test/setup-db.mts'
8
+
9
+ const baseConfig: CouchConfigInput = {
10
+ couch: TEST_DB_URL
11
+ }
12
+
13
+ type DocBody = Record<string, unknown>
14
+
15
+ async function getDoc(id: string) {
16
+ return needle('get', `${TEST_DB_URL}/${id}`, null, { json: true })
17
+ }
18
+
19
+ async function saveDoc(id: string, body: DocBody) {
20
+ const response = await needle('put', `${TEST_DB_URL}/${id}`, { _id: id, ...body }, { json: true })
21
+ if (response.statusCode !== 201 && response.statusCode !== 200) {
22
+ throw new Error(`Failed to save document ${id}: ${response.statusCode}`)
23
+ }
24
+
25
+ return response.body as { rev: string }
26
+ }
27
+
28
+ suite('put', () => {
29
+ test('rejects invalid config arguments', async () => {
30
+ await assert.rejects(async () => {
31
+ // @ts-expect-error testing invalid config
32
+ await put({ couch: DB_URL, unsupported: true }, { _id: 'bad-config-doc' })
33
+ })
34
+ })
35
+
36
+ test('propagates retryable network failures', async () => {
37
+ const offlineConfig: CouchConfigInput = {
38
+ couch: 'http://localhost:6555/offline-put-test'
39
+ }
40
+
41
+ await assert.rejects(
42
+ () => put(offlineConfig, { _id: 'offline-doc', kind: 'offline' }),
43
+ (err: unknown) => err instanceof RetryableError && err.statusCode === 503
44
+ )
45
+ })
46
+
47
+ test('integration with pouchdb-server', async t => {
48
+ let initialRev: string | undefined
49
+ const put_doc_id = `put-doc-${Date.now()}`
50
+
51
+ await t.test('creates documents via PUT', async () => {
52
+ const result = await put(baseConfig, {
53
+ _id: put_doc_id,
54
+ type: 'integration',
55
+ count: 1
56
+ })
57
+ assert.ok(result)
58
+ assert.strictEqual(result.ok, true)
59
+ assert.strictEqual(result.id, put_doc_id)
60
+ assert.strictEqual(result.statusCode, 201)
61
+ assert.ok(typeof result.rev === 'string')
62
+ initialRev = result.rev
63
+
64
+ const { statusCode, body } = await getDoc(put_doc_id)
65
+ assert.strictEqual(statusCode, 200)
66
+ assert.strictEqual(body?.type, 'integration')
67
+ assert.strictEqual(body?.count, 1)
68
+ })
69
+
70
+ await t.test('updates documents when revision supplied', async () => {
71
+ if (!initialRev) throw new Error('Expected initial revision to be captured')
72
+
73
+ const updateResult = await put(baseConfig, {
74
+ _id: put_doc_id,
75
+ _rev: initialRev,
76
+ type: 'integration',
77
+ count: 2
78
+ })
79
+ assert.ok(updateResult)
80
+ assert.strictEqual(updateResult.ok, true)
81
+ assert.strictEqual(updateResult.statusCode, 201)
82
+ assert.ok(typeof updateResult.rev === 'string')
83
+
84
+ const { body } = await getDoc(put_doc_id)
85
+ assert.strictEqual(body?.count, 2)
86
+ initialRev = updateResult.rev
87
+ })
88
+
89
+ await t.test('reports conflicts when revision is stale', async () => {
90
+ if (!initialRev) throw new Error('Expected revision to be captured')
91
+ const staleRev = initialRev
92
+ const latest = await saveDoc(put_doc_id, {
93
+ _rev: staleRev,
94
+ type: 'integration',
95
+ count: 3
96
+ })
97
+
98
+ const result = await put(baseConfig, {
99
+ _id: put_doc_id,
100
+ _rev: staleRev,
101
+ type: 'integration',
102
+ count: 4
103
+ })
104
+ assert.ok(result)
105
+ assert.strictEqual(result.ok, false)
106
+ assert.strictEqual(result.error, 'conflict')
107
+ assert.strictEqual(result.statusCode, 409)
108
+
109
+ const { body } = await getDoc(put_doc_id)
110
+ assert.strictEqual(body?.count, 3)
111
+ initialRev = latest.rev
112
+ })
113
+ })
114
+ })
package/impl/query.mts ADDED
@@ -0,0 +1,224 @@
1
+ import needle, { type BodyData, type NeedleHttpVerbs } from 'needle'
2
+ import { createLogger } from './utils/logger.mts'
3
+
4
+ import { CouchConfig, type CouchConfigInput } from '../schema/config.mts'
5
+ import { z, ZodAny, ZodNever } from 'zod'
6
+ import { queryString } from './utils/queryString.mts'
7
+ import { mergeNeedleOpts } from './utils/mergeNeedleOpts.mts'
8
+ import { RetryableError } from './utils/errors.mts'
9
+ import { ViewOptions, type ViewString } from '../schema/couch/couch.input.schema.ts'
10
+ import type { CouchDoc, ViewQueryResponseValidated } from '../schema/couch/couch.output.schema.ts'
11
+ import type { StandardSchemaV1 } from '../types/standard-schema.ts'
12
+ import { parseRows, type OnInvalidDocAction } from './utils/parseRows.mts'
13
+
14
+ export async function query<
15
+ DocSchema extends StandardSchemaV1 = typeof CouchDoc,
16
+ KeySchema extends StandardSchemaV1 = ZodAny,
17
+ ValueSchema extends StandardSchemaV1 = ZodAny
18
+ >(
19
+ config: CouchConfigInput,
20
+ view: ViewString,
21
+ options: ViewOptions & {
22
+ include_docs: true
23
+ validate?: {
24
+ onInvalidDoc?: OnInvalidDocAction
25
+ docSchema?: DocSchema
26
+ keySchema?: KeySchema
27
+ valueSchema?: ValueSchema
28
+ }
29
+ }
30
+ ): Promise<ViewQueryResponseValidated<DocSchema, KeySchema, ValueSchema>>
31
+
32
+ export async function query<
33
+ DocSchema extends StandardSchemaV1 = ZodNever,
34
+ KeySchema extends StandardSchemaV1 = ZodAny,
35
+ ValueSchema extends StandardSchemaV1 = ZodAny
36
+ >(
37
+ config: CouchConfigInput,
38
+ view: ViewString,
39
+ options: ViewOptions & {
40
+ include_docs?: false | undefined
41
+ validate?: {
42
+ onInvalidDoc?: OnInvalidDocAction
43
+ docSchema?: DocSchema
44
+ keySchema?: KeySchema
45
+ valueSchema?: ValueSchema
46
+ }
47
+ }
48
+ ): Promise<ViewQueryResponseValidated<ZodNever, KeySchema, ValueSchema>>
49
+
50
+ export async function query(
51
+ config: CouchConfigInput,
52
+ view: ViewString,
53
+ options?: ViewOptions
54
+ ): Promise<ViewQueryResponseValidated<ZodNever, ZodAny, ZodAny>>
55
+
56
+ /**
57
+ * Executes a CouchDB view query with optional schema validation and automatic handling
58
+ * of HTTP method selection, query string construction, and retryable errors.
59
+ *
60
+ * @remarks
61
+ * When using the validation feature, each row in the response will be validated against the provided
62
+ * Types will be inferred from the StandardSchemaV1 supplied in the `options.validate` object.
63
+ *
64
+ * @template DocSchema - StandardSchemaV1 used to validate each returned `doc`, if provided.
65
+ * @template KeySchema - StandardSchemaV1 used to validate each row `key`, if provided.
66
+ * @template ValueSchema - StandardSchemaV1 used to validate each row `value`, if provided.
67
+ *
68
+ * @param _config - CouchDB configuration data that is validated before use.
69
+ * @param view - Fully qualified design document and view identifier (e.g., `_design/foo/_view/bar`).
70
+ * @param options - CouchDB view options, including optional validation schemas.
71
+ *
72
+ * @returns The parsed view response with rows validated against the supplied schemas.
73
+ *
74
+ * @throws {RetryableError} When a retryable HTTP status code is encountered or no response is received.
75
+ * @throws {Error<Array<StandardSchemaV1.Issue>>} When the configuration or validation schemas fail to parse.
76
+ * @throws {Error} When CouchDB returns a non-retryable error payload.
77
+ */
78
+ export async function query<
79
+ DocSchema extends StandardSchemaV1,
80
+ KeySchema extends StandardSchemaV1,
81
+ ValueSchema extends StandardSchemaV1
82
+ >(
83
+ _config: CouchConfigInput,
84
+ view: ViewString,
85
+ options: ViewOptions & {
86
+ validate?: {
87
+ onInvalidDoc?: OnInvalidDocAction
88
+ docSchema?: DocSchema
89
+ keySchema?: KeySchema
90
+ valueSchema?: ValueSchema
91
+ }
92
+ } = {}
93
+ ): Promise<ViewQueryResponseValidated<DocSchema, KeySchema, ValueSchema>> {
94
+ const configParseResult = CouchConfig.safeParse(_config)
95
+ const logger = createLogger(_config)
96
+ logger.info(`Starting view query: ${view}`)
97
+ logger.debug('Query options:', ViewOptions.parse(options || {}))
98
+ if (!configParseResult.success) {
99
+ logger.error(`Invalid configuration provided: ${z.prettifyError(configParseResult.error)}`)
100
+ throw configParseResult.error
101
+ }
102
+
103
+ const config = configParseResult.data
104
+
105
+ let qs = queryString(options)
106
+ let method: NeedleHttpVerbs = 'get'
107
+ let payload: BodyData = null
108
+
109
+ const opts = {
110
+ json: true,
111
+ headers: {
112
+ 'Content-Type': 'application/json'
113
+ }
114
+ }
115
+
116
+ const mergedOpts = mergeNeedleOpts(config, opts)
117
+
118
+ // If keys are supplied, issue a POST to circumvent GET query string limits
119
+ // see http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options
120
+ if (typeof options.keys !== 'undefined') {
121
+ const MAX_URL_LENGTH = 2000
122
+ // according to http://stackoverflow.com/a/417184/680742,
123
+ // the de facto URL length limit is 2000 characters
124
+
125
+ const _options = structuredClone(options)
126
+ delete _options.keys
127
+ qs = queryString(_options)
128
+
129
+ const keysAsString = `keys=${JSON.stringify(options.keys)}`
130
+
131
+ if (keysAsString.length + qs.length + 1 <= MAX_URL_LENGTH) {
132
+ // If the keys are short enough, do a GET. we do this to work around
133
+ // Safari not understanding 304s on POSTs (see pouchdb/pouchdb#1239)
134
+ method = 'get'
135
+ if (qs.length > 0) qs += '&'
136
+ else qs = ''
137
+ qs += keysAsString
138
+ } else {
139
+ method = 'post'
140
+ payload = { keys: options.keys }
141
+ }
142
+ }
143
+
144
+ logger.debug('Generated query string:', qs)
145
+ const url = `${config.couch}/${view}?${qs}`
146
+ let results
147
+
148
+ try {
149
+ logger.debug(`Sending ${method} request to: ${url}`)
150
+ results =
151
+ method === 'get'
152
+ ? await needle('get', url, mergedOpts)
153
+ : await needle('post', url, payload, mergedOpts)
154
+ } catch (err) {
155
+ logger.error('Network error during query:', err)
156
+ RetryableError.handleNetworkError(err)
157
+ }
158
+
159
+ if (!results) {
160
+ logger.error('No response received from query request')
161
+ throw new RetryableError('no response', 503)
162
+ }
163
+
164
+ const body = results.body
165
+
166
+ if (RetryableError.isRetryableStatusCode(results.statusCode)) {
167
+ logger.warn(`Retryable status code received: ${results.statusCode}`)
168
+ throw new RetryableError(body.error || 'retryable error during query', results.statusCode)
169
+ }
170
+
171
+ if (body.error) {
172
+ logger.error(`Query error: ${JSON.stringify(body)}`)
173
+ throw new Error(`CouchDB query error: ${body.error} - ${body.reason || ''}`)
174
+ }
175
+
176
+ // If validation schemas are provided, validate each row accordingly
177
+ if (options.validate && body.rows) {
178
+ body.rows = await parseRows<DocSchema, KeySchema, ValueSchema>(body.rows, options.validate)
179
+ }
180
+
181
+ logger.info(`Successfully executed view query: ${view}`)
182
+ logger.debug('Query response:', body)
183
+
184
+ return body
185
+ }
186
+
187
+ export type QueryBound = {
188
+ <
189
+ DocSchema extends StandardSchemaV1 = typeof CouchDoc,
190
+ KeySchema extends StandardSchemaV1 = ZodAny,
191
+ ValueSchema extends StandardSchemaV1 = ZodAny
192
+ >(
193
+ view: ViewString,
194
+ options: ViewOptions & {
195
+ include_docs: true
196
+ validate?: {
197
+ onInvalidDoc?: OnInvalidDocAction
198
+ docSchema?: DocSchema
199
+ keySchema?: KeySchema
200
+ valueSchema?: ValueSchema
201
+ }
202
+ }
203
+ ): Promise<ViewQueryResponseValidated<DocSchema, KeySchema, ValueSchema>>
204
+ <
205
+ DocSchema extends StandardSchemaV1 = ZodNever,
206
+ KeySchema extends StandardSchemaV1 = ZodAny,
207
+ ValueSchema extends StandardSchemaV1 = ZodAny
208
+ >(
209
+ view: ViewString,
210
+ options: ViewOptions & {
211
+ include_docs?: false | undefined
212
+ validate?: {
213
+ onInvalidDoc?: OnInvalidDocAction
214
+ docSchema?: DocSchema
215
+ keySchema?: KeySchema
216
+ valueSchema?: ValueSchema
217
+ }
218
+ }
219
+ ): Promise<ViewQueryResponseValidated<ZodNever, KeySchema, ValueSchema>>
220
+ (
221
+ view: ViewString,
222
+ options?: ViewOptions
223
+ ): Promise<ViewQueryResponseValidated<ZodNever, ZodAny, ZodAny>>
224
+ }