hide-a-bed 5.0.0 → 5.0.3

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 (87) hide show
  1. package/README.md +96 -93
  2. package/cjs/impl/bulk.cjs +267 -0
  3. package/cjs/impl/changes.cjs +67 -0
  4. package/cjs/impl/crud.cjs +121 -0
  5. package/cjs/impl/errors.cjs +75 -0
  6. package/cjs/impl/logger.cjs +70 -0
  7. package/cjs/impl/patch.cjs +95 -0
  8. package/cjs/impl/query.cjs +116 -0
  9. package/cjs/impl/queryBuilder.cjs +99 -0
  10. package/cjs/impl/retry.cjs +54 -0
  11. package/cjs/impl/stream.cjs +121 -0
  12. package/cjs/impl/sugar/lock.cjs +81 -0
  13. package/cjs/impl/sugar/watch.cjs +159 -0
  14. package/cjs/impl/trackedEmitter.cjs +54 -0
  15. package/cjs/impl/transactionErrors.cjs +70 -0
  16. package/cjs/index.cjs +115 -0
  17. package/cjs/integration/changes.cjs +76 -0
  18. package/cjs/integration/disconnect-watch.cjs +52 -0
  19. package/cjs/integration/watch.cjs +59 -0
  20. package/cjs/schema/bind.cjs +51 -0
  21. package/cjs/schema/bulk.cjs +88 -0
  22. package/cjs/schema/changes.cjs +68 -0
  23. package/cjs/schema/config.cjs +48 -0
  24. package/cjs/schema/crud.cjs +77 -0
  25. package/cjs/schema/patch.cjs +53 -0
  26. package/cjs/schema/query.cjs +62 -0
  27. package/cjs/schema/stream.cjs +42 -0
  28. package/cjs/schema/sugar/lock.cjs +59 -0
  29. package/cjs/schema/sugar/watch.cjs +42 -0
  30. package/impl/bulk.d.mts +11 -0
  31. package/impl/bulk.d.mts.map +1 -0
  32. package/impl/changes.d.mts +12 -0
  33. package/impl/changes.d.mts.map +1 -0
  34. package/impl/changes.mjs +2 -7
  35. package/impl/crud.d.mts +7 -0
  36. package/impl/crud.d.mts.map +1 -0
  37. package/impl/errors.d.mts +43 -0
  38. package/impl/errors.d.mts.map +1 -0
  39. package/impl/errors.mjs +1 -1
  40. package/impl/logger.d.mts +32 -0
  41. package/impl/logger.d.mts.map +1 -0
  42. package/impl/patch.d.mts +6 -0
  43. package/impl/patch.d.mts.map +1 -0
  44. package/impl/query.d.mts +195 -0
  45. package/impl/query.d.mts.map +1 -0
  46. package/impl/query.mjs +14 -6
  47. package/impl/queryBuilder.d.mts +94 -0
  48. package/impl/queryBuilder.d.mts.map +1 -0
  49. package/impl/retry.d.mts +2 -0
  50. package/impl/retry.d.mts.map +1 -0
  51. package/impl/stream.d.mts +3 -0
  52. package/impl/stream.d.mts.map +1 -0
  53. package/impl/stream.mjs +2 -2
  54. package/impl/sugar/lock.d.mts +5 -0
  55. package/impl/sugar/lock.d.mts.map +1 -0
  56. package/impl/sugar/lock.mjs +3 -3
  57. package/impl/sugar/watch.d.mts +34 -0
  58. package/impl/sugar/watch.d.mts.map +1 -0
  59. package/impl/sugar/watch.mjs +66 -66
  60. package/impl/trackedEmitter.d.mts +8 -0
  61. package/impl/trackedEmitter.d.mts.map +1 -0
  62. package/impl/transactionErrors.d.mts +57 -0
  63. package/impl/transactionErrors.d.mts.map +1 -0
  64. package/index.d.mts +74 -0
  65. package/index.d.mts.map +1 -0
  66. package/package.json +1 -1
  67. package/schema/bind.d.mts +922 -0
  68. package/schema/bind.d.mts.map +1 -0
  69. package/schema/bulk.d.mts +910 -0
  70. package/schema/bulk.d.mts.map +1 -0
  71. package/schema/changes.d.mts +191 -0
  72. package/schema/changes.d.mts.map +1 -0
  73. package/schema/changes.mjs +1 -1
  74. package/schema/config.d.mts +79 -0
  75. package/schema/config.d.mts.map +1 -0
  76. package/schema/crud.d.mts +491 -0
  77. package/schema/crud.d.mts.map +1 -0
  78. package/schema/patch.d.mts +255 -0
  79. package/schema/patch.d.mts.map +1 -0
  80. package/schema/query.d.mts +406 -0
  81. package/schema/query.d.mts.map +1 -0
  82. package/schema/stream.d.mts +211 -0
  83. package/schema/stream.d.mts.map +1 -0
  84. package/schema/sugar/lock.d.mts +238 -0
  85. package/schema/sugar/lock.d.mts.map +1 -0
  86. package/schema/sugar/watch.d.mts +127 -0
  87. package/schema/sugar/watch.d.mts.map +1 -0
