rqlite-js 2.6.0 → 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.
Files changed (98) hide show
  1. package/README.md +207 -29
  2. package/dist/index.cjs +904 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +329 -0
  5. package/dist/index.d.ts +329 -0
  6. package/dist/index.js +865 -0
  7. package/dist/index.js.map +1 -0
  8. package/docs/MIGRATION-v2-to-v3.md +71 -0
  9. package/package.json +56 -35
  10. package/src/api/backup/index.ts +69 -0
  11. package/src/api/backup-formats.ts +8 -0
  12. package/src/api/client/index.ts +97 -0
  13. package/src/api/consistency-levels.ts +10 -0
  14. package/src/api/data/index.ts +87 -0
  15. package/src/api/paths.ts +13 -0
  16. package/src/api/query-params.ts +15 -0
  17. package/src/api/results/data-result-error.ts +41 -0
  18. package/src/api/results/data-result.ts +91 -0
  19. package/src/api/results/data-results.ts +91 -0
  20. package/src/api/results/index.ts +7 -0
  21. package/src/api/results/result-fields.ts +14 -0
  22. package/src/api/status/index.ts +53 -0
  23. package/src/http-request/auth-keys.ts +14 -0
  24. package/src/http-request/content-types.ts +4 -0
  25. package/src/http-request/errors.ts +17 -0
  26. package/src/http-request/http-headers.ts +19 -0
  27. package/src/http-request/http-methods.ts +12 -0
  28. package/src/http-request/index.ts +676 -0
  29. package/src/http-request/retryable.ts +41 -0
  30. package/src/index.ts +38 -0
  31. package/lib/api/backup/index.js +0 -72
  32. package/lib/api/backup/index.js.map +0 -1
  33. package/lib/api/backup/test.unit.js +0 -45
  34. package/lib/api/backup/test.unit.js.map +0 -1
  35. package/lib/api/backup-formats.js +0 -16
  36. package/lib/api/backup-formats.js.map +0 -1
  37. package/lib/api/client/index.js +0 -105
  38. package/lib/api/client/index.js.map +0 -1
  39. package/lib/api/client/test.unit.js +0 -53
  40. package/lib/api/client/test.unit.js.map +0 -1
  41. package/lib/api/consistency-levels.js +0 -30
  42. package/lib/api/consistency-levels.js.map +0 -1
  43. package/lib/api/data/index.js +0 -108
  44. package/lib/api/data/index.js.map +0 -1
  45. package/lib/api/data/test.unit.js +0 -98
  46. package/lib/api/data/test.unit.js.map +0 -1
  47. package/lib/api/paths.js +0 -33
  48. package/lib/api/paths.js.map +0 -1
  49. package/lib/api/query-params.js +0 -43
  50. package/lib/api/query-params.js.map +0 -1
  51. package/lib/api/results/data-result-error.js +0 -62
  52. package/lib/api/results/data-result-error.js.map +0 -1
  53. package/lib/api/results/data-result.js +0 -141
  54. package/lib/api/results/data-result.js.map +0 -1
  55. package/lib/api/results/data-results.js +0 -142
  56. package/lib/api/results/data-results.js.map +0 -1
  57. package/lib/api/results/index.js +0 -8
  58. package/lib/api/results/index.js.map +0 -1
  59. package/lib/api/results/result-fields.js +0 -47
  60. package/lib/api/results/result-fields.js.map +0 -1
  61. package/lib/api/results/test.unit.js +0 -238
  62. package/lib/api/results/test.unit.js.map +0 -1
  63. package/lib/api/status/index.js +0 -67
  64. package/lib/api/status/index.js.map +0 -1
  65. package/lib/api/status/test.unit.js +0 -57
  66. package/lib/api/status/test.unit.js.map +0 -1
  67. package/lib/http-request/auth-keys.js +0 -15
  68. package/lib/http-request/auth-keys.js.map +0 -1
  69. package/lib/http-request/content-types.js +0 -5
  70. package/lib/http-request/content-types.js.map +0 -1
  71. package/lib/http-request/errors.js +0 -17
  72. package/lib/http-request/errors.js.map +0 -1
  73. package/lib/http-request/http-headers.js +0 -20
  74. package/lib/http-request/http-headers.js.map +0 -1
  75. package/lib/http-request/http-methods.js +0 -40
  76. package/lib/http-request/http-methods.js.map +0 -1
  77. package/lib/http-request/index.js +0 -764
  78. package/lib/http-request/index.js.map +0 -1
  79. package/lib/http-request/retryable.js +0 -42
  80. package/lib/http-request/retryable.js.map +0 -1
  81. package/lib/http-request/test.unit.js +0 -560
  82. package/lib/http-request/test.unit.js.map +0 -1
  83. package/lib/index.js +0 -39
  84. package/lib/index.js.map +0 -1
  85. package/lib/test/api-data-execute-nock.js +0 -142
  86. package/lib/test/api-data-execute-nock.js.map +0 -1
  87. package/lib/test/api-data-query-nock.js +0 -210
  88. package/lib/test/api-data-query-nock.js.map +0 -1
  89. package/lib/test/backups/backup-nock.js +0 -29
  90. package/lib/test/backups/backup-nock.js.map +0 -1
  91. package/lib/test/backups/restore-nock.js +0 -30
  92. package/lib/test/backups/restore-nock.js.map +0 -1
  93. package/lib/test/env.js +0 -15
  94. package/lib/test/env.js.map +0 -1
  95. package/lib/test/unit/index.unit.js +0 -27
  96. package/lib/test/unit/index.unit.js.map +0 -1
  97. package/lib/test.integration.js +0 -238
  98. package/lib/test.integration.js.map +0 -1
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