hide-a-bed 4.0.3 → 4.1.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 (65) hide show
  1. package/README.md +304 -73
  2. package/cjs/impl/bulk.cjs +158 -10
  3. package/cjs/impl/crud.cjs +19 -12
  4. package/cjs/impl/errors.cjs +12 -0
  5. package/cjs/impl/patch.cjs +19 -0
  6. package/cjs/impl/queryBuilder.cjs +99 -0
  7. package/cjs/impl/stream.cjs +12 -1
  8. package/cjs/impl/trackedEmitter.cjs +54 -0
  9. package/cjs/impl/transactionErrors.cjs +70 -0
  10. package/cjs/index.cjs +21 -5
  11. package/cjs/schema/bind.cjs +4 -0
  12. package/cjs/schema/bulk.cjs +35 -11
  13. package/cjs/schema/config.cjs +1 -0
  14. package/cjs/schema/crud.cjs +23 -1
  15. package/cjs/schema/patch.cjs +17 -2
  16. package/cjs/schema/query.cjs +2 -1
  17. package/config.json +5 -0
  18. package/impl/bulk.d.mts +4 -0
  19. package/impl/bulk.d.mts.map +1 -1
  20. package/impl/bulk.mjs +200 -13
  21. package/impl/crud.d.mts +2 -0
  22. package/impl/crud.d.mts.map +1 -1
  23. package/impl/crud.mjs +25 -15
  24. package/impl/errors.d.mts +8 -0
  25. package/impl/errors.d.mts.map +1 -1
  26. package/impl/errors.mjs +12 -0
  27. package/impl/patch.d.mts +2 -0
  28. package/impl/patch.d.mts.map +1 -1
  29. package/impl/patch.mjs +22 -1
  30. package/impl/query.d.mts +18 -9
  31. package/impl/query.d.mts.map +1 -1
  32. package/impl/queryBuilder.d.mts +94 -0
  33. package/impl/queryBuilder.d.mts.map +1 -0
  34. package/impl/queryBuilder.mjs +99 -0
  35. package/impl/stream.d.mts.map +1 -1
  36. package/impl/stream.mjs +12 -1
  37. package/impl/trackedEmitter.d.mts +8 -0
  38. package/impl/trackedEmitter.d.mts.map +1 -0
  39. package/impl/trackedEmitter.mjs +33 -0
  40. package/impl/transactionErrors.d.mts +57 -0
  41. package/impl/transactionErrors.d.mts.map +1 -0
  42. package/impl/transactionErrors.mjs +47 -0
  43. package/index.d.mts +18 -3
  44. package/index.d.mts.map +1 -1
  45. package/index.mjs +42 -11
  46. package/package.json +9 -4
  47. package/schema/bind.d.mts +382 -45
  48. package/schema/bind.d.mts.map +1 -1
  49. package/schema/bind.mjs +6 -2
  50. package/schema/bulk.d.mts +559 -16
  51. package/schema/bulk.d.mts.map +1 -1
  52. package/schema/bulk.mjs +40 -10
  53. package/schema/config.d.mts.map +1 -1
  54. package/schema/config.mjs +1 -0
  55. package/schema/crud.d.mts +240 -15
  56. package/schema/crud.d.mts.map +1 -1
  57. package/schema/crud.mjs +27 -1
  58. package/schema/patch.d.mts +138 -2
  59. package/schema/patch.d.mts.map +1 -1
  60. package/schema/patch.mjs +22 -2
  61. package/schema/query.d.mts +62 -30
  62. package/schema/query.d.mts.map +1 -1
  63. package/schema/query.mjs +4 -1
  64. package/schema/stream.d.mts +18 -9
  65. package/schema/stream.d.mts.map +1 -1
package/impl/bulk.d.mts CHANGED
@@ -4,4 +4,8 @@ export const bulkSave: import("../schema/bulk.mjs").BulkSaveSchema;
4
4
  export const bulkGet: import("../schema/bulk.mjs").BulkGetSchema;
5
5
  /** @type { import('../schema/bulk.mjs').BulkRemoveSchema } */
6
6
  export const bulkRemove: import("../schema/bulk.mjs").BulkRemoveSchema;
