@openrai/nano-core 1.0.0 → 2.1.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 +119 -47
- package/dist/client.d.ts +18 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +71 -3
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/transport/EndpointPool.d.ts +16 -0
- package/dist/transport/EndpointPool.d.ts.map +1 -0
- package/dist/transport/EndpointPool.js +105 -0
- package/dist/transport/EndpointPool.js.map +1 -0
- package/dist/transport/errors.d.ts +4 -0
- package/dist/transport/errors.d.ts.map +1 -0
- package/dist/transport/errors.js +7 -0
- package/dist/transport/errors.js.map +1 -0
- package/dist/transport/http.d.ts +13 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +60 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/index.d.ts +7 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +6 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/normalize.d.ts +10 -0
- package/dist/transport/normalize.d.ts.map +1 -0
- package/dist/transport/normalize.js +100 -0
- package/dist/transport/normalize.js.map +1 -0
- package/dist/transport/types.d.ts +57 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/types.js +2 -0
- package/dist/transport/types.js.map +1 -0
- package/dist/transport/ws.d.ts +10 -0
- package/dist/transport/ws.d.ts.map +1 -0
- package/dist/transport/ws.js +29 -0
- package/dist/transport/ws.js.map +1 -0
- package/dist/work/WorkProvider.d.ts +7 -0
- package/dist/work/WorkProvider.d.ts.map +1 -1
- package/dist/work/WorkProvider.js +33 -3
- package/dist/work/WorkProvider.js.map +1 -1
- package/package.json +17 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
Historically, Nano developers have faced a fragmented integration landscape: ranging from "heavy, enterprise:y" backends to lightweight "frontend-only" implementation, suffering either from rigid vendor lock-in or fragile PoW architecture.
|
|
13
13
|
|
|
14
|
-
**`@openrai/nano-core`**
|
|
14
|
+
**`@openrai/nano-core`** aims to be the canonical TypeScript Nano package: typed primitives, sane defaults, transport failover, endpoint normalization, auth handling, and a stable first-party API surface even when the internals wrap other best-of-breed libraries.
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -20,7 +20,8 @@ Historically, Nano developers have faced a fragmented integration landscape: ran
|
|
|
20
20
|
* **Bypassing the "Frontier Dilemma":** Nano is a state-based block-lattice where each block depends strictly on the exact final state of the previous frontier. This strong design choise is one of the main reasons why Nano can be feeless and energy efficient, but it often becomes a stumbling block for integrations. `@openrai/nano-core` provides internal concurrent Mutex queuing to perfectly sequentialize blocks even under heavy asynchronous loads, eliminating "Fork" or "Gap" errors.
|
|
21
21
|
* **Isomorphic Proof-of-Work (JIT Profiling):** Wraps `nano-pow-with-fallback` in a Just-In-Time (JIT) environment profiler via `WorkProvider.auto()`. What it means is that whether running on an Apple Silicon Node.js server (jumping straight to local WebGPU) or on an aging mobile browser (delegating safely to remote servers by default), which generation method to use can be decided dynamically, without UI blocking or interfering with the current user flow.
|
|
22
22
|
* **No Primitive-Obsession:** Heavily-typed, precision-safe wrappers for `NanoAmount` and `NanoAddress` entirely eliminate the "Stringly-Typed Money" programming anti-pattern.
|
|
23
|
-
* **Resilient RPC Fallbacks:**
|
|
23
|
+
* **Resilient RPC / WS / Work Fallbacks:** Endpoint pools validate eagerly, deduplicate, apply endpoint-local exponential backoff, and only fail once all viable options are exhausted.
|
|
24
|
+
* **Secret-Safe Endpoint Normalization:** API keys from query params or URL userinfo are converted into structured auth metadata and redacted from canonical URLs and audit output.
|
|
24
25
|
* **Cross-Language FFI Preparation**: The TypeScript architecture strictly follows Domain-Driven Design (DDD) to promote close API compatibility across different eventual programming language ports of the library.
|
|
25
26
|
|
|
26
27
|
## 📦 Installation
|
|
@@ -32,85 +33,155 @@ npm install @openrai/nano-core
|
|
|
32
33
|
|
|
33
34
|
## 🛠 Quick Start
|
|
34
35
|
|
|
35
|
-
### 1.
|
|
36
|
-
The
|
|
36
|
+
### 1. Minimal Client
|
|
37
|
+
The default surface should feel obvious on first read:
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
```typescript
|
|
40
|
+
import { NanoClient } from '@openrai/nano-core';
|
|
41
|
+
|
|
42
|
+
const client = NanoClient.initialize({
|
|
43
|
+
rpc: [
|
|
44
|
+
'https://rpc.primary.example.com?apiKey=secret-rpc',
|
|
45
|
+
'https://rpc.nano.to',
|
|
46
|
+
], // [Optional] Defaults to the April 2026 public RPC set
|
|
47
|
+
ws: [
|
|
48
|
+
'wss://ws.primary.example.com?api_key=secret-ws',
|
|
49
|
+
'wss://rpc.nano.to',
|
|
50
|
+
], // [Optional] Defaults to public WebSocket endpoints
|
|
51
|
+
work: [
|
|
52
|
+
'https://work.primary.example.com?key=secret-work',
|
|
53
|
+
'https://rpc.nano.to',
|
|
54
|
+
], // [Optional] Defaults to `https://rpc.nano.to` as the current public work endpoint
|
|
55
|
+
warn: (message) => console.warn(message), // [Optional] Defaults to console.warn with nano-core prefix
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log(client.getAuditReport());
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 1.1 Observe Endpoint Selection
|
|
62
|
+
|
|
63
|
+
Long-running services can observe which upstream endpoint became active without
|
|
64
|
+
mutating transport configuration at runtime:
|
|
40
65
|
|
|
41
66
|
```typescript
|
|
42
67
|
import { NanoClient } from '@openrai/nano-core';
|
|
43
68
|
|
|
44
|
-
|
|
45
|
-
|
|
69
|
+
const client = NanoClient.initialize();
|
|
70
|
+
|
|
71
|
+
const unsubscribe = client.onEndpointChange((event) => {
|
|
72
|
+
console.log(event.kind, event.status, event.activeUrl, event.previousUrl);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
console.log(client.getActiveEndpoints());
|
|
76
|
+
// { rpc?: string, ws?: string, work?: string }
|
|
77
|
+
|
|
78
|
+
// Later:
|
|
79
|
+
unsubscribe();
|
|
46
80
|
```
|
|
47
81
|
|
|
48
|
-
|
|
49
|
-
|
|
82
|
+
This is an observe-only API. It does not reconfigure pools or alter failover
|
|
83
|
+
policy; it simply exposes which endpoint was selected after a successful RPC,
|
|
84
|
+
WS, or work operation.
|
|
85
|
+
|
|
86
|
+
What this buys you immediately:
|
|
87
|
+
|
|
88
|
+
- comma-separated env vars also work: `NANO_RPC_URL`, `NANO_WS_URL`, `NANO_WORK_URL`
|
|
89
|
+
- malformed or duplicate endpoints are dropped during construction
|
|
90
|
+
- API keys in query params or URL userinfo are extracted and redacted from audit output
|
|
91
|
+
- failover and endpoint-local backoff happen inside the pool, not in your app code
|
|
92
|
+
|
|
93
|
+
Current built-in defaults as of April 2026:
|
|
94
|
+
|
|
95
|
+
- RPC: `https://rpc.nano.to`, `https://node.somenano.com/proxy`, `https://rainstorm.city/api`, `https://nanoslo.0x.no/proxy`
|
|
96
|
+
- WS: `wss://rpc.nano.to`
|
|
97
|
+
- Work: `https://rpc.nano.to`
|
|
98
|
+
|
|
99
|
+
### 2. Zero-Config Prototype Mode
|
|
100
|
+
Out of the box, `NanoClient.initialize()` falls back to default public RPC / WS / work pools and auto-configures the work provider:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { NanoClient } from '@openrai/nano-core';
|
|
104
|
+
|
|
105
|
+
const client = NanoClient.initialize();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 3. Explicit Override Mode
|
|
109
|
+
For production environments, you can override only the pieces you care about while keeping the same normalized pool behavior:
|
|
50
110
|
|
|
51
111
|
```typescript
|
|
52
|
-
import {
|
|
53
|
-
NanoClient,
|
|
54
|
-
TransportFallback,
|
|
112
|
+
import {
|
|
113
|
+
NanoClient,
|
|
55
114
|
WorkProvider,
|
|
56
115
|
RemoteWorkServer,
|
|
57
|
-
LocalCompute
|
|
116
|
+
LocalCompute,
|
|
58
117
|
} from '@openrai/nano-core';
|
|
59
118
|
|
|
60
|
-
const
|
|
61
|
-
network: 'mainnet', // [Optional]
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// [Optional]
|
|
119
|
+
const client = NanoClient.initialize({
|
|
120
|
+
network: 'mainnet', // [Optional] Defaults to 'mainnet'
|
|
121
|
+
rpc: [
|
|
122
|
+
'https://rpc.private.example.com?apiKey=secret-rpc',
|
|
123
|
+
'https://rpc.nano.to',
|
|
124
|
+
], // [Optional] Defaults to the April 2026 public RPC set
|
|
125
|
+
ws: [
|
|
126
|
+
'wss://ws.private.example.com?api_key=secret-ws',
|
|
127
|
+
'wss://rpc.nano.to',
|
|
128
|
+
], // [Optional] Defaults to public WebSocket endpoints
|
|
70
129
|
workProvider: WorkProvider.auto({
|
|
130
|
+
urls: [
|
|
131
|
+
'https://work.private.example.com?key=secret-work',
|
|
132
|
+
], // [Optional] Defaults to `https://rpc.nano.to` when omitted
|
|
71
133
|
remotes: [
|
|
72
|
-
RemoteWorkServer.of(
|
|
73
|
-
|
|
134
|
+
RemoteWorkServer.of(
|
|
135
|
+
'https://work-backup.example.com',
|
|
136
|
+
{
|
|
137
|
+
timeoutMs: 5000, // [Optional] Defaults to 5000
|
|
138
|
+
circuitBreakerMs: 30000, // [Optional] Reserved for future tuning
|
|
139
|
+
},
|
|
140
|
+
),
|
|
141
|
+
], // [Optional] Additional remote work backends
|
|
74
142
|
localChain: [
|
|
75
|
-
LocalCompute.WEBGPU,
|
|
76
|
-
LocalCompute.WASM_THREADS,
|
|
77
|
-
LocalCompute.CPU
|
|
78
|
-
],
|
|
143
|
+
LocalCompute.WEBGPU,
|
|
144
|
+
LocalCompute.WASM_THREADS,
|
|
145
|
+
LocalCompute.CPU,
|
|
146
|
+
], // [Optional] Local fallback order
|
|
79
147
|
profiler: {
|
|
80
|
-
mode: 'manual',
|
|
81
|
-
preferLocalAboveMhs: 30,
|
|
82
|
-
cacheStrategy: 'persistent'
|
|
83
|
-
}
|
|
84
|
-
|
|
148
|
+
mode: 'manual',
|
|
149
|
+
preferLocalAboveMhs: 30,
|
|
150
|
+
cacheStrategy: 'persistent',
|
|
151
|
+
}, // [Optional] Calibration strategy overrides
|
|
152
|
+
warn: (message) => console.warn(message), // [Optional] Defaults to console.warn with nano-core prefix
|
|
153
|
+
}), // [Optional] Defaults to WorkProvider.auto(...) using normalized work endpoints
|
|
154
|
+
warn: (message) => console.warn(message), // [Optional] Defaults to console.warn with nano-core prefix
|
|
85
155
|
});
|
|
86
156
|
```
|
|
87
157
|
|
|
88
|
-
###
|
|
158
|
+
### 4. Isomorphic Work Calibration
|
|
89
159
|
It's critical not to freeze the browser threads. On boot, evaluate the environment. `nano-core` remembers the hardware constraints across sessions.
|
|
90
160
|
|
|
91
161
|
```typescript
|
|
92
162
|
// Auto-detects whether local WebGPU/WASM is faster than network latency to remotes
|
|
93
|
-
const profile = await
|
|
163
|
+
const profile = await client.workProvider.calibrate();
|
|
94
164
|
console.log(`Determined active PoW strategy: ${profile.activeStrategy} at ${profile.measuredMhs} MH/s.`);
|
|
95
165
|
```
|
|
96
166
|
|
|
97
|
-
###
|
|
98
|
-
`NanoAmount`
|
|
167
|
+
### 5. Precision-Safe Execution
|
|
168
|
+
`NanoAmount` and `NanoAddress` give you typed protocol primitives immediately, even while the higher-level wallet surface is still being filled out.
|
|
99
169
|
|
|
100
170
|
```typescript
|
|
101
171
|
import { NanoAddress, NanoAmount } from '@openrai/nano-core';
|
|
102
172
|
|
|
103
|
-
|
|
104
|
-
|
|
173
|
+
const destination = NanoAddress.parse(
|
|
174
|
+
'nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4',
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const amount = NanoAmount.fromNano('1.25');
|
|
105
178
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
NanoAddress.parse('nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4'),
|
|
110
|
-
NanoAmount.fromNano('1.25')
|
|
179
|
+
const wallet = await client.hydrateWallet(
|
|
180
|
+
process.env.NANO_SEED,
|
|
181
|
+
{ index: 0 }, // [Optional] Defaults to account index 0
|
|
111
182
|
);
|
|
112
183
|
|
|
113
|
-
console.log(
|
|
184
|
+
console.log({ destination, amount, wallet });
|
|
114
185
|
```
|
|
115
186
|
|
|
116
187
|
## 📐 Architecture Comparison
|
|
@@ -160,3 +231,4 @@ Across all tested engines, WebGL is the most consistently paced API. Because it
|
|
|
160
231
|
These findings conclusively validate the `WorkProvider.auto()` architecture. The SDK cannot assume local hardware is safe just because `navigator.gpu` exists.
|
|
161
232
|
|
|
162
233
|
The JIT Environment Profiler must execute a sub-50ms micro-probe on load. If the probe detects a sandboxed environment where a `Send` block will trigger the "Throttle Cliff" (taking >5 seconds), the SDK must intelligently route the work to a remote BPoW server, ensuring the application remains responsive and the user's battery is preserved.
|
|
234
|
+
See `docs/architecture/transport-auth.md` for the full transport/auth design.
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { HttpEndpointPool } from './transport/http.js';
|
|
2
|
+
import { WsEndpointPool } from './transport/ws.js';
|
|
1
3
|
import { WorkProvider } from './work/WorkProvider.js';
|
|
4
|
+
import type { EndpointActivityEvent } from './transport/types.js';
|
|
2
5
|
export interface TransportFallback {
|
|
3
6
|
urls: string[];
|
|
4
7
|
}
|
|
@@ -8,13 +11,28 @@ export declare const TransportFallback: {
|
|
|
8
11
|
export interface NanoClientOptions {
|
|
9
12
|
network?: 'mainnet' | 'testnet' | 'beta';
|
|
10
13
|
transports?: TransportFallback;
|
|
14
|
+
rpc?: string[];
|
|
15
|
+
ws?: string[];
|
|
16
|
+
work?: string[];
|
|
11
17
|
workProvider?: WorkProvider;
|
|
18
|
+
warn?: (message: string) => void;
|
|
19
|
+
}
|
|
20
|
+
export interface NanoClientActiveEndpoints {
|
|
21
|
+
rpc?: string;
|
|
22
|
+
ws?: string;
|
|
23
|
+
work?: string;
|
|
12
24
|
}
|
|
13
25
|
export declare class NanoClient {
|
|
14
26
|
workProvider: WorkProvider;
|
|
27
|
+
rpcPool: HttpEndpointPool;
|
|
28
|
+
wsPool: WsEndpointPool;
|
|
15
29
|
private options;
|
|
30
|
+
private readonly endpointListeners;
|
|
31
|
+
private readonly activeEndpoints;
|
|
16
32
|
private constructor();
|
|
17
33
|
static initialize(options?: NanoClientOptions): NanoClient;
|
|
34
|
+
onEndpointChange(listener: (event: EndpointActivityEvent) => void): () => void;
|
|
35
|
+
getActiveEndpoints(): NanoClientActiveEndpoints;
|
|
18
36
|
/**
|
|
19
37
|
* Generates a minimal JSON-serializable report of the active configuration.
|
|
20
38
|
* Useful for deploy-time auditing and startup logs to detect misconfigurations.
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAwB,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAsB,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,YAAY,EAA4B,MAAM,wBAAwB,CAAC;AAChF,OAAO,KAAK,EAAE,qBAAqB,EAAgB,MAAM,sBAAsB,CAAC;AAEhF,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,eAAO,MAAM,iBAAiB;eACjB,MAAM,EAAE,KAAG,iBAAiB;CACxC,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IACzC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,yBAAyB;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,UAAU;IACd,YAAY,EAAE,YAAY,CAAC;IAC3B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,cAAc,CAAC;IAC9B,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA8C;IAChF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwC;IAExE,OAAO;WAsDO,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU;IAI9D,gBAAgB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,GAAG,MAAM,IAAI;IAK9E,kBAAkB,IAAI,yBAAyB;IAQtD;;;OAGG;IACI,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAS/B,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO;wBAG/C,GAAG,UAAU,GAAG;;CAK3C"}
|
package/dist/client.js
CHANGED
|
@@ -1,26 +1,94 @@
|
|
|
1
|
+
import { HttpEndpointPool } from './transport/http.js';
|
|
2
|
+
import { WsEndpointPool } from './transport/ws.js';
|
|
1
3
|
import { WorkProvider } from './work/WorkProvider.js';
|
|
2
4
|
export const TransportFallback = {
|
|
3
5
|
of: (urls) => ({ urls })
|
|
4
6
|
};
|
|
5
7
|
export class NanoClient {
|
|
6
8
|
workProvider;
|
|
9
|
+
rpcPool;
|
|
10
|
+
wsPool;
|
|
7
11
|
options;
|
|
12
|
+
endpointListeners;
|
|
13
|
+
activeEndpoints;
|
|
8
14
|
constructor(options) {
|
|
9
15
|
this.options = options;
|
|
10
|
-
this.
|
|
16
|
+
this.endpointListeners = new Set();
|
|
17
|
+
this.activeEndpoints = {};
|
|
18
|
+
const warn = options.warn ?? ((message) => console.warn(`[nano-core] ${message}`));
|
|
19
|
+
const forwardEndpointChange = (event) => {
|
|
20
|
+
this.activeEndpoints[event.kind] = event.activeUrl;
|
|
21
|
+
for (const listener of this.endpointListeners) {
|
|
22
|
+
listener(event);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const defaultRpc = [
|
|
26
|
+
'https://rpc.nano.to',
|
|
27
|
+
'https://node.somenano.com/proxy',
|
|
28
|
+
'https://rainstorm.city/api',
|
|
29
|
+
'https://nanoslo.0x.no/proxy',
|
|
30
|
+
];
|
|
31
|
+
const defaultWs = ['wss://rpc.nano.to'];
|
|
32
|
+
const defaultWork = ['https://rpc.nano.to'];
|
|
33
|
+
const rpcUrls = options.rpc ?? options.transports?.urls;
|
|
34
|
+
const rpcEnv = process.env['NANO_RPC_URL'];
|
|
35
|
+
const wsEnv = process.env['NANO_WS_URL'];
|
|
36
|
+
const workEnv = process.env['NANO_WORK_URL'];
|
|
37
|
+
const rpcOptions = {
|
|
38
|
+
kind: 'rpc',
|
|
39
|
+
defaults: defaultRpc,
|
|
40
|
+
warn,
|
|
41
|
+
onActiveEndpointChange: forwardEndpointChange,
|
|
42
|
+
};
|
|
43
|
+
if (rpcUrls && rpcUrls.length > 0)
|
|
44
|
+
rpcOptions.urls = rpcUrls;
|
|
45
|
+
if (rpcEnv)
|
|
46
|
+
rpcOptions.env = rpcEnv;
|
|
47
|
+
this.rpcPool = new HttpEndpointPool(rpcOptions);
|
|
48
|
+
const wsOptions = {
|
|
49
|
+
defaults: defaultWs,
|
|
50
|
+
warn,
|
|
51
|
+
onActiveEndpointChange: forwardEndpointChange,
|
|
52
|
+
};
|
|
53
|
+
if (options.ws && options.ws.length > 0)
|
|
54
|
+
wsOptions.urls = options.ws;
|
|
55
|
+
if (wsEnv)
|
|
56
|
+
wsOptions.env = wsEnv;
|
|
57
|
+
this.wsPool = new WsEndpointPool(wsOptions);
|
|
58
|
+
const workOptions = {
|
|
59
|
+
defaults: defaultWork,
|
|
60
|
+
warn,
|
|
61
|
+
onActiveEndpointChange: forwardEndpointChange,
|
|
62
|
+
};
|
|
63
|
+
if (options.work && options.work.length > 0)
|
|
64
|
+
workOptions.urls = options.work;
|
|
65
|
+
if (workEnv)
|
|
66
|
+
workOptions.env = workEnv;
|
|
67
|
+
this.workProvider = options.workProvider ?? WorkProvider.auto(workOptions);
|
|
11
68
|
}
|
|
12
69
|
static initialize(options = {}) {
|
|
13
70
|
return new NanoClient(options);
|
|
14
71
|
}
|
|
72
|
+
onEndpointChange(listener) {
|
|
73
|
+
this.endpointListeners.add(listener);
|
|
74
|
+
return () => this.endpointListeners.delete(listener);
|
|
75
|
+
}
|
|
76
|
+
getActiveEndpoints() {
|
|
77
|
+
return {
|
|
78
|
+
...(this.activeEndpoints.rpc ? { rpc: this.activeEndpoints.rpc } : {}),
|
|
79
|
+
...(this.activeEndpoints.ws ? { ws: this.activeEndpoints.ws } : {}),
|
|
80
|
+
...(this.activeEndpoints.work ? { work: this.activeEndpoints.work } : {}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
15
83
|
/**
|
|
16
84
|
* Generates a minimal JSON-serializable report of the active configuration.
|
|
17
85
|
* Useful for deploy-time auditing and startup logs to detect misconfigurations.
|
|
18
86
|
*/
|
|
19
87
|
getAuditReport() {
|
|
20
|
-
const defaultTransports = ['https://rpc.nano.org'];
|
|
21
88
|
return {
|
|
22
89
|
network: this.options.network ?? 'mainnet',
|
|
23
|
-
|
|
90
|
+
rpc: this.rpcPool.getAuditReport(),
|
|
91
|
+
ws: this.wsPool.getAuditReport(),
|
|
24
92
|
workProvider: this.workProvider.getAuditReport()
|
|
25
93
|
};
|
|
26
94
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { NanoAddress } from './primitives/NanoAddress.js';
|
|
2
2
|
export { NanoAmount } from './primitives/NanoAmount.js';
|
|
3
3
|
export { WorkProvider, RemoteWorkServer, LocalCompute, type WorkProviderOptions } from './work/WorkProvider.js';
|
|
4
|
-
export { NanoClient, TransportFallback, type NanoClientOptions } from './client.js';
|
|
4
|
+
export { NanoClient, TransportFallback, type NanoClientOptions, type NanoClientActiveEndpoints } from './client.js';
|
|
5
|
+
export { EndpointPool, HttpEndpointPool, NanoTransportConfigError, WsEndpointPool, normalizeEndpoints, } from './transport/index.js';
|
|
6
|
+
export type { AuthSource, EndpointActivityEvent, EndpointAuditRecord, EndpointAuth, EndpointKind, EndpointPoolOptions, EndpointState, NormalizedEndpoint, TransportPolicy, } from './transport/index.js';
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,KAAK,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACpH,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,cAAc,EACd,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,UAAU,EACV,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,eAAe,GAChB,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,4 +2,5 @@ export { NanoAddress } from './primitives/NanoAddress.js';
|
|
|
2
2
|
export { NanoAmount } from './primitives/NanoAmount.js';
|
|
3
3
|
export { WorkProvider, RemoteWorkServer, LocalCompute } from './work/WorkProvider.js';
|
|
4
4
|
export { NanoClient, TransportFallback } from './client.js';
|
|
5
|
+
export { EndpointPool, HttpEndpointPool, NanoTransportConfigError, WsEndpointPool, normalizeEndpoints, } from './transport/index.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAA4B,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,UAAU,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAA4B,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAA0D,MAAM,aAAa,CAAC;AACpH,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,cAAc,EACd,kBAAkB,GACnB,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EndpointAuditRecord, EndpointPoolOptions, NormalizedEndpoint } from './types.js';
|
|
2
|
+
export declare class EndpointPool {
|
|
3
|
+
private readonly states;
|
|
4
|
+
private readonly warn;
|
|
5
|
+
private readonly now;
|
|
6
|
+
private readonly baseDelayMs;
|
|
7
|
+
private readonly maxDelayMs;
|
|
8
|
+
private readonly onActiveEndpointChange;
|
|
9
|
+
private activeEndpointUrl;
|
|
10
|
+
constructor(options: EndpointPoolOptions);
|
|
11
|
+
private emitActiveEndpointChange;
|
|
12
|
+
getEndpoints(): NormalizedEndpoint[];
|
|
13
|
+
getAuditReport(): EndpointAuditRecord[];
|
|
14
|
+
execute<T>(attempt: (endpoint: NormalizedEndpoint) => Promise<T>): Promise<T>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=EndpointPool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EndpointPool.d.ts","sourceRoot":"","sources":["../../src/transport/EndpointPool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mBAAmB,EACnB,mBAAmB,EAEnB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAQpB,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAkD;IACzF,OAAO,CAAC,iBAAiB,CAAgB;gBAE7B,OAAO,EAAE,mBAAmB;IA2BxC,OAAO,CAAC,wBAAwB;IAczB,YAAY,IAAI,kBAAkB,EAAE;IAIpC,cAAc,IAAI,mBAAmB,EAAE;IAejC,OAAO,CAAC,CAAC,EACpB,OAAO,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC,CAAC,CAAC,GACpD,OAAO,CAAC,CAAC,CAAC;CAoCd"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { normalizeEndpoints } from './normalize.js';
|
|
2
|
+
function withJitter(delay) {
|
|
3
|
+
const factor = 0.85 + (Math.random() * 0.3);
|
|
4
|
+
return Math.round(delay * factor);
|
|
5
|
+
}
|
|
6
|
+
export class EndpointPool {
|
|
7
|
+
states;
|
|
8
|
+
warn;
|
|
9
|
+
now;
|
|
10
|
+
baseDelayMs;
|
|
11
|
+
maxDelayMs;
|
|
12
|
+
onActiveEndpointChange;
|
|
13
|
+
activeEndpointUrl;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.warn = options.warn ?? (() => { });
|
|
16
|
+
this.now = options.now ?? (() => Date.now());
|
|
17
|
+
this.baseDelayMs = options.baseDelayMs ?? 500;
|
|
18
|
+
this.maxDelayMs = options.maxDelayMs ?? 30000;
|
|
19
|
+
this.onActiveEndpointChange = options.onActiveEndpointChange ?? null;
|
|
20
|
+
const endpoints = normalizeEndpoints({
|
|
21
|
+
kind: options.kind,
|
|
22
|
+
defaults: options.defaults,
|
|
23
|
+
warn: this.warn,
|
|
24
|
+
...(options.urls ? { inputs: options.urls } : {}),
|
|
25
|
+
...(options.env ? { env: options.env } : {}),
|
|
26
|
+
...(options.transportPolicy ? { transportPolicy: options.transportPolicy } : {}),
|
|
27
|
+
});
|
|
28
|
+
this.states = endpoints.map((endpoint) => ({
|
|
29
|
+
endpoint,
|
|
30
|
+
consecutiveFailures: 0,
|
|
31
|
+
lastSuccessAt: null,
|
|
32
|
+
lastFailureAt: null,
|
|
33
|
+
cooldownUntil: 0,
|
|
34
|
+
lastLatencyMs: null,
|
|
35
|
+
}));
|
|
36
|
+
this.activeEndpointUrl = null;
|
|
37
|
+
}
|
|
38
|
+
emitActiveEndpointChange(nextUrl) {
|
|
39
|
+
if (!this.onActiveEndpointChange)
|
|
40
|
+
return;
|
|
41
|
+
if (this.activeEndpointUrl === nextUrl)
|
|
42
|
+
return;
|
|
43
|
+
const previousUrl = this.activeEndpointUrl ?? undefined;
|
|
44
|
+
this.activeEndpointUrl = nextUrl;
|
|
45
|
+
this.onActiveEndpointChange({
|
|
46
|
+
kind: this.states[0]?.endpoint.kind ?? 'rpc',
|
|
47
|
+
status: previousUrl ? 'failover' : 'connected',
|
|
48
|
+
activeUrl: nextUrl,
|
|
49
|
+
...(previousUrl ? { previousUrl } : {}),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
getEndpoints() {
|
|
53
|
+
return this.states.map((state) => state.endpoint);
|
|
54
|
+
}
|
|
55
|
+
getAuditReport() {
|
|
56
|
+
return this.states.map((state) => ({
|
|
57
|
+
kind: state.endpoint.kind,
|
|
58
|
+
url: state.endpoint.url.toString(),
|
|
59
|
+
authUsed: state.endpoint.auth.type === 'api-key',
|
|
60
|
+
authSource: state.endpoint.auth.type === 'api-key' ? state.endpoint.auth.source : null,
|
|
61
|
+
policy: state.endpoint.auth.type === 'api-key' ? state.endpoint.auth.policy : null,
|
|
62
|
+
consecutiveFailures: state.consecutiveFailures,
|
|
63
|
+
lastSuccessAt: state.lastSuccessAt,
|
|
64
|
+
lastFailureAt: state.lastFailureAt,
|
|
65
|
+
cooldownUntil: state.cooldownUntil > 0 ? new Date(state.cooldownUntil).toISOString() : null,
|
|
66
|
+
lastLatencyMs: state.lastLatencyMs,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
async execute(attempt) {
|
|
70
|
+
const start = this.now();
|
|
71
|
+
const orderedStates = [...this.states].sort((a, b) => {
|
|
72
|
+
const aReady = a.cooldownUntil <= start ? 0 : 1;
|
|
73
|
+
const bReady = b.cooldownUntil <= start ? 0 : 1;
|
|
74
|
+
if (aReady !== bReady)
|
|
75
|
+
return aReady - bReady;
|
|
76
|
+
const aLatency = a.lastLatencyMs ?? Number.MAX_SAFE_INTEGER;
|
|
77
|
+
const bLatency = b.lastLatencyMs ?? Number.MAX_SAFE_INTEGER;
|
|
78
|
+
return aLatency - bLatency;
|
|
79
|
+
});
|
|
80
|
+
let lastError;
|
|
81
|
+
for (const state of orderedStates) {
|
|
82
|
+
if (state.cooldownUntil > start)
|
|
83
|
+
continue;
|
|
84
|
+
const attemptStarted = this.now();
|
|
85
|
+
try {
|
|
86
|
+
const result = await attempt(state.endpoint);
|
|
87
|
+
state.consecutiveFailures = 0;
|
|
88
|
+
state.cooldownUntil = 0;
|
|
89
|
+
state.lastSuccessAt = new Date(this.now()).toISOString();
|
|
90
|
+
state.lastLatencyMs = this.now() - attemptStarted;
|
|
91
|
+
this.emitActiveEndpointChange(state.endpoint.url.toString());
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
lastError = error;
|
|
96
|
+
state.consecutiveFailures += 1;
|
|
97
|
+
state.lastFailureAt = new Date(this.now()).toISOString();
|
|
98
|
+
const delay = Math.min(this.baseDelayMs * (2 ** (state.consecutiveFailures - 1)), this.maxDelayMs);
|
|
99
|
+
state.cooldownUntil = this.now() + withJitter(delay);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
throw lastError instanceof Error ? lastError : new Error('All endpoints exhausted');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=EndpointPool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EndpointPool.js","sourceRoot":"","sources":["../../src/transport/EndpointPool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/transport/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/transport/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACjD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EndpointAuditRecord, EndpointPoolOptions } from './types.js';
|
|
2
|
+
export interface HttpPoolOptions extends Omit<EndpointPoolOptions, 'kind'> {
|
|
3
|
+
kind?: 'rpc' | 'work';
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class HttpEndpointPool {
|
|
7
|
+
private readonly pool;
|
|
8
|
+
private readonly timeoutMs;
|
|
9
|
+
constructor(options: HttpPoolOptions);
|
|
10
|
+
getAuditReport(): EndpointAuditRecord[];
|
|
11
|
+
postJson<T>(body: Record<string, unknown>, extraHeaders?: Record<string, string>): Promise<T>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAsB,MAAM,YAAY,CAAC;AAE/F,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACxE,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAeD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;gBAE9B,OAAO,EAAE,eAAe;IAQ7B,cAAc,IAAI,mBAAmB,EAAE;IAIjC,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAqC3G"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EndpointPool } from './EndpointPool.js';
|
|
2
|
+
function buildHeaders(endpoint, extraHeaders) {
|
|
3
|
+
const headers = {
|
|
4
|
+
'Content-Type': 'application/json',
|
|
5
|
+
...(extraHeaders ?? {}),
|
|
6
|
+
};
|
|
7
|
+
if (endpoint.auth.type === 'api-key') {
|
|
8
|
+
headers['Authorization'] = `Bearer ${endpoint.auth.value}`;
|
|
9
|
+
}
|
|
10
|
+
return headers;
|
|
11
|
+
}
|
|
12
|
+
export class HttpEndpointPool {
|
|
13
|
+
pool;
|
|
14
|
+
timeoutMs;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.timeoutMs = options.timeoutMs ?? null;
|
|
17
|
+
this.pool = new EndpointPool({
|
|
18
|
+
...options,
|
|
19
|
+
kind: options.kind ?? 'rpc',
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
getAuditReport() {
|
|
23
|
+
return this.pool.getAuditReport();
|
|
24
|
+
}
|
|
25
|
+
async postJson(body, extraHeaders) {
|
|
26
|
+
return this.pool.execute(async (endpoint) => {
|
|
27
|
+
const payload = endpoint.auth.type === 'api-key' && endpoint.auth.policy === 'json-body-key'
|
|
28
|
+
? { ...body, key: endpoint.auth.value }
|
|
29
|
+
: endpoint.auth.type === 'api-key' && endpoint.auth.policy === 'bearer-and-json-body-key'
|
|
30
|
+
? { ...body, key: endpoint.auth.value }
|
|
31
|
+
: body;
|
|
32
|
+
const controller = this.timeoutMs !== null ? new AbortController() : null;
|
|
33
|
+
const timer = controller && this.timeoutMs !== null
|
|
34
|
+
? setTimeout(() => controller.abort(), this.timeoutMs)
|
|
35
|
+
: null;
|
|
36
|
+
let response;
|
|
37
|
+
try {
|
|
38
|
+
response = await fetch(endpoint.url, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: buildHeaders(endpoint, extraHeaders),
|
|
41
|
+
body: JSON.stringify(payload),
|
|
42
|
+
...(controller ? { signal: controller.signal } : {}),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
if (timer !== null)
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
}
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
51
|
+
}
|
|
52
|
+
const json = await response.json();
|
|
53
|
+
if (json.error) {
|
|
54
|
+
throw new Error(json.error);
|
|
55
|
+
}
|
|
56
|
+
return json;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { NanoTransportConfigError } from './errors.js';
|
|
2
|
+
export { normalizeEndpoints } from './normalize.js';
|
|
3
|
+
export { EndpointPool } from './EndpointPool.js';
|
|
4
|
+
export { HttpEndpointPool, type HttpPoolOptions } from './http.js';
|
|
5
|
+
export { WsEndpointPool, type WsPoolOptions } from './ws.js';
|
|
6
|
+
export type { AuthSource, EndpointActivityEvent, EndpointAuth, EndpointAuditRecord, EndpointKind, EndpointPoolOptions, EndpointState, NormalizedEndpoint, TransportPolicy, } from './types.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EACV,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,eAAe,GAChB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { NanoTransportConfigError } from './errors.js';
|
|
2
|
+
export { normalizeEndpoints } from './normalize.js';
|
|
3
|
+
export { EndpointPool } from './EndpointPool.js';
|
|
4
|
+
export { HttpEndpointPool } from './http.js';
|
|
5
|
+
export { WsEndpointPool } from './ws.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAwB,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,cAAc,EAAsB,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EndpointKind, NormalizedEndpoint, TransportPolicy } from './types.js';
|
|
2
|
+
export declare function normalizeEndpoints(options: {
|
|
3
|
+
kind: EndpointKind;
|
|
4
|
+
inputs?: string[];
|
|
5
|
+
env?: string;
|
|
6
|
+
defaults: string[];
|
|
7
|
+
warn?: (message: string) => void;
|
|
8
|
+
transportPolicy?: TransportPolicy;
|
|
9
|
+
}): NormalizedEndpoint[];
|
|
10
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/transport/normalize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,eAAe,EAChB,MAAM,YAAY,CAAC;AA6BpB,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,GAAG,kBAAkB,EAAE,CAsFvB"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { NanoTransportConfigError } from './errors.js';
|
|
2
|
+
const API_KEY_QUERY_KEYS = ['key', 'apiKey', 'api_key'];
|
|
3
|
+
function allowedProtocols(kind) {
|
|
4
|
+
switch (kind) {
|
|
5
|
+
case 'rpc':
|
|
6
|
+
case 'work':
|
|
7
|
+
return ['http:', 'https:'];
|
|
8
|
+
case 'ws':
|
|
9
|
+
return ['ws:', 'wss:'];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function defaultPolicy(kind) {
|
|
13
|
+
return kind === 'ws' ? 'bearer-header' : 'bearer-header';
|
|
14
|
+
}
|
|
15
|
+
function normalizePath(url) {
|
|
16
|
+
if (url.pathname === '')
|
|
17
|
+
url.pathname = '/';
|
|
18
|
+
}
|
|
19
|
+
function canonicalKey(endpoint) {
|
|
20
|
+
const authKey = endpoint.auth.type === 'api-key'
|
|
21
|
+
? `${endpoint.auth.type}:${endpoint.auth.value}:${endpoint.auth.policy}`
|
|
22
|
+
: 'none';
|
|
23
|
+
return `${endpoint.kind}:${endpoint.url.toString()}:${authKey}`;
|
|
24
|
+
}
|
|
25
|
+
export function normalizeEndpoints(options) {
|
|
26
|
+
const warn = options.warn ?? (() => { });
|
|
27
|
+
const rawInputs = options.inputs && options.inputs.length > 0
|
|
28
|
+
? options.inputs
|
|
29
|
+
: options.env && options.env.trim() !== ''
|
|
30
|
+
? options.env.split(',')
|
|
31
|
+
: options.defaults;
|
|
32
|
+
const allowed = allowedProtocols(options.kind);
|
|
33
|
+
const normalized = [];
|
|
34
|
+
const seen = new Set();
|
|
35
|
+
for (const raw of rawInputs) {
|
|
36
|
+
const input = raw.trim();
|
|
37
|
+
if (input === '')
|
|
38
|
+
continue;
|
|
39
|
+
let url;
|
|
40
|
+
try {
|
|
41
|
+
url = new URL(input);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
warn(`Ignoring malformed ${options.kind.toUpperCase()} endpoint "${input}": invalid URL`);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (!allowed.includes(url.protocol)) {
|
|
48
|
+
warn(`Ignoring invalid ${options.kind.toUpperCase()} endpoint "${input}": expected ${allowed.join(' or ')}`);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (url.hostname.trim() === '') {
|
|
52
|
+
warn(`Ignoring invalid ${options.kind.toUpperCase()} endpoint "${input}": hostname is required`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
let auth = { type: 'none' };
|
|
56
|
+
for (const key of API_KEY_QUERY_KEYS) {
|
|
57
|
+
const value = url.searchParams.get(key);
|
|
58
|
+
if (value && value.trim() !== '') {
|
|
59
|
+
auth = {
|
|
60
|
+
type: 'api-key',
|
|
61
|
+
value,
|
|
62
|
+
source: 'query',
|
|
63
|
+
policy: options.transportPolicy ?? defaultPolicy(options.kind),
|
|
64
|
+
};
|
|
65
|
+
url.searchParams.delete(key);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (auth.type === 'none' && url.username.trim() !== '') {
|
|
70
|
+
auth = {
|
|
71
|
+
type: 'api-key',
|
|
72
|
+
value: decodeURIComponent(url.username),
|
|
73
|
+
source: 'userinfo',
|
|
74
|
+
policy: options.transportPolicy ?? defaultPolicy(options.kind),
|
|
75
|
+
};
|
|
76
|
+
url.username = '';
|
|
77
|
+
url.password = '';
|
|
78
|
+
}
|
|
79
|
+
normalizePath(url);
|
|
80
|
+
const endpoint = {
|
|
81
|
+
kind: options.kind,
|
|
82
|
+
originalInput: input,
|
|
83
|
+
url,
|
|
84
|
+
auth,
|
|
85
|
+
auditLabel: `${url.toString()}${auth.type === 'api-key' ? ' (api-key used)' : ' (no auth)'}`,
|
|
86
|
+
};
|
|
87
|
+
const dedupeKey = canonicalKey(endpoint);
|
|
88
|
+
if (seen.has(dedupeKey)) {
|
|
89
|
+
warn(`Ignoring duplicate ${options.kind.toUpperCase()} endpoint "${endpoint.auditLabel}"`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
seen.add(dedupeKey);
|
|
93
|
+
normalized.push(endpoint);
|
|
94
|
+
}
|
|
95
|
+
if (normalized.length === 0) {
|
|
96
|
+
throw new NanoTransportConfigError(`No valid ${options.kind.toUpperCase()} endpoints remain after validation`);
|
|
97
|
+
}
|
|
98
|
+
return normalized;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/transport/normalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type EndpointKind = 'rpc' | 'ws' | 'work';
|
|
2
|
+
export type AuthSource = 'explicit' | 'query' | 'userinfo';
|
|
3
|
+
export type TransportPolicy = 'bearer-header' | 'json-body-key' | 'bearer-and-json-body-key';
|
|
4
|
+
export type EndpointAuth = {
|
|
5
|
+
type: 'none';
|
|
6
|
+
} | {
|
|
7
|
+
type: 'api-key';
|
|
8
|
+
value: string;
|
|
9
|
+
source: AuthSource;
|
|
10
|
+
policy: TransportPolicy;
|
|
11
|
+
};
|
|
12
|
+
export interface NormalizedEndpoint {
|
|
13
|
+
kind: EndpointKind;
|
|
14
|
+
originalInput: string;
|
|
15
|
+
url: URL;
|
|
16
|
+
auth: EndpointAuth;
|
|
17
|
+
auditLabel: string;
|
|
18
|
+
}
|
|
19
|
+
export interface EndpointState {
|
|
20
|
+
endpoint: NormalizedEndpoint;
|
|
21
|
+
consecutiveFailures: number;
|
|
22
|
+
lastSuccessAt: string | null;
|
|
23
|
+
lastFailureAt: string | null;
|
|
24
|
+
cooldownUntil: number;
|
|
25
|
+
lastLatencyMs: number | null;
|
|
26
|
+
}
|
|
27
|
+
export interface EndpointAuditRecord {
|
|
28
|
+
kind: EndpointKind;
|
|
29
|
+
url: string;
|
|
30
|
+
authUsed: boolean;
|
|
31
|
+
authSource: AuthSource | null;
|
|
32
|
+
policy: TransportPolicy | null;
|
|
33
|
+
consecutiveFailures: number;
|
|
34
|
+
lastSuccessAt: string | null;
|
|
35
|
+
lastFailureAt: string | null;
|
|
36
|
+
cooldownUntil: string | null;
|
|
37
|
+
lastLatencyMs: number | null;
|
|
38
|
+
}
|
|
39
|
+
export interface EndpointActivityEvent {
|
|
40
|
+
kind: EndpointKind;
|
|
41
|
+
status: 'connected' | 'failover';
|
|
42
|
+
activeUrl: string;
|
|
43
|
+
previousUrl?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface EndpointPoolOptions {
|
|
46
|
+
kind: EndpointKind;
|
|
47
|
+
env?: string;
|
|
48
|
+
urls?: string[];
|
|
49
|
+
defaults: string[];
|
|
50
|
+
warn?: (message: string) => void;
|
|
51
|
+
now?: () => number;
|
|
52
|
+
transportPolicy?: TransportPolicy;
|
|
53
|
+
baseDelayMs?: number;
|
|
54
|
+
maxDelayMs?: number;
|
|
55
|
+
onActiveEndpointChange?: (event: EndpointActivityEvent) => void;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/transport/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;AAE3D,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,eAAe,GAAG,0BAA0B,CAAC;AAE7F,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAAC;AAEpF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,YAAY,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,WAAW,GAAG,UAAU,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACjE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/transport/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EndpointAuditRecord, EndpointPoolOptions } from './types.js';
|
|
2
|
+
export interface WsPoolOptions extends Omit<EndpointPoolOptions, 'kind'> {
|
|
3
|
+
}
|
|
4
|
+
export declare class WsEndpointPool {
|
|
5
|
+
private readonly pool;
|
|
6
|
+
constructor(options: WsPoolOptions);
|
|
7
|
+
getAuditReport(): EndpointAuditRecord[];
|
|
8
|
+
connect(): Promise<WebSocket>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=ws.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../src/transport/ws.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAsB,MAAM,YAAY,CAAC;AAE/F,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC;CAAG;AAE3E,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;gBAExB,OAAO,EAAE,aAAa;IAO3B,cAAc,IAAI,mBAAmB,EAAE;IAIjC,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;CAiB3C"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { EndpointPool } from './EndpointPool.js';
|
|
2
|
+
export class WsEndpointPool {
|
|
3
|
+
pool;
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.pool = new EndpointPool({
|
|
6
|
+
...options,
|
|
7
|
+
kind: 'ws',
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
getAuditReport() {
|
|
11
|
+
return this.pool.getAuditReport();
|
|
12
|
+
}
|
|
13
|
+
async connect() {
|
|
14
|
+
return this.pool.execute(async (endpoint) => {
|
|
15
|
+
let connectUrl = endpoint.url.toString();
|
|
16
|
+
if (endpoint.auth.type === 'api-key') {
|
|
17
|
+
const url = new URL(connectUrl);
|
|
18
|
+
url.searchParams.set('api_key', endpoint.auth.value);
|
|
19
|
+
connectUrl = url.toString();
|
|
20
|
+
}
|
|
21
|
+
return await new Promise((resolve, reject) => {
|
|
22
|
+
const ws = new WebSocket(connectUrl);
|
|
23
|
+
ws.onopen = () => resolve(ws);
|
|
24
|
+
ws.onerror = () => reject(new Error(`WebSocket connect failed: ${endpoint.auditLabel}`));
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=ws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../../src/transport/ws.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAKjD,MAAM,OAAO,cAAc;IACR,IAAI,CAAe;IAEpC,YAAY,OAAsB;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,YAAY,CAAC;YAC3B,GAAG,OAAO;YACV,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;IACL,CAAC;IAEM,cAAc;QACnB,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;IACpC,CAAC;IAEM,KAAK,CAAC,OAAO;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,QAA4B,EAAE,EAAE;YAC9D,IAAI,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAEzC,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBACrC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;gBAChC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrD,UAAU,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;YAC9B,CAAC;YAED,OAAO,MAAM,IAAI,OAAO,CAAY,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACtD,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC;gBACrC,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC9B,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YAC3F,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EndpointActivityEvent } from '../transport/types.js';
|
|
1
2
|
/**
|
|
2
3
|
* Local compute engines available.
|
|
3
4
|
*/
|
|
@@ -19,6 +20,11 @@ export declare class RemoteWorkServer {
|
|
|
19
20
|
}): RemoteWorkServer;
|
|
20
21
|
}
|
|
21
22
|
export interface WorkProviderOptions {
|
|
23
|
+
urls?: string[];
|
|
24
|
+
env?: string;
|
|
25
|
+
defaults?: string[];
|
|
26
|
+
warn?: (message: string) => void;
|
|
27
|
+
onActiveEndpointChange?: (event: EndpointActivityEvent) => void;
|
|
22
28
|
remotes?: RemoteWorkServer[];
|
|
23
29
|
localChain?: LocalCompute[];
|
|
24
30
|
profiler?: {
|
|
@@ -29,6 +35,7 @@ export interface WorkProviderOptions {
|
|
|
29
35
|
}
|
|
30
36
|
export declare class WorkProvider {
|
|
31
37
|
private options;
|
|
38
|
+
private remotePool;
|
|
32
39
|
private constructor();
|
|
33
40
|
static auto(options: WorkProviderOptions): WorkProvider;
|
|
34
41
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkProvider.d.ts","sourceRoot":"","sources":["../../src/work/WorkProvider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WorkProvider.d.ts","sourceRoot":"","sources":["../../src/work/WorkProvider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGnE;;GAEG;AACH,oBAAY,YAAY;IACtB,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,YAAY,SAAS;IACrB,GAAG,QAAQ;CACZ;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,UAAU,CAAS;gBAEf,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAK1C,IAAW,GAAG,IAAI,MAAM,CAAsB;IAC9C,IAAW,SAAS,IAAI,MAAM,CAA4B;IAE1D,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAO;CAGvF;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAChE,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE;QACT,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;QACxB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,YAAY,GAAG,QAAQ,CAAC;KACxC,CAAC;CACH;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,UAAU,CAA0B;IAE5C,OAAO;WAsBO,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY;IAI9D;;OAEG;IACI,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAS5C;;;OAGG;IACU,SAAS,IAAI,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IAWlF;;OAEG;IACU,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAazE"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HttpEndpointPool } from '../transport/http.js';
|
|
1
2
|
/**
|
|
2
3
|
* Local compute engines available.
|
|
3
4
|
*/
|
|
@@ -23,8 +24,28 @@ export class RemoteWorkServer {
|
|
|
23
24
|
}
|
|
24
25
|
export class WorkProvider {
|
|
25
26
|
options;
|
|
27
|
+
remotePool;
|
|
26
28
|
constructor(options) {
|
|
27
29
|
this.options = options;
|
|
30
|
+
const hasRemotePool = (options.urls && options.urls.length > 0) || options.env || (options.defaults && options.defaults.length > 0);
|
|
31
|
+
if (!hasRemotePool) {
|
|
32
|
+
this.remotePool = null;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const remotePoolOptions = {
|
|
36
|
+
kind: 'work',
|
|
37
|
+
defaults: options.defaults ?? [],
|
|
38
|
+
transportPolicy: 'bearer-and-json-body-key',
|
|
39
|
+
};
|
|
40
|
+
if (options.urls && options.urls.length > 0)
|
|
41
|
+
remotePoolOptions.urls = options.urls;
|
|
42
|
+
if (options.env)
|
|
43
|
+
remotePoolOptions.env = options.env;
|
|
44
|
+
if (options.warn)
|
|
45
|
+
remotePoolOptions.warn = options.warn;
|
|
46
|
+
if (options.onActiveEndpointChange)
|
|
47
|
+
remotePoolOptions.onActiveEndpointChange = options.onActiveEndpointChange;
|
|
48
|
+
this.remotePool = new HttpEndpointPool(remotePoolOptions);
|
|
28
49
|
}
|
|
29
50
|
static auto(options) {
|
|
30
51
|
return new WorkProvider(options);
|
|
@@ -34,6 +55,7 @@ export class WorkProvider {
|
|
|
34
55
|
*/
|
|
35
56
|
getAuditReport() {
|
|
36
57
|
return {
|
|
58
|
+
remotePool: this.remotePool?.getAuditReport() ?? [],
|
|
37
59
|
remotes: this.options.remotes?.map(r => ({ url: r.url, timeoutMs: r.timeoutMs })) || [],
|
|
38
60
|
localChain: this.options.localChain || [],
|
|
39
61
|
profiler: this.options.profiler || 'default'
|
|
@@ -56,9 +78,17 @@ export class WorkProvider {
|
|
|
56
78
|
* Generate Proof-of-Work for a given hash.
|
|
57
79
|
*/
|
|
58
80
|
async generate(hash, difficulty) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
81
|
+
if (this.remotePool) {
|
|
82
|
+
const response = await this.remotePool.postJson({
|
|
83
|
+
action: 'work_generate',
|
|
84
|
+
hash,
|
|
85
|
+
difficulty,
|
|
86
|
+
});
|
|
87
|
+
if (response.work)
|
|
88
|
+
return response.work;
|
|
89
|
+
}
|
|
90
|
+
// In real implementation, this would route to local fallback engines.
|
|
91
|
+
return '0000000000000000';
|
|
62
92
|
}
|
|
63
93
|
}
|
|
64
94
|
//# sourceMappingURL=WorkProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkProvider.js","sourceRoot":"","sources":["../../src/work/WorkProvider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WorkProvider.js","sourceRoot":"","sources":["../../src/work/WorkProvider.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openrai/nano-core",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Protocol engine for Nano integration ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,6 +9,18 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"import": "./dist/index.js",
|
|
11
11
|
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./transport": {
|
|
14
|
+
"import": "./dist/transport/index.js",
|
|
15
|
+
"types": "./dist/transport/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./transport/http": {
|
|
18
|
+
"import": "./dist/transport/http.js",
|
|
19
|
+
"types": "./dist/transport/http.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./transport/ws": {
|
|
22
|
+
"import": "./dist/transport/ws.js",
|
|
23
|
+
"types": "./dist/transport/ws.d.ts"
|
|
12
24
|
}
|
|
13
25
|
},
|
|
14
26
|
"scripts": {
|
|
@@ -22,6 +34,9 @@
|
|
|
22
34
|
],
|
|
23
35
|
"author": "OpenRai",
|
|
24
36
|
"license": "MIT",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
25
40
|
"files": [
|
|
26
41
|
"dist"
|
|
27
42
|
],
|
|
@@ -30,6 +45,7 @@
|
|
|
30
45
|
"nanocurrency": "^2.5.0"
|
|
31
46
|
},
|
|
32
47
|
"devDependencies": {
|
|
48
|
+
"@types/node": "^24.6.0",
|
|
33
49
|
"typescript": "^6.0.2",
|
|
34
50
|
"vitest": "^4.1.2"
|
|
35
51
|
}
|