@promigence/sdk 0.1.0-beta.13 → 0.1.0-beta.15

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/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 EARLY_TIMEOUT_MS = 2_000;
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 r = new (require("node:dns").Resolver)({ timeout: timeoutMs, tries: 1 });
895
- if (Array.isArray(servers) && servers.length > 0) r.setServers(servers);
896
- r.resolve4(host, { ttl: true }, (err, recs) => {
897
- if (err || !Array.isArray(recs) || recs.length === 0) return done({ host, none: (err && err.code) || "empty" });
898
- done({ host, addrs: recs.map((x) => x.address), ttl: Math.min(...recs.map((x) => x.ttl)), at: Date.now() });
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: EARLY_TIMEOUT_MS,
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
- if (!this.listening) {
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: EARLY_TIMEOUT_MS, trace: trace_js_1.TRACE_ON,
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
- const entry = pool.tryAcquire() ?? await pool.acquire(deadline, init.signal);
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
- if (transportThread !== undefined && transportThread.origin === url.origin) {
2008
- transportThread.poll();
2009
- if (transportThread.state !== "closed" || transportThread.everReady)
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);