rqlite-js 2.5.7 → 3.0.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 +354 -157
- package/dist/index.cjs +904 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +329 -0
- package/dist/index.d.ts +329 -0
- package/dist/index.js +865 -0
- package/dist/index.js.map +1 -0
- package/docs/MIGRATION-v2-to-v3.md +71 -0
- package/package.json +73 -42
- package/src/api/backup/index.ts +69 -0
- package/src/api/backup-formats.ts +8 -0
- package/src/api/client/index.ts +97 -0
- package/src/api/consistency-levels.ts +10 -0
- package/src/api/data/index.ts +87 -0
- package/src/api/paths.ts +13 -0
- package/src/api/query-params.ts +15 -0
- package/src/api/results/data-result-error.ts +41 -0
- package/src/api/results/data-result.ts +91 -0
- package/src/api/results/data-results.ts +91 -0
- package/src/api/results/index.ts +7 -0
- package/src/api/results/result-fields.ts +14 -0
- package/src/api/status/index.ts +53 -0
- package/src/http-request/auth-keys.ts +14 -0
- package/{es6/http-request/errors.js → src/http-request/errors.ts} +4 -3
- package/src/http-request/http-headers.ts +19 -0
- package/{es6/http-request/http-methods.js → src/http-request/http-methods.ts} +0 -27
- package/src/http-request/index.ts +676 -0
- package/{es6/http-request/retryable.js → src/http-request/retryable.ts} +1 -1
- package/src/index.ts +38 -0
- package/.eslintrc +0 -20
- package/.travis.yml +0 -24
- package/es6/api/backup/index.js +0 -71
- package/es6/api/client/index.js +0 -83
- package/es6/api/data/index.js +0 -129
- package/es6/api/results/data-result-error.js +0 -32
- package/es6/api/results/data-result.js +0 -134
- package/es6/api/results/data-results.js +0 -121
- package/es6/api/results/index.js +0 -7
- package/es6/api/status/index.js +0 -54
- package/es6/http-request/index.js +0 -743
- package/es6/index.js +0 -19
- package/jsconfig.json +0 -19
- package/lib/api/backup/index.js +0 -72
- package/lib/api/backup/index.js.map +0 -1
- package/lib/api/client/index.js +0 -84
- package/lib/api/client/index.js.map +0 -1
- package/lib/api/data/index.js +0 -130
- package/lib/api/data/index.js.map +0 -1
- package/lib/api/results/data-result-error.js +0 -33
- package/lib/api/results/data-result-error.js.map +0 -1
- package/lib/api/results/data-result.js +0 -135
- package/lib/api/results/data-result.js.map +0 -1
- package/lib/api/results/data-results.js +0 -122
- package/lib/api/results/data-results.js.map +0 -1
- package/lib/api/results/index.js +0 -8
- package/lib/api/results/index.js.map +0 -1
- package/lib/api/status/index.js +0 -55
- package/lib/api/status/index.js.map +0 -1
- package/lib/http-request/content-types.js +0 -5
- package/lib/http-request/content-types.js.map +0 -1
- package/lib/http-request/errors.js +0 -17
- package/lib/http-request/errors.js.map +0 -1
- package/lib/http-request/http-methods.js +0 -40
- package/lib/http-request/http-methods.js.map +0 -1
- package/lib/http-request/index.js +0 -744
- package/lib/http-request/index.js.map +0 -1
- package/lib/http-request/retryable.js +0 -42
- package/lib/http-request/retryable.js.map +0 -1
- package/lib/index.js +0 -20
- package/lib/index.js.map +0 -1
- /package/{es6/http-request/content-types.js → src/http-request/content-types.ts} +0 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
|
+
//#region rolldown:runtime
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
let node_stream = require("node:stream");
|
|
26
|
+
node_stream = __toESM(node_stream);
|
|
27
|
+
|
|
28
|
+
//#region src/http-request/http-methods.ts
|
|
29
|
+
/**
|
|
30
|
+
* Http methods
|
|
31
|
+
* @module http-request/http-methods
|
|
32
|
+
*/
|
|
33
|
+
const HTTP_METHOD_DELETE = "DELETE";
|
|
34
|
+
const HTTP_METHOD_GET = "GET";
|
|
35
|
+
const HTTP_METHOD_HEAD = "HEAD";
|
|
36
|
+
const HTTP_METHOD_OPTIONS = "OPTIONS";
|
|
37
|
+
const HTTP_METHOD_PATCH = "PATCH";
|
|
38
|
+
const HTTP_METHOD_POST = "POST";
|
|
39
|
+
const HTTP_METHOD_PUT = "PUT";
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/http-request/content-types.ts
|
|
43
|
+
const CONTENT_TYPE_TEXT_PLAIN = "text/plain";
|
|
44
|
+
const CONTENT_TYPE_APPLICATION_JSON = "application/json";
|
|
45
|
+
const CONTENT_TYPE_APPLICATION_OCTET_STREAM = "application/octet-stream";
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/http-request/errors.ts
|
|
49
|
+
/**
|
|
50
|
+
* Http request errors
|
|
51
|
+
* @module http-request/errors
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* Error when max redirect attempts have been reached
|
|
55
|
+
*/
|
|
56
|
+
var ERROR_HTTP_REQUEST_MAX_REDIRECTS = class extends Error {
|
|
57
|
+
code;
|
|
58
|
+
constructor(...args) {
|
|
59
|
+
super(...args);
|
|
60
|
+
this.name = this.constructor.name;
|
|
61
|
+
this.code = this.constructor.name;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/http-request/retryable.ts
|
|
67
|
+
/**
|
|
68
|
+
* Error codes which can be retried
|
|
69
|
+
*/
|
|
70
|
+
const RETRYABLE_ERROR_CODES = new Set([
|
|
71
|
+
"EADDRINUSE",
|
|
72
|
+
"EAI_AGAIN",
|
|
73
|
+
"ECONNREFUSED",
|
|
74
|
+
"ECONNRESET",
|
|
75
|
+
"ENETUNREACH",
|
|
76
|
+
"ENOTFOUND",
|
|
77
|
+
"EPIPE",
|
|
78
|
+
"ETIMEDOUT"
|
|
79
|
+
]);
|
|
80
|
+
/**
|
|
81
|
+
* HTTP status codes which can be retried
|
|
82
|
+
*/
|
|
83
|
+
const RETRYABLE_STATUS_CODES = new Set([
|
|
84
|
+
408,
|
|
85
|
+
413,
|
|
86
|
+
429,
|
|
87
|
+
500,
|
|
88
|
+
502,
|
|
89
|
+
503,
|
|
90
|
+
504,
|
|
91
|
+
521,
|
|
92
|
+
522,
|
|
93
|
+
524
|
|
94
|
+
]);
|
|
95
|
+
/**
|
|
96
|
+
* HTTP methods which can be retried
|
|
97
|
+
*/
|
|
98
|
+
const RETRYABLE_HTTP_METHODS = new Set([
|
|
99
|
+
HTTP_METHOD_DELETE,
|
|
100
|
+
HTTP_METHOD_GET,
|
|
101
|
+
HTTP_METHOD_HEAD,
|
|
102
|
+
HTTP_METHOD_OPTIONS,
|
|
103
|
+
HTTP_METHOD_PATCH,
|
|
104
|
+
HTTP_METHOD_POST,
|
|
105
|
+
HTTP_METHOD_PUT
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/http-request/auth-keys.ts
|
|
110
|
+
/**
|
|
111
|
+
* Authentication Map key constants
|
|
112
|
+
* @module http-request/auth-keys
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* Username key for authentication
|
|
116
|
+
*/
|
|
117
|
+
const AUTH_USERNAME = "username";
|
|
118
|
+
/**
|
|
119
|
+
* Password key for authentication
|
|
120
|
+
*/
|
|
121
|
+
const AUTH_PASSWORD = "password";
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/http-request/http-headers.ts
|
|
125
|
+
/**
|
|
126
|
+
* HTTP header name constants used by RQLite clients
|
|
127
|
+
* @module http-request/http-headers
|
|
128
|
+
*/
|
|
129
|
+
/**
|
|
130
|
+
* Accept request header
|
|
131
|
+
*/
|
|
132
|
+
const HTTP_HEADER_ACCEPT = "Accept";
|
|
133
|
+
/**
|
|
134
|
+
* Content-Type request header
|
|
135
|
+
*/
|
|
136
|
+
const HTTP_HEADER_CONTENT_TYPE = "Content-Type";
|
|
137
|
+
/**
|
|
138
|
+
* Location response header (redirects)
|
|
139
|
+
*/
|
|
140
|
+
const HTTP_HEADER_LOCATION = "Location";
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/http-request/index.ts
|
|
144
|
+
/**
|
|
145
|
+
* The default timeout value
|
|
146
|
+
*/
|
|
147
|
+
const DEAULT_TIMEOUT = 3e4;
|
|
148
|
+
/**
|
|
149
|
+
* Create default header for all HTTP requests
|
|
150
|
+
*/
|
|
151
|
+
function createDefaultHeaders(headers = {}) {
|
|
152
|
+
const accept = headers[HTTP_HEADER_ACCEPT] || CONTENT_TYPE_APPLICATION_JSON;
|
|
153
|
+
return {
|
|
154
|
+
...headers,
|
|
155
|
+
[HTTP_HEADER_ACCEPT]: accept
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Clean the request path remove / from the beginning
|
|
160
|
+
*/
|
|
161
|
+
function cleanPath(path) {
|
|
162
|
+
return String(path).replace(/^\//, "");
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Serialize query params similar to qs arrayFormat: 'brackets'
|
|
166
|
+
*/
|
|
167
|
+
function stringifyQuery(params = {}) {
|
|
168
|
+
const search = new URLSearchParams();
|
|
169
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
170
|
+
if (typeof value === "undefined") return;
|
|
171
|
+
if (Array.isArray(value)) {
|
|
172
|
+
value.forEach((entry) => {
|
|
173
|
+
search.append(`${key}[]`, String(entry));
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
search.append(key, String(value));
|
|
178
|
+
});
|
|
179
|
+
return search.toString();
|
|
180
|
+
}
|
|
181
|
+
function basicAuthHeader(username, password) {
|
|
182
|
+
return `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
|
|
183
|
+
}
|
|
184
|
+
function getErrorCode(error) {
|
|
185
|
+
if (!error || typeof error !== "object") return;
|
|
186
|
+
const err = error;
|
|
187
|
+
if (typeof err.code === "string") return err.code;
|
|
188
|
+
if (err.cause && typeof err.cause.code === "string") return err.cause.code;
|
|
189
|
+
if (err.name === "AbortError" || err.name === "TimeoutError") return "ETIMEDOUT";
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Returns the next wait interval, in milliseconds, using an exponential
|
|
193
|
+
* backoff algorithm.
|
|
194
|
+
*/
|
|
195
|
+
function getWaitTimeExponential(attempt = 0, base = 100, exponent = 2) {
|
|
196
|
+
if (attempt === 0) return 0;
|
|
197
|
+
return exponent ** attempt * base;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Generic HTTP Request class which all RQLiteJS clients should extend
|
|
201
|
+
*/
|
|
202
|
+
var HttpRequest = class {
|
|
203
|
+
activeHostIndex = 0;
|
|
204
|
+
activeHostRoundRobin = true;
|
|
205
|
+
absoluteUriPattern = /^https?:\/\//;
|
|
206
|
+
hosts = [];
|
|
207
|
+
httpAgent;
|
|
208
|
+
httpsAgent;
|
|
209
|
+
leaderHostIndex = 0;
|
|
210
|
+
retryableErrorCodes = RETRYABLE_ERROR_CODES;
|
|
211
|
+
retryableStatusCodes = RETRYABLE_STATUS_CODES;
|
|
212
|
+
retryableHttpMethods = RETRYABLE_HTTP_METHODS;
|
|
213
|
+
exponentailBackoffBase = 100;
|
|
214
|
+
exponentailBackoffExponent = 1.1;
|
|
215
|
+
authentication = /* @__PURE__ */ new Map();
|
|
216
|
+
constructor(hosts, options = {}) {
|
|
217
|
+
this.setHosts(hosts);
|
|
218
|
+
if (this.getTotalHosts() === 0) throw new Error("At least one host must be provided");
|
|
219
|
+
const { activeHostRoundRobin = true, httpAgent, httpsAgent, retryableErrorCodes, retryableStatusCodes, retryableHttpMethods, exponentailBackoffExponent, exponentailBackoffBase, authentication } = options;
|
|
220
|
+
if (typeof authentication === "object") {
|
|
221
|
+
const { username, password } = authentication;
|
|
222
|
+
if (username) this.authentication.set(AUTH_USERNAME, username);
|
|
223
|
+
if (password) this.authentication.set(AUTH_PASSWORD, password);
|
|
224
|
+
}
|
|
225
|
+
if (typeof activeHostRoundRobin !== "undefined") this.setActiveHostRoundRobin(activeHostRoundRobin);
|
|
226
|
+
if (typeof httpAgent !== "undefined") this.setHttpAgent(httpAgent);
|
|
227
|
+
if (typeof httpsAgent !== "undefined") this.setHttpsAgent(httpsAgent);
|
|
228
|
+
if (retryableErrorCodes instanceof Set || Array.isArray(retryableErrorCodes)) this.setRetryableErrorCodes(Array.isArray(retryableErrorCodes) ? new Set(retryableErrorCodes) : retryableErrorCodes);
|
|
229
|
+
if (retryableStatusCodes instanceof Set || Array.isArray(retryableStatusCodes)) this.setRetryableStatusCodes(Array.isArray(retryableStatusCodes) ? new Set(retryableStatusCodes) : retryableStatusCodes);
|
|
230
|
+
if (retryableHttpMethods instanceof Set || Array.isArray(retryableHttpMethods)) this.setRetryableHttpMethods(Array.isArray(retryableHttpMethods) ? new Set(retryableHttpMethods) : retryableHttpMethods);
|
|
231
|
+
if (Number.isFinite(exponentailBackoffBase)) this.setExponentailBackoffBase(exponentailBackoffBase);
|
|
232
|
+
if (Number.isFinite(exponentailBackoffExponent)) this.setExponentailBackoffExponent(exponentailBackoffExponent);
|
|
233
|
+
}
|
|
234
|
+
setAuthentication(authentication = {}) {
|
|
235
|
+
const { username, password } = authentication;
|
|
236
|
+
if (username) this.authentication.set(AUTH_USERNAME, username);
|
|
237
|
+
if (password) this.authentication.set(AUTH_PASSWORD, password);
|
|
238
|
+
}
|
|
239
|
+
setExponentailBackoffBase(exponentailBackoffBase) {
|
|
240
|
+
this.exponentailBackoffBase = exponentailBackoffBase;
|
|
241
|
+
}
|
|
242
|
+
getExponentailBackoffBase() {
|
|
243
|
+
return this.exponentailBackoffBase;
|
|
244
|
+
}
|
|
245
|
+
setExponentailBackoffExponent(exponentailBackoffExponent) {
|
|
246
|
+
this.exponentailBackoffExponent = exponentailBackoffExponent;
|
|
247
|
+
}
|
|
248
|
+
getExponentailBackoffExponent() {
|
|
249
|
+
return this.exponentailBackoffExponent;
|
|
250
|
+
}
|
|
251
|
+
setRetryableErrorCodes(retryableErrorCodes) {
|
|
252
|
+
this.retryableErrorCodes = retryableErrorCodes;
|
|
253
|
+
}
|
|
254
|
+
getRetryableErrorCodes() {
|
|
255
|
+
return this.retryableErrorCodes;
|
|
256
|
+
}
|
|
257
|
+
setRetryableStatusCodes(retryableStatusCodes) {
|
|
258
|
+
this.retryableStatusCodes = retryableStatusCodes;
|
|
259
|
+
}
|
|
260
|
+
getRetryableStatusCodes() {
|
|
261
|
+
return this.retryableStatusCodes;
|
|
262
|
+
}
|
|
263
|
+
setRetryableHttpMethods(retryableHttpMethods) {
|
|
264
|
+
this.retryableHttpMethods = retryableHttpMethods;
|
|
265
|
+
}
|
|
266
|
+
getRetryableHttpMethods() {
|
|
267
|
+
return this.retryableHttpMethods;
|
|
268
|
+
}
|
|
269
|
+
setHttpAgent(httpAgent) {
|
|
270
|
+
this.httpAgent = httpAgent;
|
|
271
|
+
}
|
|
272
|
+
getHttpAgent() {
|
|
273
|
+
return this.httpAgent;
|
|
274
|
+
}
|
|
275
|
+
setHttpsAgent(httpsAgent) {
|
|
276
|
+
this.httpsAgent = httpsAgent;
|
|
277
|
+
}
|
|
278
|
+
getHttpsAgent() {
|
|
279
|
+
return this.httpsAgent;
|
|
280
|
+
}
|
|
281
|
+
setHosts(hosts) {
|
|
282
|
+
this.hosts = (!Array.isArray(hosts) ? String(hosts).split(",") : hosts).reduce((acc, v) => {
|
|
283
|
+
const host = String(v).trim().replace(/\/$/, "");
|
|
284
|
+
if (!host) return acc;
|
|
285
|
+
return acc.concat(host);
|
|
286
|
+
}, []);
|
|
287
|
+
}
|
|
288
|
+
getHosts() {
|
|
289
|
+
return this.hosts;
|
|
290
|
+
}
|
|
291
|
+
findHostIndex(host) {
|
|
292
|
+
let parsedHostToFind;
|
|
293
|
+
try {
|
|
294
|
+
parsedHostToFind = new URL(host);
|
|
295
|
+
} catch {
|
|
296
|
+
return -1;
|
|
297
|
+
}
|
|
298
|
+
return this.getHosts().findIndex((v) => {
|
|
299
|
+
try {
|
|
300
|
+
const parsedHost = new URL(v);
|
|
301
|
+
return [
|
|
302
|
+
"hostname",
|
|
303
|
+
"protocol",
|
|
304
|
+
"port",
|
|
305
|
+
"pathname"
|
|
306
|
+
].every((field) => parsedHostToFind[field] === parsedHost[field]);
|
|
307
|
+
} catch {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
getActiveHost(useLeader) {
|
|
313
|
+
const activeHostIndex = useLeader ? this.getLeaderHostIndex() : this.getActiveHostIndex();
|
|
314
|
+
return this.getHosts()[activeHostIndex];
|
|
315
|
+
}
|
|
316
|
+
setActiveHostIndex(activeHostIndex) {
|
|
317
|
+
if (!Number.isFinite(activeHostIndex)) throw new Error("The activeHostIndex should be a finite number");
|
|
318
|
+
const totalHosts = this.getTotalHosts();
|
|
319
|
+
if (activeHostIndex < 0) this.activeHostIndex = 0;
|
|
320
|
+
else if (activeHostIndex >= totalHosts) this.activeHostIndex = totalHosts - 1;
|
|
321
|
+
else this.activeHostIndex = activeHostIndex;
|
|
322
|
+
return this.activeHostIndex;
|
|
323
|
+
}
|
|
324
|
+
getLeaderHostIndex() {
|
|
325
|
+
return this.leaderHostIndex;
|
|
326
|
+
}
|
|
327
|
+
setLeaderHostIndex(leaderHostIndex) {
|
|
328
|
+
if (!Number.isFinite(leaderHostIndex)) throw new Error("The leaderHostIndex should be a finite number");
|
|
329
|
+
const totalHosts = this.getTotalHosts();
|
|
330
|
+
if (leaderHostIndex < 0) this.leaderHostIndex = 0;
|
|
331
|
+
else if (leaderHostIndex > totalHosts) this.leaderHostIndex = totalHosts - 1;
|
|
332
|
+
else this.leaderHostIndex = leaderHostIndex;
|
|
333
|
+
return this.leaderHostIndex;
|
|
334
|
+
}
|
|
335
|
+
getActiveHostIndex() {
|
|
336
|
+
return this.activeHostIndex;
|
|
337
|
+
}
|
|
338
|
+
setActiveHostRoundRobin(activeHostRoundRobin) {
|
|
339
|
+
if (typeof activeHostRoundRobin !== "boolean") throw new Error("The activeHostRoundRobin argument must be boolean");
|
|
340
|
+
this.activeHostRoundRobin = activeHostRoundRobin;
|
|
341
|
+
}
|
|
342
|
+
getActiveHostRoundRobin() {
|
|
343
|
+
return this.activeHostRoundRobin;
|
|
344
|
+
}
|
|
345
|
+
getNextActiveHostIndex(activeHostIndex = this.getActiveHostIndex()) {
|
|
346
|
+
const totalHosts = this.getTotalHosts();
|
|
347
|
+
const nextIndex = activeHostIndex + 1;
|
|
348
|
+
if (totalHosts === nextIndex) return 0;
|
|
349
|
+
return nextIndex;
|
|
350
|
+
}
|
|
351
|
+
setNextActiveHostIndex() {
|
|
352
|
+
if (!this.getActiveHostRoundRobin()) return;
|
|
353
|
+
const totalHosts = this.getTotalHosts();
|
|
354
|
+
if (this.getActiveHostRoundRobin() && totalHosts <= 1) return;
|
|
355
|
+
this.setActiveHostIndex(this.getNextActiveHostIndex());
|
|
356
|
+
}
|
|
357
|
+
getTotalHosts() {
|
|
358
|
+
return this.getHosts().length;
|
|
359
|
+
}
|
|
360
|
+
uriIsAbsolute(uri) {
|
|
361
|
+
return this.absoluteUriPattern.test(uri);
|
|
362
|
+
}
|
|
363
|
+
requestIsRetryable(options = {}) {
|
|
364
|
+
const { statusCode, errorCode, httpMethod } = options;
|
|
365
|
+
if (!httpMethod || !this.getRetryableHttpMethods().has(httpMethod)) return false;
|
|
366
|
+
if (statusCode && this.getRetryableStatusCodes().has(statusCode)) return true;
|
|
367
|
+
if (errorCode && this.getRetryableErrorCodes().has(errorCode)) return true;
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Perform an HTTP request using the provided options
|
|
372
|
+
*/
|
|
373
|
+
async fetch(options = {}) {
|
|
374
|
+
const { body, headers = {}, httpMethod = HTTP_METHOD_GET, query, stream = false, timeout = DEAULT_TIMEOUT, useLeader = false, retries = this.getTotalHosts() * 3, maxRedirects = 10, attempt = 0, retryAttempt = 0, redirectAttempt = 0, attemptHostIndex, exponentailBackoffExponent = this.getExponentailBackoffExponent(), exponentailBackoffBase = this.getExponentailBackoffBase(), httpAgent = this.getHttpAgent(), httpsAgent = this.getHttpsAgent() } = options;
|
|
375
|
+
const activeHost = Number.isFinite(attemptHostIndex) ? this.getHosts()[attemptHostIndex] : this.getActiveHost(useLeader);
|
|
376
|
+
let { uri } = options;
|
|
377
|
+
if (!uri) throw new Error("The uri option is required");
|
|
378
|
+
uri = this.uriIsAbsolute(uri) ? uri : `${activeHost}/${cleanPath(uri)}`;
|
|
379
|
+
const requestHeaders = createDefaultHeaders(headers);
|
|
380
|
+
if (this.authentication.size) requestHeaders.Authorization = basicAuthHeader(this.authentication.get(AUTH_USERNAME) || "", this.authentication.get(AUTH_PASSWORD) || "");
|
|
381
|
+
const url = new URL(uri);
|
|
382
|
+
if (query && typeof query === "object") {
|
|
383
|
+
const serialized = stringifyQuery(query);
|
|
384
|
+
if (serialized) url.search = serialized;
|
|
385
|
+
}
|
|
386
|
+
const controller = new AbortController();
|
|
387
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
388
|
+
const fetchInit = {
|
|
389
|
+
method: httpMethod,
|
|
390
|
+
headers: requestHeaders,
|
|
391
|
+
redirect: "manual",
|
|
392
|
+
signal: controller.signal
|
|
393
|
+
};
|
|
394
|
+
const agent = url.protocol === "https:" ? httpsAgent : httpAgent;
|
|
395
|
+
if (agent && typeof agent.dispatch === "function") {
|
|
396
|
+
const init = fetchInit;
|
|
397
|
+
init.dispatcher = agent;
|
|
398
|
+
}
|
|
399
|
+
if (typeof body !== "undefined" && body !== null) if (Buffer.isBuffer(body) || typeof body === "string") fetchInit.body = body;
|
|
400
|
+
else {
|
|
401
|
+
fetchInit.body = JSON.stringify(body);
|
|
402
|
+
if (!requestHeaders[HTTP_HEADER_ACCEPT]) requestHeaders[HTTP_HEADER_ACCEPT] = CONTENT_TYPE_APPLICATION_JSON;
|
|
403
|
+
if (!(HTTP_HEADER_CONTENT_TYPE in requestHeaders) && !Object.keys(requestHeaders).some((k) => k.toLowerCase() === "content-type")) requestHeaders[HTTP_HEADER_CONTENT_TYPE] = CONTENT_TYPE_APPLICATION_JSON;
|
|
404
|
+
fetchInit.headers = requestHeaders;
|
|
405
|
+
}
|
|
406
|
+
const retryOrRedirect = async (responseStatus, responseHeaders, errorCode, error) => {
|
|
407
|
+
const retryable = this.requestIsRetryable({
|
|
408
|
+
statusCode: responseStatus,
|
|
409
|
+
errorCode,
|
|
410
|
+
httpMethod
|
|
411
|
+
});
|
|
412
|
+
let nextAttemptHostIndex = Number.isFinite(attemptHostIndex) ? attemptHostIndex : this.getActiveHostIndex();
|
|
413
|
+
nextAttemptHostIndex += 1;
|
|
414
|
+
if (nextAttemptHostIndex === this.getTotalHosts()) nextAttemptHostIndex = 0;
|
|
415
|
+
if (responseStatus === 301 || responseStatus === 302) {
|
|
416
|
+
if (redirectAttempt >= maxRedirects) throw new ERROR_HTTP_REQUEST_MAX_REDIRECTS(`The maximum number of redirects ${maxRedirects} has been reached`);
|
|
417
|
+
let location;
|
|
418
|
+
if (responseHeaders && typeof responseHeaders.get === "function") location = responseHeaders.get(HTTP_HEADER_LOCATION) || responseHeaders.get(HTTP_HEADER_LOCATION.toLowerCase()) || void 0;
|
|
419
|
+
else if (responseHeaders && typeof responseHeaders === "object") {
|
|
420
|
+
const headerMap = responseHeaders;
|
|
421
|
+
location = headerMap[HTTP_HEADER_LOCATION.toLowerCase()] || headerMap[HTTP_HEADER_LOCATION];
|
|
422
|
+
}
|
|
423
|
+
if (useLeader && location) {
|
|
424
|
+
const newLeaderHostIndex = this.findHostIndex(location);
|
|
425
|
+
if (newLeaderHostIndex > -1) this.setLeaderHostIndex(newLeaderHostIndex);
|
|
426
|
+
}
|
|
427
|
+
return this.fetch({
|
|
428
|
+
...options,
|
|
429
|
+
uri: location,
|
|
430
|
+
attempt: attempt + 1,
|
|
431
|
+
redirectAttempt: redirectAttempt + 1,
|
|
432
|
+
attemptHostIndex: nextAttemptHostIndex
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
if (retryable && retryAttempt < retries) {
|
|
436
|
+
const waitTime = getWaitTimeExponential(retryAttempt, exponentailBackoffBase, exponentailBackoffExponent);
|
|
437
|
+
await new Promise((resolve) => {
|
|
438
|
+
setTimeout(() => resolve(), waitTime);
|
|
439
|
+
});
|
|
440
|
+
return this.fetch({
|
|
441
|
+
...options,
|
|
442
|
+
attempt: attempt + 1,
|
|
443
|
+
retryAttempt: retryAttempt + 1,
|
|
444
|
+
attemptHostIndex: nextAttemptHostIndex
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
if (error instanceof Error) throw error;
|
|
448
|
+
const wrapped = new Error(error?.message || String(error));
|
|
449
|
+
Object.assign(wrapped, error);
|
|
450
|
+
throw wrapped;
|
|
451
|
+
};
|
|
452
|
+
try {
|
|
453
|
+
const response = await fetch(url, fetchInit);
|
|
454
|
+
clearTimeout(timeoutId);
|
|
455
|
+
if (response.status === 301 || response.status === 302) return retryOrRedirect(response.status, response.headers, void 0, /* @__PURE__ */ new Error(`Redirect ${response.status}`));
|
|
456
|
+
if (response.status >= 400) return retryOrRedirect(response.status, response.headers, void 0, Object.assign(/* @__PURE__ */ new Error(`HTTP ${response.status}`), { response: {
|
|
457
|
+
status: response.status,
|
|
458
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
459
|
+
} }));
|
|
460
|
+
if (stream) {
|
|
461
|
+
if (!response.body) return node_stream.Readable.from([]);
|
|
462
|
+
return node_stream.Readable.fromWeb(response.body);
|
|
463
|
+
}
|
|
464
|
+
const contentType = response.headers.get("content-type") || "";
|
|
465
|
+
let responseBody;
|
|
466
|
+
if (contentType.includes("application/json")) responseBody = await response.json();
|
|
467
|
+
else responseBody = await response.text();
|
|
468
|
+
return {
|
|
469
|
+
body: responseBody,
|
|
470
|
+
status: response.status
|
|
471
|
+
};
|
|
472
|
+
} catch (e) {
|
|
473
|
+
clearTimeout(timeoutId);
|
|
474
|
+
const errorCode = getErrorCode(e);
|
|
475
|
+
const err = e;
|
|
476
|
+
const responseStatus = err.response?.status;
|
|
477
|
+
const responseHeaders = err.response?.headers;
|
|
478
|
+
return retryOrRedirect(responseStatus, responseHeaders, errorCode, e);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async get(...args) {
|
|
482
|
+
const options = args[0] || {};
|
|
483
|
+
return this.fetch({
|
|
484
|
+
...options,
|
|
485
|
+
httpMethod: HTTP_METHOD_GET
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
async post(...args) {
|
|
489
|
+
const options = args[0] || {};
|
|
490
|
+
return this.fetch({
|
|
491
|
+
...options,
|
|
492
|
+
httpMethod: HTTP_METHOD_POST
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
//#endregion
|
|
498
|
+
//#region src/api/query-params.ts
|
|
499
|
+
/**
|
|
500
|
+
* RQLite HTTP query parameter name constants
|
|
501
|
+
* @module api/query-params
|
|
502
|
+
* @see https://rqlite.io/docs/api/api/
|
|
503
|
+
* @see https://rqlite.io/docs/api/read-consistency/
|
|
504
|
+
* @see https://rqlite.io/docs/guides/backup/
|
|
505
|
+
*/
|
|
506
|
+
const QUERY_PARAM_LEVEL = "level";
|
|
507
|
+
const QUERY_PARAM_PRETTY = "pretty";
|
|
508
|
+
const QUERY_PARAM_TIMINGS = "timings";
|
|
509
|
+
const QUERY_PARAM_ATOMIC = "atomic";
|
|
510
|
+
const QUERY_PARAM_TRANSACTION = "transaction";
|
|
511
|
+
const QUERY_PARAM_Q = "q";
|
|
512
|
+
const QUERY_PARAM_FMT = "fmt";
|
|
513
|
+
|
|
514
|
+
//#endregion
|
|
515
|
+
//#region src/api/client/index.ts
|
|
516
|
+
/**
|
|
517
|
+
* Create the base HTTP query options from RQLite API options
|
|
518
|
+
* @see https://rqlite.io/docs/api/api/
|
|
519
|
+
* @see module:api/query-params
|
|
520
|
+
*/
|
|
521
|
+
function createQuery(options = {}) {
|
|
522
|
+
const { level, pretty, timings, atomic, transaction } = options;
|
|
523
|
+
const query = {
|
|
524
|
+
[QUERY_PARAM_LEVEL]: level,
|
|
525
|
+
[QUERY_PARAM_PRETTY]: pretty,
|
|
526
|
+
[QUERY_PARAM_TIMINGS]: timings,
|
|
527
|
+
[QUERY_PARAM_ATOMIC]: atomic,
|
|
528
|
+
[QUERY_PARAM_TRANSACTION]: transaction
|
|
529
|
+
};
|
|
530
|
+
return Object.entries(query).reduce((acc, entry) => {
|
|
531
|
+
const [key, val] = entry;
|
|
532
|
+
if (typeof val !== "undefined") acc[key] = val;
|
|
533
|
+
return acc;
|
|
534
|
+
}, {});
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Base API client for RQLite which abstracts the HTTP calls from the user
|
|
538
|
+
*/
|
|
539
|
+
var ApiClient = class extends HttpRequest {
|
|
540
|
+
/**
|
|
541
|
+
* Perform a RQLite data API get request
|
|
542
|
+
* @see https://rqlite.io/docs/api/api/
|
|
543
|
+
*/
|
|
544
|
+
async get(...args) {
|
|
545
|
+
const path = args[0];
|
|
546
|
+
const sql = args[1];
|
|
547
|
+
const options = args[2] || {};
|
|
548
|
+
const { useLeader } = options;
|
|
549
|
+
if (!path) throw new Error("The path argument is required");
|
|
550
|
+
return super.get({
|
|
551
|
+
useLeader,
|
|
552
|
+
uri: path,
|
|
553
|
+
httpMethod: HTTP_METHOD_GET,
|
|
554
|
+
query: {
|
|
555
|
+
...createQuery(options),
|
|
556
|
+
[QUERY_PARAM_Q]: sql
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Perform a RQLite data API post request
|
|
562
|
+
* @see https://rqlite.io/docs/api/api/
|
|
563
|
+
*/
|
|
564
|
+
async post(...args) {
|
|
565
|
+
const path = args[0];
|
|
566
|
+
const sql = args[1];
|
|
567
|
+
const options = args[2] || {};
|
|
568
|
+
const { useLeader } = options;
|
|
569
|
+
if (!path) throw new Error("The path argument is required");
|
|
570
|
+
return super.post({
|
|
571
|
+
useLeader,
|
|
572
|
+
uri: path,
|
|
573
|
+
httpMethod: HTTP_METHOD_POST,
|
|
574
|
+
query: createQuery(options),
|
|
575
|
+
body: Array.isArray(sql) ? sql : [sql]
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/api/results/result-fields.ts
|
|
582
|
+
/**
|
|
583
|
+
* RQLite JSON response field name constants
|
|
584
|
+
* @module api/results/result-fields
|
|
585
|
+
* @see https://rqlite.io/docs/api/api/
|
|
586
|
+
*/
|
|
587
|
+
const RESULT_FIELD_TIME = "time";
|
|
588
|
+
const RESULT_FIELD_ROWS_AFFECTED = "rows_affected";
|
|
589
|
+
const RESULT_FIELD_LAST_INSERT_ID = "last_insert_id";
|
|
590
|
+
const RESULT_FIELD_COLUMNS = "columns";
|
|
591
|
+
const RESULT_FIELD_VALUES = "values";
|
|
592
|
+
const RESULT_FIELD_ERROR = "error";
|
|
593
|
+
const RESULT_FIELD_ERROR_V2 = "error_v2";
|
|
594
|
+
const RESULT_FIELD_RESULTS = "results";
|
|
595
|
+
|
|
596
|
+
//#endregion
|
|
597
|
+
//#region src/api/results/data-result.ts
|
|
598
|
+
/**
|
|
599
|
+
* A class that represents one data result from an RQLite query or execute API call
|
|
600
|
+
* @see https://rqlite.io/docs/api/api/
|
|
601
|
+
*/
|
|
602
|
+
var DataResult = class {
|
|
603
|
+
time = 0;
|
|
604
|
+
lastInsertId;
|
|
605
|
+
rowsAffected = 0;
|
|
606
|
+
data = {};
|
|
607
|
+
constructor(result, valuesIndex) {
|
|
608
|
+
if (typeof result !== "object" || result === null) throw new Error("The result argument is required to be an object");
|
|
609
|
+
if (typeof valuesIndex !== "undefined" && !Number.isFinite(valuesIndex)) throw new Error("The valuesIndex argument is required to be a finite number when provided");
|
|
610
|
+
this.time = parseFloat(String(result[RESULT_FIELD_TIME] || 0));
|
|
611
|
+
this.rowsAffected = parseInt(String(result[RESULT_FIELD_ROWS_AFFECTED] || 0), 10);
|
|
612
|
+
this.lastInsertId = result[RESULT_FIELD_LAST_INSERT_ID];
|
|
613
|
+
if (Number.isFinite(valuesIndex)) {
|
|
614
|
+
const columns = result[RESULT_FIELD_COLUMNS];
|
|
615
|
+
const resultValues = result[RESULT_FIELD_VALUES][valuesIndex];
|
|
616
|
+
if (resultValues) this.data = resultValues.reduce((acc, val, i) => {
|
|
617
|
+
const col = columns[i];
|
|
618
|
+
acc[col] = val;
|
|
619
|
+
return acc;
|
|
620
|
+
}, {});
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
get(property) {
|
|
624
|
+
return this.data[property];
|
|
625
|
+
}
|
|
626
|
+
getTime() {
|
|
627
|
+
return this.time;
|
|
628
|
+
}
|
|
629
|
+
getLastInsertId() {
|
|
630
|
+
return this.lastInsertId;
|
|
631
|
+
}
|
|
632
|
+
getRowsAffected() {
|
|
633
|
+
return this.rowsAffected;
|
|
634
|
+
}
|
|
635
|
+
toObject() {
|
|
636
|
+
return JSON.parse(JSON.stringify(this.data));
|
|
637
|
+
}
|
|
638
|
+
toArray() {
|
|
639
|
+
return Object.values(this.data);
|
|
640
|
+
}
|
|
641
|
+
toColumnsArray() {
|
|
642
|
+
return Object.keys(this.data);
|
|
643
|
+
}
|
|
644
|
+
toString() {
|
|
645
|
+
return JSON.stringify(this.data);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
//#endregion
|
|
650
|
+
//#region src/api/results/data-result-error.ts
|
|
651
|
+
/**
|
|
652
|
+
* A class that represents one data error result from an RQLite query or execute API call
|
|
653
|
+
* @see https://rqlite.io/docs/api/api/
|
|
654
|
+
*/
|
|
655
|
+
var DataResultError = class extends Error {
|
|
656
|
+
code;
|
|
657
|
+
errorV2;
|
|
658
|
+
constructor(message, errorV2) {
|
|
659
|
+
super(message);
|
|
660
|
+
this.name = this.constructor.name;
|
|
661
|
+
this.code = this.constructor.name;
|
|
662
|
+
if (errorV2) this.errorV2 = errorV2;
|
|
663
|
+
}
|
|
664
|
+
getErrorV2() {
|
|
665
|
+
return this.errorV2;
|
|
666
|
+
}
|
|
667
|
+
toObject() {
|
|
668
|
+
const obj = { [RESULT_FIELD_ERROR]: this.message };
|
|
669
|
+
if (this.errorV2) obj[RESULT_FIELD_ERROR_V2] = this.errorV2;
|
|
670
|
+
return obj;
|
|
671
|
+
}
|
|
672
|
+
toString() {
|
|
673
|
+
return JSON.stringify(this.toObject());
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
//#endregion
|
|
678
|
+
//#region src/api/results/data-results.ts
|
|
679
|
+
/**
|
|
680
|
+
* A class to manage a list of results from an RQLite query or execute API response
|
|
681
|
+
* @see https://rqlite.io/docs/api/api/
|
|
682
|
+
*/
|
|
683
|
+
var DataResults = class {
|
|
684
|
+
time = 0;
|
|
685
|
+
results = [];
|
|
686
|
+
constructor(data) {
|
|
687
|
+
this.setApiData(data);
|
|
688
|
+
}
|
|
689
|
+
setApiData(data) {
|
|
690
|
+
if (typeof data !== "object" || data === null) throw new Error("The data argument is required to be an object");
|
|
691
|
+
if (!data[RESULT_FIELD_RESULTS]) throw new Error("The data object is required to have a results property");
|
|
692
|
+
this.time = parseFloat(String(data[RESULT_FIELD_TIME] || 0));
|
|
693
|
+
this.results = (data[RESULT_FIELD_RESULTS] || []).reduce((acc, result) => {
|
|
694
|
+
if (typeof result === "object" && result !== null && result[RESULT_FIELD_ERROR]) return acc.concat(new DataResultError(String(result[RESULT_FIELD_ERROR]), result[RESULT_FIELD_ERROR_V2]));
|
|
695
|
+
const vals = result[RESULT_FIELD_VALUES];
|
|
696
|
+
if (!vals) return acc.concat(new DataResult(result));
|
|
697
|
+
const dataResults = vals.map((_v, valuesIndex) => new DataResult(result, valuesIndex));
|
|
698
|
+
return acc.concat(dataResults);
|
|
699
|
+
}, []);
|
|
700
|
+
}
|
|
701
|
+
hasError() {
|
|
702
|
+
return !!this.getFirstError();
|
|
703
|
+
}
|
|
704
|
+
getFirstError() {
|
|
705
|
+
return this.results.find((v) => v instanceof DataResultError);
|
|
706
|
+
}
|
|
707
|
+
getTime() {
|
|
708
|
+
return this.time;
|
|
709
|
+
}
|
|
710
|
+
get(index) {
|
|
711
|
+
return this.results[index];
|
|
712
|
+
}
|
|
713
|
+
getResults() {
|
|
714
|
+
return this.results;
|
|
715
|
+
}
|
|
716
|
+
toArray() {
|
|
717
|
+
return this.results.map((result) => result.toObject());
|
|
718
|
+
}
|
|
719
|
+
toString() {
|
|
720
|
+
const list = this.results.map((result) => result.toObject());
|
|
721
|
+
return JSON.stringify(list);
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
//#endregion
|
|
726
|
+
//#region src/api/paths.ts
|
|
727
|
+
/**
|
|
728
|
+
* RQLite HTTP API path constants
|
|
729
|
+
* @module api/paths
|
|
730
|
+
* @see https://rqlite.io/docs/api/api/
|
|
731
|
+
* @see https://rqlite.io/docs/guides/backup/
|
|
732
|
+
* @see https://rqlite.io/docs/guides/monitoring-rqlite/
|
|
733
|
+
*/
|
|
734
|
+
const PATH_QUERY = "/db/query";
|
|
735
|
+
const PATH_EXECUTE = "/db/execute";
|
|
736
|
+
const PATH_LOAD = "/db/load";
|
|
737
|
+
const PATH_BACKUP = "/db/backup";
|
|
738
|
+
const PATH_STATUS = "/status";
|
|
739
|
+
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/api/consistency-levels.ts
|
|
742
|
+
/**
|
|
743
|
+
* RQLite read consistency level constants
|
|
744
|
+
* @module api/consistency-levels
|
|
745
|
+
* @see https://rqlite.io/docs/api/read-consistency/
|
|
746
|
+
*/
|
|
747
|
+
const CONSISTENCY_LEVEL_NONE = "none";
|
|
748
|
+
const CONSISTENCY_LEVEL_STRONG = "strong";
|
|
749
|
+
const CONSISTENCY_LEVEL_LINEARIZABLE = "linearizable";
|
|
750
|
+
const CONSISTENCY_LEVEL_WEAK = "weak";
|
|
751
|
+
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region src/api/data/index.ts
|
|
754
|
+
function handleResponse(response, options = {}) {
|
|
755
|
+
const { raw } = options;
|
|
756
|
+
const { body } = response;
|
|
757
|
+
if (raw) return response;
|
|
758
|
+
return new DataResults(body);
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Data api client to perform RQLite queries
|
|
762
|
+
*/
|
|
763
|
+
var DataApiClient = class extends ApiClient {
|
|
764
|
+
/**
|
|
765
|
+
* Send an RQLite query API request to the RQLite server
|
|
766
|
+
* @see https://rqlite.io/docs/api/api/
|
|
767
|
+
* @see https://rqlite.io/docs/api/read-consistency/
|
|
768
|
+
*/
|
|
769
|
+
async query(sql, options = {}) {
|
|
770
|
+
const { level } = options;
|
|
771
|
+
let { useLeader } = options;
|
|
772
|
+
if (level !== CONSISTENCY_LEVEL_NONE) useLeader = true;
|
|
773
|
+
let response;
|
|
774
|
+
if (Array.isArray(sql)) response = await super.post(PATH_QUERY, sql, {
|
|
775
|
+
...options,
|
|
776
|
+
useLeader
|
|
777
|
+
});
|
|
778
|
+
else response = await super.get(PATH_QUERY, sql, {
|
|
779
|
+
...options,
|
|
780
|
+
useLeader
|
|
781
|
+
});
|
|
782
|
+
if (!useLeader) this.setNextActiveHostIndex();
|
|
783
|
+
return handleResponse(response, options);
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Send an RQLite execute API request to the RQLite server
|
|
787
|
+
* @see https://rqlite.io/docs/api/api/
|
|
788
|
+
*/
|
|
789
|
+
async execute(sql, options = {}) {
|
|
790
|
+
return handleResponse(await super.post(PATH_EXECUTE, sql, options), options);
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
//#endregion
|
|
795
|
+
//#region src/api/backup-formats.ts
|
|
796
|
+
/**
|
|
797
|
+
* RQLite backup data format constants
|
|
798
|
+
* @module api/backup-formats
|
|
799
|
+
* @see https://rqlite.io/docs/guides/backup/
|
|
800
|
+
*/
|
|
801
|
+
const BACKUP_DATA_FORMAT_SQL = "sql";
|
|
802
|
+
const BACKUP_DATA_FORMAT_DUMP = "dump";
|
|
803
|
+
|
|
804
|
+
//#endregion
|
|
805
|
+
//#region src/api/backup/index.ts
|
|
806
|
+
/**
|
|
807
|
+
* Backup api client to perform RQLite back up and load operations
|
|
808
|
+
*/
|
|
809
|
+
var BackupApiClient = class extends HttpRequest {
|
|
810
|
+
/**
|
|
811
|
+
* Perform a SQL dump backup from the RQLite server
|
|
812
|
+
* @see https://rqlite.io/docs/guides/backup/
|
|
813
|
+
* @see module:api/backup-formats
|
|
814
|
+
*/
|
|
815
|
+
async backup(format = BACKUP_DATA_FORMAT_DUMP) {
|
|
816
|
+
return super.get({
|
|
817
|
+
headers: { [HTTP_HEADER_ACCEPT]: CONTENT_TYPE_APPLICATION_OCTET_STREAM },
|
|
818
|
+
query: { [QUERY_PARAM_FMT]: format === BACKUP_DATA_FORMAT_SQL ? format : void 0 },
|
|
819
|
+
json: false,
|
|
820
|
+
stream: true,
|
|
821
|
+
uri: PATH_BACKUP,
|
|
822
|
+
useLeader: true
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Perform a SQL restore by sending data the RQLite server
|
|
827
|
+
* @see https://rqlite.io/docs/guides/backup/
|
|
828
|
+
* @see module:api/backup-formats
|
|
829
|
+
*/
|
|
830
|
+
async load(data, format = BACKUP_DATA_FORMAT_SQL) {
|
|
831
|
+
return super.post({
|
|
832
|
+
body: data,
|
|
833
|
+
headers: { [HTTP_HEADER_CONTENT_TYPE]: format === BACKUP_DATA_FORMAT_SQL ? CONTENT_TYPE_TEXT_PLAIN : CONTENT_TYPE_APPLICATION_OCTET_STREAM },
|
|
834
|
+
json: false,
|
|
835
|
+
stream: true,
|
|
836
|
+
uri: PATH_LOAD,
|
|
837
|
+
useLeader: true
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
//#endregion
|
|
843
|
+
//#region src/api/status/index.ts
|
|
844
|
+
/**
|
|
845
|
+
* Data api client to perform RQLite status queries
|
|
846
|
+
*/
|
|
847
|
+
var StatusApiClient = class extends ApiClient {
|
|
848
|
+
/**
|
|
849
|
+
* Get the RQLite server status which defaults to the leader host
|
|
850
|
+
* @see https://rqlite.io/docs/guides/monitoring-rqlite/
|
|
851
|
+
*/
|
|
852
|
+
async status(options = {}) {
|
|
853
|
+
return this.fetch({
|
|
854
|
+
...options,
|
|
855
|
+
uri: PATH_STATUS,
|
|
856
|
+
httpMethod: HTTP_METHOD_GET,
|
|
857
|
+
useLeader: typeof options.useLeader === "boolean" ? options.useLeader : true
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Get the RQLite server status for all hosts
|
|
862
|
+
* @see https://rqlite.io/docs/guides/monitoring-rqlite/
|
|
863
|
+
*/
|
|
864
|
+
async statusAllHosts(options = {}) {
|
|
865
|
+
const hosts = this.getHosts();
|
|
866
|
+
const promises = hosts.map(async (_host, attemptHostIndex) => {
|
|
867
|
+
return {
|
|
868
|
+
response: await this.status({
|
|
869
|
+
...options,
|
|
870
|
+
useLeader: false,
|
|
871
|
+
attemptHostIndex
|
|
872
|
+
}),
|
|
873
|
+
host: hosts[attemptHostIndex]
|
|
874
|
+
};
|
|
875
|
+
});
|
|
876
|
+
return Promise.all(promises);
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
//#endregion
|
|
881
|
+
//#region src/index.ts
|
|
882
|
+
var src_default = {
|
|
883
|
+
DataApiClient,
|
|
884
|
+
BackupApiClient,
|
|
885
|
+
StatusApiClient
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
//#endregion
|
|
889
|
+
exports.BACKUP_DATA_FORMAT_DUMP = BACKUP_DATA_FORMAT_DUMP;
|
|
890
|
+
exports.BACKUP_DATA_FORMAT_SQL = BACKUP_DATA_FORMAT_SQL;
|
|
891
|
+
exports.BackupApiClient = BackupApiClient;
|
|
892
|
+
exports.CONSISTENCY_LEVEL_LINEARIZABLE = CONSISTENCY_LEVEL_LINEARIZABLE;
|
|
893
|
+
exports.CONSISTENCY_LEVEL_NONE = CONSISTENCY_LEVEL_NONE;
|
|
894
|
+
exports.CONSISTENCY_LEVEL_STRONG = CONSISTENCY_LEVEL_STRONG;
|
|
895
|
+
exports.CONSISTENCY_LEVEL_WEAK = CONSISTENCY_LEVEL_WEAK;
|
|
896
|
+
exports.DataApiClient = DataApiClient;
|
|
897
|
+
exports.PATH_BACKUP = PATH_BACKUP;
|
|
898
|
+
exports.PATH_EXECUTE = PATH_EXECUTE;
|
|
899
|
+
exports.PATH_LOAD = PATH_LOAD;
|
|
900
|
+
exports.PATH_QUERY = PATH_QUERY;
|
|
901
|
+
exports.PATH_STATUS = PATH_STATUS;
|
|
902
|
+
exports.StatusApiClient = StatusApiClient;
|
|
903
|
+
exports.default = src_default;
|
|
904
|
+
//# sourceMappingURL=index.cjs.map
|