@midsummer-ai/sdk 0.1.0-next.1305 → 0.1.0-next.1723
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.
|
@@ -164,12 +164,15 @@ var Aimd = class {
|
|
|
164
164
|
return this.v;
|
|
165
165
|
}
|
|
166
166
|
};
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/database/client/connection.mjs
|
|
169
|
+
/** How long a socket's open may take (its upgrade answered), ms; a reconnect's try still unopened at each of HEDGE_AT
|
|
170
|
+
* (ms from its start) gets another socket beside it, at most three at once. */
|
|
171
|
+
const OPEN_WAIT = 1e4;
|
|
172
|
+
const HEDGE_AT = [100, 200];
|
|
173
|
+
/** A reconnect's waits between tries after the first (which is at once): RETRY_FIRST ms, doubling to RETRY_MAX. */
|
|
174
|
+
const RETRY_FIRST = 50;
|
|
175
|
+
const RETRY_MAX = 1e3;
|
|
173
176
|
const TAG_TTL = 864e5;
|
|
174
177
|
const groupOf = (id) => {
|
|
175
178
|
const t = String(id ?? ""), k = t.indexOf("/");
|
|
@@ -403,8 +406,8 @@ var Connection = class Connection {
|
|
|
403
406
|
...options,
|
|
404
407
|
shape
|
|
405
408
|
};
|
|
406
|
-
if (o.store === void 0 && typeof indexedDB !== "undefined" && typeof document !== "undefined") o.store = (await import("./store-
|
|
407
|
-
if (o.tabs !== false && typeof document !== "undefined") return (await import("./tabs-
|
|
409
|
+
if (o.store === void 0 && typeof indexedDB !== "undefined" && typeof document !== "undefined") o.store = (await import("./store-BA8K-bAR.mjs")).idbStore(new URL(url).host);
|
|
410
|
+
if (o.tabs !== false && typeof document !== "undefined") return (await import("./tabs-DEeMee7M.mjs")).ConnectionTabs.connect(url, o);
|
|
408
411
|
return Connection.open(url, o);
|
|
409
412
|
}
|
|
410
413
|
/** A Connection over one socket, of the shape given (default "browser"), no detection (tabs.mjs opens its own so). */
|
|
@@ -412,6 +415,10 @@ var Connection = class Connection {
|
|
|
412
415
|
const g = new Connection(url, options);
|
|
413
416
|
if (g.store) await g.store.attach(g);
|
|
414
417
|
if (options.functions) g.predictor = new (await (import("./predict-Drfm_8pO.mjs"))).Predictor(g, options.functions);
|
|
418
|
+
if (options.shape === "server" && options.http !== false) {
|
|
419
|
+
g.http = true;
|
|
420
|
+
return g;
|
|
421
|
+
}
|
|
415
422
|
await g.open();
|
|
416
423
|
if (!g.dir) await Promise.race([g.hello, new Promise((r) => setTimeout(r, HELLO_WAIT_MS))]);
|
|
417
424
|
return g;
|
|
@@ -589,7 +596,7 @@ var Connection = class Connection {
|
|
|
589
596
|
try {
|
|
590
597
|
ws?.close(1e3);
|
|
591
598
|
} catch {}
|
|
592
|
-
},
|
|
599
|
+
}, OPEN_WAIT);
|
|
593
600
|
const opened = (ws) => {
|
|
594
601
|
clearTimeout(timer);
|
|
595
602
|
if (this.closed) {
|
|
@@ -599,6 +606,13 @@ var Connection = class Connection {
|
|
|
599
606
|
ready();
|
|
600
607
|
return;
|
|
601
608
|
}
|
|
609
|
+
if (this.ws && this.ws !== ws && this.ws.readyState === 1) {
|
|
610
|
+
try {
|
|
611
|
+
ws.close(1e3);
|
|
612
|
+
} catch {}
|
|
613
|
+
ready();
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
602
616
|
this.ws = ws;
|
|
603
617
|
this.heard = this.answeredAt = this.openedAt = performance.now();
|
|
604
618
|
this.probe = 0;
|
|
@@ -648,22 +662,39 @@ var Connection = class Connection {
|
|
|
648
662
|
}
|
|
649
663
|
});
|
|
650
664
|
}
|
|
651
|
-
/** Opens a socket again until one opens (one loop at a time)
|
|
652
|
-
*
|
|
653
|
-
|
|
665
|
+
/** Opens a socket again until one opens (one loop at a time): the first try at once, then after 50 ms doubling to
|
|
666
|
+
* 1 s, each wait jittered (clients a dead server dropped together do not come back together). A try still unopened
|
|
667
|
+
* at 100 ms gets a second socket beside it and at 200 ms a third, and the first to open serves (the others are
|
|
668
|
+
* closed): a socket the network sent to a replica that is gone costs at most 100 ms, while a slow open (a wake, a
|
|
669
|
+
* long replay, a takeover waiting for a dead replica's lease) is never given up. */
|
|
670
|
+
async reconnect(delay = 0) {
|
|
654
671
|
if (this.reopening || this.closed) return;
|
|
655
672
|
this.reopening = true;
|
|
656
673
|
this.stats.reconnects += 1;
|
|
674
|
+
const opens = () => this.open().then(() => true, () => false);
|
|
675
|
+
const firstOf = (tries) => new Promise((done) => {
|
|
676
|
+
let n = 0;
|
|
677
|
+
for (const p of tries) p.then((ok) => {
|
|
678
|
+
if (ok) done(true);
|
|
679
|
+
else if (++n === tries.length) done(false);
|
|
680
|
+
});
|
|
681
|
+
});
|
|
657
682
|
try {
|
|
658
|
-
for (;;) {
|
|
659
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
683
|
+
for (let step = RETRY_FIRST;;) {
|
|
684
|
+
if (delay > 0) await new Promise((r) => setTimeout(r, delay));
|
|
660
685
|
if (this.closed || this.ws?.readyState === 1) return;
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
686
|
+
const tries = [opens()];
|
|
687
|
+
let opened = null;
|
|
688
|
+
for (let k = 0, at = 0; k <= HEDGE_AT.length; k += 1) {
|
|
689
|
+
const wait = k < HEDGE_AT.length ? new Promise((r) => setTimeout(() => r(null), HEDGE_AT[k] - at)) : new Promise(() => {});
|
|
690
|
+
opened = await Promise.race([firstOf(tries), wait]);
|
|
691
|
+
if (opened !== null) break;
|
|
692
|
+
at = HEDGE_AT[k];
|
|
693
|
+
tries.push(opens());
|
|
666
694
|
}
|
|
695
|
+
if (opened) return;
|
|
696
|
+
delay = step * (.5 + Math.random() / 2);
|
|
697
|
+
step = Math.min(RETRY_MAX, step * 2);
|
|
667
698
|
}
|
|
668
699
|
} finally {
|
|
669
700
|
this.reopening = false;
|
|
@@ -779,6 +810,7 @@ var Connection = class Connection {
|
|
|
779
810
|
* same tag, and its answer is still read here); "b" or "e": never waits for durable (server shapes), and its round
|
|
780
811
|
* trip tunes the conflation window. */
|
|
781
812
|
send(unit, op, keep = false, now = null, route = void 0, relax = null) {
|
|
813
|
+
if (this.http && !this.ws && !this.reopening && !this.closed) this.reconnect(0);
|
|
782
814
|
const i = op.i ?? this.next++;
|
|
783
815
|
const o = {
|
|
784
816
|
i,
|
|
@@ -1379,12 +1411,15 @@ var Connection = class Connection {
|
|
|
1379
1411
|
this.notifyRow(key);
|
|
1380
1412
|
}
|
|
1381
1413
|
const guess = optimistic || as != null ? null : this.predictor?.begin(name, args, route);
|
|
1382
|
-
const
|
|
1414
|
+
const op = {
|
|
1383
1415
|
k: "m",
|
|
1384
1416
|
f: name,
|
|
1385
1417
|
a: args,
|
|
1386
1418
|
t: tag ?? this.tag(),
|
|
1387
|
-
...asOf(as)
|
|
1419
|
+
...asOf(as)
|
|
1420
|
+
};
|
|
1421
|
+
const a = this.http && this.ws?.readyState !== 1 ? await this.overHttp(op) : await this.send(this.unitOf(route), {
|
|
1422
|
+
...op,
|
|
1388
1423
|
...this.urgent()
|
|
1389
1424
|
}, true, null, route);
|
|
1390
1425
|
guess?.end(a);
|
|
@@ -1395,6 +1430,68 @@ var Connection = class Connection {
|
|
|
1395
1430
|
if (refused(a)) throw coded(a);
|
|
1396
1431
|
return a;
|
|
1397
1432
|
}
|
|
1433
|
+
/** One query or mutation over HTTP (`POST <db>/call`, the token in its header), with no socket: its answer as a
|
|
1434
|
+
* socket's. A mutation's tag rides with it, so one sent again (after a failure the network gave no answer to)
|
|
1435
|
+
* applies once; a query, or a tagged mutation, is sent again on a 5xx or no answer, twice at most; a 401 renews the
|
|
1436
|
+
* token once. */
|
|
1437
|
+
async overHttp(op) {
|
|
1438
|
+
const body = JSON.stringify([{
|
|
1439
|
+
i: 1,
|
|
1440
|
+
...op
|
|
1441
|
+
}]), again = op.k === "q" || !!op.t;
|
|
1442
|
+
for (let tries = 0;; tries += 1) {
|
|
1443
|
+
let r;
|
|
1444
|
+
try {
|
|
1445
|
+
r = await fetchAt(`${this.base}/call`, {
|
|
1446
|
+
method: "POST",
|
|
1447
|
+
headers: {
|
|
1448
|
+
authorization: `Bearer ${this.token}`,
|
|
1449
|
+
"content-type": "application/json"
|
|
1450
|
+
},
|
|
1451
|
+
body
|
|
1452
|
+
});
|
|
1453
|
+
} catch (e) {
|
|
1454
|
+
if (again && tries < 2) continue;
|
|
1455
|
+
return {
|
|
1456
|
+
i: 1,
|
|
1457
|
+
ok: false,
|
|
1458
|
+
why: "unavailable",
|
|
1459
|
+
error: String(e?.message ?? e)
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
if (r.status === 401 && this.refresh && tries === 0) {
|
|
1463
|
+
await this.renew(false);
|
|
1464
|
+
continue;
|
|
1465
|
+
}
|
|
1466
|
+
const text = await r.text();
|
|
1467
|
+
if (!r.ok) {
|
|
1468
|
+
if (r.status >= 500 && again && tries < 2) continue;
|
|
1469
|
+
let j = {};
|
|
1470
|
+
try {
|
|
1471
|
+
j = JSON.parse(text);
|
|
1472
|
+
} catch {}
|
|
1473
|
+
return {
|
|
1474
|
+
i: 1,
|
|
1475
|
+
ok: false,
|
|
1476
|
+
why: r.status === 401 ? "denied" : j.error ?? "refused",
|
|
1477
|
+
error: j.why ?? text.slice(0, 200)
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
for (const line of text.split("\n")) try {
|
|
1481
|
+
const v = JSON.parse(line);
|
|
1482
|
+
if (Array.isArray(v)) {
|
|
1483
|
+
const a = v.find((x) => x?.i === 1);
|
|
1484
|
+
if (a) return a;
|
|
1485
|
+
}
|
|
1486
|
+
} catch {}
|
|
1487
|
+
return {
|
|
1488
|
+
i: 1,
|
|
1489
|
+
ok: false,
|
|
1490
|
+
why: "unavailable",
|
|
1491
|
+
error: "no answer in the call's reply"
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1398
1495
|
/** An ACTION of a the database app (it may do I/O; not a transaction): called as a mutation is, by reference or
|
|
1399
1496
|
* name, tagged (a resend after a reconnect is answered from its tag). Resolves its answer. */
|
|
1400
1497
|
async action(name, args, options = {}) {
|
|
@@ -1403,12 +1500,13 @@ var Connection = class Connection {
|
|
|
1403
1500
|
/** A query function's value, once, on unit `unit` (default "0"). */
|
|
1404
1501
|
async query(name, args, { unit = "0", as } = {}) {
|
|
1405
1502
|
name = fnName(name);
|
|
1406
|
-
const
|
|
1503
|
+
const op = {
|
|
1407
1504
|
k: "q",
|
|
1408
1505
|
f: name,
|
|
1409
1506
|
a: args,
|
|
1410
1507
|
...asOf(as)
|
|
1411
|
-
}
|
|
1508
|
+
};
|
|
1509
|
+
const a = this.http && this.ws?.readyState !== 1 ? await this.overHttp(op) : await this.send(String(unit), op);
|
|
1412
1510
|
if (!a.ok) throw coded(a);
|
|
1413
1511
|
return a.value;
|
|
1414
1512
|
}
|
package/database/client.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DatabaseError } from "./errors.mjs";
|
|
2
|
-
import { t as Connection } from "../connection-
|
|
2
|
+
import { t as Connection } from "../connection-BHjDaLYA.mjs";
|
|
3
3
|
//#region src/database/client.ts
|
|
4
4
|
/**
|
|
5
5
|
* `@midsummer-ai/sdk/database`: a Project's Database, from its app or its server.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midsummer-ai/sdk",
|
|
3
|
-
"version": "0.1.0-next.
|
|
3
|
+
"version": "0.1.0-next.1723",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Midsummer's handles for an app's code: its Database, Storage, Queues, Vector DB, AI, Email and Agents, with stand-ins for tests.",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as Connection } from "./connection-
|
|
1
|
+
import { t as Connection } from "./connection-BHjDaLYA.mjs";
|
|
2
2
|
//#region src/database/client/tabs.mjs
|
|
3
3
|
/** Argument index of the options that take a `tag` (a tab tags its own writes: the same tag after a handover). */
|
|
4
4
|
const TAGGED = {
|