hide-a-bed 4.0.1 → 4.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.
- package/cjs/impl/bulk.cjs +1 -8
- package/cjs/impl/logger.cjs +19 -10
- package/cjs/impl/patch.cjs +21 -2
- package/cjs/impl/query.cjs +18 -2
- package/cjs/impl/stream.cjs +10 -2
- package/cjs/index.cjs +1 -0
- package/cjs/schema/config.cjs +1 -0
- package/cjs/schema/stream.cjs +1 -1
- package/impl/bulk.d.mts +7 -0
- package/impl/bulk.d.mts.map +1 -0
- package/impl/bulk.mjs +4 -12
- package/impl/crud.d.mts +5 -0
- package/impl/crud.d.mts.map +1 -0
- package/impl/crud.mjs +7 -7
- package/impl/errors.d.mts +35 -0
- package/impl/errors.d.mts.map +1 -0
- package/impl/errors.mjs +11 -11
- package/impl/logger.d.mts +32 -0
- package/impl/logger.d.mts.map +1 -0
- package/impl/logger.mjs +1 -1
- package/impl/patch.d.mts +4 -0
- package/impl/patch.d.mts.map +1 -0
- package/impl/patch.mjs +6 -7
- package/impl/query.d.mts +173 -0
- package/impl/query.d.mts.map +1 -0
- package/impl/query.mjs +1 -5
- package/impl/retry.d.mts +2 -0
- package/impl/retry.d.mts.map +1 -0
- package/impl/retry.mjs +3 -3
- package/impl/stream.d.mts +3 -0
- package/impl/stream.d.mts.map +1 -0
- package/impl/stream.mjs +9 -25
- package/index.d.mts +41 -0
- package/index.d.mts.map +1 -0
- package/index.mjs +1 -1
- package/package.json +1 -1
- package/schema/bind.d.mts +473 -0
- package/schema/bind.d.mts.map +1 -0
- package/schema/bulk.d.mts +367 -0
- package/schema/bulk.d.mts.map +1 -0
- package/schema/bulk.mjs +2 -2
- package/schema/config.d.mts +79 -0
- package/schema/config.d.mts.map +1 -0
- package/schema/config.mjs +2 -2
- package/schema/crud.d.mts +266 -0
- package/schema/crud.d.mts.map +1 -0
- package/schema/patch.d.mts +119 -0
- package/schema/patch.d.mts.map +1 -0
- package/schema/query.d.mts +365 -0
- package/schema/query.d.mts.map +1 -0
- package/schema/query.mjs +1 -1
- package/schema/stream.d.mts +196 -0
- package/schema/stream.d.mts.map +1 -0
package/cjs/impl/bulk.cjs
CHANGED
|
@@ -104,14 +104,7 @@ const bulkGet = import_bulk.BulkGet.implement(async (config, ids) => {
|
|
|
104
104
|
throw new Error("could not fetch");
|
|
105
105
|
}
|
|
106
106
|
const rows = resp?.body?.rows || [];
|
|
107
|
-
const docs =
|
|
108
|
-
rows.forEach((r) => {
|
|
109
|
-
if (r.error) return;
|
|
110
|
-
if (!r.key) return;
|
|
111
|
-
if (!r.doc) return;
|
|
112
|
-
const doc = r.doc;
|
|
113
|
-
docs.push(doc);
|
|
114
|
-
});
|
|
107
|
+
const docs = rows.map((r) => r.doc);
|
|
115
108
|
logger.info(`Successfully retrieved ${docs.length} documents`);
|
|
116
109
|
return docs;
|
|
117
110
|
});
|
package/cjs/impl/logger.cjs
CHANGED
|
@@ -26,16 +26,25 @@ function createLogger(config) {
|
|
|
26
26
|
return config._normalizedLogger;
|
|
27
27
|
}
|
|
28
28
|
if (!config.logger) {
|
|
29
|
-
config.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
if (config.useConsoleLogger) {
|
|
30
|
+
config._normalizedLogger = {
|
|
31
|
+
error: (...args) => console.error(...args),
|
|
32
|
+
warn: (...args) => console.warn(...args),
|
|
33
|
+
info: (...args) => console.info(...args),
|
|
34
|
+
debug: (...args) => console.debug(...args)
|
|
35
|
+
};
|
|
36
|
+
} else {
|
|
37
|
+
config._normalizedLogger = {
|
|
38
|
+
error: () => {
|
|
39
|
+
},
|
|
40
|
+
warn: () => {
|
|
41
|
+
},
|
|
42
|
+
info: () => {
|
|
43
|
+
},
|
|
44
|
+
debug: () => {
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
39
48
|
return config._normalizedLogger;
|
|
40
49
|
}
|
|
41
50
|
if (typeof config.logger === "function") {
|
package/cjs/impl/patch.cjs
CHANGED
|
@@ -24,34 +24,53 @@ __export(patch_exports, {
|
|
|
24
24
|
module.exports = __toCommonJS(patch_exports);
|
|
25
25
|
var import_crud = require("./crud.cjs");
|
|
26
26
|
var import_patch = require("../schema/patch.cjs");
|
|
27
|
+
var import_logger = require("./logger.cjs");
|
|
27
28
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
28
29
|
const patch = import_patch.Patch.implement(async (config, id, properties) => {
|
|
30
|
+
const logger = (0, import_logger.createLogger)(config);
|
|
29
31
|
const maxRetries = config.maxRetries || 5;
|
|
30
32
|
let delay = config.initialDelay || 1e3;
|
|
31
33
|
let attempts = 0;
|
|
34
|
+
logger.info(`Starting patch operation for document ${id}`);
|
|
35
|
+
logger.debug("Patch properties:", properties);
|
|
32
36
|
while (attempts <= maxRetries) {
|
|
37
|
+
logger.debug(`Attempt ${attempts + 1} of ${maxRetries + 1}`);
|
|
33
38
|
try {
|
|
34
39
|
const doc = await (0, import_crud.get)(config, id);
|
|
35
|
-
if (!doc)
|
|
40
|
+
if (!doc) {
|
|
41
|
+
logger.warn(`Document ${id} not found`);
|
|
42
|
+
return { ok: false, statusCode: 404, error: "not_found" };
|
|
43
|
+
}
|
|
36
44
|
const updatedDoc = { ...doc, ...properties };
|
|
45
|
+
logger.debug("Merged document:", updatedDoc);
|
|
37
46
|
const result = await (0, import_crud.put)(config, updatedDoc);
|
|
38
47
|
if (result.ok) {
|
|
48
|
+
logger.info(`Successfully patched document ${id}, rev: ${result.rev}`);
|
|
39
49
|
return result;
|
|
40
50
|
}
|
|
41
51
|
attempts++;
|
|
42
52
|
if (attempts > maxRetries) {
|
|
53
|
+
logger.error(`Failed to patch ${id} after ${maxRetries} attempts`);
|
|
43
54
|
throw new Error(`Failed to patch after ${maxRetries} attempts`);
|
|
44
55
|
}
|
|
56
|
+
logger.warn(`Conflict detected for ${id}, retrying (attempt ${attempts})`);
|
|
45
57
|
await sleep(delay);
|
|
46
58
|
delay *= config.backoffFactor;
|
|
59
|
+
logger.debug(`Next retry delay: ${delay}ms`);
|
|
47
60
|
} catch (err) {
|
|
48
|
-
if (err.message
|
|
61
|
+
if (err.message === "not_found") {
|
|
62
|
+
logger.warn(`Document ${id} not found during patch operation`);
|
|
63
|
+
return { ok: false, statusCode: 404, error: "not_found" };
|
|
64
|
+
}
|
|
49
65
|
attempts++;
|
|
50
66
|
if (attempts > maxRetries) {
|
|
51
67
|
const error = `Failed to patch after ${maxRetries} attempts: ${err.message}`;
|
|
68
|
+
logger.error(error);
|
|
52
69
|
return { ok: false, statusCode: 500, error };
|
|
53
70
|
}
|
|
71
|
+
logger.warn(`Error during patch attempt ${attempts}: ${err.message}`);
|
|
54
72
|
await sleep(delay);
|
|
73
|
+
logger.debug(`Retrying after ${delay}ms`);
|
|
55
74
|
}
|
|
56
75
|
}
|
|
57
76
|
});
|
package/cjs/impl/query.cjs
CHANGED
|
@@ -36,10 +36,15 @@ var import_zod = require("zod");
|
|
|
36
36
|
var import_needle = __toESM(require("needle"), 1);
|
|
37
37
|
var import_query = require("../schema/query.cjs");
|
|
38
38
|
var import_errors = require("./errors.cjs");
|
|
39
|
+
var import_logger = require("./logger.cjs");
|
|
39
40
|
var import_lodash = __toESM(require("lodash"), 1);
|
|
40
41
|
const { includes } = import_lodash.default;
|
|
41
42
|
const query = import_query.SimpleViewQuery.implement(async (config, view, options) => {
|
|
43
|
+
const logger = (0, import_logger.createLogger)(config);
|
|
44
|
+
logger.info(`Starting view query: ${view}`);
|
|
45
|
+
logger.debug("Query options:", options);
|
|
42
46
|
const qs = queryString(options, ["key", "startkey", "endkey", "reduce", "group", "group_level", "stale", "limit"]);
|
|
47
|
+
logger.debug("Generated query string:", qs);
|
|
43
48
|
const opts = {
|
|
44
49
|
json: true,
|
|
45
50
|
headers: {
|
|
@@ -49,16 +54,27 @@ const query = import_query.SimpleViewQuery.implement(async (config, view, option
|
|
|
49
54
|
const url = `${config.couch}/${view}?${qs.toString()}`;
|
|
50
55
|
let results;
|
|
51
56
|
try {
|
|
57
|
+
logger.debug(`Sending GET request to: ${url}`);
|
|
52
58
|
results = await (0, import_needle.default)("get", url, opts);
|
|
53
59
|
} catch (err) {
|
|
60
|
+
logger.error("Network error during query:", err);
|
|
54
61
|
import_errors.RetryableError.handleNetworkError(err);
|
|
55
62
|
}
|
|
56
|
-
if (!results)
|
|
63
|
+
if (!results) {
|
|
64
|
+
logger.error("No response received from query request");
|
|
65
|
+
throw new import_errors.RetryableError("no response", 503);
|
|
66
|
+
}
|
|
57
67
|
const body = results.body;
|
|
58
68
|
if (import_errors.RetryableError.isRetryableStatusCode(results.statusCode)) {
|
|
69
|
+
logger.warn(`Retryable status code received: ${results.statusCode}`);
|
|
59
70
|
throw new import_errors.RetryableError(body.error || "retryable error during query", results.statusCode);
|
|
60
71
|
}
|
|
61
|
-
if (body.error)
|
|
72
|
+
if (body.error) {
|
|
73
|
+
logger.error(`Query error: ${body.error}`);
|
|
74
|
+
throw new Error(body.error);
|
|
75
|
+
}
|
|
76
|
+
logger.info(`Successfully executed view query: ${view}`);
|
|
77
|
+
logger.debug("Query response:", body);
|
|
62
78
|
return body;
|
|
63
79
|
});
|
|
64
80
|
function queryString(options, params) {
|
package/cjs/impl/stream.cjs
CHANGED
|
@@ -34,6 +34,7 @@ module.exports = __toCommonJS(stream_exports);
|
|
|
34
34
|
var import_needle = __toESM(require("needle"), 1);
|
|
35
35
|
var import_query = require("./query.cjs");
|
|
36
36
|
var import_errors = require("./errors.cjs");
|
|
37
|
+
var import_logger = require("./logger.cjs");
|
|
37
38
|
var import_JSONStream = __toESM(require("JSONStream"), 1);
|
|
38
39
|
const queryStream = (config, view, options, onRow) => new Promise((resolve, reject) => {
|
|
39
40
|
if (!options) options = {};
|
|
@@ -48,7 +49,15 @@ const queryStream = (config, view, options, onRow) => new Promise((resolve, reje
|
|
|
48
49
|
// Keep as stream
|
|
49
50
|
};
|
|
50
51
|
const streamer = import_JSONStream.default.parse("rows.*");
|
|
51
|
-
|
|
52
|
+
let rowCount = 0;
|
|
53
|
+
streamer.on(
|
|
54
|
+
"data",
|
|
55
|
+
/** @param {object} row */
|
|
56
|
+
(row) => {
|
|
57
|
+
rowCount++;
|
|
58
|
+
onRow(row);
|
|
59
|
+
}
|
|
60
|
+
);
|
|
52
61
|
streamer.on(
|
|
53
62
|
"error",
|
|
54
63
|
/** @param {Error} err */
|
|
@@ -74,7 +83,6 @@ const queryStream = (config, view, options, onRow) => new Promise((resolve, reje
|
|
|
74
83
|
req.on("response", (response) => {
|
|
75
84
|
if (import_errors.RetryableError.isRetryableStatusCode(response.statusCode)) {
|
|
76
85
|
reject(new import_errors.RetryableError("retryable error during stream query", response.statusCode));
|
|
77
|
-
return;
|
|
78
86
|
}
|
|
79
87
|
});
|
|
80
88
|
req.on("error", (err) => {
|
package/cjs/index.cjs
CHANGED
|
@@ -52,6 +52,7 @@ const schema = {
|
|
|
52
52
|
OnRow: import_stream2.OnRow,
|
|
53
53
|
BulkSave: import_bulk2.BulkSave,
|
|
54
54
|
BulkGet: import_bulk2.BulkGet,
|
|
55
|
+
BulkRemove: import_bulk2.BulkRemove,
|
|
55
56
|
CouchGet: import_crud2.CouchGet,
|
|
56
57
|
CouchPut: import_crud2.CouchPut,
|
|
57
58
|
CouchDoc: import_crud2.CouchDoc,
|
package/cjs/schema/config.cjs
CHANGED
|
@@ -40,6 +40,7 @@ const CouchConfig = import_zod.z.object({
|
|
|
40
40
|
maxRetries: import_zod.z.number().optional().default(3).describe("maximum number of retry attempts"),
|
|
41
41
|
initialDelay: import_zod.z.number().optional().default(1e3).describe("initial retry delay in milliseconds"),
|
|
42
42
|
backoffFactor: import_zod.z.number().optional().default(2).describe("multiplier for exponential backoff"),
|
|
43
|
+
useConsoleLogger: import_zod.z.boolean().optional().default(false).describe("turn on console as a fallback logger"),
|
|
43
44
|
logger: LoggerSchema.optional().describe("logging interface supporting winston-like or simple function interface"),
|
|
44
45
|
_normalizedLogger: import_zod.z.any().optional()
|
|
45
46
|
// Internal property for caching normalized logger
|
package/cjs/schema/stream.cjs
CHANGED
|
@@ -28,7 +28,7 @@ var import_config = require("./config.cjs");
|
|
|
28
28
|
var import_query = require("./query.cjs");
|
|
29
29
|
const OnRow = import_zod.z.function().args(
|
|
30
30
|
import_query.ViewRow
|
|
31
|
-
)
|
|
31
|
+
);
|
|
32
32
|
const SimpleViewQueryStream = import_zod.z.function().args(
|
|
33
33
|
import_config.CouchConfig,
|
|
34
34
|
import_zod.z.string().describe("the view name"),
|
package/impl/bulk.d.mts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** @type { import('../schema/bulk.mjs').BulkSaveSchema } */
|
|
2
|
+
export const bulkSave: import("../schema/bulk.mjs").BulkSaveSchema;
|
|
3
|
+
/** @type { import('../schema/bulk.mjs').BulkGetSchema } */
|
|
4
|
+
export const bulkGet: import("../schema/bulk.mjs").BulkGetSchema;
|
|
5
|
+
/** @type { import('../schema/bulk.mjs').BulkRemoveSchema } */
|
|
6
|
+
export const bulkRemove: import("../schema/bulk.mjs").BulkRemoveSchema;
|
|
7
|
+
//# sourceMappingURL=bulk.d.mts.map
|
|
@@ -0,0 +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"}
|
package/impl/bulk.mjs
CHANGED
|
@@ -15,7 +15,7 @@ const opts = {
|
|
|
15
15
|
export const bulkSave = BulkSave.implement(async (config, docs) => {
|
|
16
16
|
/** @type {import('./logger.mjs').Logger } */
|
|
17
17
|
const logger = createLogger(config)
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
if (!docs) {
|
|
20
20
|
logger.warn('bulkSave called with no docs')
|
|
21
21
|
return { ok: false, error: 'noDocs', reason: 'no docs provided' }
|
|
@@ -55,7 +55,7 @@ export const bulkSave = BulkSave.implement(async (config, docs) => {
|
|
|
55
55
|
export const bulkGet = BulkGet.implement(async (config, ids) => {
|
|
56
56
|
const logger = createLogger(config)
|
|
57
57
|
const keys = ids
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
logger.info(`Starting bulk get for ${keys.length} documents`)
|
|
60
60
|
const url = `${config.couch}/_all_docs?include_docs=true`
|
|
61
61
|
const body = { keys }
|
|
@@ -80,17 +80,9 @@ export const bulkGet = BulkGet.implement(async (config, ids) => {
|
|
|
80
80
|
}
|
|
81
81
|
const rows = resp?.body?.rows || []
|
|
82
82
|
/** @type {Array<import('../schema/crud.mjs').CouchDocSchema>} */
|
|
83
|
-
const docs =
|
|
84
|
-
rows.forEach((
|
|
83
|
+
const docs = rows.map((
|
|
85
84
|
/** @type {{ error?: any, key?: string, doc?: import('../schema/crud.mjs').CouchDocSchema }} */ r
|
|
86
|
-
) =>
|
|
87
|
-
if (r.error) return
|
|
88
|
-
if (!r.key) return
|
|
89
|
-
if (!r.doc) return
|
|
90
|
-
/** @type { import('../schema/crud.mjs').CouchDocSchema } */
|
|
91
|
-
const doc = r.doc
|
|
92
|
-
docs.push(doc)
|
|
93
|
-
})
|
|
85
|
+
) => r.doc)
|
|
94
86
|
logger.info(`Successfully retrieved ${docs.length} documents`)
|
|
95
87
|
return docs
|
|
96
88
|
})
|
package/impl/crud.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** @type { import('../schema/crud.mjs').CouchGetSchema } */
|
|
2
|
+
export const get: import("../schema/crud.mjs").CouchGetSchema;
|
|
3
|
+
/** @type { import('../schema/crud.mjs').CouchPutSchema } */
|
|
4
|
+
export const put: import("../schema/crud.mjs").CouchPutSchema;
|
|
5
|
+
//# sourceMappingURL=crud.d.mts.map
|
|
@@ -0,0 +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"}
|
package/impl/crud.mjs
CHANGED
|
@@ -16,7 +16,7 @@ export const get = CouchGet.implement(async (config, id) => {
|
|
|
16
16
|
const logger = createLogger(config)
|
|
17
17
|
const url = `${config.couch}/${id}`
|
|
18
18
|
logger.info(`Getting document with id: ${id}`)
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
try {
|
|
21
21
|
const resp = await needle('get', url, opts)
|
|
22
22
|
if (!resp) {
|
|
@@ -58,7 +58,7 @@ export const put = CouchPut.implement(async (config, doc) => {
|
|
|
58
58
|
const logger = createLogger(config)
|
|
59
59
|
const url = `${config.couch}/${doc._id}`
|
|
60
60
|
const body = doc
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
logger.info(`Putting document with id: ${doc._id}`)
|
|
63
63
|
let resp
|
|
64
64
|
try {
|
|
@@ -67,27 +67,27 @@ export const put = CouchPut.implement(async (config, doc) => {
|
|
|
67
67
|
logger.error('Error during put operation:', err)
|
|
68
68
|
RetryableError.handleNetworkError(err)
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
if (!resp) {
|
|
72
72
|
logger.error('No response received from put request')
|
|
73
73
|
throw new RetryableError('no response', 503)
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
const result = resp?.body || {}
|
|
77
77
|
result.statusCode = resp.statusCode
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
if (resp.statusCode === 409) {
|
|
80
80
|
logger.warn(`Conflict detected for document: ${doc._id}`)
|
|
81
81
|
result.ok = false
|
|
82
82
|
result.error = 'conflict'
|
|
83
83
|
return result
|
|
84
84
|
}
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
if (RetryableError.isRetryableStatusCode(resp.statusCode)) {
|
|
87
87
|
logger.warn(`Retryable status code received: ${resp.statusCode}`)
|
|
88
88
|
throw new RetryableError(result.reason || 'retryable error', resp.statusCode)
|
|
89
89
|
}
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
logger.info(`Successfully saved document: ${doc._id}`)
|
|
92
92
|
return result
|
|
93
93
|
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} NetworkError
|
|
3
|
+
* @property {string} code - The error code
|
|
4
|
+
* @property {string} [message] - Optional error message
|
|
5
|
+
*/
|
|
6
|
+
export class RetryableError extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* @param {number|undefined} statusCode - The HTTP status code to check
|
|
9
|
+
* @returns {boolean} Whether the status code is retryable
|
|
10
|
+
*/
|
|
11
|
+
static isRetryableStatusCode(statusCode: number | undefined): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* @param {NetworkError | unknown} err - The network error to handle
|
|
14
|
+
* @throws {RetryableError} If the error is retryable
|
|
15
|
+
* @throws {Error} If the error is not retryable
|
|
16
|
+
*/
|
|
17
|
+
static handleNetworkError(err: NetworkError | unknown): void;
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} message - The error message
|
|
20
|
+
* @param {number|undefined} statusCode - The HTTP status code
|
|
21
|
+
*/
|
|
22
|
+
constructor(message: string, statusCode: number | undefined);
|
|
23
|
+
statusCode: number | undefined;
|
|
24
|
+
}
|
|
25
|
+
export type NetworkError = {
|
|
26
|
+
/**
|
|
27
|
+
* - The error code
|
|
28
|
+
*/
|
|
29
|
+
code: string;
|
|
30
|
+
/**
|
|
31
|
+
* - Optional error message
|
|
32
|
+
*/
|
|
33
|
+
message?: string | undefined;
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=errors.d.mts.map
|
|
@@ -0,0 +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"}
|
package/impl/errors.mjs
CHANGED
|
@@ -11,19 +11,19 @@ export class RetryableError extends Error {
|
|
|
11
11
|
* @param {string} message - The error message
|
|
12
12
|
* @param {number|undefined} statusCode - The HTTP status code
|
|
13
13
|
*/
|
|
14
|
-
constructor(message, statusCode) {
|
|
15
|
-
super(message)
|
|
16
|
-
this.name = 'RetryableError'
|
|
17
|
-
this.statusCode = statusCode
|
|
14
|
+
constructor (message, statusCode) {
|
|
15
|
+
super(message)
|
|
16
|
+
this.name = 'RetryableError'
|
|
17
|
+
this.statusCode = statusCode
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* @param {number|undefined} statusCode - The HTTP status code to check
|
|
22
22
|
* @returns {boolean} Whether the status code is retryable
|
|
23
23
|
*/
|
|
24
|
-
static isRetryableStatusCode(statusCode) {
|
|
25
|
-
if (statusCode === undefined) return false
|
|
26
|
-
return [408, 429, 500, 502, 503, 504].includes(statusCode)
|
|
24
|
+
static isRetryableStatusCode (statusCode) {
|
|
25
|
+
if (statusCode === undefined) return false
|
|
26
|
+
return [408, 429, 500, 502, 503, 504].includes(statusCode)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -31,7 +31,7 @@ export class RetryableError extends Error {
|
|
|
31
31
|
* @throws {RetryableError} If the error is retryable
|
|
32
32
|
* @throws {Error} If the error is not retryable
|
|
33
33
|
*/
|
|
34
|
-
static handleNetworkError(err) {
|
|
34
|
+
static handleNetworkError (err) {
|
|
35
35
|
/** @type {Record<string, number>} */
|
|
36
36
|
const networkErrors = {
|
|
37
37
|
ECONNREFUSED: 503,
|
|
@@ -42,12 +42,12 @@ export class RetryableError extends Error {
|
|
|
42
42
|
EPIPE: 503,
|
|
43
43
|
EHOSTUNREACH: 503,
|
|
44
44
|
ESOCKETTIMEDOUT: 503
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
46
|
|
|
47
47
|
// Type guard for NetworkError shape
|
|
48
48
|
if (typeof err === 'object' && err !== null && 'code' in err && typeof err.code === 'string' && networkErrors[err.code]) {
|
|
49
|
-
throw new RetryableError(`Network error: ${err.code}`, networkErrors[err.code])
|
|
49
|
+
throw new RetryableError(`Network error: ${err.code}`, networkErrors[err.code])
|
|
50
50
|
}
|
|
51
|
-
throw err
|
|
51
|
+
throw err
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} Logger
|
|
3
|
+
* @property {(...args: any[]) => void} error - Log error messages
|
|
4
|
+
* @property {(...args: any[]) => void} warn - Log warning messages
|
|
5
|
+
* @property {(...args: any[]) => void} info - Log info messages
|
|
6
|
+
* @property {(...args: any[]) => void} debug - Log debug messages
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Creates a unified logger interface that works with both function and object-style loggers
|
|
10
|
+
* @param {import('../schema/config.mjs').CouchConfigSchema} config
|
|
11
|
+
* @returns {Logger} Normalized logger interface
|
|
12
|
+
*/
|
|
13
|
+
export function createLogger(config: import("../schema/config.mjs").CouchConfigSchema): Logger;
|
|
14
|
+
export type Logger = {
|
|
15
|
+
/**
|
|
16
|
+
* - Log error messages
|
|
17
|
+
*/
|
|
18
|
+
error: (...args: any[]) => void;
|
|
19
|
+
/**
|
|
20
|
+
* - Log warning messages
|
|
21
|
+
*/
|
|
22
|
+
warn: (...args: any[]) => void;
|
|
23
|
+
/**
|
|
24
|
+
* - Log info messages
|
|
25
|
+
*/
|
|
26
|
+
info: (...args: any[]) => void;
|
|
27
|
+
/**
|
|
28
|
+
* - Log debug messages
|
|
29
|
+
*/
|
|
30
|
+
debug: (...args: any[]) => void;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=logger.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.mts","sourceRoot":"","sources":["logger.mjs"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;GAIG;AACH,qCAHW,OAAO,sBAAsB,EAAE,iBAAiB,GAC9C,MAAM,CA+ClB;;;;;WAxDa,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI;;;;UACxB,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI;;;;UACxB,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI;;;;WACxB,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI"}
|
package/impl/logger.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @param {import('../schema/config.mjs').CouchConfigSchema} config
|
|
12
12
|
* @returns {Logger} Normalized logger interface
|
|
13
13
|
*/
|
|
14
|
-
export function createLogger(config) {
|
|
14
|
+
export function createLogger (config) {
|
|
15
15
|
// Return cached logger if it exists
|
|
16
16
|
if (config._normalizedLogger) {
|
|
17
17
|
return config._normalizedLogger
|
package/impl/patch.d.mts
ADDED
|
@@ -0,0 +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"}
|
package/impl/patch.mjs
CHANGED
|
@@ -22,10 +22,10 @@ export const patch = Patch.implement(async (config, id, properties) => {
|
|
|
22
22
|
logger.warn(`Document ${id} not found`)
|
|
23
23
|
return { ok: false, statusCode: 404, error: 'not_found' }
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
const updatedDoc = { ...doc, ...properties }
|
|
27
27
|
logger.debug('Merged document:', updatedDoc)
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
const result = await put(config, updatedDoc)
|
|
30
30
|
|
|
31
31
|
// Check if the response indicates a conflict
|
|
@@ -33,25 +33,24 @@ export const patch = Patch.implement(async (config, id, properties) => {
|
|
|
33
33
|
logger.info(`Successfully patched document ${id}, rev: ${result.rev}`)
|
|
34
34
|
return result
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
// If not ok, treat as conflict and retry
|
|
38
38
|
attempts++
|
|
39
39
|
if (attempts > maxRetries) {
|
|
40
40
|
logger.error(`Failed to patch ${id} after ${maxRetries} attempts`)
|
|
41
41
|
throw new Error(`Failed to patch after ${maxRetries} attempts`)
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
logger.warn(`Conflict detected for ${id}, retrying (attempt ${attempts})`)
|
|
45
45
|
await sleep(delay)
|
|
46
46
|
delay *= config.backoffFactor
|
|
47
47
|
logger.debug(`Next retry delay: ${delay}ms`)
|
|
48
|
-
|
|
49
48
|
} catch (err) {
|
|
50
49
|
if (err.message === 'not_found') {
|
|
51
50
|
logger.warn(`Document ${id} not found during patch operation`)
|
|
52
51
|
return { ok: false, statusCode: 404, error: 'not_found' }
|
|
53
52
|
}
|
|
54
|
-
|
|
53
|
+
|
|
55
54
|
// Handle other errors (network, etc)
|
|
56
55
|
attempts++
|
|
57
56
|
if (attempts > maxRetries) {
|
|
@@ -59,7 +58,7 @@ export const patch = Patch.implement(async (config, id, properties) => {
|
|
|
59
58
|
logger.error(error)
|
|
60
59
|
return { ok: false, statusCode: 500, error }
|
|
61
60
|
}
|
|
62
|
-
|
|
61
|
+
|
|
63
62
|
logger.warn(`Error during patch attempt ${attempts}: ${err.message}`)
|
|
64
63
|
await sleep(delay)
|
|
65
64
|
logger.debug(`Retrying after ${delay}ms`)
|