hide-a-bed 7.0.0 → 7.1.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.
- package/README.md +167 -145
- package/dist/cjs/index.cjs +48 -2
- package/dist/esm/index.mjs +48 -3
- package/impl/bindConfig.mts +72 -60
- package/impl/headDB.mts +55 -0
- package/impl/utils/errors.mts +189 -158
- package/impl/utils/fetch.mts +102 -93
- package/impl/utils/queryString.mts +1 -2
- package/index.mts +56 -41
- package/package.json +1 -1
- package/types/output/impl/bindConfig.d.mts +16 -14
- package/types/output/impl/bindConfig.d.mts.map +1 -1
- package/types/output/impl/headDB.d.mts +13 -0
- package/types/output/impl/headDB.d.mts.map +1 -0
- package/types/output/impl/headDB.test.d.mts +2 -0
- package/types/output/impl/headDB.test.d.mts.map +1 -0
- package/types/output/impl/utils/errors.d.mts +12 -12
- package/types/output/impl/utils/errors.d.mts.map +1 -1
- package/types/output/impl/utils/fetch.d.mts +4 -4
- package/types/output/impl/utils/fetch.d.mts.map +1 -1
- package/types/output/impl/utils/queryString.d.mts.map +1 -1
- package/types/output/index.d.mts +35 -34
- package/types/output/index.d.mts.map +1 -1
package/dist/esm/index.mjs
CHANGED
|
@@ -1126,8 +1126,7 @@ const KEYS_TO_QUOTE = [
|
|
|
1126
1126
|
"key",
|
|
1127
1127
|
"keys",
|
|
1128
1128
|
"startkey",
|
|
1129
|
-
"startkey_docid"
|
|
1130
|
-
"update"
|
|
1129
|
+
"startkey_docid"
|
|
1131
1130
|
];
|
|
1132
1131
|
/**
|
|
1133
1132
|
* Serialize CouchDB view options into a URL-safe query string, quoting values CouchDB expects as JSON.
|
|
@@ -2039,6 +2038,51 @@ const getDBInfo = async (configInput) => {
|
|
|
2039
2038
|
return CouchDBInfo.parse(resp.body);
|
|
2040
2039
|
};
|
|
2041
2040
|
|
|
2041
|
+
//#endregion
|
|
2042
|
+
//#region impl/headDB.mts
|
|
2043
|
+
/**
|
|
2044
|
+
* Performs a health check against the target CouchDB database using `HEAD /{db}`.
|
|
2045
|
+
*
|
|
2046
|
+
* @see {@link https://docs.couchdb.org/en/stable/api/database/common.html#head--db | CouchDB API Documentation}
|
|
2047
|
+
*
|
|
2048
|
+
* @param configInput - The CouchDB configuration input.
|
|
2049
|
+
* @returns A promise that resolves to `true` when the database responds successfully.
|
|
2050
|
+
* @throws {RetryableError} `RetryableError` If a retryable error occurs during the request.
|
|
2051
|
+
* @throws {OperationError} For other non-retryable response failures.
|
|
2052
|
+
*/
|
|
2053
|
+
const headDB = async (configInput) => {
|
|
2054
|
+
const config = CouchConfig.parse(configInput);
|
|
2055
|
+
const logger = createLogger(config);
|
|
2056
|
+
const url = createCouchDbUrl(config.couch);
|
|
2057
|
+
let resp;
|
|
2058
|
+
try {
|
|
2059
|
+
resp = await fetchCouchJson({
|
|
2060
|
+
auth: config.auth,
|
|
2061
|
+
method: "HEAD",
|
|
2062
|
+
operation: "headDB",
|
|
2063
|
+
request: config.request,
|
|
2064
|
+
url
|
|
2065
|
+
});
|
|
2066
|
+
if (!isSuccessStatusCode("database", resp.statusCode)) {
|
|
2067
|
+
logger.error(`Non-success status code received: ${resp.statusCode}`);
|
|
2068
|
+
throw createResponseError({
|
|
2069
|
+
body: resp.body,
|
|
2070
|
+
defaultMessage: "Database health check failed",
|
|
2071
|
+
operation: "headDB",
|
|
2072
|
+
statusCode: resp.statusCode
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
} catch (err) {
|
|
2076
|
+
logger.error("Error during head operation:", err);
|
|
2077
|
+
RetryableError.handleNetworkError(err, "headDB");
|
|
2078
|
+
}
|
|
2079
|
+
if (!resp) {
|
|
2080
|
+
logger.error("No response received from head request");
|
|
2081
|
+
throw new RetryableError("Database health check failed", 503, { operation: "headDB" });
|
|
2082
|
+
}
|
|
2083
|
+
return true;
|
|
2084
|
+
};
|
|
2085
|
+
|
|
2042
2086
|
//#endregion
|
|
2043
2087
|
//#region schema/sugar/lock.mts
|
|
2044
2088
|
const LockDoc = CouchDoc.extend({
|
|
@@ -2372,6 +2416,7 @@ function doBind(config) {
|
|
|
2372
2416
|
bulkSave: config.bindWithRetry ? withRetry(bulkSave.bind(null, config), retryOptions) : bulkSave.bind(null, config),
|
|
2373
2417
|
bulkSaveTransaction: bulkSaveTransaction.bind(null, config),
|
|
2374
2418
|
getDBInfo: config.bindWithRetry ? withRetry(getDBInfo.bind(null, config), retryOptions) : getDBInfo.bind(null, config),
|
|
2419
|
+
headDB: config.bindWithRetry ? withRetry(headDB.bind(null, config), retryOptions) : headDB.bind(null, config),
|
|
2375
2420
|
patch: config.bindWithRetry ? withRetry(patch.bind(null, config), retryOptions) : patch.bind(null, config),
|
|
2376
2421
|
patchDangerously: patchDangerously.bind(null, config),
|
|
2377
2422
|
put: config.bindWithRetry ? withRetry(put.bind(null, config), retryOptions) : put.bind(null, config),
|
|
@@ -2384,4 +2429,4 @@ function doBind(config) {
|
|
|
2384
2429
|
}
|
|
2385
2430
|
|
|
2386
2431
|
//#endregion
|
|
2387
|
-
export { ConflictError, HideABedError, NotFoundError, OperationError, QueryBuilder, RetryableError, ValidationError, bindConfig, bulkGet, bulkGetDictionary, bulkRemove, bulkRemoveMap, bulkSave, bulkSaveTransaction, createLock, createQuery, get, getAtRev, getDBInfo, patch, patchDangerously, put, query, queryStream, remove, removeLock, watchDocs, withRetry };
|
|
2432
|
+
export { ConflictError, HideABedError, NotFoundError, OperationError, QueryBuilder, RetryableError, ValidationError, bindConfig, bulkGet, bulkGetDictionary, bulkRemove, bulkRemoveMap, bulkSave, bulkSaveTransaction, createLock, createQuery, get, getAtRev, getDBInfo, headDB, patch, patchDangerously, put, query, queryStream, remove, removeLock, watchDocs, withRetry };
|
package/impl/bindConfig.mts
CHANGED
|
@@ -1,56 +1,58 @@
|
|
|
1
1
|
// oxlint-disable typescript/no-explicit-any
|
|
2
|
-
import type z from
|
|
3
|
-
import { CouchConfig, type CouchConfigInput } from
|
|
4
|
-
import { withRetry } from
|
|
2
|
+
import type z from "zod";
|
|
3
|
+
import { CouchConfig, type CouchConfigInput } from "../schema/config.mts";
|
|
4
|
+
import { withRetry } from "./retry.mts";
|
|
5
5
|
import {
|
|
6
6
|
type BulkGetBound,
|
|
7
7
|
bulkGet,
|
|
8
8
|
type BulkGetDictionaryBound,
|
|
9
|
-
bulkGetDictionary
|
|
10
|
-
} from
|
|
11
|
-
import { type GetBound, type GetAtRevBound, getAtRev, get } from
|
|
12
|
-
import { queryStream } from
|
|
13
|
-
import { patch, patchDangerously } from
|
|
14
|
-
import { put } from
|
|
15
|
-
import type { QueryBound } from
|
|
16
|
-
import { query } from
|
|
17
|
-
import { bulkRemove, bulkRemoveMap } from
|
|
18
|
-
import { bulkSave, bulkSaveTransaction } from
|
|
19
|
-
import { getDBInfo } from
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
9
|
+
bulkGetDictionary,
|
|
10
|
+
} from "./bulkGet.mts";
|
|
11
|
+
import { type GetBound, type GetAtRevBound, getAtRev, get } from "./get.mts";
|
|
12
|
+
import { queryStream } from "./stream.mts";
|
|
13
|
+
import { patch, patchDangerously } from "./patch.mts";
|
|
14
|
+
import { put } from "./put.mts";
|
|
15
|
+
import type { QueryBound } from "./query.mts";
|
|
16
|
+
import { query } from "./query.mts";
|
|
17
|
+
import { bulkRemove, bulkRemoveMap } from "./bulkRemove.mts";
|
|
18
|
+
import { bulkSave, bulkSaveTransaction } from "./bulkSave.mts";
|
|
19
|
+
import { getDBInfo } from "./getDBInfo.mts";
|
|
20
|
+
import { headDB } from "./headDB.mts";
|
|
21
|
+
import { remove } from "./remove.mts";
|
|
22
|
+
import { createLock, removeLock } from "./sugar/lock.mts";
|
|
23
|
+
import { watchDocs } from "./sugar/watch.mts";
|
|
23
24
|
|
|
24
25
|
type BoundConfigMethod<T> = T extends (...args: infer Args) => infer Result
|
|
25
26
|
? Args extends [unknown, ...infer Rest]
|
|
26
27
|
? (...args: Rest) => Result
|
|
27
28
|
: never
|
|
28
|
-
: never
|
|
29
|
+
: never;
|
|
29
30
|
|
|
30
31
|
type BoundMethods = {
|
|
31
|
-
bulkGet: BulkGetBound
|
|
32
|
-
bulkGetDictionary: BulkGetDictionaryBound
|
|
33
|
-
get: GetBound
|
|
34
|
-
getAtRev: GetAtRevBound
|
|
35
|
-
query: QueryBound
|
|
36
|
-
bulkRemove: BoundConfigMethod<typeof bulkRemove
|
|
37
|
-
bulkRemoveMap: BoundConfigMethod<typeof bulkRemoveMap
|
|
38
|
-
bulkSave: BoundConfigMethod<typeof bulkSave
|
|
39
|
-
bulkSaveTransaction: BoundConfigMethod<typeof bulkSaveTransaction
|
|
40
|
-
getDBInfo: BoundConfigMethod<typeof getDBInfo
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
32
|
+
bulkGet: BulkGetBound;
|
|
33
|
+
bulkGetDictionary: BulkGetDictionaryBound;
|
|
34
|
+
get: GetBound;
|
|
35
|
+
getAtRev: GetAtRevBound;
|
|
36
|
+
query: QueryBound;
|
|
37
|
+
bulkRemove: BoundConfigMethod<typeof bulkRemove>;
|
|
38
|
+
bulkRemoveMap: BoundConfigMethod<typeof bulkRemoveMap>;
|
|
39
|
+
bulkSave: BoundConfigMethod<typeof bulkSave>;
|
|
40
|
+
bulkSaveTransaction: BoundConfigMethod<typeof bulkSaveTransaction>;
|
|
41
|
+
getDBInfo: BoundConfigMethod<typeof getDBInfo>;
|
|
42
|
+
headDB: BoundConfigMethod<typeof headDB>;
|
|
43
|
+
patch: BoundConfigMethod<typeof patch>;
|
|
44
|
+
patchDangerously: BoundConfigMethod<typeof patchDangerously>;
|
|
45
|
+
put: BoundConfigMethod<typeof put>;
|
|
46
|
+
queryStream: BoundConfigMethod<typeof queryStream>;
|
|
47
|
+
remove: BoundConfigMethod<typeof remove>;
|
|
48
|
+
createLock: BoundConfigMethod<typeof createLock>;
|
|
49
|
+
removeLock: BoundConfigMethod<typeof removeLock>;
|
|
50
|
+
watchDocs: BoundConfigMethod<typeof watchDocs>;
|
|
51
|
+
};
|
|
50
52
|
|
|
51
53
|
export type BoundInstance = BoundMethods & {
|
|
52
|
-
options(overrides: Partial<z.input<typeof CouchConfig>>): BoundInstance
|
|
53
|
-
}
|
|
54
|
+
options(overrides: Partial<z.input<typeof CouchConfig>>): BoundInstance;
|
|
55
|
+
};
|
|
54
56
|
|
|
55
57
|
/**
|
|
56
58
|
* Build a validated binding that exposes CouchDB helpers plus an options() helper for overrides.
|
|
@@ -58,20 +60,22 @@ export type BoundInstance = BoundMethods & {
|
|
|
58
60
|
* @returns A bound instance with CouchDB operations and an options() method for overrides
|
|
59
61
|
*/
|
|
60
62
|
export const bindConfig = (config: CouchConfigInput): BoundInstance => {
|
|
61
|
-
const parsedConfig = CouchConfig.parse(config)
|
|
63
|
+
const parsedConfig = CouchConfig.parse(config);
|
|
62
64
|
|
|
63
|
-
const funcs = doBind(parsedConfig)
|
|
65
|
+
const funcs = doBind(parsedConfig);
|
|
64
66
|
|
|
65
67
|
// Add the options function that returns a new bound instance
|
|
66
68
|
// this allows the user to override some options
|
|
67
|
-
const reconfigure: BoundInstance[
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
const reconfigure: BoundInstance["options"] = (
|
|
70
|
+
overrides: Partial<CouchConfigInput>,
|
|
71
|
+
) => {
|
|
72
|
+
const newConfig: z.input<typeof CouchConfig> = { ...config, ...overrides };
|
|
73
|
+
return bindConfig(newConfig);
|
|
74
|
+
};
|
|
71
75
|
|
|
72
|
-
const bound: BoundInstance = { ...funcs, options: reconfigure }
|
|
73
|
-
return bound
|
|
74
|
-
}
|
|
76
|
+
const bound: BoundInstance = { ...funcs, options: reconfigure };
|
|
77
|
+
return bound;
|
|
78
|
+
};
|
|
75
79
|
|
|
76
80
|
/**
|
|
77
81
|
* @internal
|
|
@@ -82,19 +86,21 @@ export const bindConfig = (config: CouchConfigInput): BoundInstance => {
|
|
|
82
86
|
* @param config The CouchDB configuration
|
|
83
87
|
* @returns The bound function, possibly wrapped with retry logic
|
|
84
88
|
*/
|
|
85
|
-
export function getBoundWithRetry<
|
|
89
|
+
export function getBoundWithRetry<
|
|
90
|
+
TBound extends (...args: any[]) => Promise<any>,
|
|
91
|
+
>(
|
|
86
92
|
func: (config: CouchConfig, ...args: any[]) => Promise<any>,
|
|
87
|
-
config: CouchConfig
|
|
93
|
+
config: CouchConfig,
|
|
88
94
|
) {
|
|
89
|
-
const bound = func.bind(null, config)
|
|
95
|
+
const bound = func.bind(null, config);
|
|
90
96
|
if (config.bindWithRetry) {
|
|
91
97
|
return withRetry(bound, {
|
|
92
98
|
maxRetries: config.maxRetries ?? 10,
|
|
93
99
|
initialDelay: config.initialDelay ?? 1000,
|
|
94
|
-
backoffFactor: config.backoffFactor ?? 2
|
|
95
|
-
}) as TBound
|
|
100
|
+
backoffFactor: config.backoffFactor ?? 2,
|
|
101
|
+
}) as TBound;
|
|
96
102
|
} else {
|
|
97
|
-
return bound as TBound
|
|
103
|
+
return bound as TBound;
|
|
98
104
|
}
|
|
99
105
|
}
|
|
100
106
|
|
|
@@ -110,8 +116,8 @@ function doBind(config: CouchConfig): BoundMethods {
|
|
|
110
116
|
const retryOptions = {
|
|
111
117
|
maxRetries: config.maxRetries ?? 10,
|
|
112
118
|
initialDelay: config.initialDelay ?? 1000,
|
|
113
|
-
backoffFactor: config.backoffFactor ?? 2
|
|
114
|
-
}
|
|
119
|
+
backoffFactor: config.backoffFactor ?? 2,
|
|
120
|
+
};
|
|
115
121
|
|
|
116
122
|
// Create the object without the config property first
|
|
117
123
|
const result: BoundMethods = {
|
|
@@ -120,7 +126,10 @@ function doBind(config: CouchConfig): BoundMethods {
|
|
|
120
126
|
* To preserve the overloads we need dedicated Bound types
|
|
121
127
|
*/
|
|
122
128
|
bulkGet: getBoundWithRetry<BulkGetBound>(bulkGet, config),
|
|
123
|
-
bulkGetDictionary: getBoundWithRetry<BulkGetDictionaryBound>(
|
|
129
|
+
bulkGetDictionary: getBoundWithRetry<BulkGetDictionaryBound>(
|
|
130
|
+
bulkGetDictionary,
|
|
131
|
+
config,
|
|
132
|
+
),
|
|
124
133
|
get: getBoundWithRetry<GetBound>(get, config),
|
|
125
134
|
getAtRev: getBoundWithRetry<GetAtRevBound>(getAtRev, config),
|
|
126
135
|
query: getBoundWithRetry<QueryBound>(query, config),
|
|
@@ -141,6 +150,9 @@ function doBind(config: CouchConfig): BoundMethods {
|
|
|
141
150
|
getDBInfo: config.bindWithRetry
|
|
142
151
|
? withRetry(getDBInfo.bind(null, config), retryOptions)
|
|
143
152
|
: getDBInfo.bind(null, config),
|
|
153
|
+
headDB: config.bindWithRetry
|
|
154
|
+
? withRetry(headDB.bind(null, config), retryOptions)
|
|
155
|
+
: headDB.bind(null, config),
|
|
144
156
|
patch: config.bindWithRetry
|
|
145
157
|
? withRetry(patch.bind(null, config), retryOptions)
|
|
146
158
|
: patch.bind(null, config),
|
|
@@ -157,8 +169,8 @@ function doBind(config: CouchConfig): BoundMethods {
|
|
|
157
169
|
|
|
158
170
|
createLock: createLock.bind(null, config),
|
|
159
171
|
removeLock: removeLock.bind(null, config),
|
|
160
|
-
watchDocs: watchDocs.bind(null, config)
|
|
161
|
-
}
|
|
172
|
+
watchDocs: watchDocs.bind(null, config),
|
|
173
|
+
};
|
|
162
174
|
|
|
163
|
-
return result
|
|
175
|
+
return result;
|
|
164
176
|
}
|
package/impl/headDB.mts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { RetryableError, createResponseError } from "./utils/errors.mts";
|
|
2
|
+
import { createLogger } from "./utils/logger.mts";
|
|
3
|
+
import { CouchConfig, type CouchConfigInput } from "../schema/config.mts";
|
|
4
|
+
import { fetchCouchJson } from "./utils/fetch.mts";
|
|
5
|
+
import { isSuccessStatusCode } from "./utils/response.mts";
|
|
6
|
+
import { createCouchDbUrl } from "./utils/url.mts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Performs a health check against the target CouchDB database using `HEAD /{db}`.
|
|
10
|
+
*
|
|
11
|
+
* @see {@link https://docs.couchdb.org/en/stable/api/database/common.html#head--db | CouchDB API Documentation}
|
|
12
|
+
*
|
|
13
|
+
* @param configInput - The CouchDB configuration input.
|
|
14
|
+
* @returns A promise that resolves to `true` when the database responds successfully.
|
|
15
|
+
* @throws {RetryableError} `RetryableError` If a retryable error occurs during the request.
|
|
16
|
+
* @throws {OperationError} For other non-retryable response failures.
|
|
17
|
+
*/
|
|
18
|
+
export const headDB = async (configInput: CouchConfigInput): Promise<true> => {
|
|
19
|
+
const config = CouchConfig.parse(configInput);
|
|
20
|
+
const logger = createLogger(config);
|
|
21
|
+
const url = createCouchDbUrl(config.couch);
|
|
22
|
+
|
|
23
|
+
let resp;
|
|
24
|
+
try {
|
|
25
|
+
resp = await fetchCouchJson({
|
|
26
|
+
auth: config.auth,
|
|
27
|
+
method: "HEAD",
|
|
28
|
+
operation: "headDB",
|
|
29
|
+
request: config.request,
|
|
30
|
+
url,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (!isSuccessStatusCode("database", resp.statusCode)) {
|
|
34
|
+
logger.error(`Non-success status code received: ${resp.statusCode}`);
|
|
35
|
+
throw createResponseError({
|
|
36
|
+
body: resp.body,
|
|
37
|
+
defaultMessage: "Database health check failed",
|
|
38
|
+
operation: "headDB",
|
|
39
|
+
statusCode: resp.statusCode,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
} catch (err) {
|
|
43
|
+
logger.error("Error during head operation:", err);
|
|
44
|
+
RetryableError.handleNetworkError(err, "headDB");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!resp) {
|
|
48
|
+
logger.error("No response received from head request");
|
|
49
|
+
throw new RetryableError("Database health check failed", 503, {
|
|
50
|
+
operation: "headDB",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return true;
|
|
55
|
+
};
|