@promigence/sdk 0.1.0-beta.13 → 0.1.0-beta.14
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/dist/cjs/core.d.ts +1 -1
- package/dist/cjs/core.js +1 -1
- package/dist/cjs/http2.d.ts +1 -0
- package/dist/cjs/http2.js +194 -22
- package/dist/cjs/trace.d.ts +2 -0
- package/dist/cjs/trace.js +17 -0
- package/dist/cjs/transport-worker.js +72 -12
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/http2.d.ts +1 -0
- package/dist/http2.js +195 -23
- package/dist/trace.d.ts +2 -0
- package/dist/trace.js +16 -0
- package/dist/transport-worker.js +72 -12
- package/package.json +1 -1
package/dist/cjs/core.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Promigence SDK — constants, error hierarchy and shared types. */
|
|
2
|
-
export declare const VERSION = "0.1.0-beta.
|
|
2
|
+
export declare const VERSION = "0.1.0-beta.14";
|
|
3
3
|
export declare const DOCS_URL = "https://promigence.ai";
|
|
4
4
|
/** The error reference; each code is an anchor on it (the API's `docs` link has the same form). */
|
|
5
5
|
export declare const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
package/dist/cjs/core.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.fixBeyondMessage = fixBeyondMessage;
|
|
|
7
7
|
exports.previewsOff = previewsOff;
|
|
8
8
|
exports.exitForCode = exitForCode;
|
|
9
9
|
const hint_js_1 = require("./hint.js");
|
|
10
|
-
exports.VERSION = "0.1.0-beta.
|
|
10
|
+
exports.VERSION = "0.1.0-beta.14";
|
|
11
11
|
exports.DOCS_URL = "https://promigence.ai";
|
|
12
12
|
exports.ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
|
13
13
|
exports.DEFAULT_API_URL = "https://api.promigence.ai/v1";
|
package/dist/cjs/http2.d.ts
CHANGED
|
@@ -189,6 +189,7 @@ export declare const __http2Internals: {
|
|
|
189
189
|
closedReason: string;
|
|
190
190
|
messages: number;
|
|
191
191
|
posts: number;
|
|
192
|
+
startedAt: number;
|
|
192
193
|
} | undefined;
|
|
193
194
|
/** The transport thread's number of connections and spacing between new requests on one, for an environment (`threadKnobs`). */
|
|
194
195
|
threadKnobs(env: NodeJS.ProcessEnv): {
|
package/dist/cjs/http2.js
CHANGED
|
@@ -21,6 +21,7 @@ const MAX_SESSIONS = 4;
|
|
|
21
21
|
const FIRST_SESSIONS = 2;
|
|
22
22
|
const BURST_MIN = 8;
|
|
23
23
|
const HOLD_MS = 10;
|
|
24
|
+
const STANDBY_AFTER_MS = 300;
|
|
24
25
|
const LOAD_FIRST_USE_MS = 120_000;
|
|
25
26
|
const CONNECT_TIMEOUT_MS = 10_000;
|
|
26
27
|
const IDLE_CLOSE_MS = 10_000;
|
|
@@ -572,6 +573,7 @@ exports.tuning = {
|
|
|
572
573
|
holdMs: HOLD_MS,
|
|
573
574
|
burstMin: BURST_MIN,
|
|
574
575
|
loadFirstUseMs: LOAD_FIRST_USE_MS,
|
|
576
|
+
standbyAfterMs: STANDBY_AFTER_MS,
|
|
575
577
|
earlyPlatform: undefined,
|
|
576
578
|
earlyServers: undefined,
|
|
577
579
|
earlyHostsFile: undefined,
|
|
@@ -855,7 +857,9 @@ function apiLookup(asked, observe, ipv4First = v4First(), earlyOk = true) {
|
|
|
855
857
|
const earlies = new Map();
|
|
856
858
|
const MAX_EARLIES = 4;
|
|
857
859
|
const EARLY_MAX_AGE_MS = 60_000;
|
|
858
|
-
const
|
|
860
|
+
const LOOKUP_FIRST_MS = 150;
|
|
861
|
+
const LOOKUP_TRIES = 3;
|
|
862
|
+
const LOOKUP_AT_MS = [0, 100, 250];
|
|
859
863
|
const DNS_AT_LOAD = (() => {
|
|
860
864
|
try {
|
|
861
865
|
return nodeDns();
|
|
@@ -876,8 +880,8 @@ function lookupReplaced() {
|
|
|
876
880
|
const EARLY_WORKER = `
|
|
877
881
|
const { workerData } = require("node:worker_threads");
|
|
878
882
|
const { isIPv4 } = require("node:net");
|
|
879
|
-
const { port, host, timeoutMs, servers, hostsFile } = workerData;
|
|
880
|
-
const done = (m) => { try { port.postMessage(m); } catch {} try { port.close(); } catch {} };
|
|
883
|
+
const { port, host, timeoutMs, tries, atMs, servers, hostsFile } = workerData;
|
|
884
|
+
const done = (m) => { m.hr = Number(process.hrtime.bigint()) / 1e6; try { port.postMessage(m); } catch {} try { port.close(); } catch {} };
|
|
881
885
|
try {
|
|
882
886
|
let named = false;
|
|
883
887
|
const v4 = [];
|
|
@@ -891,12 +895,28 @@ try {
|
|
|
891
895
|
} catch {}
|
|
892
896
|
if (named) done(v4.length > 0 ? { host, addrs: v4, ttl: 60, at: Date.now() } : { host, none: "hosts" });
|
|
893
897
|
else {
|
|
894
|
-
const
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
});
|
|
898
|
+
const { Resolver } = require("node:dns");
|
|
899
|
+
const rs = [];
|
|
900
|
+
const timers = [];
|
|
901
|
+
let fin = false, asked = 0, failed = 0;
|
|
902
|
+
const finish = (m) => { if (fin) return; fin = true; for (const t of timers) clearTimeout(t); for (const r of rs) { try { r.cancel(); } catch {} } done(m); };
|
|
903
|
+
const gaveUp = (why) => { if (++failed < asked) return; if (asked < atMs.length) ask(); else finish({ host, none: why }); };
|
|
904
|
+
const ask = () => {
|
|
905
|
+
if (fin || asked >= atMs.length) return;
|
|
906
|
+
const k = asked++;
|
|
907
|
+
try {
|
|
908
|
+
const r = new Resolver({ timeout: timeoutMs, tries });
|
|
909
|
+
const list = Array.isArray(servers) && servers.length > 0 ? servers : r.getServers();
|
|
910
|
+
if (list.length > 0) r.setServers(list.map((_, i) => list[(i + k) % list.length]));
|
|
911
|
+
rs.push(r);
|
|
912
|
+
r.resolve4(host, { ttl: true }, (err, recs) => {
|
|
913
|
+
if (fin) return;
|
|
914
|
+
if (!err && Array.isArray(recs) && recs.length > 0) return finish({ host, addrs: recs.map((x) => x.address), ttl: Math.min(...recs.map((x) => x.ttl)), at: Date.now() });
|
|
915
|
+
gaveUp((err && err.code) || "empty");
|
|
916
|
+
});
|
|
917
|
+
} catch { gaveUp("error"); }
|
|
918
|
+
};
|
|
919
|
+
for (const ms of atMs) { if (ms <= 0) ask(); else timers.push(setTimeout(ask, ms)); }
|
|
900
920
|
}
|
|
901
921
|
} catch { done({ host, none: "error" }); }
|
|
902
922
|
`;
|
|
@@ -927,7 +947,7 @@ function earlyLookupFor(host) {
|
|
|
927
947
|
const worker = new node_worker_threads_1.Worker(EARLY_WORKER, {
|
|
928
948
|
eval: true,
|
|
929
949
|
workerData: {
|
|
930
|
-
port: port2, host, timeoutMs:
|
|
950
|
+
port: port2, host, timeoutMs: LOOKUP_FIRST_MS, tries: LOOKUP_TRIES, atMs: LOOKUP_AT_MS,
|
|
931
951
|
servers: exports.tuning.earlyServers ?? (Array.isArray(earlySeam().servers) ? earlySeam().servers : undefined),
|
|
932
952
|
hostsFile: exports.tuning.earlyHostsFile ?? (typeof earlySeam().hostsFile === "string" ? earlySeam().hostsFile : undefined),
|
|
933
953
|
},
|
|
@@ -975,6 +995,10 @@ function settleEarly(e, message) {
|
|
|
975
995
|
else {
|
|
976
996
|
e.state = "none";
|
|
977
997
|
}
|
|
998
|
+
if (trace_js_1.TRACE_ON) {
|
|
999
|
+
const at = typeof m?.hr === "number" ? m.hr - (Number(process.hrtime.bigint()) / 1e6 - performance.now()) : performance.now();
|
|
1000
|
+
(0, trace_js_1.traceEventAt)(e.state === "answered" ? `dns:early-answer ${addrs.length}` : `dns:early-none ${typeof m?.none === "string" ? m.none : "gone"}`, at);
|
|
1001
|
+
}
|
|
978
1002
|
safely(() => { e.port.close(); });
|
|
979
1003
|
const waiters = e.waiters.splice(0);
|
|
980
1004
|
for (const w of waiters)
|
|
@@ -1338,6 +1362,8 @@ class TransportThread {
|
|
|
1338
1362
|
everReady = false;
|
|
1339
1363
|
closedReason = "";
|
|
1340
1364
|
onFail;
|
|
1365
|
+
startedAt = performance.now();
|
|
1366
|
+
startWaiters = [];
|
|
1341
1367
|
inflight = new Map();
|
|
1342
1368
|
nextId = 1;
|
|
1343
1369
|
listening = false;
|
|
@@ -1377,10 +1403,41 @@ class TransportThread {
|
|
|
1377
1403
|
}
|
|
1378
1404
|
}
|
|
1379
1405
|
ready() {
|
|
1380
|
-
if (!this.listening && this.state !== "closed")
|
|
1406
|
+
if ((!this.listening || this.state === "starting") && this.state !== "closed")
|
|
1381
1407
|
this.poll();
|
|
1382
1408
|
return this.state === "ready" && this.sessions > 0 && this.inflight.size < this.sessions * this.limit && this.freeMarks.length > 0;
|
|
1383
1409
|
}
|
|
1410
|
+
starting() {
|
|
1411
|
+
if (this.state === "starting")
|
|
1412
|
+
this.poll();
|
|
1413
|
+
return this.state === "starting";
|
|
1414
|
+
}
|
|
1415
|
+
whenStarted(cb) {
|
|
1416
|
+
if (!this.starting()) {
|
|
1417
|
+
queueMicrotask(cb);
|
|
1418
|
+
return () => { };
|
|
1419
|
+
}
|
|
1420
|
+
this.startWaiters.push(cb);
|
|
1421
|
+
this.listen();
|
|
1422
|
+
return () => {
|
|
1423
|
+
const i = this.startWaiters.indexOf(cb);
|
|
1424
|
+
if (i >= 0)
|
|
1425
|
+
this.startWaiters.splice(i, 1);
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
started() {
|
|
1429
|
+
for (const cb of this.startWaiters.splice(0))
|
|
1430
|
+
safely(cb);
|
|
1431
|
+
}
|
|
1432
|
+
listen() {
|
|
1433
|
+
if (this.listening)
|
|
1434
|
+
return;
|
|
1435
|
+
this.listening = true;
|
|
1436
|
+
this.poll();
|
|
1437
|
+
this.port.on("message", (m) => { this.onMessage(m); });
|
|
1438
|
+
if (!this.refd)
|
|
1439
|
+
safely(() => { this.port.unref(); });
|
|
1440
|
+
}
|
|
1384
1441
|
onMessage(m) {
|
|
1385
1442
|
const msg = m;
|
|
1386
1443
|
this.messages++;
|
|
@@ -1403,8 +1460,16 @@ class TransportThread {
|
|
|
1403
1460
|
if (typeof c.index === "number" && typeof c.addr === "string" && typeof c.localPort === "number")
|
|
1404
1461
|
(0, trace_js_1.traceEventAt)(`thread:conn${c.index} ${c.addr} ${c.localPort}`, t);
|
|
1405
1462
|
}
|
|
1463
|
+
if (first)
|
|
1464
|
+
this.started();
|
|
1406
1465
|
}
|
|
1407
1466
|
return;
|
|
1467
|
+
case "ev": {
|
|
1468
|
+
const e = msg;
|
|
1469
|
+
if (typeof e.n === "string" && typeof e.hr === "number")
|
|
1470
|
+
(0, trace_js_1.traceEventAt)(`thread:w-${e.n}`, e.hr - this.hrOffset);
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1408
1473
|
case "sessions":
|
|
1409
1474
|
if (typeof msg.sessions === "number")
|
|
1410
1475
|
this.sessions = msg.sessions;
|
|
@@ -1415,6 +1480,7 @@ class TransportThread {
|
|
|
1415
1480
|
if (this.closedReason === "")
|
|
1416
1481
|
this.closedReason = msg.reason ?? "draining";
|
|
1417
1482
|
(0, trace_js_1.traceEvent)("thread:draining");
|
|
1483
|
+
this.started();
|
|
1418
1484
|
}
|
|
1419
1485
|
return;
|
|
1420
1486
|
case "closed":
|
|
@@ -1495,6 +1561,7 @@ class TransportThread {
|
|
|
1495
1561
|
this.port.postMessage({ t: "drain" });
|
|
1496
1562
|
}
|
|
1497
1563
|
catch { }
|
|
1564
|
+
this.started();
|
|
1498
1565
|
}
|
|
1499
1566
|
close(reason) {
|
|
1500
1567
|
if (this.state === "closed")
|
|
@@ -1503,6 +1570,7 @@ class TransportThread {
|
|
|
1503
1570
|
if (this.closedReason === "")
|
|
1504
1571
|
this.closedReason = reason;
|
|
1505
1572
|
(0, trace_js_1.traceEvent)("thread:closed");
|
|
1573
|
+
this.started();
|
|
1506
1574
|
this.outbox = [];
|
|
1507
1575
|
for (const [id, p] of [...this.inflight]) {
|
|
1508
1576
|
const sent = this.acked(p);
|
|
@@ -1525,11 +1593,7 @@ class TransportThread {
|
|
|
1525
1593
|
}
|
|
1526
1594
|
}
|
|
1527
1595
|
ref() {
|
|
1528
|
-
|
|
1529
|
-
this.listening = true;
|
|
1530
|
-
this.poll();
|
|
1531
|
-
this.port.on("message", (m) => { this.onMessage(m); });
|
|
1532
|
-
}
|
|
1596
|
+
this.listen();
|
|
1533
1597
|
if (this.refd)
|
|
1534
1598
|
return;
|
|
1535
1599
|
this.refd = true;
|
|
@@ -1670,6 +1734,55 @@ function threadFor(origin) {
|
|
|
1670
1734
|
return undefined;
|
|
1671
1735
|
return t.ready() && !lookupReplaced() ? t : undefined;
|
|
1672
1736
|
}
|
|
1737
|
+
function startingThread(origin) {
|
|
1738
|
+
const t = transportThread;
|
|
1739
|
+
if (t === undefined || t.origin !== origin || lookupReplaced())
|
|
1740
|
+
return undefined;
|
|
1741
|
+
return t.starting() ? t : undefined;
|
|
1742
|
+
}
|
|
1743
|
+
let raceTraced = false;
|
|
1744
|
+
function firstUp(t, pool, deadline, signal) {
|
|
1745
|
+
const first = !raceTraced;
|
|
1746
|
+
if (first) {
|
|
1747
|
+
raceTraced = true;
|
|
1748
|
+
(0, trace_js_1.traceEvent)("h2:race");
|
|
1749
|
+
}
|
|
1750
|
+
const stop = new AbortController();
|
|
1751
|
+
const both = signal === undefined ? stop.signal : AbortSignal.any([signal, stop.signal]);
|
|
1752
|
+
return new Promise((resolve, reject) => {
|
|
1753
|
+
let done = false;
|
|
1754
|
+
const forget = t.whenStarted(() => {
|
|
1755
|
+
if (done)
|
|
1756
|
+
return;
|
|
1757
|
+
const ready = threadFor(t.origin);
|
|
1758
|
+
if (ready === undefined)
|
|
1759
|
+
return;
|
|
1760
|
+
done = true;
|
|
1761
|
+
if (first)
|
|
1762
|
+
(0, trace_js_1.traceEvent)("h2:race-thread");
|
|
1763
|
+
stop.abort();
|
|
1764
|
+
resolve({ thread: ready });
|
|
1765
|
+
});
|
|
1766
|
+
pool.acquire(deadline, both).then((entry) => {
|
|
1767
|
+
if (done) {
|
|
1768
|
+
if (entry !== null)
|
|
1769
|
+
pool.end(entry);
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
done = true;
|
|
1773
|
+
forget();
|
|
1774
|
+
if (first)
|
|
1775
|
+
(0, trace_js_1.traceEvent)("h2:race-pool");
|
|
1776
|
+
resolve({ entry });
|
|
1777
|
+
}, (err) => {
|
|
1778
|
+
if (done)
|
|
1779
|
+
return;
|
|
1780
|
+
done = true;
|
|
1781
|
+
forget();
|
|
1782
|
+
reject(err);
|
|
1783
|
+
});
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1673
1786
|
function ownFile() {
|
|
1674
1787
|
const frame = (new Error().stack ?? "").split("\n")[1] ?? "";
|
|
1675
1788
|
const m = /\((.*):\d+:\d+\)\s*$/.exec(frame) ?? /at (.*):\d+:\d+\s*$/.exec(frame);
|
|
@@ -1712,7 +1825,7 @@ function startTransportThread(origin, onFail) {
|
|
|
1712
1825
|
workerData: {
|
|
1713
1826
|
promigenceTransport: true, port: port2, acks, origin: origin.origin, sessions: knobs.sessions, gapUs: knobs.gapUs,
|
|
1714
1827
|
firstUseMs: exports.tuning.loadFirstUseMs, idleMs: IDLE_CLOSE_MS, connectTimeoutMs: CONNECT_TIMEOUT_MS,
|
|
1715
|
-
lookupTimeoutMs:
|
|
1828
|
+
lookupTimeoutMs: LOOKUP_FIRST_MS, lookupTries: LOOKUP_TRIES, lookupAtMs: LOOKUP_AT_MS, trace: trace_js_1.TRACE_ON,
|
|
1716
1829
|
servers: exports.tuning.earlyServers ?? (Array.isArray(seam.servers) ? seam.servers : undefined),
|
|
1717
1830
|
hostsFile: exports.tuning.earlyHostsFile ?? (typeof seam.hostsFile === "string" ? seam.hostsFile : undefined),
|
|
1718
1831
|
distrustedForTest: ca_guard_js_1.__caGuardInternals.testHashes(),
|
|
@@ -1730,6 +1843,11 @@ function startTransportThread(origin, onFail) {
|
|
|
1730
1843
|
worker.on("exit", () => { t.poll(); t.close("exit"); });
|
|
1731
1844
|
transportThread = t;
|
|
1732
1845
|
(0, trace_js_1.traceEvent)("thread:start");
|
|
1846
|
+
(0, trace_js_1.onTraceFlush)(() => {
|
|
1847
|
+
t.poll();
|
|
1848
|
+
for (const e of earlies.values())
|
|
1849
|
+
pollEarly(e);
|
|
1850
|
+
});
|
|
1733
1851
|
}
|
|
1734
1852
|
catch {
|
|
1735
1853
|
transportThread = undefined;
|
|
@@ -1816,8 +1934,34 @@ class Http2Transport {
|
|
|
1816
1934
|
const pool = poolFor(target.origin);
|
|
1817
1935
|
if (!pool.usable())
|
|
1818
1936
|
return this.viaFetch(url, init, deadline);
|
|
1937
|
+
let raced = false;
|
|
1819
1938
|
for (;;) {
|
|
1820
|
-
|
|
1939
|
+
let entry = pool.tryAcquire();
|
|
1940
|
+
if (entry === undefined) {
|
|
1941
|
+
const starting = raced || resends > 0 || replays > 0 ? undefined : startingThread(target.origin);
|
|
1942
|
+
if (starting !== undefined) {
|
|
1943
|
+
raced = true;
|
|
1944
|
+
const won = await firstUp(starting, pool, deadline, init.signal);
|
|
1945
|
+
if (won.thread !== undefined) {
|
|
1946
|
+
if (tr !== undefined)
|
|
1947
|
+
(0, trace_js_1.mark)(tr, "race");
|
|
1948
|
+
const out = await won.thread.send(target, init, replayable, deadline);
|
|
1949
|
+
if (out === VIA_FETCH)
|
|
1950
|
+
return this.viaFetch(url, init, deadline);
|
|
1951
|
+
if (!(out instanceof HandBack))
|
|
1952
|
+
return out;
|
|
1953
|
+
resends = out.resends;
|
|
1954
|
+
replays = out.replays;
|
|
1955
|
+
if (tr !== undefined)
|
|
1956
|
+
delete tr.via;
|
|
1957
|
+
continue;
|
|
1958
|
+
}
|
|
1959
|
+
entry = won.entry;
|
|
1960
|
+
}
|
|
1961
|
+
else {
|
|
1962
|
+
entry = await pool.acquire(deadline, init.signal);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1821
1965
|
if (tr !== undefined)
|
|
1822
1966
|
(0, trace_js_1.mark)(tr, "acq");
|
|
1823
1967
|
if (entry === null)
|
|
@@ -1917,7 +2061,7 @@ exports.__http2Internals = {
|
|
|
1917
2061
|
if (t === undefined)
|
|
1918
2062
|
return undefined;
|
|
1919
2063
|
t.poll();
|
|
1920
|
-
return { state: t.state, sessions: t.sessions, limit: t.limit, answered: t.answered, everReady: t.everReady, closedReason: t.closedReason, messages: t.messages, posts: t.posts };
|
|
2064
|
+
return { state: t.state, sessions: t.sessions, limit: t.limit, answered: t.answered, everReady: t.everReady, closedReason: t.closedReason, messages: t.messages, posts: t.posts, startedAt: t.startedAt };
|
|
1921
2065
|
},
|
|
1922
2066
|
threadKnobs(env) {
|
|
1923
2067
|
return threadKnobs(env);
|
|
@@ -1966,6 +2110,15 @@ function isMainThread() {
|
|
|
1966
2110
|
return false;
|
|
1967
2111
|
}
|
|
1968
2112
|
}
|
|
2113
|
+
function standby(url) {
|
|
2114
|
+
try {
|
|
2115
|
+
(0, trace_js_1.traceEvent)("h2:standby");
|
|
2116
|
+
warmTls();
|
|
2117
|
+
poolFor(url.origin).warm(exports.tuning.loadFirstUseMs);
|
|
2118
|
+
}
|
|
2119
|
+
catch {
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
1969
2122
|
function connectAtLoad() {
|
|
1970
2123
|
try {
|
|
1971
2124
|
if (!isNode() || Number(process.versions.node.split(".")[0]) < 22)
|
|
@@ -1986,6 +2139,8 @@ function connectAtLoad() {
|
|
|
1986
2139
|
if (url.protocol === "https:" && !(0, dispatcher_js_1.customDispatcher)() && earlyPlatform() && v4First() && !threadOff && earlySeam().transportThread !== false) {
|
|
1987
2140
|
startTransportThread(url, () => { if (loadTurnRan)
|
|
1988
2141
|
warmAtLoad(); });
|
|
2142
|
+
if ((0, node_net_1.isIP)(host) === 0)
|
|
2143
|
+
earlyLookupFor(host);
|
|
1989
2144
|
}
|
|
1990
2145
|
else if (url.protocol === "https:" && !(0, dispatcher_js_1.customDispatcher)() && (0, node_net_1.isIP)(host) === 0 && earlyPlatform() && v4First())
|
|
1991
2146
|
earlyLookupFor(host);
|
|
@@ -2004,10 +2159,27 @@ function connectAtLoad() {
|
|
|
2004
2159
|
const url = new URL(raw ? raw : core_js_1.DEFAULT_API_URL);
|
|
2005
2160
|
if (url.protocol !== "https:" || (0, dispatcher_js_1.customDispatcher)())
|
|
2006
2161
|
return;
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2162
|
+
const t = transportThread;
|
|
2163
|
+
if (t !== undefined && t.origin === url.origin) {
|
|
2164
|
+
t.poll();
|
|
2165
|
+
if (t.everReady)
|
|
2166
|
+
return;
|
|
2167
|
+
if (t.state !== "closed") {
|
|
2168
|
+
const check = () => {
|
|
2169
|
+
t.poll();
|
|
2170
|
+
if (t.everReady || t.state === "closed")
|
|
2171
|
+
return;
|
|
2172
|
+
const wait = t.startedAt + exports.tuning.standbyAfterMs - performance.now();
|
|
2173
|
+
if (wait > 0) {
|
|
2174
|
+
const timer = setTimeout(check, wait);
|
|
2175
|
+
timer.unref?.();
|
|
2176
|
+
return;
|
|
2177
|
+
}
|
|
2178
|
+
standby(url);
|
|
2179
|
+
};
|
|
2180
|
+
check();
|
|
2010
2181
|
return;
|
|
2182
|
+
}
|
|
2011
2183
|
}
|
|
2012
2184
|
warmTls();
|
|
2013
2185
|
poolFor(url.origin).warm(exports.tuning.loadFirstUseMs);
|
package/dist/cjs/trace.d.ts
CHANGED
|
@@ -31,3 +31,5 @@ export declare function mark(s: TraceStamp | undefined, phase: string): void;
|
|
|
31
31
|
export declare function traceEvent(name: string): void;
|
|
32
32
|
/** A process event that happened at `t` (`performance.now()` terms), recorded now: one another thread reported. */
|
|
33
33
|
export declare function traceEventAt(name: string, t: number): void;
|
|
34
|
+
/** `fn` runs once at exit, before the trace is written (tracing on only). */
|
|
35
|
+
export declare function onTraceFlush(fn: () => void): void;
|
package/dist/cjs/trace.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.traceBegin = traceBegin;
|
|
|
6
6
|
exports.mark = mark;
|
|
7
7
|
exports.traceEvent = traceEvent;
|
|
8
8
|
exports.traceEventAt = traceEventAt;
|
|
9
|
+
exports.onTraceFlush = onTraceFlush;
|
|
9
10
|
const node_fs_1 = require("node:fs");
|
|
10
11
|
const core_js_1 = require("./core.js");
|
|
11
12
|
exports.TRACE_ON = (() => {
|
|
@@ -68,7 +69,23 @@ function modulePath() {
|
|
|
68
69
|
const m = /\((.*):\d+:\d+\)\s*$/.exec(frame) ?? /at (.*):\d+:\d+\s*$/.exec(frame);
|
|
69
70
|
return m?.[1] ?? "";
|
|
70
71
|
}
|
|
72
|
+
const beforeFlush = [];
|
|
73
|
+
function onTraceFlush(fn) {
|
|
74
|
+
if (!exports.TRACE_ON)
|
|
75
|
+
return;
|
|
76
|
+
beforeFlush.push(fn);
|
|
77
|
+
if (!hooked) {
|
|
78
|
+
hooked = true;
|
|
79
|
+
process.once("exit", flush);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
71
82
|
function flush() {
|
|
83
|
+
for (const fn of beforeFlush.splice(0)) {
|
|
84
|
+
try {
|
|
85
|
+
fn();
|
|
86
|
+
}
|
|
87
|
+
catch { }
|
|
88
|
+
}
|
|
72
89
|
const head = {
|
|
73
90
|
trace: "promigence-sdk",
|
|
74
91
|
version: core_js_1.VERSION,
|
|
@@ -36,6 +36,11 @@ function start(init) {
|
|
|
36
36
|
}
|
|
37
37
|
catch { }
|
|
38
38
|
};
|
|
39
|
+
const ev = (n) => {
|
|
40
|
+
if (init.trace === true)
|
|
41
|
+
post({ t: "ev", n, hr: hrNow() });
|
|
42
|
+
};
|
|
43
|
+
ev("boot");
|
|
39
44
|
const live = () => sessions.filter((e) => !e.retired && !e.session.closed && !e.session.destroyed);
|
|
40
45
|
const end = (reason) => {
|
|
41
46
|
if (closed)
|
|
@@ -96,14 +101,14 @@ function start(init) {
|
|
|
96
101
|
catch {
|
|
97
102
|
context = undefined;
|
|
98
103
|
}
|
|
99
|
-
const connect = (address) => new Promise((resolve) => {
|
|
104
|
+
const connect = (address, k) => new Promise((resolve) => {
|
|
100
105
|
let done = false;
|
|
101
106
|
const finish = (e) => { if (!done) {
|
|
102
107
|
done = true;
|
|
103
108
|
clearTimeout(timer);
|
|
104
109
|
resolve(e);
|
|
105
110
|
} };
|
|
106
|
-
const timer = setTimeout(() => { try {
|
|
111
|
+
const timer = setTimeout(() => { ev(`timeout${k}`); try {
|
|
107
112
|
socket.destroy();
|
|
108
113
|
}
|
|
109
114
|
catch { } finish(null); }, init.connectTimeoutMs);
|
|
@@ -115,8 +120,11 @@ function start(init) {
|
|
|
115
120
|
checkServerIdentity: ca_guard_js_1.guardCertChain,
|
|
116
121
|
...(context === undefined ? {} : { secureContext: context }),
|
|
117
122
|
});
|
|
118
|
-
|
|
123
|
+
if (init.trace === true)
|
|
124
|
+
socket.once("connect", () => { ev(`tcp${k}`); });
|
|
125
|
+
socket.once("error", (e) => { ev(`fail${k} ${e?.code ?? "error"}`); finish(null); });
|
|
119
126
|
socket.once("secureConnect", () => {
|
|
127
|
+
ev(`tls${k}`);
|
|
120
128
|
if (socket.alpnProtocol !== "h2") {
|
|
121
129
|
socket.destroy();
|
|
122
130
|
finish(null);
|
|
@@ -169,14 +177,66 @@ function start(init) {
|
|
|
169
177
|
resolve(v4);
|
|
170
178
|
return;
|
|
171
179
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
const at = init.lookupAtMs.length > 0 ? init.lookupAtMs : [0];
|
|
181
|
+
const resolvers = [];
|
|
182
|
+
const timers = [];
|
|
183
|
+
let done = false;
|
|
184
|
+
let asked = 0;
|
|
185
|
+
let failed = 0;
|
|
186
|
+
const finish = (addrs) => {
|
|
187
|
+
if (done)
|
|
188
|
+
return;
|
|
189
|
+
done = true;
|
|
190
|
+
for (const t of timers)
|
|
191
|
+
clearTimeout(t);
|
|
192
|
+
for (const r of resolvers) {
|
|
193
|
+
try {
|
|
194
|
+
r.cancel();
|
|
195
|
+
}
|
|
196
|
+
catch { }
|
|
197
|
+
}
|
|
198
|
+
resolve(addrs);
|
|
199
|
+
};
|
|
200
|
+
const gaveUp = () => {
|
|
201
|
+
if (++failed < asked)
|
|
202
|
+
return;
|
|
203
|
+
if (asked < at.length)
|
|
204
|
+
ask();
|
|
205
|
+
else
|
|
206
|
+
finish([]);
|
|
207
|
+
};
|
|
208
|
+
const ask = () => {
|
|
209
|
+
if (done || asked >= at.length)
|
|
210
|
+
return;
|
|
211
|
+
const k = asked++;
|
|
212
|
+
try {
|
|
213
|
+
const r = new dns.Resolver({ timeout: init.lookupTimeoutMs, tries: init.lookupTries });
|
|
214
|
+
const list = Array.isArray(init.servers) && init.servers.length > 0 ? init.servers : r.getServers();
|
|
215
|
+
if (list.length > 0)
|
|
216
|
+
r.setServers(list.map((_, i) => list[(i + k) % list.length]));
|
|
217
|
+
resolvers.push(r);
|
|
218
|
+
ev(`lookup${k + 1}`);
|
|
219
|
+
r.resolve4(name, (err, addrs) => {
|
|
220
|
+
if (done)
|
|
221
|
+
return;
|
|
222
|
+
if (!err && Array.isArray(addrs) && addrs.length > 0) {
|
|
223
|
+
ev(`addrs${k + 1} ${addrs.length}`);
|
|
224
|
+
finish(addrs);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
ev(`nolookup${k + 1} ${err?.code ?? "empty"}`);
|
|
228
|
+
gaveUp();
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
gaveUp();
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
for (const ms of at) {
|
|
236
|
+
if (ms <= 0)
|
|
237
|
+
ask();
|
|
238
|
+
else
|
|
239
|
+
timers.push(setTimeout(ask, ms));
|
|
180
240
|
}
|
|
181
241
|
});
|
|
182
242
|
const limitOf = (e) => {
|
|
@@ -198,7 +258,7 @@ function start(init) {
|
|
|
198
258
|
}
|
|
199
259
|
const n = Math.max(1, init.sessions);
|
|
200
260
|
pendingConnects = n;
|
|
201
|
-
const tries = Array.from({ length: n }, (_, i) => connect(addrs[i % addrs.length]).then((e) => {
|
|
261
|
+
const tries = Array.from({ length: n }, (_, i) => connect(addrs[i % addrs.length], i).then((e) => {
|
|
202
262
|
pendingConnects--;
|
|
203
263
|
if (closed) {
|
|
204
264
|
if (e)
|
package/dist/core.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Promigence SDK — constants, error hierarchy and shared types. */
|
|
2
|
-
export declare const VERSION = "0.1.0-beta.
|
|
2
|
+
export declare const VERSION = "0.1.0-beta.14";
|
|
3
3
|
export declare const DOCS_URL = "https://promigence.ai";
|
|
4
4
|
/** The error reference; each code is an anchor on it (the API's `docs` link has the same form). */
|
|
5
5
|
export declare const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
package/dist/core.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hintArg } from "./hint.js";
|
|
2
|
-
export const VERSION = "0.1.0-beta.
|
|
2
|
+
export const VERSION = "0.1.0-beta.14";
|
|
3
3
|
export const DOCS_URL = "https://promigence.ai";
|
|
4
4
|
export const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
|
5
5
|
export const DEFAULT_API_URL = "https://api.promigence.ai/v1";
|
package/dist/http2.d.ts
CHANGED
|
@@ -189,6 +189,7 @@ export declare const __http2Internals: {
|
|
|
189
189
|
closedReason: string;
|
|
190
190
|
messages: number;
|
|
191
191
|
posts: number;
|
|
192
|
+
startedAt: number;
|
|
192
193
|
} | undefined;
|
|
193
194
|
/** The transport thread's number of connections and spacing between new requests on one, for an environment (`threadKnobs`). */
|
|
194
195
|
threadKnobs(env: NodeJS.ProcessEnv): {
|
package/dist/http2.js
CHANGED
|
@@ -6,11 +6,12 @@ import * as zlib from "node:zlib";
|
|
|
6
6
|
import { __caGuardInternals, guardCertChain } from "./ca-guard.js";
|
|
7
7
|
import { DEFAULT_API_URL } from "./core.js";
|
|
8
8
|
import { customDispatcher } from "./dispatcher.js";
|
|
9
|
-
import { mark, TRACE_ON, traceEvent, traceEventAt } from "./trace.js";
|
|
9
|
+
import { mark, onTraceFlush, TRACE_ON, traceEvent, traceEventAt } from "./trace.js";
|
|
10
10
|
const MAX_SESSIONS = 4;
|
|
11
11
|
const FIRST_SESSIONS = 2;
|
|
12
12
|
const BURST_MIN = 8;
|
|
13
13
|
const HOLD_MS = 10;
|
|
14
|
+
const STANDBY_AFTER_MS = 300;
|
|
14
15
|
const LOAD_FIRST_USE_MS = 120_000;
|
|
15
16
|
const CONNECT_TIMEOUT_MS = 10_000;
|
|
16
17
|
const IDLE_CLOSE_MS = 10_000;
|
|
@@ -561,6 +562,7 @@ export const tuning = {
|
|
|
561
562
|
holdMs: HOLD_MS,
|
|
562
563
|
burstMin: BURST_MIN,
|
|
563
564
|
loadFirstUseMs: LOAD_FIRST_USE_MS,
|
|
565
|
+
standbyAfterMs: STANDBY_AFTER_MS,
|
|
564
566
|
earlyPlatform: undefined,
|
|
565
567
|
earlyServers: undefined,
|
|
566
568
|
earlyHostsFile: undefined,
|
|
@@ -844,7 +846,9 @@ function apiLookup(asked, observe, ipv4First = v4First(), earlyOk = true) {
|
|
|
844
846
|
const earlies = new Map();
|
|
845
847
|
const MAX_EARLIES = 4;
|
|
846
848
|
const EARLY_MAX_AGE_MS = 60_000;
|
|
847
|
-
const
|
|
849
|
+
const LOOKUP_FIRST_MS = 150;
|
|
850
|
+
const LOOKUP_TRIES = 3;
|
|
851
|
+
const LOOKUP_AT_MS = [0, 100, 250];
|
|
848
852
|
const DNS_AT_LOAD = (() => {
|
|
849
853
|
try {
|
|
850
854
|
return nodeDns();
|
|
@@ -865,8 +869,8 @@ function lookupReplaced() {
|
|
|
865
869
|
const EARLY_WORKER = `
|
|
866
870
|
const { workerData } = require("node:worker_threads");
|
|
867
871
|
const { isIPv4 } = require("node:net");
|
|
868
|
-
const { port, host, timeoutMs, servers, hostsFile } = workerData;
|
|
869
|
-
const done = (m) => { try { port.postMessage(m); } catch {} try { port.close(); } catch {} };
|
|
872
|
+
const { port, host, timeoutMs, tries, atMs, servers, hostsFile } = workerData;
|
|
873
|
+
const done = (m) => { m.hr = Number(process.hrtime.bigint()) / 1e6; try { port.postMessage(m); } catch {} try { port.close(); } catch {} };
|
|
870
874
|
try {
|
|
871
875
|
let named = false;
|
|
872
876
|
const v4 = [];
|
|
@@ -880,12 +884,28 @@ try {
|
|
|
880
884
|
} catch {}
|
|
881
885
|
if (named) done(v4.length > 0 ? { host, addrs: v4, ttl: 60, at: Date.now() } : { host, none: "hosts" });
|
|
882
886
|
else {
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
});
|
|
887
|
+
const { Resolver } = require("node:dns");
|
|
888
|
+
const rs = [];
|
|
889
|
+
const timers = [];
|
|
890
|
+
let fin = false, asked = 0, failed = 0;
|
|
891
|
+
const finish = (m) => { if (fin) return; fin = true; for (const t of timers) clearTimeout(t); for (const r of rs) { try { r.cancel(); } catch {} } done(m); };
|
|
892
|
+
const gaveUp = (why) => { if (++failed < asked) return; if (asked < atMs.length) ask(); else finish({ host, none: why }); };
|
|
893
|
+
const ask = () => {
|
|
894
|
+
if (fin || asked >= atMs.length) return;
|
|
895
|
+
const k = asked++;
|
|
896
|
+
try {
|
|
897
|
+
const r = new Resolver({ timeout: timeoutMs, tries });
|
|
898
|
+
const list = Array.isArray(servers) && servers.length > 0 ? servers : r.getServers();
|
|
899
|
+
if (list.length > 0) r.setServers(list.map((_, i) => list[(i + k) % list.length]));
|
|
900
|
+
rs.push(r);
|
|
901
|
+
r.resolve4(host, { ttl: true }, (err, recs) => {
|
|
902
|
+
if (fin) return;
|
|
903
|
+
if (!err && Array.isArray(recs) && recs.length > 0) return finish({ host, addrs: recs.map((x) => x.address), ttl: Math.min(...recs.map((x) => x.ttl)), at: Date.now() });
|
|
904
|
+
gaveUp((err && err.code) || "empty");
|
|
905
|
+
});
|
|
906
|
+
} catch { gaveUp("error"); }
|
|
907
|
+
};
|
|
908
|
+
for (const ms of atMs) { if (ms <= 0) ask(); else timers.push(setTimeout(ask, ms)); }
|
|
889
909
|
}
|
|
890
910
|
} catch { done({ host, none: "error" }); }
|
|
891
911
|
`;
|
|
@@ -916,7 +936,7 @@ function earlyLookupFor(host) {
|
|
|
916
936
|
const worker = new Worker(EARLY_WORKER, {
|
|
917
937
|
eval: true,
|
|
918
938
|
workerData: {
|
|
919
|
-
port: port2, host, timeoutMs:
|
|
939
|
+
port: port2, host, timeoutMs: LOOKUP_FIRST_MS, tries: LOOKUP_TRIES, atMs: LOOKUP_AT_MS,
|
|
920
940
|
servers: tuning.earlyServers ?? (Array.isArray(earlySeam().servers) ? earlySeam().servers : undefined),
|
|
921
941
|
hostsFile: tuning.earlyHostsFile ?? (typeof earlySeam().hostsFile === "string" ? earlySeam().hostsFile : undefined),
|
|
922
942
|
},
|
|
@@ -964,6 +984,10 @@ function settleEarly(e, message) {
|
|
|
964
984
|
else {
|
|
965
985
|
e.state = "none";
|
|
966
986
|
}
|
|
987
|
+
if (TRACE_ON) {
|
|
988
|
+
const at = typeof m?.hr === "number" ? m.hr - (Number(process.hrtime.bigint()) / 1e6 - performance.now()) : performance.now();
|
|
989
|
+
traceEventAt(e.state === "answered" ? `dns:early-answer ${addrs.length}` : `dns:early-none ${typeof m?.none === "string" ? m.none : "gone"}`, at);
|
|
990
|
+
}
|
|
967
991
|
safely(() => { e.port.close(); });
|
|
968
992
|
const waiters = e.waiters.splice(0);
|
|
969
993
|
for (const w of waiters)
|
|
@@ -1327,6 +1351,8 @@ class TransportThread {
|
|
|
1327
1351
|
everReady = false;
|
|
1328
1352
|
closedReason = "";
|
|
1329
1353
|
onFail;
|
|
1354
|
+
startedAt = performance.now();
|
|
1355
|
+
startWaiters = [];
|
|
1330
1356
|
inflight = new Map();
|
|
1331
1357
|
nextId = 1;
|
|
1332
1358
|
listening = false;
|
|
@@ -1366,10 +1392,41 @@ class TransportThread {
|
|
|
1366
1392
|
}
|
|
1367
1393
|
}
|
|
1368
1394
|
ready() {
|
|
1369
|
-
if (!this.listening && this.state !== "closed")
|
|
1395
|
+
if ((!this.listening || this.state === "starting") && this.state !== "closed")
|
|
1370
1396
|
this.poll();
|
|
1371
1397
|
return this.state === "ready" && this.sessions > 0 && this.inflight.size < this.sessions * this.limit && this.freeMarks.length > 0;
|
|
1372
1398
|
}
|
|
1399
|
+
starting() {
|
|
1400
|
+
if (this.state === "starting")
|
|
1401
|
+
this.poll();
|
|
1402
|
+
return this.state === "starting";
|
|
1403
|
+
}
|
|
1404
|
+
whenStarted(cb) {
|
|
1405
|
+
if (!this.starting()) {
|
|
1406
|
+
queueMicrotask(cb);
|
|
1407
|
+
return () => { };
|
|
1408
|
+
}
|
|
1409
|
+
this.startWaiters.push(cb);
|
|
1410
|
+
this.listen();
|
|
1411
|
+
return () => {
|
|
1412
|
+
const i = this.startWaiters.indexOf(cb);
|
|
1413
|
+
if (i >= 0)
|
|
1414
|
+
this.startWaiters.splice(i, 1);
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
started() {
|
|
1418
|
+
for (const cb of this.startWaiters.splice(0))
|
|
1419
|
+
safely(cb);
|
|
1420
|
+
}
|
|
1421
|
+
listen() {
|
|
1422
|
+
if (this.listening)
|
|
1423
|
+
return;
|
|
1424
|
+
this.listening = true;
|
|
1425
|
+
this.poll();
|
|
1426
|
+
this.port.on("message", (m) => { this.onMessage(m); });
|
|
1427
|
+
if (!this.refd)
|
|
1428
|
+
safely(() => { this.port.unref(); });
|
|
1429
|
+
}
|
|
1373
1430
|
onMessage(m) {
|
|
1374
1431
|
const msg = m;
|
|
1375
1432
|
this.messages++;
|
|
@@ -1392,8 +1449,16 @@ class TransportThread {
|
|
|
1392
1449
|
if (typeof c.index === "number" && typeof c.addr === "string" && typeof c.localPort === "number")
|
|
1393
1450
|
traceEventAt(`thread:conn${c.index} ${c.addr} ${c.localPort}`, t);
|
|
1394
1451
|
}
|
|
1452
|
+
if (first)
|
|
1453
|
+
this.started();
|
|
1395
1454
|
}
|
|
1396
1455
|
return;
|
|
1456
|
+
case "ev": {
|
|
1457
|
+
const e = msg;
|
|
1458
|
+
if (typeof e.n === "string" && typeof e.hr === "number")
|
|
1459
|
+
traceEventAt(`thread:w-${e.n}`, e.hr - this.hrOffset);
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1397
1462
|
case "sessions":
|
|
1398
1463
|
if (typeof msg.sessions === "number")
|
|
1399
1464
|
this.sessions = msg.sessions;
|
|
@@ -1404,6 +1469,7 @@ class TransportThread {
|
|
|
1404
1469
|
if (this.closedReason === "")
|
|
1405
1470
|
this.closedReason = msg.reason ?? "draining";
|
|
1406
1471
|
traceEvent("thread:draining");
|
|
1472
|
+
this.started();
|
|
1407
1473
|
}
|
|
1408
1474
|
return;
|
|
1409
1475
|
case "closed":
|
|
@@ -1484,6 +1550,7 @@ class TransportThread {
|
|
|
1484
1550
|
this.port.postMessage({ t: "drain" });
|
|
1485
1551
|
}
|
|
1486
1552
|
catch { }
|
|
1553
|
+
this.started();
|
|
1487
1554
|
}
|
|
1488
1555
|
close(reason) {
|
|
1489
1556
|
if (this.state === "closed")
|
|
@@ -1492,6 +1559,7 @@ class TransportThread {
|
|
|
1492
1559
|
if (this.closedReason === "")
|
|
1493
1560
|
this.closedReason = reason;
|
|
1494
1561
|
traceEvent("thread:closed");
|
|
1562
|
+
this.started();
|
|
1495
1563
|
this.outbox = [];
|
|
1496
1564
|
for (const [id, p] of [...this.inflight]) {
|
|
1497
1565
|
const sent = this.acked(p);
|
|
@@ -1514,11 +1582,7 @@ class TransportThread {
|
|
|
1514
1582
|
}
|
|
1515
1583
|
}
|
|
1516
1584
|
ref() {
|
|
1517
|
-
|
|
1518
|
-
this.listening = true;
|
|
1519
|
-
this.poll();
|
|
1520
|
-
this.port.on("message", (m) => { this.onMessage(m); });
|
|
1521
|
-
}
|
|
1585
|
+
this.listen();
|
|
1522
1586
|
if (this.refd)
|
|
1523
1587
|
return;
|
|
1524
1588
|
this.refd = true;
|
|
@@ -1659,6 +1723,55 @@ function threadFor(origin) {
|
|
|
1659
1723
|
return undefined;
|
|
1660
1724
|
return t.ready() && !lookupReplaced() ? t : undefined;
|
|
1661
1725
|
}
|
|
1726
|
+
function startingThread(origin) {
|
|
1727
|
+
const t = transportThread;
|
|
1728
|
+
if (t === undefined || t.origin !== origin || lookupReplaced())
|
|
1729
|
+
return undefined;
|
|
1730
|
+
return t.starting() ? t : undefined;
|
|
1731
|
+
}
|
|
1732
|
+
let raceTraced = false;
|
|
1733
|
+
function firstUp(t, pool, deadline, signal) {
|
|
1734
|
+
const first = !raceTraced;
|
|
1735
|
+
if (first) {
|
|
1736
|
+
raceTraced = true;
|
|
1737
|
+
traceEvent("h2:race");
|
|
1738
|
+
}
|
|
1739
|
+
const stop = new AbortController();
|
|
1740
|
+
const both = signal === undefined ? stop.signal : AbortSignal.any([signal, stop.signal]);
|
|
1741
|
+
return new Promise((resolve, reject) => {
|
|
1742
|
+
let done = false;
|
|
1743
|
+
const forget = t.whenStarted(() => {
|
|
1744
|
+
if (done)
|
|
1745
|
+
return;
|
|
1746
|
+
const ready = threadFor(t.origin);
|
|
1747
|
+
if (ready === undefined)
|
|
1748
|
+
return;
|
|
1749
|
+
done = true;
|
|
1750
|
+
if (first)
|
|
1751
|
+
traceEvent("h2:race-thread");
|
|
1752
|
+
stop.abort();
|
|
1753
|
+
resolve({ thread: ready });
|
|
1754
|
+
});
|
|
1755
|
+
pool.acquire(deadline, both).then((entry) => {
|
|
1756
|
+
if (done) {
|
|
1757
|
+
if (entry !== null)
|
|
1758
|
+
pool.end(entry);
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
done = true;
|
|
1762
|
+
forget();
|
|
1763
|
+
if (first)
|
|
1764
|
+
traceEvent("h2:race-pool");
|
|
1765
|
+
resolve({ entry });
|
|
1766
|
+
}, (err) => {
|
|
1767
|
+
if (done)
|
|
1768
|
+
return;
|
|
1769
|
+
done = true;
|
|
1770
|
+
forget();
|
|
1771
|
+
reject(err);
|
|
1772
|
+
});
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1662
1775
|
function ownFile() {
|
|
1663
1776
|
const frame = (new Error().stack ?? "").split("\n")[1] ?? "";
|
|
1664
1777
|
const m = /\((.*):\d+:\d+\)\s*$/.exec(frame) ?? /at (.*):\d+:\d+\s*$/.exec(frame);
|
|
@@ -1701,7 +1814,7 @@ function startTransportThread(origin, onFail) {
|
|
|
1701
1814
|
workerData: {
|
|
1702
1815
|
promigenceTransport: true, port: port2, acks, origin: origin.origin, sessions: knobs.sessions, gapUs: knobs.gapUs,
|
|
1703
1816
|
firstUseMs: tuning.loadFirstUseMs, idleMs: IDLE_CLOSE_MS, connectTimeoutMs: CONNECT_TIMEOUT_MS,
|
|
1704
|
-
lookupTimeoutMs:
|
|
1817
|
+
lookupTimeoutMs: LOOKUP_FIRST_MS, lookupTries: LOOKUP_TRIES, lookupAtMs: LOOKUP_AT_MS, trace: TRACE_ON,
|
|
1705
1818
|
servers: tuning.earlyServers ?? (Array.isArray(seam.servers) ? seam.servers : undefined),
|
|
1706
1819
|
hostsFile: tuning.earlyHostsFile ?? (typeof seam.hostsFile === "string" ? seam.hostsFile : undefined),
|
|
1707
1820
|
distrustedForTest: __caGuardInternals.testHashes(),
|
|
@@ -1719,6 +1832,11 @@ function startTransportThread(origin, onFail) {
|
|
|
1719
1832
|
worker.on("exit", () => { t.poll(); t.close("exit"); });
|
|
1720
1833
|
transportThread = t;
|
|
1721
1834
|
traceEvent("thread:start");
|
|
1835
|
+
onTraceFlush(() => {
|
|
1836
|
+
t.poll();
|
|
1837
|
+
for (const e of earlies.values())
|
|
1838
|
+
pollEarly(e);
|
|
1839
|
+
});
|
|
1722
1840
|
}
|
|
1723
1841
|
catch {
|
|
1724
1842
|
transportThread = undefined;
|
|
@@ -1805,8 +1923,34 @@ export class Http2Transport {
|
|
|
1805
1923
|
const pool = poolFor(target.origin);
|
|
1806
1924
|
if (!pool.usable())
|
|
1807
1925
|
return this.viaFetch(url, init, deadline);
|
|
1926
|
+
let raced = false;
|
|
1808
1927
|
for (;;) {
|
|
1809
|
-
|
|
1928
|
+
let entry = pool.tryAcquire();
|
|
1929
|
+
if (entry === undefined) {
|
|
1930
|
+
const starting = raced || resends > 0 || replays > 0 ? undefined : startingThread(target.origin);
|
|
1931
|
+
if (starting !== undefined) {
|
|
1932
|
+
raced = true;
|
|
1933
|
+
const won = await firstUp(starting, pool, deadline, init.signal);
|
|
1934
|
+
if (won.thread !== undefined) {
|
|
1935
|
+
if (tr !== undefined)
|
|
1936
|
+
mark(tr, "race");
|
|
1937
|
+
const out = await won.thread.send(target, init, replayable, deadline);
|
|
1938
|
+
if (out === VIA_FETCH)
|
|
1939
|
+
return this.viaFetch(url, init, deadline);
|
|
1940
|
+
if (!(out instanceof HandBack))
|
|
1941
|
+
return out;
|
|
1942
|
+
resends = out.resends;
|
|
1943
|
+
replays = out.replays;
|
|
1944
|
+
if (tr !== undefined)
|
|
1945
|
+
delete tr.via;
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
entry = won.entry;
|
|
1949
|
+
}
|
|
1950
|
+
else {
|
|
1951
|
+
entry = await pool.acquire(deadline, init.signal);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1810
1954
|
if (tr !== undefined)
|
|
1811
1955
|
mark(tr, "acq");
|
|
1812
1956
|
if (entry === null)
|
|
@@ -1905,7 +2049,7 @@ export const __http2Internals = {
|
|
|
1905
2049
|
if (t === undefined)
|
|
1906
2050
|
return undefined;
|
|
1907
2051
|
t.poll();
|
|
1908
|
-
return { state: t.state, sessions: t.sessions, limit: t.limit, answered: t.answered, everReady: t.everReady, closedReason: t.closedReason, messages: t.messages, posts: t.posts };
|
|
2052
|
+
return { state: t.state, sessions: t.sessions, limit: t.limit, answered: t.answered, everReady: t.everReady, closedReason: t.closedReason, messages: t.messages, posts: t.posts, startedAt: t.startedAt };
|
|
1909
2053
|
},
|
|
1910
2054
|
threadKnobs(env) {
|
|
1911
2055
|
return threadKnobs(env);
|
|
@@ -1954,6 +2098,15 @@ function isMainThread() {
|
|
|
1954
2098
|
return false;
|
|
1955
2099
|
}
|
|
1956
2100
|
}
|
|
2101
|
+
function standby(url) {
|
|
2102
|
+
try {
|
|
2103
|
+
traceEvent("h2:standby");
|
|
2104
|
+
warmTls();
|
|
2105
|
+
poolFor(url.origin).warm(tuning.loadFirstUseMs);
|
|
2106
|
+
}
|
|
2107
|
+
catch {
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
1957
2110
|
function connectAtLoad() {
|
|
1958
2111
|
try {
|
|
1959
2112
|
if (!isNode() || Number(process.versions.node.split(".")[0]) < 22)
|
|
@@ -1974,6 +2127,8 @@ function connectAtLoad() {
|
|
|
1974
2127
|
if (url.protocol === "https:" && !customDispatcher() && earlyPlatform() && v4First() && !threadOff && earlySeam().transportThread !== false) {
|
|
1975
2128
|
startTransportThread(url, () => { if (loadTurnRan)
|
|
1976
2129
|
warmAtLoad(); });
|
|
2130
|
+
if (isIP(host) === 0)
|
|
2131
|
+
earlyLookupFor(host);
|
|
1977
2132
|
}
|
|
1978
2133
|
else if (url.protocol === "https:" && !customDispatcher() && isIP(host) === 0 && earlyPlatform() && v4First())
|
|
1979
2134
|
earlyLookupFor(host);
|
|
@@ -1992,10 +2147,27 @@ function connectAtLoad() {
|
|
|
1992
2147
|
const url = new URL(raw ? raw : DEFAULT_API_URL);
|
|
1993
2148
|
if (url.protocol !== "https:" || customDispatcher())
|
|
1994
2149
|
return;
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2150
|
+
const t = transportThread;
|
|
2151
|
+
if (t !== undefined && t.origin === url.origin) {
|
|
2152
|
+
t.poll();
|
|
2153
|
+
if (t.everReady)
|
|
2154
|
+
return;
|
|
2155
|
+
if (t.state !== "closed") {
|
|
2156
|
+
const check = () => {
|
|
2157
|
+
t.poll();
|
|
2158
|
+
if (t.everReady || t.state === "closed")
|
|
2159
|
+
return;
|
|
2160
|
+
const wait = t.startedAt + tuning.standbyAfterMs - performance.now();
|
|
2161
|
+
if (wait > 0) {
|
|
2162
|
+
const timer = setTimeout(check, wait);
|
|
2163
|
+
timer.unref?.();
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
standby(url);
|
|
2167
|
+
};
|
|
2168
|
+
check();
|
|
1998
2169
|
return;
|
|
2170
|
+
}
|
|
1999
2171
|
}
|
|
2000
2172
|
warmTls();
|
|
2001
2173
|
poolFor(url.origin).warm(tuning.loadFirstUseMs);
|
package/dist/trace.d.ts
CHANGED
|
@@ -31,3 +31,5 @@ export declare function mark(s: TraceStamp | undefined, phase: string): void;
|
|
|
31
31
|
export declare function traceEvent(name: string): void;
|
|
32
32
|
/** A process event that happened at `t` (`performance.now()` terms), recorded now: one another thread reported. */
|
|
33
33
|
export declare function traceEventAt(name: string, t: number): void;
|
|
34
|
+
/** `fn` runs once at exit, before the trace is written (tracing on only). */
|
|
35
|
+
export declare function onTraceFlush(fn: () => void): void;
|
package/dist/trace.js
CHANGED
|
@@ -60,7 +60,23 @@ function modulePath() {
|
|
|
60
60
|
const m = /\((.*):\d+:\d+\)\s*$/.exec(frame) ?? /at (.*):\d+:\d+\s*$/.exec(frame);
|
|
61
61
|
return m?.[1] ?? "";
|
|
62
62
|
}
|
|
63
|
+
const beforeFlush = [];
|
|
64
|
+
export function onTraceFlush(fn) {
|
|
65
|
+
if (!TRACE_ON)
|
|
66
|
+
return;
|
|
67
|
+
beforeFlush.push(fn);
|
|
68
|
+
if (!hooked) {
|
|
69
|
+
hooked = true;
|
|
70
|
+
process.once("exit", flush);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
63
73
|
function flush() {
|
|
74
|
+
for (const fn of beforeFlush.splice(0)) {
|
|
75
|
+
try {
|
|
76
|
+
fn();
|
|
77
|
+
}
|
|
78
|
+
catch { }
|
|
79
|
+
}
|
|
64
80
|
const head = {
|
|
65
81
|
trace: "promigence-sdk",
|
|
66
82
|
version: VERSION,
|
package/dist/transport-worker.js
CHANGED
|
@@ -34,6 +34,11 @@ function start(init) {
|
|
|
34
34
|
}
|
|
35
35
|
catch { }
|
|
36
36
|
};
|
|
37
|
+
const ev = (n) => {
|
|
38
|
+
if (init.trace === true)
|
|
39
|
+
post({ t: "ev", n, hr: hrNow() });
|
|
40
|
+
};
|
|
41
|
+
ev("boot");
|
|
37
42
|
const live = () => sessions.filter((e) => !e.retired && !e.session.closed && !e.session.destroyed);
|
|
38
43
|
const end = (reason) => {
|
|
39
44
|
if (closed)
|
|
@@ -94,14 +99,14 @@ function start(init) {
|
|
|
94
99
|
catch {
|
|
95
100
|
context = undefined;
|
|
96
101
|
}
|
|
97
|
-
const connect = (address) => new Promise((resolve) => {
|
|
102
|
+
const connect = (address, k) => new Promise((resolve) => {
|
|
98
103
|
let done = false;
|
|
99
104
|
const finish = (e) => { if (!done) {
|
|
100
105
|
done = true;
|
|
101
106
|
clearTimeout(timer);
|
|
102
107
|
resolve(e);
|
|
103
108
|
} };
|
|
104
|
-
const timer = setTimeout(() => { try {
|
|
109
|
+
const timer = setTimeout(() => { ev(`timeout${k}`); try {
|
|
105
110
|
socket.destroy();
|
|
106
111
|
}
|
|
107
112
|
catch { } finish(null); }, init.connectTimeoutMs);
|
|
@@ -113,8 +118,11 @@ function start(init) {
|
|
|
113
118
|
checkServerIdentity: guardCertChain,
|
|
114
119
|
...(context === undefined ? {} : { secureContext: context }),
|
|
115
120
|
});
|
|
116
|
-
|
|
121
|
+
if (init.trace === true)
|
|
122
|
+
socket.once("connect", () => { ev(`tcp${k}`); });
|
|
123
|
+
socket.once("error", (e) => { ev(`fail${k} ${e?.code ?? "error"}`); finish(null); });
|
|
117
124
|
socket.once("secureConnect", () => {
|
|
125
|
+
ev(`tls${k}`);
|
|
118
126
|
if (socket.alpnProtocol !== "h2") {
|
|
119
127
|
socket.destroy();
|
|
120
128
|
finish(null);
|
|
@@ -167,14 +175,66 @@ function start(init) {
|
|
|
167
175
|
resolve(v4);
|
|
168
176
|
return;
|
|
169
177
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
const at = init.lookupAtMs.length > 0 ? init.lookupAtMs : [0];
|
|
179
|
+
const resolvers = [];
|
|
180
|
+
const timers = [];
|
|
181
|
+
let done = false;
|
|
182
|
+
let asked = 0;
|
|
183
|
+
let failed = 0;
|
|
184
|
+
const finish = (addrs) => {
|
|
185
|
+
if (done)
|
|
186
|
+
return;
|
|
187
|
+
done = true;
|
|
188
|
+
for (const t of timers)
|
|
189
|
+
clearTimeout(t);
|
|
190
|
+
for (const r of resolvers) {
|
|
191
|
+
try {
|
|
192
|
+
r.cancel();
|
|
193
|
+
}
|
|
194
|
+
catch { }
|
|
195
|
+
}
|
|
196
|
+
resolve(addrs);
|
|
197
|
+
};
|
|
198
|
+
const gaveUp = () => {
|
|
199
|
+
if (++failed < asked)
|
|
200
|
+
return;
|
|
201
|
+
if (asked < at.length)
|
|
202
|
+
ask();
|
|
203
|
+
else
|
|
204
|
+
finish([]);
|
|
205
|
+
};
|
|
206
|
+
const ask = () => {
|
|
207
|
+
if (done || asked >= at.length)
|
|
208
|
+
return;
|
|
209
|
+
const k = asked++;
|
|
210
|
+
try {
|
|
211
|
+
const r = new dns.Resolver({ timeout: init.lookupTimeoutMs, tries: init.lookupTries });
|
|
212
|
+
const list = Array.isArray(init.servers) && init.servers.length > 0 ? init.servers : r.getServers();
|
|
213
|
+
if (list.length > 0)
|
|
214
|
+
r.setServers(list.map((_, i) => list[(i + k) % list.length]));
|
|
215
|
+
resolvers.push(r);
|
|
216
|
+
ev(`lookup${k + 1}`);
|
|
217
|
+
r.resolve4(name, (err, addrs) => {
|
|
218
|
+
if (done)
|
|
219
|
+
return;
|
|
220
|
+
if (!err && Array.isArray(addrs) && addrs.length > 0) {
|
|
221
|
+
ev(`addrs${k + 1} ${addrs.length}`);
|
|
222
|
+
finish(addrs);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
ev(`nolookup${k + 1} ${err?.code ?? "empty"}`);
|
|
226
|
+
gaveUp();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
gaveUp();
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
for (const ms of at) {
|
|
234
|
+
if (ms <= 0)
|
|
235
|
+
ask();
|
|
236
|
+
else
|
|
237
|
+
timers.push(setTimeout(ask, ms));
|
|
178
238
|
}
|
|
179
239
|
});
|
|
180
240
|
const limitOf = (e) => {
|
|
@@ -196,7 +256,7 @@ function start(init) {
|
|
|
196
256
|
}
|
|
197
257
|
const n = Math.max(1, init.sessions);
|
|
198
258
|
pendingConnects = n;
|
|
199
|
-
const tries = Array.from({ length: n }, (_, i) => connect(addrs[i % addrs.length]).then((e) => {
|
|
259
|
+
const tries = Array.from({ length: n }, (_, i) => connect(addrs[i % addrs.length], i).then((e) => {
|
|
200
260
|
pendingConnects--;
|
|
201
261
|
if (closed) {
|
|
202
262
|
if (e)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promigence/sdk",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.14",
|
|
4
4
|
"description": "The fastest sandboxes for AI agents and code. The Promigence TypeScript SDK: snapshot an environment once, fork it as often as you need, run commands in every copy. A simple Sandbox class and the full PromigenceClient, with zero runtime dependencies.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://www.promigence.ai",
|