7
+ /** @type { import('../schema/bulk.mjs').BulkGetDictionarySchema } */
8
+ export const bulkGetDictionary: import("../schema/bulk.mjs").BulkGetDictionarySchema;
9
+ /** @type { import('../schema/bulk.mjs').BulkSaveTransactionSchema } bulkSaveTransaction */
10
+ export const bulkSaveTransaction: import("../schema/bulk.mjs").BulkSaveTransactionSchema;
7
11
  //# sourceMappingURL=bulk.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bulk.d.mts","sourceRoot":"","sources":["bulk.mjs"],"names":[],"mappings":"AAaA,4DAA4D;AAC5D,uBADY,OAAO,oBAAoB,EAAE,cAAc,CAsCrD;AAEF,2DAA2D;AAC3D,sBADY,OAAO,oBAAoB,EAAE,aAAa,CAkCpD;AAEF,8DAA8D;AAC9D,yBADY,OAAO,oBAAoB,EAAE,gBAAgB,CAOvD"}
1
+ {"version":3,"file":"bulk.d.mts","sourceRoot":"","sources":["bulk.mjs"],"names":[],"mappings":"AAkBA,4DAA4D;AAC5D,uBADY,OAAO,oBAAoB,EAAE,cAAc,CAsCrD;AAEF,2DAA2D;AAC3D,sBADY,OAAO,oBAAoB,EAAE,aAAa,CA8BpD;AAIF,8DAA8D;AAC9D,yBADY,OAAO,oBAAoB,EAAE,gBAAgB,CAkBvD;AAEF,qEAAqE;AACrE,gCADY,OAAO,oBAAoB,EAAE,uBAAuB,CAwB9D;AAEF,2FAA2F;AAC3F,kCADY,OAAO,oBAAoB,EAAE,yBAAyB,CAiJhE"}
package/impl/bulk.mjs CHANGED
@@ -1,8 +1,13 @@
1
1
  // @ts-check
2
2
  import needle from 'needle'
3
- import { BulkSave, BulkGet, BulkRemove } from '../schema/bulk.mjs'
3
+ import { BulkSave, BulkGet, BulkRemove, BulkGetDictionary, BulkSaveTransaction } from '../schema/bulk.mjs'
4
+ import { withRetry } from './retry.mjs'
5
+ import { put } from './crud.mjs'
4
6
  import { RetryableError } from './errors.mjs'
7
+ import { TransactionSetupError, TransactionVersionConflictError, TransactionBulkOperationError, TransactionRollbackError } from './transactionErrors.mjs'
5
8
  import { createLogger } from './logger.mjs'
9
+ import { CouchDoc } from '../schema/crud.mjs'
10
+ import { setupEmitter } from './trackedEmitter.mjs'
6
11
 