package/README.md CHANGED
@@ -1,5 +1,22 @@
1
- API
2
- -------------
1
+ ### API Quick Reference
2
+
3
+ 🍭 denotes a *Sugar* api - helps makes some tasks sweet and easy, but may hide some complexities you might want to deal with.
4
+
5
+ | Document Operations | Bulk Operations | View Operations | Changes Feed |
6
+ |-------------------|-----------------|-----------------|-----------------|
7
+ | [`get()`](#get) | [`bulkGet()`](#bulkget) | [`query()`](#query) | [`changes()`](#changes) |
8
+ | [`put()`](#put) | [`bulkSave()`](#bulksave) | [`queryStream()`](#querystream) | [`watchDocs()`](#watchDocs) 🍭 |
9
+ | [`patch()`](#patch) 🍭 | [`bulkRemove()`](#bulkremove) | | |
10
+ | [`patchDangerously()`](#patchdangerously) 🍭 | [`bulkGetDictionary()`](#bulkgetdictionary) 🍭 | | |
11
+ | [`getAtRev()`](#getatrev) 🍭 | [`bulkSaveTransaction()`](#bulksavetransaction) 🍭 | | |
12
+ | [`createLock()`](#createLock) 🍭 | | | |
13
+ | [`removeLock()`](#removeLock) 🍭 | | | |
14
+
15
+ And some utility apis
16
+
17
+ - [`createQuery()`](#createquery) 🍭
18
+ - [`withRetry()`](#withretry)
19
+
3
20
 
4
21
  ### Setup
5
22
 
@@ -26,24 +43,6 @@ const db = bindConfig(process.env)
26
43
  const doc = db.get('doc-123')
27
44
  ```
28
45
 
29
- ### API Quick Reference
30
-
31
- | Document Operations | Bulk Operations | View Operations |
32
- |-------------------|-----------------|-----------------|
33
- | [`get()`](#get) | [`bulkGet()`](#bulkget) | [`query()`](#query) |
34
- | [`put()`](#put) | [`bulkSave()`](#bulksave) | [`queryStream()`](#querystream) |
35
- | [`patch()`](#patch) | [`bulkRemove()`](#bulkremove) | [`createQuery()`](#createquery) |
36
- | [`patchDangerously()`](#patchdangerously) | [`bulkGetDictionary()`](#bulkgetdictionary) | |
37
- | [`getAtRev()`](#getatrev) | [`bulkSaveTransaction()`](#bulksavetransaction) | |
38
-
39
-
40
- Some *Sugar* API helpers
41
-
42
- - [`createLock()`](#createLock)
43
- - [`removeLock()`](#removeLock)
44
- - [`changes()`](#changes)
45
- - [`watchDocs()`](#watchDocs)
46
-
47
46
  ### Document Operations
48
47
 
49
48
  #### get
@@ -284,7 +283,7 @@ const results = await bulkRemove(config, ids)
284
283
 
285
284
  #### bulkGetDictionary
286
285
 
287
- Adds some convenience to bulkGet. Found and notFound documents are separated. Both properties are records of id to result. This makes it easy to deal with the results.
286
+ Adds some convenience to bulkGet. Organizes found and notFound documents into properties that are {id:result}. This makes it easy to deal with the results.
288
287
 
289
288
  **Parameters:**
290
289
  - `config`: Object with `couch` URL string
@@ -480,79 +479,9 @@ const init = async () => {
480
479
  }
481
480
  init()
482
481
  ```
483
- Advanced Config Options
484
- =======================
485
-
486
- The config object supports the following properties:
487
-
488
- | Property | Type | Default | Description |
489
- |----------|------|---------|-------------|
490
- | couch | string | required | The URL of the CouchDB database |
491
- | throwOnGetNotFound | boolean | false | If true, throws an error when get() returns 404. If false, returns undefined |
492
- | bindWithRetry | boolean | true | When using bindConfig(), adds retry logic to bound methods |
493
- | maxRetries | number | 3 | Maximum number of retry attempts for retryable operations |
494
- | initialDelay | number | 1000 | Initial delay in milliseconds before first retry |
495
- | backoffFactor | number | 2 | Multiplier for exponential backoff between retries |
496
- | useConsoleLogger | boolean | false | If true, enables console logging when no logger is provided |
497
- | logger | object/function | undefined | Custom logging interface (winston-style object or function) |
498
-
499
- Example configuration with all options:
500
- ```javascript
501
- const config = {
502
- couch: 'http://localhost:5984/mydb',
503
- throwOnGetNotFound: true,
504
- bindWithRetry: true,
505
- maxRetries: 5,
506
- initialDelay: 2000,
507
- backoffFactor: 1.5,
508
- useConsoleLogger: true,
509
- logger: (level, ...args) => console.log(level, ...args)
510
- }
511
- ```
512
482
 
513
-
514
- Logging Support
515
- ==============
516
-
517
- The library supports flexible logging options that can be configured through the config object:
518
-
519
- ```javascript
520
- // Enable console logging (error, warn, info, debug)
521
- const config = {
522
- couch: 'http://localhost:5984/mydb',
523
- useConsoleLogger: true
524
- }
525
-
526
- // Use a custom logger object (winston-style)
527
- const config = {
528
- couch: 'http://localhost:5984/mydb',
529
- logger: {
530
- error: (msg) => console.error(msg),
531
- warn: (msg) => console.warn(msg),
532
- info: (msg) => console.info(msg),
533
- debug: (msg) => console.debug(msg)
534
- }
535
- }
536
-
537
- // Use a simple function logger
538
- const config = {
539
- couch: 'http://localhost:5984/mydb',
540
- logger: (level, ...args) => console.log(level, ...args)
541
- }
542
- ```
543
-
544
- The logger will track operations including:
545
- - Document operations (get, put, patch)
546
- - Bulk operations
547
- - View queries
548
- - Streaming operations
549
- - Retries and error handling
550
-
551
- Each operation logs appropriate information at these levels:
552
- - error: Fatal/unrecoverable errors
553
- - warn: Retryable errors, conflicts
554
- - info: Operation start/completion
555
- - debug: Detailed operation information
483
+ Want to consume this in the browser? I'd recomment https://www.npmjs.com/package/ndjson-readablestream
484
+ here is a react component that consumes it https://github.com/Azure-Samples/azure-search-openai-demo/pull/532/files#diff-506debba46b93087dc46a916384e56392808bcc02a99d9291557f3e674d4ad6c
556
485
 
557
486
  #### changes()
558
487
 
@@ -659,3 +588,77 @@ Watch specific documents for changes in real-time.
659
588
  - Triggering actions when particular documents change
660
589
  - Maintaining cached copies of frequently accessed documents
661
590
 
591
+ Advanced Config Options
592
+ =======================
593
+
594
+ The config object supports the following properties:
595
+
596
+ | Property | Type | Default | Description |
597
+ |----------|------|---------|-------------|
598
+ | couch | string | required | The URL of the CouchDB database |
599
+ | throwOnGetNotFound | boolean | false | If true, throws an error when get() returns 404. If false, returns undefined |
600
+ | bindWithRetry | boolean | true | When using bindConfig(), adds retry logic to bound methods |
601
+ | maxRetries | number | 3 | Maximum number of retry attempts for retryable operations |
602
+ | initialDelay | number | 1000 | Initial delay in milliseconds before first retry |
603
+ | backoffFactor | number | 2 | Multiplier for exponential backoff between retries |
604
+ | useConsoleLogger | boolean | false | If true, enables console logging when no logger is provided |
605
+ | logger | object/function | undefined | Custom logging interface (winston-style object or function) |
606
+
607
+ Example configuration with all options:
608
+ ```javascript
609
+ const config = {
610
+ couch: 'http://localhost:5984/mydb',
611
+ throwOnGetNotFound: true,
612
+ bindWithRetry: true,
613
+ maxRetries: 5,
614
+ initialDelay: 2000,
615
+ backoffFactor: 1.5,
616
+ useConsoleLogger: true,
617
+ logger: (level, ...args) => console.log(level, ...args)
618
+ }
619
+ ```
620
+
621
+
622
+ Logging Support
623
+ ==============
624
+
625
+ The library supports flexible logging options that can be configured through the config object:
626
+
627
+ ```javascript
628
+ // Enable console logging (error, warn, info, debug)
629
+ const config = {
630
+ couch: 'http://localhost:5984/mydb',
631
+ useConsoleLogger: true
632
+ }
633
+
634
+ // Use a custom logger object (winston-style)
635
+ const config = {
636
+ couch: 'http://localhost:5984/mydb',
637
+ logger: {
638
+ error: (msg) => console.error(msg),
639
+ warn: (msg) => console.warn(msg),
640
+ info: (msg) => console.info(msg),
641
+ debug: (msg) => console.debug(msg)
642
+ }
643
+ }
644
+
645
+ // Use a simple function logger
646
+ const config = {
647
+ couch: 'http://localhost:5984/mydb',
648
+ logger: (level, ...args) => console.log(level, ...args)
649
+ }
650
+ ```
651
+
652
+ The logger will track operations including:
653
+ - Document operations (get, put, patch)
654
+ - Bulk operations
655
+ - View queries
656
+ - Streaming operations
657
+ - Retries and error handling
658
+
659
+ Each operation logs appropriate information at these levels:
660
+ - error: Fatal/unrecoverable errors
661
+ - warn: Retryable errors, conflicts
662
+ - info: Operation start/completion
663
+ - debug: Detailed operation information
664
+
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var bulk_exports = {};
30
+ __export(bulk_exports, {
31
+ bulkGet: () => bulkGet,
32
+ bulkGetDictionary: () => bulkGetDictionary,
33
+ bulkRemove: () => bulkRemove,
34
+ bulkSave: () => bulkSave,
35
+ bulkSaveTransaction: () => bulkSaveTransaction
36
+ });
37
+ module.exports = __toCommonJS(bulk_exports);
38
+ var import_needle = __toESM(require("needle"), 1);
39
+ var import_bulk = require("../schema/bulk.cjs");
40
+ var import_retry = require("./retry.cjs");
41
+ var import_crud = require("./crud.cjs");
42
+ var import_errors = require("./errors.cjs");
43
+ var import_transactionErrors = require("./transactionErrors.cjs");
44
+ var import_logger = require("./logger.cjs");
45
+ var import_crud2 = require("../schema/crud.cjs");
46
+ var import_trackedEmitter = require("./trackedEmitter.cjs");
47
+ const opts = {
48
+ json: true,
49
+ headers: {
50
+ "Content-Type": "application/json"
51
+ }
52
+ };
53
+ const bulkSave = import_bulk.BulkSave.implement(async (config, docs) => {
54
+ const logger = (0, import_logger.createLogger)(config);
55
+ if (!docs) {
56
+ logger.warn("bulkSave called with no docs");
57
+ return { ok: false, error: "noDocs", reason: "no docs provided" };
58
+ }
59
+ if (!docs.length) {
60
+ logger.warn("bulkSave called with empty docs array");
61
+ return { ok: false, error: "noDocs", reason: "no docs provided" };
62
+ }
63
+ logger.info(`Starting bulk save of ${docs.length} documents`);
64
+ const url = `${config.couch}/_bulk_docs`;
65
+ const body = { docs };
66
+ let resp;
67
+ try {
68
+ resp = await (0, import_needle.default)("post", url, body, opts);
69
+ } catch (err) {
70
+ logger.error("Network error during bulk save:", err);
71
+ import_errors.RetryableError.handleNetworkError(err);
72
+ }
73
+ if (!resp) {
74
+ logger.error("No response received from bulk save request");
75
+ throw new import_errors.RetryableError("no response", 503);
76
+ }
77
+ if (import_errors.RetryableError.isRetryableStatusCode(resp.statusCode)) {
78
+ logger.warn(`Retryable status code received: ${resp.statusCode}`);
79
+ throw new import_errors.RetryableError("retryable error during bulk save", resp.statusCode);
80
+ }
81
+ if (resp.statusCode !== 201) {
82
+ logger.error(`Unexpected status code: ${resp.statusCode}`);
83
+ throw new Error("could not save");
84
+ }
85
+ const results = resp?.body || [];
86
+ return results;
87
+ });
88
+ const bulkGet = import_bulk.BulkGet.implement(async (config, ids) => {
89
+ const logger = (0, import_logger.createLogger)(config);
90
+ const keys = ids;
91
+ logger.info(`Starting bulk get for ${keys.length} documents`);
92
+ const url = `${config.couch}/_all_docs?include_docs=true`;
93
+ const payload = { keys };
94
+ let resp;
95
+ try {
96
+ resp = await (0, import_needle.default)("post", url, payload, opts);
97
+ } catch (err) {
98
+ logger.error("Network error during bulk get:", err);
99
+ import_errors.RetryableError.handleNetworkError(err);
100
+ }
101
+ if (!resp) {
102
+ logger.error("No response received from bulk get request");
103
+ throw new import_errors.RetryableError("no response", 503);
104
+ }
105
+ if (import_errors.RetryableError.isRetryableStatusCode(resp.statusCode)) {
106
+ logger.warn(`Retryable status code received: ${resp.statusCode}`);
107
+ throw new import_errors.RetryableError("retryable error during bulk get", resp.statusCode);
108
+ }
109
+ if (resp.statusCode !== 200) {
110
+ logger.error(`Unexpected status code: ${resp.statusCode}`);
111
+ throw new Error("could not fetch");
112
+ }
113
+ const body = resp.body;
114
+ return body;
115
+ });
116
+ const bulkRemove = import_bulk.BulkRemove.implement(async (config, ids) => {
117
+ const logger = (0, import_logger.createLogger)(config);
118
+ logger.info(`Starting bulk remove for ${ids.length} documents`);
119
+ const resp = await bulkGet(config, ids);
120
+ const toRemove = [];
121
+ resp.rows.forEach((row) => {
122
+ if (!row.doc) return;
123
+ try {
124
+ const d = import_crud2.CouchDoc.parse(row.doc);
125
+ d._deleted = true;
126
+ toRemove.push(d);
127
+ } catch (e) {
128
+ logger.warn(`Invalid document structure in bulk remove: ${row.id}`, e);
129
+ }
130
+ });
131
+ return bulkSave(config, toRemove);
132
+ });
133
+ const bulkGetDictionary = import_bulk.BulkGetDictionary.implement(async (config, ids) => {
134
+ const resp = await bulkGet(config, ids);
135
+ const results = { found: {}, notFound: {} };
136
+ resp.rows.forEach(
137
+ /** @param { import('../schema/query.mjs').ViewRowSchema } row */
138
+ (row) => {
139
+ if (!row.key) return;
140
+ if (row.error) {
141
+ results.notFound[row.key] = row;
142
+ return;
143
+ }
144
+ try {
145
+ const doc = import_crud2.CouchDoc.parse(row.doc);
146
+ results.found[doc._id] = doc;
147
+ } catch (e) {
148
+ results.notFound[row.key] = row;
149
+ }
150
+ }
151
+ );
152
+ return results;
153
+ });
154
+ const bulkSaveTransaction = import_bulk.BulkSaveTransaction.implement(async (config, transactionId, docs) => {
155
+ const emitter = (0, import_trackedEmitter.setupEmitter)(config);
156
+ const logger = (0, import_logger.createLogger)(config);
157
+ const retryOptions = {
158
+ maxRetries: config.maxRetries ?? 10,
159
+ initialDelay: config.initialDelay ?? 1e3,
160
+ backoffFactor: config.backoffFactor ?? 2
161
+ };
162
+ const _put = config.bindWithRetry ? (0, import_retry.withRetry)(import_crud.put.bind(null, config), retryOptions) : import_crud.put.bind(null, config);
163
+ logger.info(`Starting bulk save transaction ${transactionId} for ${docs.length} documents`);
164
+ const txnDoc = {
165
+ _id: `txn:${transactionId}`,
166
+ _rev: null,
167
+ type: "transaction",
168
+ status: "pending",
169
+ changes: docs,
170
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
171
+ };
172
+ let txnresp = await _put(txnDoc);
173
+ logger.debug("Transaction document created:", txnDoc, txnresp);
174
+ await emitter.emit("transaction-created", { txnresp, txnDoc });
175
+ if (txnresp.error) {
176
+ throw new import_transactionErrors.TransactionSetupError("Failed to create transaction document", {
177
+ error: txnresp.error,
178
+ response: txnresp.body
179
+ });
180
+ }
181
+ const existingDocs = await bulkGetDictionary(config, docs.map((d) => d._id));
182
+ logger.debug("Fetched current revisions of documents:", existingDocs);
183
+ await emitter.emit("transaction-revs-fetched", existingDocs);
184
+ const revErrors = [];
185
+ docs.forEach((d) => {
186
+ if (existingDocs.found[d._id] && existingDocs.found[d._id]._rev !== d._rev) revErrors.push(d._id);
187
+ if (existingDocs.notFound[d._id] && d._rev) revErrors.push(d._id);
188
+ });
189
+ if (revErrors.length > 0) {
190
+ throw new import_transactionErrors.TransactionVersionConflictError(revErrors);
191
+ }
192
+ logger.debug("Checked document revisions:", existingDocs);
193
+ await emitter.emit("transaction-revs-checked", existingDocs);
194
+ const providedDocsById = {};
195
+ docs.forEach((d) => {
196
+ if (!d._id) return;
197
+ providedDocsById[d._id] = d;
198
+ });
199
+ const newDocsToRollback = [];
200
+ const potentialExistingDocsToRollack = [];
201
+ const failedDocs = [];
202
+ try {
203
+ logger.info("Transaction started:", txnDoc);
204
+ await emitter.emit("transaction-started", txnDoc);
205
+ const results = await bulkSave(config, docs);
206
+ logger.info("Transaction updates applied:", results);
207
+ await emitter.emit("transaction-updates-applied", results);
208
+ results.forEach((r) => {
209
+ if (!r.id) return;
210
+ if (!r.error) {
211
+ if (existingDocs.notFound[r.id]) newDocsToRollback.push(r);
212
+ if (existingDocs.found[r.id]) potentialExistingDocsToRollack.push(r);
213
+ } else {
214
+ failedDocs.push(r);
215
+ }
216
+ });
217
+ if (failedDocs.length > 0) {
218
+ throw new import_transactionErrors.TransactionBulkOperationError(failedDocs);
219
+ }
220
+ txnDoc.status = "completed";
221
+ txnDoc._rev = txnresp.rev;
222
+ txnresp = await _put(txnDoc);
223
+ logger.info("Transaction completed:", txnDoc);
224
+ await emitter.emit("transaction-completed", { txnresp, txnDoc });
225
+ if (txnresp.statusCode !== 201) {
226
+ logger.error("Failed to update transaction status to completed");
227
+ }
228
+ return results;
229
+ } catch (error) {
230
+ logger.error("Transaction failed, attempting rollback:", error);
231
+ const toRollback = [];
232
+ potentialExistingDocsToRollack.forEach((row) => {
233
+ if (!row.id || !row.rev) return;
234
+ const doc = existingDocs.found[row.id];
235
+ doc._rev = row.rev;
236
+ toRollback.push(doc);
237
+ });
238
+ newDocsToRollback.forEach((d) => {
239
+ if (!d.id || !d.rev) return;
240
+ const before = structuredClone(providedDocsById[d.id]);
241
+ before._rev = d.rev;
242
+ before._deleted = true;
243
+ toRollback.push(before);
244
+ });
245
+ const bulkRollbackResult = await bulkSave(config, toRollback);
246
+ let status = "rolled_back";
247
+ bulkRollbackResult.forEach((r) => {
248
+ if (r.error) status = "rollback_failed";
249
+ });
250
+ logger.warn("Transaction rolled back:", { bulkRollbackResult, status });
251
+ await emitter.emit("transaction-rolled-back", { bulkRollbackResult, status });
252
+ txnDoc.status = status;
253
+ txnDoc._rev = txnresp.rev;
254
+ txnresp = await _put(txnDoc);
255
+ logger.warn("Transaction rollback status updated:", txnDoc);
256
+ await emitter.emit("transaction-rolled-back-status", { txnresp, txnDoc });
257
+ if (txnresp.statusCode !== 201) {
258
+ logger.error("Failed to update transaction status to rolled_back");
259
+ }
260
+ throw new import_transactionErrors.TransactionRollbackError(
261
+ "Transaction failed and rollback was unsuccessful",
262
+ /** @type {Error} */
263
+ error,
264
+ bulkRollbackResult
265
+ );
266
+ }
267
+ });
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var changes_exports = {};
30
+ __export(changes_exports, {
31
+ changes: () => changes
32
+ });
33
+ module.exports = __toCommonJS(changes_exports);
34
+ var import_needle = __toESM(require("needle"), 1);
35
+ var import_events = require("events");
36
+ var import_changes = require("../schema/changes.cjs");
37
+ var import_changes_stream = __toESM(require("changes-stream"), 1);
38
+ const changes = import_changes.Changes.implement(async (config, onChange, options = {}) => {
39
+ const emitter = new import_events.EventEmitter();
40
+ options.db = config.couch;
41
+ if (options.since && options.since === "now") {
42
+ const opts = {
43
+ json: true,
44
+ headers: {
45
+ "Content-Type": "application/json"
46
+ }
47
+ };
48
+ const resp = await (0, import_needle.default)("get", config.couch, opts);
49
+ options.since = resp.body.update_seq;
50
+ }
51
+ const changes2 = (0, import_changes_stream.default)(options);
52
+ changes2.on("readable", () => {
53
+ const change = changes2.read();
54
+ if (change.results && Array.isArray(change.results)) {
55
+ change.results.forEach((c) => emitter.emit("change", c));
56
+ } else emitter.emit("change", change);
57
+ });
58
+ emitter.on("change", onChange);
59
+ return {
60
+ on: (event, listener) => emitter.on(event, listener),
61
+ removeListener: (event, listener) => emitter.removeListener(event, listener),
62
+ stop: () => {
63
+ changes2.destroy();
64
+ emitter.removeAllListeners();
65
+ }
66
+ };
67
+ });
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var crud_exports = {};
30
+ __export(crud_exports, {
31
+ get: () => get,
32
+ getAtRev: () => getAtRev,
33
+ put: () => put
34
+ });
35
+ module.exports = __toCommonJS(crud_exports);
36
+ var import_needle = __toESM(require("needle"), 1);
37
+ var import_crud = require("../schema/crud.cjs");
38
+ var import_errors = require("./errors.cjs");
39
+ var import_logger = require("./logger.cjs");
40
+ const opts = {
41
+ json: true,
42
+ headers: {
43
+ "Content-Type": "application/json"
44
+ }
45
+ };
46
+ const _getWithOptions = import_crud.CouchGetWithOptions.implement(async (config, id, getOpts) => {
47
+ const logger = (0, import_logger.createLogger)(config);
48
+ const rev = getOpts?.rev;
49
+ const path = rev ? `${id}?rev=${rev}` : id;
50
+ const url = `${config.couch}/${path}`;
51
+ logger.info(`Getting document with id: ${id}, rev ${rev || "latest"}`);
52
+ try {
53
+ const resp = await (0, import_needle.default)("get", url, opts);
54
+ if (!resp) {
55
+ logger.error("No response received from get request");
56
+ throw new import_errors.RetryableError("no response", 503);
57
+ }
58
+ const result = resp?.body || {};
59
+ if (resp.statusCode === 404) {
60
+ if (config.throwOnGetNotFound) {
61
+ logger.warn(`Document not found (throwing error): ${id}, rev ${rev || "latest"}`);
62
+ throw new import_errors.NotFoundError(id, result.reason || "not_found");
63
+ } else {
64
+ logger.debug(`Document not found (returning undefined): ${id}, rev ${rev || "latest"}`);
65
+ return null;
66
+ }
67
+ }
68
+ if (import_errors.RetryableError.isRetryableStatusCode(resp.statusCode)) {
69
+ logger.warn(`Retryable status code received: ${resp.statusCode}`);
70
+ throw new import_errors.RetryableError(result.reason || "retryable error", resp.statusCode);
71
+ }
72
+ if (resp.statusCode !== 200) {
73
+ logger.error(`Unexpected status code: ${resp.statusCode}`);
74
+ throw new Error(result.reason || "failed");
75
+ }
76
+ logger.info(`Successfully retrieved document: ${id}, rev ${rev || "latest"}`);
77
+ return result;
78
+ } catch (err) {
79
+ logger.error("Error during get operation:", err);
80
+ import_errors.RetryableError.handleNetworkError(err);
81
+ }
82
+ });
83
+ const get = import_crud.CouchGet.implement(async (config, id) => {
84
+ const getOptions = {};
85
+ return _getWithOptions(config, id, getOptions);
86
+ });
87
+ const getAtRev = import_crud.CouchGetAtRev.implement(async (config, id, rev) => {
88
+ const getOptions = { rev };
89
+ return _getWithOptions(config, id, getOptions);
90
+ });
91
+ const put = import_crud.CouchPut.implement(async (config, doc) => {
92
+ const logger = (0, import_logger.createLogger)(config);
93
+ const url = `${config.couch}/${doc._id}`;
94
+ const body = doc;
95
+ logger.info(`Putting document with id: ${doc._id}`);
96
+ let resp;
97
+ try {
98
+ resp = await (0, import_needle.default)("put", url, body, opts);
99
+ } catch (err) {
100
+ logger.error("Error during put operation:", err);
101
+ import_errors.RetryableError.handleNetworkError(err);
102
+ }
103
+ if (!resp) {
104
+ logger.error("No response received from put request");
105
+ throw new import_errors.RetryableError("no response", 503);
106
+ }
107
+ const result = resp?.body || {};
108
+ result.statusCode = resp.statusCode;
109
+ if (resp.statusCode === 409) {
110
+ logger.warn(`Conflict detected for document: ${doc._id}`);
111
+ result.ok = false;
112
+ result.error = "conflict";
113
+ return result;
114
+ }
115
+ if (import_errors.RetryableError.isRetryableStatusCode(resp.statusCode)) {
116
+ logger.warn(`Retryable status code received: ${resp.statusCode}`);
117
+ throw new import_errors.RetryableError(result.reason || "retryable error", resp.statusCode);
118
+ }
119
+ logger.info(`Successfully saved document: ${doc._id}`);
120
+ return result;
121
+ });