@antseed/node 0.2.114 → 0.2.115

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.
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Background chain-RPC reachability monitor.
3
+ *
4
+ * Payments must never be disabled for a whole session because the RPC was
5
+ * unreachable for 1.5 seconds at launch: buyer-side payments only *sign*
6
+ * off-chain (ReserveAuth / SpendingAuth) and all on-chain reads at request
7
+ * time are best-effort. This monitor turns "is the chain reachable?" into a
8
+ * live signal — callers keep payments enabled and consult `reachable` to skip
9
+ * on-chain reads while the RPC is down, instead of deciding once at startup.
10
+ *
11
+ * Probing stops after the first success — there is no steady-state polling.
12
+ * A later outage is re-detected on demand: call sites report observed on-chain
13
+ * failures via `reportFailure()`, which restarts the backoff probe loop until
14
+ * the RPC answers again. `probeNow()` runs a single re-check on demand.
15
+ */
16
+ const DEFAULT_PROBE_TIMEOUT_MS = 1_500;
17
+ const DEFAULT_RETRY_BASE_MS = 5_000;
18
+ const DEFAULT_RETRY_MAX_MS = 60_000;
19
+ /** POST eth_chainId to one endpoint; true only for a well-formed 0x result. */
20
+ export async function probeRpcEndpoint(rpcUrl, timeoutMs = DEFAULT_PROBE_TIMEOUT_MS) {
21
+ const controller = new AbortController();
22
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
23
+ try {
24
+ const response = await fetch(rpcUrl, {
25
+ method: 'POST',
26
+ headers: { 'content-type': 'application/json' },
27
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_chainId', params: [] }),
28
+ signal: controller.signal,
29
+ });
30
+ if (!response.ok)
31
+ return false;
32
+ const payload = await response.json();
33
+ return typeof payload.result === 'string' && payload.result.startsWith('0x');
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ finally {
39
+ clearTimeout(timer);
40
+ }
41
+ }
42
+ export class RpcHealthMonitor {
43
+ _rpcUrls;
44
+ _probeTimeoutMs;
45
+ _retryBaseMs;
46
+ _retryMaxMs;
47
+ _onReady = [];
48
+ _state = 'unknown';
49
+ _lastCheckedAt = null;
50
+ _lastReadyAt = null;
51
+ _lastError = null;
52
+ _attempts = 0;
53
+ _timer = null;
54
+ _probing = null;
55
+ _loopActive = false;
56
+ _stopped = false;
57
+ constructor(options) {
58
+ if (options.rpcUrls.length === 0) {
59
+ throw new Error('RpcHealthMonitor requires at least one RPC URL');
60
+ }
61
+ this._rpcUrls = [...options.rpcUrls];
62
+ this._probeTimeoutMs = options.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
63
+ this._retryBaseMs = options.retryBaseMs ?? DEFAULT_RETRY_BASE_MS;
64
+ this._retryMaxMs = options.retryMaxMs ?? DEFAULT_RETRY_MAX_MS;
65
+ }
66
+ /**
67
+ * False only while the RPC is known-unreachable. 'unknown' counts as
68
+ * reachable so the first requests are not gated on a probe that has not
69
+ * finished — call sites tolerate their own on-chain failures.
70
+ */
71
+ get reachable() {
72
+ return this._state !== 'unreachable';
73
+ }
74
+ status() {
75
+ return {
76
+ state: this._state,
77
+ lastCheckedAt: this._lastCheckedAt,
78
+ lastReadyAt: this._lastReadyAt,
79
+ lastError: this._lastError,
80
+ attempts: this._attempts,
81
+ };
82
+ }
83
+ /** Fired on every transition into 'ready' (first success after start or after a failed run). */
84
+ onReady(listener) {
85
+ this._onReady.push(listener);
86
+ }
87
+ /** Begin probing; retries with backoff until the first success, then stops. */
88
+ start() {
89
+ this._stopped = false;
90
+ if (!this._loopActive) {
91
+ void this._runProbeLoop(0);
92
+ }
93
+ }
94
+ stop() {
95
+ this._stopped = true;
96
+ this._loopActive = false;
97
+ if (this._timer) {
98
+ clearTimeout(this._timer);
99
+ this._timer = null;
100
+ }
101
+ }
102
+ /**
103
+ * Tell the monitor an on-chain read just failed. Restarts background probing
104
+ * (with backoff) so a mid-session outage is re-detected and `reachable`
105
+ * flips to false until the RPC answers again. No-op while a probe loop is
106
+ * already running, so bursts of failures cost at most one probe round.
107
+ */
108
+ reportFailure() {
109
+ if (this._stopped || this._loopActive)
110
+ return;
111
+ void this._runProbeLoop(0);
112
+ }
113
+ /** One on-demand probe round; updates state and returns the outcome. */
114
+ probeNow() {
115
+ if (this._probing)
116
+ return this._probing;
117
+ const run = this._probeOnce().finally(() => {
118
+ this._probing = null;
119
+ });
120
+ this._probing = run;
121
+ return run;
122
+ }
123
+ async _probeOnce() {
124
+ let ok = false;
125
+ let lastError = null;
126
+ for (const url of this._rpcUrls) {
127
+ if (await probeRpcEndpoint(url, this._probeTimeoutMs)) {
128
+ ok = true;
129
+ break;
130
+ }
131
+ lastError = `no eth_chainId response from ${url} within ${this._probeTimeoutMs}ms`;
132
+ }
133
+ this._attempts += 1;
134
+ this._lastCheckedAt = Date.now();
135
+ if (ok) {
136
+ const wasReady = this._state === 'ready';
137
+ this._state = 'ready';
138
+ this._lastReadyAt = this._lastCheckedAt;
139
+ this._lastError = null;
140
+ if (!wasReady) {
141
+ for (const listener of this._onReady) {
142
+ try {
143
+ listener();
144
+ }
145
+ catch {
146
+ // Listener errors must not break the monitor.
147
+ }
148
+ }
149
+ }
150
+ }
151
+ else {
152
+ this._state = 'unreachable';
153
+ this._lastError = lastError;
154
+ }
155
+ return ok;
156
+ }
157
+ async _runProbeLoop(round) {
158
+ if (this._stopped) {
159
+ this._loopActive = false;
160
+ return;
161
+ }
162
+ this._loopActive = true;
163
+ const ok = await this.probeNow();
164
+ if (ok || this._stopped) {
165
+ this._loopActive = false;
166
+ return;
167
+ }
168
+ const delay = Math.min(this._retryBaseMs * 2 ** round, this._retryMaxMs);
169
+ this._timer = setTimeout(() => {
170
+ this._timer = null;
171
+ void this._runProbeLoop(round + 1);
172
+ }, delay);
173
+ // Do not hold the process open just to re-probe a dead RPC.
174
+ this._timer.unref?.();
175
+ }
176
+ }
177
+ //# sourceMappingURL=rpc-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-health.js","sourceRoot":"","sources":["../../src/payments/rpc-health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAuBH,MAAM,wBAAwB,GAAG,KAAK,CAAC;AACvC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AACpC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAEpC,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,SAAS,GAAG,wBAAwB;IACzF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;YACnC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YAClF,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;QAC/B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA0B,CAAC;QAC9D,OAAO,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC/E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,OAAO,gBAAgB;IACV,QAAQ,CAAW;IACnB,eAAe,CAAS;IACxB,YAAY,CAAS;IACrB,WAAW,CAAS;IACpB,QAAQ,GAAsB,EAAE,CAAC;IAE1C,MAAM,GAAmB,SAAS,CAAC;IACnC,cAAc,GAAkB,IAAI,CAAC;IACrC,YAAY,GAAkB,IAAI,CAAC;IACnC,UAAU,GAAkB,IAAI,CAAC;IACjC,SAAS,GAAG,CAAC,CAAC;IACd,MAAM,GAAyC,IAAI,CAAC;IACpD,QAAQ,GAA4B,IAAI,CAAC;IACzC,WAAW,GAAG,KAAK,CAAC;IACpB,QAAQ,GAAG,KAAK,CAAC;IAEzB,YAAY,OAAgC;QAC1C,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,cAAc,IAAI,wBAAwB,CAAC;QAC1E,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,WAAW,IAAI,qBAAqB,CAAC;QACjE,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,UAAU,IAAI,oBAAoB,CAAC;IAChE,CAAC;IAED;;;;OAIG;IACH,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,KAAK,aAAa,CAAC;IACvC,CAAC;IAED,MAAM;QACJ,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,MAAM;YAClB,aAAa,EAAE,IAAI,CAAC,cAAc;YAClC,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,QAAQ,EAAE,IAAI,CAAC,SAAS;SACzB,CAAC;IACJ,CAAC;IAED,gGAAgG;IAChG,OAAO,CAAC,QAAoB;QAC1B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAED,+EAA+E;IAC/E,KAAK;QACH,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,KAAK,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,IAAI;QACF,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,aAAa;QACX,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9C,KAAK,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IAED,wEAAwE;IACxE,QAAQ;QACN,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;QACpB,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,IAAI,EAAE,GAAG,KAAK,CAAC;QACf,IAAI,SAAS,GAAkB,IAAI,CAAC;QACpC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChC,IAAI,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;gBACtD,EAAE,GAAG,IAAI,CAAC;gBACV,MAAM;YACR,CAAC;YACD,SAAS,GAAG,gCAAgC,GAAG,WAAW,IAAI,CAAC,eAAe,IAAI,CAAC;QACrF,CAAC;QACD,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QACpB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,IAAI,EAAE,EAAE,CAAC;YACP,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,OAAO,CAAC;YACzC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;YACtB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC;YACxC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACrC,IAAI,CAAC;wBACH,QAAQ,EAAE,CAAC;oBACb,CAAC;oBAAC,MAAM,CAAC;wBACP,8CAA8C;oBAChD,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC;YAC5B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC9B,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,KAAa;QACvC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjC,IAAI,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACzE,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,KAAK,IAAI,CAAC,aAAa,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACrC,CAAC,EAAE,KAAK,CAAC,CAAC;QACV,4DAA4D;QAC5D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;IACxB,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antseed/node",
3
- "version": "0.2.114",
3
+ "version": "0.2.115",
4
4
  "description": "Antseed Network protocol SDK — P2P inference marketplace",
5
5
  "type": "module",
6
6
  "files": [
@@ -62,8 +62,8 @@
62
62
  "node-datachannel": "^0.7.0",
63
63
  "tokenx": "^1.3.0",
64
64
  "@antseed/api-adapter": "0.1.45",
65
- "@antseed/protocol": "0.1.4",
66
- "@antseed/buyer-core": "0.1.6"
65
+ "@antseed/buyer-core": "0.1.7",
66
+ "@antseed/protocol": "0.1.4"
67
67
  },
68
68
  "optionalDependencies": {
69
69
  "keytar": "^7.9.0"