stratus-db 0.2.0 → 0.2.1
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 +2 -1
- package/package.json +1 -1
- package/stratus-db.js +14 -2
package/README.md
CHANGED
|
@@ -51,7 +51,8 @@ of the intended type.
|
|
|
51
51
|
## API
|
|
52
52
|
|
|
53
53
|
All methods return promises; failures reject with `StratusError` (`status`, `error` - e.g.
|
|
54
|
-
`"invalid-query"`, `"conflict"` -, `message`). 429s are retried with backoff (`retries`)
|
|
54
|
+
`"invalid-query"`, `"conflict"` -, `message`). 429s are retried with backoff (`retries`), and so is API
|
|
55
|
+
Gateway's 403 Forbidden for a brand-new key, which works within a minute or two (`newKeyWaitMs`, 120000).
|
|
55
56
|
|
|
56
57
|
- Catalog: `listDbs({deleted})`, `dbByName(name)`, `dbRecord(id)`, `createDb({name, primaryKey})`,
|
|
57
58
|
`renameDb(id, name)`, `deleteDb(id)`.
|
package/package.json
CHANGED
package/stratus-db.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// against a local service, opts.caller names the caller (x-stratus-caller).
|
|
19
19
|
|
|
20
20
|
/** The client's version: package.json's, which release-js.yml checks against it. */
|
|
21
|
-
export const VERSION = "0.2.
|
|
21
|
+
export const VERSION = "0.2.1";
|
|
22
22
|
|
|
23
23
|
const USER_AGENT = `stratus-db-js/${VERSION}`;
|
|
24
24
|
const IN_NODE = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
@@ -537,6 +537,8 @@ export function connect(opts) {
|
|
|
537
537
|
return new Client(opts);
|
|
538
538
|
}
|
|
539
539
|
|
|
540
|
+
const GATEWAY_FORBIDDEN = /^\s*\{\s*"message"\s*:\s*"Forbidden"\s*\}\s*$/;
|
|
541
|
+
|
|
540
542
|
export class Client {
|
|
541
543
|
/**
|
|
542
544
|
* @param {object} opts
|
|
@@ -545,6 +547,8 @@ export class Client {
|
|
|
545
547
|
* @param {"hosted"|"function-urls"} [opts.addressing]
|
|
546
548
|
* @param {string} [opts.caller] x-stratus-caller, for a local service
|
|
547
549
|
* @param {number} [opts.retries] attempts after a 429 (default 10)
|
|
550
|
+
* @param {number} [opts.newKeyWaitMs] how long to keep retrying API Gateway's own 403 Forbidden, which a
|
|
551
|
+
* brand-new key gets for its first minute or so (default 120000; 0: never)
|
|
548
552
|
* @param {number} [opts.resolveMs] function-urls: how long a database's URL is cached (5 minutes)
|
|
549
553
|
* @param {typeof fetch} [opts.fetch] default globalThis.fetch
|
|
550
554
|
*/
|
|
@@ -556,6 +560,7 @@ export class Client {
|
|
|
556
560
|
this.apiKey = opts.apiKey;
|
|
557
561
|
this.caller = opts.caller;
|
|
558
562
|
this.retries = opts.retries ?? 10;
|
|
563
|
+
this.newKeyWaitMs = opts.newKeyWaitMs ?? 120000;
|
|
559
564
|
this.resolveMs = opts.resolveMs ?? 300000;
|
|
560
565
|
this.fetch = opts.fetch ?? globalThis.fetch;
|
|
561
566
|
this.urls = { at: 0, byId: new Map() };
|
|
@@ -570,7 +575,8 @@ export class Client {
|
|
|
570
575
|
if (body !== undefined) headers["content-type"] = "application/json";
|
|
571
576
|
if (this.apiKey) headers.authorization = `Bearer ${this.apiKey}`;
|
|
572
577
|
if (this.caller) headers["x-stratus-caller"] = this.caller;
|
|
573
|
-
|
|
578
|
+
const started = Date.now();
|
|
579
|
+
for (let attempt = 0, forbidden = 0; ; attempt++) {
|
|
574
580
|
const r = await this.fetch(url, { method, headers, body: body === undefined ? undefined : encode(body) });
|
|
575
581
|
const textBody = await r.text();
|
|
576
582
|
if (r.status === 429 && attempt < this.retries) {
|
|
@@ -578,6 +584,12 @@ export class Client {
|
|
|
578
584
|
await sleep(Math.random() * Math.min(5000, 50 * 2 ** attempt));
|
|
579
585
|
continue;
|
|
580
586
|
}
|
|
587
|
+
// API Gateway's own refusal of a key it hasn't learned yet (a new key, its first minute or so):
|
|
588
|
+
// {"message":"Forbidden"} - the service's own errors always carry "error", and aren't retried
|
|
589
|
+
if (r.status === 403 && GATEWAY_FORBIDDEN.test(textBody) && Date.now() - started < this.newKeyWaitMs) {
|
|
590
|
+
await sleep(Math.min(15000, 1000 * 2 ** forbidden++));
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
581
593
|
const ct = r.headers.get("content-type") ?? "";
|
|
582
594
|
let value = textBody;
|
|
583
595
|
if (ct.startsWith("application/json") && textBody !== "") {
|