stratus-db 0.2.1 → 0.3.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 +6 -7
- package/package.json +1 -1
- package/stratus-db.js +11 -51
package/README.md
CHANGED
|
@@ -59,20 +59,19 @@ Gateway's 403 Forbidden for a brand-new key, which works within a minute or two
|
|
|
59
59
|
- A database: `status(id)`, `transact(id, {tx, retract, delete, txId, match})`, `query(id, q)`,
|
|
60
60
|
`queryAll(id, q)` (async iterator), `pull(id, pks, {asOf})` (and `asOf: t` in a query: the database as it stood after t), `requestIndex(id, att)`,
|
|
61
61
|
`awaitIndex(id, att)`, `dropIndex(id, att)`, `history(id, {from, limit})`, `historyAll`,
|
|
62
|
-
`fork(id, {t, name})` (
|
|
62
|
+
`fork(id, {t, name})` (a new database, this one as of t), `excise(id, {rows,
|
|
63
63
|
attributes})` (erasure: rows and {pk, att} out of the state, indexes, history and forks),
|
|
64
64
|
`setHistoryRetention(id, days)` (history kept 30 to 36500 days, or `null` for forever).
|
|
65
|
-
- Keys (
|
|
65
|
+
- Keys (with a key reaching every database): `listKeys()`, `createKey({name, scope})`,
|
|
66
66
|
`revokeKey(keyId)`.
|
|
67
67
|
- Codec: `encode`, `decode` (typed-JSON text), `toJSONValue`, `fromJSONValue`.
|
|
68
68
|
|
|
69
|
-
Queries: docs/query.md ("JSON").
|
|
70
|
-
|
|
71
|
-
resolved through its catalog; `caller` for a local service).
|
|
69
|
+
Queries: docs/query.md ("JSON"). Requests go to `<url>/v1/dbs/<id>/...` with `Authorization:
|
|
70
|
+
Bearer <apiKey>`.
|
|
72
71
|
|
|
73
72
|
## Tests
|
|
74
73
|
|
|
75
74
|
`node --test test/ejson.test.js` (the codec: velojity's own output in
|
|
76
75
|
`clients/fixtures/velojity-ejson.json`, which stratus's client-fixtures-test keeps current), and
|
|
77
|
-
`scripts/test-js-client` from the repo root: the codec, then the client against the
|
|
78
|
-
|
|
76
|
+
`scripts/test-js-client` from the repo root: the codec, then the client against the hosted
|
|
77
|
+
product run locally.
|
package/package.json
CHANGED
package/stratus-db.js
CHANGED
|
@@ -11,14 +11,11 @@
|
|
|
11
11
|
// await s.transact(id, { tx: [{ "order/id": squuid(), "order/status": kw("status/new") }] });
|
|
12
12
|
// const { rows } = await s.query(id, { where: ["=", "order/status", kw("status/new")] });
|
|
13
13
|
//
|
|
14
|
-
// Addressing:
|
|
15
|
-
//
|
|
16
|
-
// internal service: the catalog at url, each database at the Function URL its catalog record
|
|
17
|
-
// names (looked up, cached for resolveMs, and again when one answers 403 or 404); for testing
|
|
18
|
-
// against a local service, opts.caller names the caller (x-stratus-caller).
|
|
14
|
+
// Addressing: the catalog at <url>/v1/dbs, a database at <url>/v1/dbs/<id>/..., the API key as
|
|
15
|
+
// "Authorization: Bearer <key>".
|
|
19
16
|
|
|
20
17
|
/** The client's version: package.json's, which release-js.yml checks against it. */
|
|
21
|
-
export const VERSION = "0.
|
|
18
|
+
export const VERSION = "0.3.0";
|
|
22
19
|
|
|
23
20
|
const USER_AGENT = `stratus-db-js/${VERSION}`;
|
|
24
21
|
const IN_NODE = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
@@ -542,28 +539,20 @@ const GATEWAY_FORBIDDEN = /^\s*\{\s*"message"\s*:\s*"Forbidden"\s*\}\s*$/;
|
|
|
542
539
|
export class Client {
|
|
543
540
|
/**
|
|
544
541
|
* @param {object} opts
|
|
545
|
-
* @param {string} opts.url the service's base URL
|
|
542
|
+
* @param {string} opts.url the service's base URL
|
|
546
543
|
* @param {string} [opts.apiKey] sent as "Authorization: Bearer <key>"
|
|
547
|
-
* @param {"hosted"|"function-urls"} [opts.addressing]
|
|
548
|
-
* @param {string} [opts.caller] x-stratus-caller, for a local service
|
|
549
544
|
* @param {number} [opts.retries] attempts after a 429 (default 10)
|
|
550
545
|
* @param {number} [opts.newKeyWaitMs] how long to keep retrying API Gateway's own 403 Forbidden, which a
|
|
551
546
|
* brand-new key gets for its first minute or so (default 120000; 0: never)
|
|
552
|
-
* @param {number} [opts.resolveMs] function-urls: how long a database's URL is cached (5 minutes)
|
|
553
547
|
* @param {typeof fetch} [opts.fetch] default globalThis.fetch
|
|
554
548
|
*/
|
|
555
549
|
constructor(opts) {
|
|
556
550
|
if (!opts?.url) throw new TypeError("connect needs { url }");
|
|
557
551
|
this.url = trimSlash(opts.url);
|
|
558
|
-
this.addressing = opts.addressing ?? "hosted";
|
|
559
|
-
if (!["hosted", "function-urls"].includes(this.addressing)) throw new TypeError(`addressing: ${this.addressing}`);
|
|
560
552
|
this.apiKey = opts.apiKey;
|
|
561
|
-
this.caller = opts.caller;
|
|
562
553
|
this.retries = opts.retries ?? 10;
|
|
563
554
|
this.newKeyWaitMs = opts.newKeyWaitMs ?? 120000;
|
|
564
|
-
this.resolveMs = opts.resolveMs ?? 300000;
|
|
565
555
|
this.fetch = opts.fetch ?? globalThis.fetch;
|
|
566
|
-
this.urls = { at: 0, byId: new Map() };
|
|
567
556
|
}
|
|
568
557
|
|
|
569
558
|
// -- requests
|
|
@@ -574,7 +563,6 @@ export class Client {
|
|
|
574
563
|
if (IN_NODE) headers["user-agent"] = USER_AGENT;
|
|
575
564
|
if (body !== undefined) headers["content-type"] = "application/json";
|
|
576
565
|
if (this.apiKey) headers.authorization = `Bearer ${this.apiKey}`;
|
|
577
|
-
if (this.caller) headers["x-stratus-caller"] = this.caller;
|
|
578
566
|
const started = Date.now();
|
|
579
567
|
for (let attempt = 0, forbidden = 0; ; attempt++) {
|
|
580
568
|
const r = await this.fetch(url, { method, headers, body: body === undefined ? undefined : encode(body) });
|
|
@@ -606,42 +594,14 @@ export class Client {
|
|
|
606
594
|
throw new StratusError(r.status, isObj ? b.error : null, isObj ? b.message : String(b ?? ""), b);
|
|
607
595
|
}
|
|
608
596
|
|
|
609
|
-
catalogUrl(path) {
|
|
610
|
-
return this.addressing === "hosted" ? `${this.url}/v1${path}` : `${this.url}${path}`;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
597
|
/** method path body to the catalog: { status, body }. */
|
|
614
598
|
callCatalog(method, path, body) {
|
|
615
|
-
return this.send(this.
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
async resolve() {
|
|
619
|
-
const recs = Client.ok(await this.callCatalog("GET", "/dbs"));
|
|
620
|
-
const byId = new Map();
|
|
621
|
-
for (const r of recs) if (r.url) byId.set(String(r.id), r.url);
|
|
622
|
-
this.urls = { at: Date.now(), byId };
|
|
623
|
-
return byId;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
async dbBase(id) {
|
|
627
|
-
const key = String(id);
|
|
628
|
-
if (this.addressing === "hosted") return `${this.url}/v1/dbs/${encodeURIComponent(key)}`;
|
|
629
|
-
const { at, byId } = this.urls;
|
|
630
|
-
if (byId.has(key) && Date.now() - at < this.resolveMs) return trimSlash(byId.get(key));
|
|
631
|
-
const u = (await this.resolve()).get(key);
|
|
632
|
-
return u === undefined ? null : trimSlash(u);
|
|
599
|
+
return this.send(`${this.url}/v1${path}`, method, body);
|
|
633
600
|
}
|
|
634
601
|
|
|
635
602
|
/** method path body to database id: { status, body }. */
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
if (base === null) return { status: 404, body: { error: "no-database", message: `No database ${id}` } };
|
|
639
|
-
const r = await this.send(base + path, method, body);
|
|
640
|
-
if (this.addressing === "function-urls" && (r.status === 403 || r.status === 404)) {
|
|
641
|
-
const again = (await this.resolve()).get(String(id));
|
|
642
|
-
if (again !== undefined && trimSlash(again) !== base) return this.send(trimSlash(again) + path, method, body);
|
|
643
|
-
}
|
|
644
|
-
return r;
|
|
603
|
+
callDb(id, method, path, body) {
|
|
604
|
+
return this.send(`${this.url}/v1/dbs/${encodeURIComponent(String(id))}${path}`, method, body);
|
|
645
605
|
}
|
|
646
606
|
|
|
647
607
|
// -- the catalog
|
|
@@ -676,7 +636,7 @@ export class Client {
|
|
|
676
636
|
return Client.ok(await this.callCatalog("DELETE", `/dbs/${encodeURIComponent(String(id))}`));
|
|
677
637
|
}
|
|
678
638
|
|
|
679
|
-
// -- keys (
|
|
639
|
+
// -- keys (an account's keys, managed with a key whose scope is every database)
|
|
680
640
|
|
|
681
641
|
/** The account's keys' records (no secrets). */
|
|
682
642
|
async listKeys() {
|
|
@@ -700,7 +660,7 @@ export class Client {
|
|
|
700
660
|
|
|
701
661
|
/** The database's state: { id, "primary-key", t, "checkpoint-t", indexes, "index-errors", status }. */
|
|
702
662
|
async status(id) {
|
|
703
|
-
return Client.ok(await this.callDb(id, "GET",
|
|
663
|
+
return Client.ok(await this.callDb(id, "GET", "/status"));
|
|
704
664
|
}
|
|
705
665
|
|
|
706
666
|
/**
|
|
@@ -767,7 +727,7 @@ export class Client {
|
|
|
767
727
|
}
|
|
768
728
|
|
|
769
729
|
/**
|
|
770
|
-
* Forks database id
|
|
730
|
+
* Forks database id: a new database, this one as of t (default its latest), recorded
|
|
771
731
|
* in both logs. The fork's record ({ id, "fork-of": { db, t }, ... }).
|
|
772
732
|
*/
|
|
773
733
|
async fork(id, { t, name } = {}) {
|
|
@@ -779,7 +739,7 @@ export class Client {
|
|
|
779
739
|
|
|
780
740
|
/**
|
|
781
741
|
* Excises rows (primary keys) and attributes ({ pk, att }) - erasure: gone from the current
|
|
782
|
-
* state, its indexes and history (and
|
|
742
|
+
* state, its indexes and history (and the database's forks): { t, rows, attributes }.
|
|
783
743
|
*/
|
|
784
744
|
async excise(id, { rows, attributes } = {}) {
|
|
785
745
|
const body = {};
|