7
12
  const opts = {
8
13
  json: true,
@@ -58,10 +63,10 @@ export const bulkGet = BulkGet.implement(async (config, ids) => {
58
63
 
59
64
  logger.info(`Starting bulk get for ${keys.length} documents`)
60
65
  const url = `${config.couch}/_all_docs?include_docs=true`
61
- const body = { keys }
66
+ const payload = { keys }
62
67
  let resp
63
68
  try {
64
- resp = await needle('post', url, body, opts)
69
+ resp = await needle('post', url, payload, opts)
65
70
  } catch (err) {
66
71
  logger.error('Network error during bulk get:', err)
67
72
  RetryableError.handleNetworkError(err)
@@ -78,20 +83,202 @@ export const bulkGet = BulkGet.implement(async (config, ids) => {
78
83
  logger.error(`Unexpected status code: ${resp.statusCode}`)
79
84
  throw new Error('could not fetch')
80
85
  }
81
- const rows = resp?.body?.rows || []
82
- /** @type {Array<import('../schema/crud.mjs').CouchDocSchema>} */
83
- const docs = rows.map((
84
- /** @type {{ error?: any, key?: string, doc?: import('../schema/crud.mjs').CouchDocSchema }} */ r
85
- ) => r.doc)
86
- logger.info(`Successfully retrieved ${docs.length} documents`)
87
- return docs
86
+ /** @type { import('../schema/query.mjs').SimpleViewQueryResponseSchema } body */
87
+ const body = resp.body
88
+ return body
88
89
  })
89
90
 
91
+ // sugar methods
92
+
90
93
  /** @type { import('../schema/bulk.mjs').BulkRemoveSchema } */
91
94
  export const bulkRemove = BulkRemove.implement(async (config, ids) => {
92
95
  const logger = createLogger(config)
93
96
  logger.info(`Starting bulk remove for ${ids.length} documents`)
94
- const docs = await bulkGet(config, ids)
95
- docs.forEach(d => { d._deleted = true })
96
- return bulkSave(config, docs)
97
+ const resp = await bulkGet(config, ids)
98
+ /** @type { Array<import('../schema/crud.mjs').CouchDocSchema> } toRemove */
99
+ const toRemove = []
100
+ resp.rows.forEach(row => {
101
+ if (!row.doc) return
102
+ try {
103
+ const d = CouchDoc.parse(row.doc)
104
+ d._deleted = true
105
+ toRemove.push(d)
106
+ } catch (e) {
107
+ logger.warn(`Invalid document structure in bulk remove: ${row.id}`, e)
108
+ }
109
+ })
110
+ return bulkSave(config, toRemove)
111
+ })
112
+
113
+ /** @type { import('../schema/bulk.mjs').BulkGetDictionarySchema } */
114
+ export const bulkGetDictionary = BulkGetDictionary.implement(async (config, ids) => {
115
+ const resp = await bulkGet(config, ids)
116
+
117
+ /** @type { import('../schema/bulk.mjs').BulkGetDictionaryResponseSchema } results */
118
+ const results = { found: {}, notFound: {} }
119
+
120
+ resp.rows.forEach(
121
+ /** @param { import('../schema/query.mjs').ViewRowSchema } row */
122
+ row => {
123
+ if (!row.key) return
124
+ if (row.error) {
125
+ results.notFound[row.key] = row
126
+ return
127
+ }
128
+ try {
129
+ /** @type { import('../schema/crud.mjs').CouchDocSchema } doc */
130
+ const doc = CouchDoc.parse(row.doc)
131
+ results.found[doc._id] = doc
132
+ } catch (e) {
133
+ results.notFound[row.key] = row
134
+ }
135
+ })
136
+ return results
137
+ })
138
+
139
+ /** @type { import('../schema/bulk.mjs').BulkSaveTransactionSchema } bulkSaveTransaction */
140
+ export const bulkSaveTransaction = BulkSaveTransaction.implement(async (config, transactionId, docs) => {
141
+ const emitter = setupEmitter(config)
142
+ const logger = createLogger(config)
143
+ const retryOptions = {
144
+ maxRetries: config.maxRetries ?? 10,
145
+ initialDelay: config.initialDelay ?? 1000,
146
+ backoffFactor: config.backoffFactor ?? 2
147
+ }
148
+ const _put = config.bindWithRetry ? withRetry(put.bind(null, config), retryOptions) : put.bind(null, config)
149
+ logger.info(`Starting bulk save transaction ${transactionId} for ${docs.length} documents`)
150
+
151
+ // Create transaction document
152
+ const txnDoc = {
153
+ _id: `txn:${transactionId}`,
154
+ _rev: null,
155
+ type: 'transaction',
156
+ status: 'pending',
157
+ changes: docs,
158
+ timestamp: new Date().toISOString()
159
+ }
160
+
161
+ // Save transaction document
162
+ let txnresp = await _put(txnDoc)
163
+ logger.debug('Transaction document created:', txnDoc, txnresp)
164
+ await emitter.emit('transaction-created', { txnresp, txnDoc })
165
+ if (txnresp.error) {
166
+ throw new TransactionSetupError('Failed to create transaction document', {
167
+ error: txnresp.error,
168
+ response: txnresp.body
169
+ })
170
+ }
171
+
172
+ // Get current revisions of all documents
173
+ const existingDocs = await bulkGetDictionary(config, docs.map(d => d._id))
174
+ logger.debug('Fetched current revisions of documents:', existingDocs)
175
+ await emitter.emit('transaction-revs-fetched', existingDocs)
176
+
177
+ /** @type {string[]} */
178
+ const revErrors = []
179
+ // if any of the existingDocs, and the docs provided dont match on rev, then throw an error
180
+ docs.forEach(d => {
181
+ if (existingDocs.found[d._id] && existingDocs.found[d._id]._rev !== d._rev) revErrors.push(d._id)
182
+ if (existingDocs.notFound[d._id] && d._rev) revErrors.push(d._id)
183
+ })
184
+
185
+ if (revErrors.length > 0) {
186
+ throw new TransactionVersionConflictError(revErrors)
187
+ }
188
+ logger.debug('Checked document revisions:', existingDocs)
189
+ await emitter.emit('transaction-revs-checked', existingDocs)
190
+
191
+ /** @type {Record<string, import('../schema/crud.mjs').CouchDocSchema>} providedDocsById */
192
+ const providedDocsById = {}
193
+ docs.forEach((
194
+ /** @type {import('../schema/crud.mjs').CouchDocSchema} */ d
195
+ ) => {
196
+ if (!d._id) return
197
+ providedDocsById[d._id] = d
198
+ })
199
+
200
+ /** @type {import('../schema/bulk.mjs').Response} */
201
+ const newDocsToRollback = []
202
+ /** @type {import('../schema/bulk.mjs').Response} */
203
+ const potentialExistingDocsToRollack = []
204
+ /** @type {import('../schema/bulk.mjs').Response} */
205
+ const failedDocs = []
206
+
207
+ try {
208
+ logger.info('Transaction started:', txnDoc)
209
+ await emitter.emit('transaction-started', txnDoc)
210
+ // Apply updates
211
+ const results = await bulkSave(config, docs)
212
+ logger.info('Transaction updates applied:', results)
213
+ await emitter.emit('transaction-updates-applied', results)
214
+
215
+ // Check for failures
216
+ results.forEach(r => {
217
+ if (!r.id) return // not enough info
218
+ if (!r.error) {
219
+ if (existingDocs.notFound[r.id]) newDocsToRollback.push(r)
220
+ if (existingDocs.found[r.id]) potentialExistingDocsToRollack.push(r)
221
+ } else {
222
+ failedDocs.push(r)
223
+ }
224
+ })
225
+ if (failedDocs.length > 0) {
226
+ throw new TransactionBulkOperationError(failedDocs)
227
+ }
228
+
229
+ // Update transaction status to completed
230
+ txnDoc.status = 'completed'
231
+ txnDoc._rev = txnresp.rev
232
+ txnresp = await _put(txnDoc)
233
+ logger.info('Transaction completed:', txnDoc)
234
+ await emitter.emit('transaction-completed', { txnresp, txnDoc })
235
+ if (txnresp.statusCode !== 201) {
236
+ logger.error('Failed to update transaction status to completed')
237
+ }
238
+
239
+ return results
240
+ } catch (error) {
241
+ logger.error('Transaction failed, attempting rollback:', error)
242
+
243
+ // Rollback changes
244
+ /** @type {Array<import('../schema/crud.mjs').CouchDocSchema>} */
245
+ const toRollback = []
246
+ potentialExistingDocsToRollack.forEach(row => {
247
+ if (!row.id || !row.rev) return
248
+ const doc = existingDocs.found[row.id]
249
+ doc._rev = row.rev
250
+ toRollback.push(doc)
251
+ })
252
+ newDocsToRollback.forEach(d => {
253
+ if (!d.id || !d.rev) return
254
+ const before = structuredClone(providedDocsById[d.id])
255
+ before._rev = d.rev
256
+ before._deleted = true
257
+ toRollback.push(before)
258
+ })
259
+
260
+ // rollback all the changes
261
+ const bulkRollbackResult = await bulkSave(config, toRollback)
262
+ let status = 'rolled_back'
263
+ bulkRollbackResult.forEach(r => {
264
+ if (r.error) status = 'rollback_failed'
265
+ })
266
+ logger.warn('Transaction rolled back:', { bulkRollbackResult, status })
267
+ await emitter.emit('transaction-rolled-back', { bulkRollbackResult, status })
268
+
269
+ // Update transaction status to rolled back
270
+ txnDoc.status = status
271
+ txnDoc._rev = txnresp.rev
272
+ txnresp = await _put(txnDoc)
273
+ logger.warn('Transaction rollback status updated:', txnDoc)
274
+ await emitter.emit('transaction-rolled-back-status', { txnresp, txnDoc })
275
+ if (txnresp.statusCode !== 201) {
276
+ logger.error('Failed to update transaction status to rolled_back')
277
+ }
278
+ throw new TransactionRollbackError(
279
+ 'Transaction failed and rollback was unsuccessful',
280
+ /** @type {Error} */ (error),
281
+ bulkRollbackResult
282
+ )
283
+ }
97
284
  })
package/impl/crud.d.mts CHANGED
@@ -1,5 +1,7 @@
1
1
  /** @type { import('../schema/crud.mjs').CouchGetSchema } */
2
2
  export const get: import("../schema/crud.mjs").CouchGetSchema;
3
+ /** @type { import('../schema/crud.mjs').CouchGetAtRevSchema } */
4
+ export const getAtRev: import("../schema/crud.mjs").CouchGetAtRevSchema;
3
5
  /** @type { import('../schema/crud.mjs').CouchPutSchema } */
4
6
  export const put: import("../schema/crud.mjs").CouchPutSchema;
5
7
  //# sourceMappingURL=crud.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"crud.d.mts","sourceRoot":"","sources":["crud.mjs"],"names":[],"mappings":"AAaA,4DAA4D;AAC5D,kBADY,OAAO,oBAAoB,EAAE,cAAc,CAwCrD;AAEF,4DAA4D;AAC5D,kBADY,OAAO,oBAAoB,EAAE,cAAc,CAqCrD"}
1
+ {"version":3,"file":"crud.d.mts","sourceRoot":"","sources":["crud.mjs"],"names":[],"mappings":"AAqDA,4DAA4D;AAC5D,kBADY,OAAO,oBAAoB,EAAE,cAAc,CAIrD;AAEF,iEAAiE;AACjE,uBADY,OAAO,oBAAoB,EAAE,mBAAmB,CAI1D;AAEF,4DAA4D;AAC5D,kBADY,OAAO,oBAAoB,EAAE,cAAc,CAqCrD"}
package/impl/crud.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
  import needle from 'needle'
3
- import { CouchGet, CouchPut } from '../schema/crud.mjs'
4
- import { RetryableError } from './errors.mjs'
3
+ import { CouchGet, CouchPut, CouchGetWithOptions, CouchGetAtRev } from '../schema/crud.mjs'
4
+ import { RetryableError, NotFoundError } from './errors.mjs'
5
5
  import { createLogger } from './logger.mjs'
6
6
 
7
7
  const opts = {
@@ -11,11 +11,13 @@ const opts = {
11
11
  }
12
12
  }
13
13
 
14
- /** @type { import('../schema/crud.mjs').CouchGetSchema } */
15
- export const get = CouchGet.implement(async (config, id) => {
14
+ /** @type { import('../schema/crud.mjs').CouchGetWithOptionsSchema } */
15
+ const _getWithOptions = CouchGetWithOptions.implement(async (config, id, getOpts) => {
16
16
  const logger = createLogger(config)
17
- const url = `${config.couch}/${id}`
18
- logger.info(`Getting document with id: ${id}`)
17
+ const rev = getOpts?.rev
18
+ const path = rev ? `${id}?rev=${rev}` : id
19
+ const url = `${config.couch}/${path}`
20
+ logger.info(`Getting document with id: ${id}, rev ${rev || 'latest'}`)
19
21
 
20
22
  try {
21
23
  const resp = await needle('get', url, opts)
@@ -23,18 +25,14 @@ export const get = CouchGet.implement(async (config, id) => {
23
25
  logger.error('No response received from get request')
24
26
  throw new RetryableError('no response', 503)
25
27
  }
26
- if (resp.statusCode === 404) {
27
- logger.debug(`Document not found: ${id}`)
28
- return null
29
- }
30
28
  const result = resp?.body || {}
31
29
  if (resp.statusCode === 404) {
32
30
  if (config.throwOnGetNotFound) {
33
- logger.warn(`Document not found (throwing error): ${id}`)
34
- throw new Error(result.reason || 'not_found')
31
+ logger.warn(`Document not found (throwing error): ${id}, rev ${rev || 'latest'}`)
32
+ throw new NotFoundError(id, result.reason || 'not_found')
35
33
  } else {
36
- logger.debug(`Document not found (returning undefined): ${id}`)
37
- return undefined
34
+ logger.debug(`Document not found (returning undefined): ${id}, rev ${rev || 'latest'}`)
35
+ return null
38
36
  }
39
37
  }
40
38
  if (RetryableError.isRetryableStatusCode(resp.statusCode)) {
@@ -45,7 +43,7 @@ export const get = CouchGet.implement(async (config, id) => {
45
43
  logger.error(`Unexpected status code: ${resp.statusCode}`)
46
44
  throw new Error(result.reason || 'failed')
47
45
  }
48
- logger.info(`Successfully retrieved document: ${id}`)
46
+ logger.info(`Successfully retrieved document: ${id}, rev ${rev || 'latest'}`)
49
47
  return result
50
48
  } catch (err) {
51
49
  logger.error('Error during get operation:', err)
@@ -53,6 +51,18 @@ export const get = CouchGet.implement(async (config, id) => {
53
51
  }
54
52
  })
55
53
 
54
+ /** @type { import('../schema/crud.mjs').CouchGetSchema } */
55
+ export const get = CouchGet.implement(async (config, id) => {
56
+ const getOptions = {}
57
+ return _getWithOptions(config, id, getOptions)
58
+ })
59
+
60
+ /** @type { import('../schema/crud.mjs').CouchGetAtRevSchema } */
61
+ export const getAtRev = CouchGetAtRev.implement(async (config, id, rev) => {
62
+ const getOptions = { rev }
63
+ return _getWithOptions(config, id, getOptions)
64
+ })
65
+
56
66
  /** @type { import('../schema/crud.mjs').CouchPutSchema } */
57
67
  export const put = CouchPut.implement(async (config, doc) => {
58
68
  const logger = createLogger(config)
package/impl/errors.d.mts CHANGED
@@ -3,6 +3,14 @@
3
3
  * @property {string} code - The error code
4
4
  * @property {string} [message] - Optional error message
5
5
  */
6
+ export class NotFoundError extends Error {
7
+ /**
8
+ * @param {string} docId - The ID of the document that wasn't found
9
+ * @param {string} [message] - Optional error message
10
+ */
11
+ constructor(docId: string, message?: string | undefined);
12
+ docId: string;
13
+ }
6
14
  export class RetryableError extends Error {
7
15
  /**
8
16
  * @param {number|undefined} statusCode - The HTTP status code to check
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.mts","sourceRoot":"","sources":["errors.mjs"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH;IAWE;;;OAGG;IACH,yCAHW,MAAM,GAAC,SAAS,GACd,OAAO,CAKnB;IAED;;;;OAIG;IACH,+BAJW,YAAY,GAAG,OAAO,QAsBhC;IA1CD;;;OAGG;IACH,qBAHW,MAAM,cACN,MAAM,GAAC,SAAS,EAM1B;IADC,+BAA4B;CAoC/B;;;;;UAhDa,MAAM"}
1
+ {"version":3,"file":"errors.d.mts","sourceRoot":"","sources":["errors.mjs"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH;IACE;;;OAGG;IACH,mBAHW,MAAM,gCAOhB;IADC,cAAkB;CAErB;AAED;IAWE;;;OAGG;IACH,yCAHW,MAAM,GAAC,SAAS,GACd,OAAO,CAKnB;IAED;;;;OAIG;IACH,+BAJW,YAAY,GAAG,OAAO,QAsBhC;IA1CD;;;OAGG;IACH,qBAHW,MAAM,cACN,MAAM,GAAC,SAAS,EAM1B;IADC,+BAA4B;CAoC/B;;;;;UA5Da,MAAM"}
package/impl/errors.mjs CHANGED
@@ -6,6 +6,18 @@
6
6
  * @property {string} [message] - Optional error message
7
7
  */
8
8
 
9
+ export class NotFoundError extends Error {
10
+ /**
11
+ * @param {string} docId - The ID of the document that wasn't found
12
+ * @param {string} [message] - Optional error message
13
+ */
14
+ constructor(docId, message = 'Document not found') {
15
+ super(message)
16
+ this.name = 'NotFoundError'
17
+ this.docId = docId
18
+ }
19
+ }
20
+
9
21
  export class RetryableError extends Error {
10
22
  /**
11
23
  * @param {string} message - The error message
package/impl/patch.d.mts CHANGED
@@ -1,4 +1,6 @@
1
1
  export function sleep(ms: any): Promise<any>;
2
2
  /** @type { import('../schema/patch.mjs').PatchSchema } */
3
3
  export const patch: import("../schema/patch.mjs").PatchSchema;
4
+ /** @type { import('../schema/patch.mjs').PatchDangerouslySchema } */
5
+ export const patchDangerously: import("../schema/patch.mjs").PatchDangerouslySchema;
4
6
  //# sourceMappingURL=patch.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"patch.d.mts","sourceRoot":"","sources":["patch.mjs"],"names":[],"mappings":"AAIO,6CAAmE;AAE1E,0DAA0D;AAC1D,oBADY,OAAO,qBAAqB,EAAE,WAAW,CA4DnD"}
1
+ {"version":3,"file":"patch.d.mts","sourceRoot":"","sources":["patch.mjs"],"names":[],"mappings":"AAIO,6CAAmE;AAE1E,0DAA0D;AAC1D,oBADY,OAAO,qBAAqB,EAAE,WAAW,CAmBnD;AAEF,qEAAqE;AACrE,+BADY,OAAO,qBAAqB,EAAE,sBAAsB,CA4D9D"}
package/impl/patch.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { get, put } from './crud.mjs'
2
- import { Patch } from '../schema/patch.mjs'
2
+ import { Patch, PatchDangerously } from '../schema/patch.mjs'
3
3
  import { createLogger } from './logger.mjs'
4
4
 
5
5
  export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
@@ -7,6 +7,27 @@ export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
7
7
  /** @type { import('../schema/patch.mjs').PatchSchema } */
8
8
  export const patch = Patch.implement(async (config, id, properties) => {
9
9
  const logger = createLogger(config)
10
+
11
+ logger.info(`Starting patch operation for document ${id}`)
12
+ logger.debug('Patch properties:', properties)
13
+ const doc = await get(config, id)
14
+ if (doc._rev !== properties._rev) {
15
+ const result = {}
16
+ result.ok = false
17
+ result.error = 'conflict'
18
+ result.statusCode = 409
19
+ return result
20
+ }
21
+ const updatedDoc = { ...doc, ...properties }
22
+ logger.debug('Merged document:', updatedDoc)
23
+ const result = await put(config, updatedDoc)
24
+ logger.info(`Successfully patched document ${id}, rev: ${result.rev}`)
25
+ return result
26
+ })
27
+
28
+ /** @type { import('../schema/patch.mjs').PatchDangerouslySchema } */
29
+ export const patchDangerously = PatchDangerously.implement(async (config, id, properties) => {
30
+ const logger = createLogger(config)
10
31
  const maxRetries = config.maxRetries || 5
11
32
  let delay = config.initialDelay || 1000
12
33
  let attempts = 0
package/impl/query.d.mts CHANGED
@@ -120,17 +120,20 @@ export const query: z.infer<z.ZodFunction<z.ZodTuple<[z.ZodObject<{
120
120
  id: z.ZodOptional<z.ZodString>;
121
121
  key: z.ZodNullable<z.ZodAny>;
122
122
  value: z.ZodNullable<z.ZodAny>;
123
- doc: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
123
+ doc: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>>>;
124
+ error: z.ZodOptional<z.ZodString>;
124
125
  }, "strip", z.ZodTypeAny, {
125
126
  id?: string | undefined;
126
127
  key?: any;
127
128
  value?: any;
128
- doc?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
129
+ doc?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | null | undefined;
130
+ error?: string | undefined;
129
131
  }, {
130
132
  id?: string | undefined;
131
133
  key?: any;
132
134
  value?: any;
133
- doc?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
135
+ doc?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | null | undefined;
136
+ error?: string | undefined;
134
137
  }>, "many">;
135
138
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
136
139
  error: z.ZodOptional<z.ZodString>;
@@ -138,17 +141,20 @@ export const query: z.infer<z.ZodFunction<z.ZodTuple<[z.ZodObject<{
138
141
  id: z.ZodOptional<z.ZodString>;
139
142
  key: z.ZodNullable<z.ZodAny>;
140
143
  value: z.ZodNullable<z.ZodAny>;
141
- doc: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
144
+ doc: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>>>;
145
+ error: z.ZodOptional<z.ZodString>;
142
146
  }, "strip", z.ZodTypeAny, {
143
147
  id?: string | undefined;
144
148
  key?: any;
145
149
  value?: any;
146
- doc?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
150
+ doc?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | null | undefined;
151
+ error?: string | undefined;
147
152
  }, {
148
153
  id?: string | undefined;
149
154
  key?: any;
150
155
  value?: any;
151
- doc?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
156
+ doc?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | null | undefined;
157
+ error?: string | undefined;
152
158
  }>, "many">;
153
159
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
154
160
  error: z.ZodOptional<z.ZodString>;
@@ -156,17 +162,20 @@ export const query: z.infer<z.ZodFunction<z.ZodTuple<[z.ZodObject<{
156
162
  id: z.ZodOptional<z.ZodString>;
157
163
  key: z.ZodNullable<z.ZodAny>;
158
164
  value: z.ZodNullable<z.ZodAny>;
159
- doc: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
165
+ doc: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>>>;
166
+ error: z.ZodOptional<z.ZodString>;
160
167
  }, "strip", z.ZodTypeAny, {
161
168
  id?: string | undefined;
162
169
  key?: any;
163
170
  value?: any;
164
- doc?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
171
+ doc?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | null | undefined;
172
+ error?: string | undefined;
165
173
  }, {
166
174
  id?: string | undefined;
167
175
  key?: any;
168
176
  value?: any;
169
- doc?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
177
+ doc?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | null | undefined;
178
+ error?: string | undefined;
170
179
  }>, "many">;
171
180
  }, z.ZodTypeAny, "passthrough">>>>>;
172
181
  import { z } from 'zod';
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.mts","sourceRoot":"","sources":["query.mjs"],"names":[],"mappings":"AA6DA;;;GAGG;AACH,qCAHW;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,UACtB,MAAM,EAAE,UAoBlB;AAxED,+CAA+C;AAC/C,oBADY,CAAC,CAAC,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAAiB,CAgDlC;kBAzDgB,KAAK"}
1
+ {"version":3,"file":"query.d.mts","sourceRoot":"","sources":["query.mjs"],"names":[],"mappings":"AA6DA;;;GAGG;AACH,qCAHW;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,UACtB,MAAM,EAAE,UAoBlB;AAxED,+CAA+C;AAC/C,oBADY,CAAC,CAAC,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAAiB,CAgDlC;kBAzDgB,KAAK"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @typedef {Object} QueryOptions
3
+ * @property {any} [key] - Exact key to match
4
+ * @property {any} [startkey] - Start of key range
5
+ * @property {any} [endkey] - End of key range
6
+ * @property {boolean} [reduce] - Whether to use reduce function
7
+ * @property {boolean} [group] - Whether to group results
8
+ * @property {number} [group_level] - Level at which to group
9
+ * @property {string} [stale] - Stale parameter value
10
+ * @property {number} [limit] - Max number of results
11
+ */
12
+ export class QueryBuilder {
13
+ /**
14
+ * @param {any} key
15
+ * @returns {QueryBuilder}
16
+ */
17
+ key(key: any): QueryBuilder;
18
+ /**
19
+ * @param {any} startkey
20
+ * @returns {QueryBuilder}
21
+ */
22
+ startKey(startkey: any): QueryBuilder;
23
+ /**
24
+ * @param {any} endkey
25
+ * @returns {QueryBuilder}
26
+ */
27
+ endKey(endkey: any): QueryBuilder;
28
+ /**
29
+ * @param {boolean} reduce
30
+ * @returns {QueryBuilder}
31
+ */
32
+ reduce(reduce?: boolean): QueryBuilder;
33
+ /**
34
+ * @param {boolean} group
35
+ * @returns {QueryBuilder}
36
+ */
37
+ group(group?: boolean): QueryBuilder;
38
+ /**
39
+ * @param {number} level
40
+ * @returns {QueryBuilder}
41
+ */
42
+ groupLevel(level: number): QueryBuilder;
43
+ /**
44
+ * @param {string} stale
45
+ * @returns {QueryBuilder}
46
+ */
47
+ stale(stale: string): QueryBuilder;
48
+ /**
49
+ * @param {number} limit
50
+ * @returns {QueryBuilder}
51
+ */
52
+ limit(limit: number): QueryBuilder;
53
+ /**
54
+ * @returns {QueryOptions}
55
+ */
56
+ build(): QueryOptions;
57
+ #private;
58
+ }
59
+ export function createQuery(): QueryBuilder;
60
+ export type QueryOptions = {
61
+ /**
62
+ * - Exact key to match
63
+ */
64
+ key?: any;
65
+ /**
66
+ * - Start of key range
67
+ */
68
+ startkey?: any;
69
+ /**
70
+ * - End of key range
71
+ */
72
+ endkey?: any;
73
+ /**
74
+ * - Whether to use reduce function
75
+ */
76
+ reduce?: boolean | undefined;
77
+ /**
78
+ * - Whether to group results
79
+ */
80
+ group?: boolean | undefined;
81
+ /**
82
+ * - Level at which to group
83
+ */
84
+ group_level?: number | undefined;
85
+ /**
86
+ * - Stale parameter value
87
+ */
88
+ stale?: string | undefined;
89
+ /**
90
+ * - Max number of results
91
+ */
92
+ limit?: number | undefined;
93
+ };
94
+ //# sourceMappingURL=queryBuilder.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryBuilder.d.mts","sourceRoot":"","sources":["queryBuilder.mjs"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH;IAIE;;;OAGG;IACH,SAHW,GAAG,GACD,YAAY,CAKxB;IAED;;;OAGG;IACH,mBAHW,GAAG,GACD,YAAY,CAKxB;IAED;;;OAGG;IACH,eAHW,GAAG,GACD,YAAY,CAKxB;IAED;;;OAGG;IACH,gBAHW,OAAO,GACL,YAAY,CAKxB;IAED;;;OAGG;IACH,cAHW,OAAO,GACL,YAAY,CAKxB;IAED;;;OAGG;IACH,kBAHW,MAAM,GACJ,YAAY,CAKxB;IAED;;;OAGG;IACH,aAHW,MAAM,GACJ,YAAY,CAKxB;IAED;;;OAGG;IACH,aAHW,MAAM,GACJ,YAAY,CAKxB;IAED;;OAEG;IACH,SAFa,YAAY,CAIxB;;CACF;AAEM,4CAA4C;;;;;UA9FrC,GAAG;;;;eACH,GAAG;;;;aACH,GAAG"}