@mindpeeker/entropy 0.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/LICENSE +21 -0
- package/README.md +496 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/bits.d.ts +9 -0
- package/dist/internal/bits.d.ts.map +1 -0
- package/dist/internal/bits.js +29 -0
- package/dist/internal/bits.js.map +1 -0
- package/dist/internal/byte-source.d.ts +16 -0
- package/dist/internal/byte-source.d.ts.map +1 -0
- package/dist/internal/byte-source.js +96 -0
- package/dist/internal/byte-source.js.map +1 -0
- package/dist/internal/bytes.d.ts +5 -0
- package/dist/internal/bytes.d.ts.map +1 -0
- package/dist/internal/bytes.js +56 -0
- package/dist/internal/bytes.js.map +1 -0
- package/dist/internal/composite.d.ts +15 -0
- package/dist/internal/composite.d.ts.map +1 -0
- package/dist/internal/composite.js +36 -0
- package/dist/internal/composite.js.map +1 -0
- package/dist/internal/condition.d.ts +38 -0
- package/dist/internal/condition.d.ts.map +1 -0
- package/dist/internal/condition.js +49 -0
- package/dist/internal/condition.js.map +1 -0
- package/dist/internal/encoding.d.ts +14 -0
- package/dist/internal/encoding.d.ts.map +1 -0
- package/dist/internal/encoding.js +91 -0
- package/dist/internal/encoding.js.map +1 -0
- package/dist/internal/health.d.ts +25 -0
- package/dist/internal/health.d.ts.map +1 -0
- package/dist/internal/health.js +84 -0
- package/dist/internal/health.js.map +1 -0
- package/dist/internal/http.d.ts +25 -0
- package/dist/internal/http.d.ts.map +1 -0
- package/dist/internal/http.js +76 -0
- package/dist/internal/http.js.map +1 -0
- package/dist/internal/nist-pulse.d.ts +31 -0
- package/dist/internal/nist-pulse.d.ts.map +1 -0
- package/dist/internal/nist-pulse.js +62 -0
- package/dist/internal/nist-pulse.js.map +1 -0
- package/dist/internal/provider.d.ts +19 -0
- package/dist/internal/provider.d.ts.map +1 -0
- package/dist/internal/provider.js +62 -0
- package/dist/internal/provider.js.map +1 -0
- package/dist/internal/rate-limit.d.ts +15 -0
- package/dist/internal/rate-limit.d.ts.map +1 -0
- package/dist/internal/rate-limit.js +47 -0
- package/dist/internal/rate-limit.js.map +1 -0
- package/dist/internal/sampled.d.ts +25 -0
- package/dist/internal/sampled.d.ts.map +1 -0
- package/dist/internal/sampled.js +49 -0
- package/dist/internal/sampled.js.map +1 -0
- package/dist/internal/stream.d.ts +29 -0
- package/dist/internal/stream.d.ts.map +1 -0
- package/dist/internal/stream.js +78 -0
- package/dist/internal/stream.js.map +1 -0
- package/dist/internal/validate.d.ts +5 -0
- package/dist/internal/validate.d.ts.map +1 -0
- package/dist/internal/validate.js +39 -0
- package/dist/internal/validate.js.map +1 -0
- package/dist/node/ffmpeg-frames.d.ts +27 -0
- package/dist/node/ffmpeg-frames.d.ts.map +1 -0
- package/dist/node/ffmpeg-frames.js +102 -0
- package/dist/node/ffmpeg-frames.js.map +1 -0
- package/dist/node/ffmpeg-samples.d.ts +21 -0
- package/dist/node/ffmpeg-samples.d.ts.map +1 -0
- package/dist/node/ffmpeg-samples.js +81 -0
- package/dist/node/ffmpeg-samples.js.map +1 -0
- package/dist/node/hwrng.d.ts +12 -0
- package/dist/node/hwrng.d.ts.map +1 -0
- package/dist/node/hwrng.js +43 -0
- package/dist/node/hwrng.js.map +1 -0
- package/dist/node/index.d.ts +9 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +7 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/rtl-sdr.d.ts +31 -0
- package/dist/node/rtl-sdr.d.ts.map +1 -0
- package/dist/node/rtl-sdr.js +63 -0
- package/dist/node/rtl-sdr.js.map +1 -0
- package/dist/node/serial-source.d.ts +22 -0
- package/dist/node/serial-source.d.ts.map +1 -0
- package/dist/node/serial-source.js +63 -0
- package/dist/node/serial-source.js.map +1 -0
- package/dist/providers/anu-legacy.d.ts +17 -0
- package/dist/providers/anu-legacy.d.ts.map +1 -0
- package/dist/providers/anu-legacy.js +56 -0
- package/dist/providers/anu-legacy.js.map +1 -0
- package/dist/providers/anu.d.ts +14 -0
- package/dist/providers/anu.d.ts.map +1 -0
- package/dist/providers/anu.js +42 -0
- package/dist/providers/anu.js.map +1 -0
- package/dist/providers/bitcoin.d.ts +14 -0
- package/dist/providers/bitcoin.d.ts.map +1 -0
- package/dist/providers/bitcoin.js +95 -0
- package/dist/providers/bitcoin.js.map +1 -0
- package/dist/providers/camera.d.ts +49 -0
- package/dist/providers/camera.d.ts.map +1 -0
- package/dist/providers/camera.js +123 -0
- package/dist/providers/camera.js.map +1 -0
- package/dist/providers/crypto.d.ts +8 -0
- package/dist/providers/crypto.d.ts.map +1 -0
- package/dist/providers/crypto.js +26 -0
- package/dist/providers/crypto.js.map +1 -0
- package/dist/providers/curby.d.ts +17 -0
- package/dist/providers/curby.d.ts.map +1 -0
- package/dist/providers/curby.js +113 -0
- package/dist/providers/curby.js.map +1 -0
- package/dist/providers/drand.d.ts +19 -0
- package/dist/providers/drand.d.ts.map +1 -0
- package/dist/providers/drand.js +87 -0
- package/dist/providers/drand.js.map +1 -0
- package/dist/providers/flow.d.ts +15 -0
- package/dist/providers/flow.d.ts.map +1 -0
- package/dist/providers/flow.js +82 -0
- package/dist/providers/flow.js.map +1 -0
- package/dist/providers/index.d.ts +56 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +32 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/inmetro.d.ts +17 -0
- package/dist/providers/inmetro.d.ts.map +1 -0
- package/dist/providers/inmetro.js +25 -0
- package/dist/providers/inmetro.js.map +1 -0
- package/dist/providers/jitter.d.ts +33 -0
- package/dist/providers/jitter.d.ts.map +1 -0
- package/dist/providers/jitter.js +81 -0
- package/dist/providers/jitter.js.map +1 -0
- package/dist/providers/lfdr.d.ts +11 -0
- package/dist/providers/lfdr.d.ts.map +1 -0
- package/dist/providers/lfdr.js +34 -0
- package/dist/providers/lfdr.js.map +1 -0
- package/dist/providers/microphone.d.ts +26 -0
- package/dist/providers/microphone.d.ts.map +1 -0
- package/dist/providers/microphone.js +120 -0
- package/dist/providers/microphone.js.map +1 -0
- package/dist/providers/nist-beacon.d.ts +11 -0
- package/dist/providers/nist-beacon.d.ts.map +1 -0
- package/dist/providers/nist-beacon.js +20 -0
- package/dist/providers/nist-beacon.js.map +1 -0
- package/dist/providers/nqsn.d.ts +11 -0
- package/dist/providers/nqsn.d.ts.map +1 -0
- package/dist/providers/nqsn.js +16 -0
- package/dist/providers/nqsn.js.map +1 -0
- package/dist/providers/outshift.d.ts +14 -0
- package/dist/providers/outshift.d.ts.map +1 -0
- package/dist/providers/outshift.js +52 -0
- package/dist/providers/outshift.js.map +1 -0
- package/dist/providers/padova.d.ts +12 -0
- package/dist/providers/padova.d.ts.map +1 -0
- package/dist/providers/padova.js +56 -0
- package/dist/providers/padova.js.map +1 -0
- package/dist/providers/qbck.d.ts +13 -0
- package/dist/providers/qbck.d.ts.map +1 -0
- package/dist/providers/qbck.js +62 -0
- package/dist/providers/qbck.js.map +1 -0
- package/dist/providers/qci.d.ts +14 -0
- package/dist/providers/qci.d.ts.map +1 -0
- package/dist/providers/qci.js +91 -0
- package/dist/providers/qci.js.map +1 -0
- package/dist/providers/qrandom.d.ts +12 -0
- package/dist/providers/qrandom.d.ts.map +1 -0
- package/dist/providers/qrandom.js +39 -0
- package/dist/providers/qrandom.js.map +1 -0
- package/dist/providers/randao.d.ts +14 -0
- package/dist/providers/randao.d.ts.map +1 -0
- package/dist/providers/randao.js +81 -0
- package/dist/providers/randao.js.map +1 -0
- package/dist/providers/random-org.d.ts +13 -0
- package/dist/providers/random-org.d.ts.map +1 -0
- package/dist/providers/random-org.js +89 -0
- package/dist/providers/random-org.js.map +1 -0
- package/dist/providers/sdr.d.ts +21 -0
- package/dist/providers/sdr.d.ts.map +1 -0
- package/dist/providers/sdr.js +63 -0
- package/dist/providers/sdr.js.map +1 -0
- package/dist/providers/sensor.d.ts +24 -0
- package/dist/providers/sensor.d.ts.map +1 -0
- package/dist/providers/sensor.js +119 -0
- package/dist/providers/sensor.js.map +1 -0
- package/dist/providers/serial.d.ts +35 -0
- package/dist/providers/serial.d.ts.map +1 -0
- package/dist/providers/serial.js +78 -0
- package/dist/providers/serial.js.map +1 -0
- package/dist/providers/solana.d.ts +13 -0
- package/dist/providers/solana.d.ts.map +1 -0
- package/dist/providers/solana.js +83 -0
- package/dist/providers/solana.js.map +1 -0
- package/dist/providers/superrand.d.ts +35 -0
- package/dist/providers/superrand.d.ts.map +1 -0
- package/dist/providers/superrand.js +215 -0
- package/dist/providers/superrand.js.map +1 -0
- package/dist/providers/tezos.d.ts +13 -0
- package/dist/providers/tezos.d.ts.map +1 -0
- package/dist/providers/tezos.js +92 -0
- package/dist/providers/tezos.js.map +1 -0
- package/dist/providers/uchile.d.ts +11 -0
- package/dist/providers/uchile.d.ts.map +1 -0
- package/dist/providers/uchile.js +22 -0
- package/dist/providers/uchile.js.map +1 -0
- package/dist/strategies/fallback.d.ts +12 -0
- package/dist/strategies/fallback.d.ts.map +1 -0
- package/dist/strategies/fallback.js +45 -0
- package/dist/strategies/fallback.js.map +1 -0
- package/dist/strategies/race.d.ts +7 -0
- package/dist/strategies/race.d.ts.map +1 -0
- package/dist/strategies/race.js +41 -0
- package/dist/strategies/race.js.map +1 -0
- package/dist/strategies/xor.d.ts +9 -0
- package/dist/strategies/xor.d.ts.map +1 -0
- package/dist/strategies/xor.js +56 -0
- package/dist/strategies/xor.js.map +1 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { concatBytes } from '../internal/bytes.js';
|
|
3
|
+
import { base58Decode } from '../internal/encoding.js';
|
|
4
|
+
import { fetchJson } from '../internal/http.js';
|
|
5
|
+
import { defineProvider } from '../internal/provider.js';
|
|
6
|
+
import { sleep } from '../internal/rate-limit.js';
|
|
7
|
+
import { beaconStream } from '../internal/stream.js';
|
|
8
|
+
const INFO = Object.freeze({
|
|
9
|
+
name: 'solana',
|
|
10
|
+
kind: 'beacon',
|
|
11
|
+
privacy: 'public',
|
|
12
|
+
});
|
|
13
|
+
const HASH_BYTES = 32;
|
|
14
|
+
const DEFAULT_URL = 'https://api.mainnet-beta.solana.com/';
|
|
15
|
+
// ~400 ms slots; poll politely.
|
|
16
|
+
const DEFAULT_POLL_INTERVAL_MS = 500;
|
|
17
|
+
/**
|
|
18
|
+
* Solana latest blockhash as a PUBLIC crypto-beacon (fast ~400 ms slots).
|
|
19
|
+
* The slot leader influences block content, so treat it like every other
|
|
20
|
+
* chain beacon: auditable public randomness, never private entropy.
|
|
21
|
+
*/
|
|
22
|
+
export function solanaBeacon(opts = {}) {
|
|
23
|
+
const { url = DEFAULT_URL, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, fetch: fetchImpl } = opts;
|
|
24
|
+
async function fetchLatest(signal) {
|
|
25
|
+
const res = await fetchJson(url, {
|
|
26
|
+
provider: INFO.name,
|
|
27
|
+
method: 'POST',
|
|
28
|
+
body: { jsonrpc: '2.0', id: 1, method: 'getLatestBlockhash' },
|
|
29
|
+
signal,
|
|
30
|
+
fetchImpl,
|
|
31
|
+
});
|
|
32
|
+
const slot = res?.result?.context?.slot;
|
|
33
|
+
const blockhash = res?.result?.value?.blockhash;
|
|
34
|
+
if (!Number.isInteger(slot) || typeof blockhash !== 'string') {
|
|
35
|
+
throw new EntropyError('bad_response', 'missing blockhash/slot', { provider: INFO.name });
|
|
36
|
+
}
|
|
37
|
+
let bytes;
|
|
38
|
+
try {
|
|
39
|
+
bytes = base58Decode(blockhash);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new EntropyError('bad_response', 'invalid base58 blockhash', {
|
|
43
|
+
provider: INFO.name,
|
|
44
|
+
cause: error,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (bytes.length !== HASH_BYTES) {
|
|
48
|
+
throw new EntropyError('bad_response', `blockhash decodes to ${bytes.length} bytes`, {
|
|
49
|
+
provider: INFO.name,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return { slot: slot, bytes };
|
|
53
|
+
}
|
|
54
|
+
return defineProvider({
|
|
55
|
+
...INFO,
|
|
56
|
+
defaultChunkBytes: HASH_BYTES,
|
|
57
|
+
async getBytes(length, reqOpts) {
|
|
58
|
+
// no keyless historical route — aggregate consecutive fresh slots,
|
|
59
|
+
// waiting out repeats (slots are ~400 ms, so this stays cheap)
|
|
60
|
+
const chunks = [];
|
|
61
|
+
let collected = 0;
|
|
62
|
+
let lastSlot = -1;
|
|
63
|
+
while (collected < length) {
|
|
64
|
+
const { slot, bytes } = await fetchLatest(reqOpts?.signal);
|
|
65
|
+
if (slot === lastSlot) {
|
|
66
|
+
await sleep(pollIntervalMs, reqOpts?.signal);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
lastSlot = slot;
|
|
70
|
+
chunks.push(bytes);
|
|
71
|
+
collected += bytes.length;
|
|
72
|
+
}
|
|
73
|
+
return { bytes: concatBytes(chunks).slice(0, length), sources: [INFO] };
|
|
74
|
+
},
|
|
75
|
+
stream(streamOpts = {}) {
|
|
76
|
+
return beaconStream(async (signal) => {
|
|
77
|
+
const { slot, bytes } = await fetchLatest(signal);
|
|
78
|
+
return { id: slot, bytes };
|
|
79
|
+
}, pollIntervalMs, HASH_BYTES, INFO.name, streamOpts);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=solana.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solana.js","sourceRoot":"","sources":["../../src/providers/solana.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { EntropyProvider } from '../types.js';
|
|
2
|
+
/** The subset of the WebSocket surface this provider uses (injectable in tests). */
|
|
3
|
+
export interface WebSocketLike {
|
|
4
|
+
send(data: string): void;
|
|
5
|
+
close(code?: number, reason?: string): void;
|
|
6
|
+
onopen: (() => void) | null;
|
|
7
|
+
onmessage: ((event: {
|
|
8
|
+
data: string;
|
|
9
|
+
}) => void) | null;
|
|
10
|
+
onerror: (() => void) | null;
|
|
11
|
+
onclose: ((event: {
|
|
12
|
+
code: number;
|
|
13
|
+
wasClean: boolean;
|
|
14
|
+
}) => void) | null;
|
|
15
|
+
}
|
|
16
|
+
export type WebSocketConstructor = new (url: string | URL) => WebSocketLike;
|
|
17
|
+
export interface SuperRandOptions {
|
|
18
|
+
/** SuperRand API key (goes into the query string per their API design). */
|
|
19
|
+
apiKey: string;
|
|
20
|
+
fetch?: typeof fetch;
|
|
21
|
+
/** Injectable WebSocket constructor — also the Node 20 escape hatch. */
|
|
22
|
+
WebSocketCtor?: WebSocketConstructor | typeof WebSocket;
|
|
23
|
+
baseUrl?: string;
|
|
24
|
+
wsUrl?: string;
|
|
25
|
+
/** Base delay for exponential reconnect backoff. Default 500. */
|
|
26
|
+
reconnectBaseDelayMs?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* SuperRand (Spence Technologies) — electromagnetic background-noise TRNG.
|
|
30
|
+
* REST for `getBytes`; the only public entropy API with WebSocket delivery,
|
|
31
|
+
* used for `stream()`. The stream keeps exactly one request in flight per
|
|
32
|
+
* connection (the protocol has no correlation id — responses are FIFO).
|
|
33
|
+
*/
|
|
34
|
+
export declare function superRand(opts: SuperRandOptions): EntropyProvider;
|
|
35
|
+
//# sourceMappingURL=superrand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superrand.d.ts","sourceRoot":"","sources":["../../src/providers/superrand.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAA2C,MAAM,aAAa,CAAA;AAe3F,oFAAoF;AACpF,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3C,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;IACrD,OAAO,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;IAC5B,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;CACvE;AAED,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,EAAE,MAAM,GAAG,GAAG,KAAK,aAAa,CAAA;AAE3E,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,wEAAwE;IACxE,aAAa,CAAC,EAAE,oBAAoB,GAAG,OAAO,SAAS,CAAA;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B;AA8BD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,eAAe,CA2NjE"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { concatBytes } from '../internal/bytes.js';
|
|
3
|
+
import { fetchJson } from '../internal/http.js';
|
|
4
|
+
import { defineProvider } from '../internal/provider.js';
|
|
5
|
+
import { sleep } from '../internal/rate-limit.js';
|
|
6
|
+
import { byteArrayFrom } from '../internal/validate.js';
|
|
7
|
+
const INFO = Object.freeze({
|
|
8
|
+
name: 'superrand',
|
|
9
|
+
kind: 'trng',
|
|
10
|
+
privacy: 'private',
|
|
11
|
+
});
|
|
12
|
+
// Live-verified: the API rejects count > 256 with INVALID_COUNT_RANGE.
|
|
13
|
+
const MAX_PER_REQUEST = 256;
|
|
14
|
+
const DEFAULT_BASE_URL = 'https://api.super-rand.io/v1/';
|
|
15
|
+
const DEFAULT_WS_URL = 'wss://api.super-rand.io/v1/';
|
|
16
|
+
const DEFAULT_STREAM_CHUNK = 32;
|
|
17
|
+
const MAX_RECONNECTS = 3;
|
|
18
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
|
|
19
|
+
// Wire format LIVE-VERIFIED 2026-07-07 against api.super-rand.io:
|
|
20
|
+
// - Request (REST body and WS frame, strict schema — extra fields rejected):
|
|
21
|
+
// {request:'integer', min:0, max:255, count:n} with n ≤ 256
|
|
22
|
+
// - REST response: {res: number | number[], jobType, length, ...}
|
|
23
|
+
// (`res` is a bare number when count is 1/omitted)
|
|
24
|
+
// - WS response: {status:'success', data:{res,...}, signature}
|
|
25
|
+
// | {status:'error', error:{code,message}}
|
|
26
|
+
// No tag/id field exists; responses arrive in request (FIFO) order.
|
|
27
|
+
function integerRequest(count) {
|
|
28
|
+
return { request: 'integer', min: 0, max: 255, count };
|
|
29
|
+
}
|
|
30
|
+
/** `res` is a bare number for single-value requests — normalize to an array. */
|
|
31
|
+
function normalizeRes(res) {
|
|
32
|
+
return typeof res === 'number' ? [res] : res;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* SuperRand (Spence Technologies) — electromagnetic background-noise TRNG.
|
|
36
|
+
* REST for `getBytes`; the only public entropy API with WebSocket delivery,
|
|
37
|
+
* used for `stream()`. The stream keeps exactly one request in flight per
|
|
38
|
+
* connection (the protocol has no correlation id — responses are FIFO).
|
|
39
|
+
*/
|
|
40
|
+
export function superRand(opts) {
|
|
41
|
+
if (!opts?.apiKey)
|
|
42
|
+
throw new TypeError('superRand({ apiKey }) requires a non-empty apiKey');
|
|
43
|
+
const { apiKey, baseUrl = DEFAULT_BASE_URL, wsUrl = DEFAULT_WS_URL, reconnectBaseDelayMs = 500, fetch: fetchImpl, } = opts;
|
|
44
|
+
const WebSocketCtor = (opts.WebSocketCtor ??
|
|
45
|
+
globalThis.WebSocket);
|
|
46
|
+
function openConnection() {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
if (!WebSocketCtor) {
|
|
49
|
+
reject(new TypeError('superRand stream() needs a WebSocket implementation — pass WebSocketCtor (Node 20 has no global WebSocket)'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const socket = new WebSocketCtor(`${wsUrl}?key=${encodeURIComponent(apiKey)}`);
|
|
53
|
+
let pending = null;
|
|
54
|
+
let opened = false;
|
|
55
|
+
let closedByUs = false;
|
|
56
|
+
const connection = {
|
|
57
|
+
dead: false,
|
|
58
|
+
request(n, signal, timeoutMs) {
|
|
59
|
+
return new Promise((resolveReq, rejectReq) => {
|
|
60
|
+
if (connection.dead) {
|
|
61
|
+
rejectReq(new EntropyError('network', 'connection is closed', { provider: INFO.name }));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const timer = setTimeout(() => {
|
|
65
|
+
settle(new EntropyError('timeout', `no response within ${timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS}ms`, { provider: INFO.name }));
|
|
66
|
+
}, timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
|
|
67
|
+
const onAbort = () => settle(new EntropyError('aborted', 'request aborted', { provider: INFO.name }));
|
|
68
|
+
const settle = (error, bytes) => {
|
|
69
|
+
pending = null;
|
|
70
|
+
clearTimeout(timer);
|
|
71
|
+
signal?.removeEventListener('abort', onAbort);
|
|
72
|
+
if (error)
|
|
73
|
+
rejectReq(error);
|
|
74
|
+
else
|
|
75
|
+
resolveReq(bytes);
|
|
76
|
+
};
|
|
77
|
+
if (signal?.aborted) {
|
|
78
|
+
settle(new EntropyError('aborted', 'request aborted', { provider: INFO.name }));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
82
|
+
pending = { expected: n, settle };
|
|
83
|
+
socket.send(JSON.stringify(integerRequest(n)));
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
close() {
|
|
87
|
+
closedByUs = true;
|
|
88
|
+
connection.dead = true;
|
|
89
|
+
socket.close();
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
const fail = () => {
|
|
93
|
+
if (connection.dead && !pending)
|
|
94
|
+
return;
|
|
95
|
+
connection.dead = true;
|
|
96
|
+
const error = new EntropyError('network', 'SuperRand WebSocket connection lost', {
|
|
97
|
+
provider: INFO.name,
|
|
98
|
+
});
|
|
99
|
+
pending?.settle(error);
|
|
100
|
+
pending = null;
|
|
101
|
+
if (!opened && !closedByUs)
|
|
102
|
+
reject(error);
|
|
103
|
+
};
|
|
104
|
+
socket.onopen = () => {
|
|
105
|
+
opened = true;
|
|
106
|
+
resolve(connection);
|
|
107
|
+
};
|
|
108
|
+
socket.onerror = fail;
|
|
109
|
+
socket.onclose = fail;
|
|
110
|
+
socket.onmessage = (event) => {
|
|
111
|
+
let message;
|
|
112
|
+
try {
|
|
113
|
+
message = JSON.parse(String(event.data));
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return; // ignore unparseable frames
|
|
117
|
+
}
|
|
118
|
+
const entry = pending;
|
|
119
|
+
if (!entry)
|
|
120
|
+
return; // nothing in flight — late or unsolicited frame
|
|
121
|
+
if (message.status === 'error') {
|
|
122
|
+
entry.settle(new EntropyError('bad_response', `SuperRand error ${message.error?.code ?? 'unknown'}: ${message.error?.message ?? ''}`, { provider: INFO.name }));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
entry.settle(null, byteArrayFrom(normalizeRes(message.data?.res), entry.expected, INFO.name));
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
entry.settle(error instanceof EntropyError
|
|
130
|
+
? error
|
|
131
|
+
: new EntropyError('bad_response', 'malformed frame', {
|
|
132
|
+
provider: INFO.name,
|
|
133
|
+
cause: error,
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async function* wsStream(opts) {
|
|
140
|
+
const chunkBytes = opts.chunkBytes ?? DEFAULT_STREAM_CHUNK;
|
|
141
|
+
let connection = null;
|
|
142
|
+
let failures = 0;
|
|
143
|
+
try {
|
|
144
|
+
while (true) {
|
|
145
|
+
if (opts.signal?.aborted) {
|
|
146
|
+
throw new EntropyError('aborted', 'stream aborted', { provider: INFO.name });
|
|
147
|
+
}
|
|
148
|
+
if (!connection || connection.dead) {
|
|
149
|
+
try {
|
|
150
|
+
connection = await openConnection();
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
failures++;
|
|
154
|
+
if (failures > MAX_RECONNECTS) {
|
|
155
|
+
throw error instanceof TypeError
|
|
156
|
+
? error
|
|
157
|
+
: new EntropyError('network', `SuperRand WebSocket failed after ${MAX_RECONNECTS} reconnect attempts`, { provider: INFO.name, cause: error });
|
|
158
|
+
}
|
|
159
|
+
await sleep(reconnectBaseDelayMs * 2 ** (failures - 1), opts.signal);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const bytes = await connection.request(chunkBytes, opts.signal, opts.timeoutMs);
|
|
165
|
+
failures = 0;
|
|
166
|
+
yield bytes;
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
if (error instanceof EntropyError &&
|
|
170
|
+
(error.code === 'aborted' ||
|
|
171
|
+
error.code === 'bad_response' ||
|
|
172
|
+
error.code === 'invalid_request')) {
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
connection.close();
|
|
176
|
+
connection = null;
|
|
177
|
+
failures++;
|
|
178
|
+
if (failures > MAX_RECONNECTS) {
|
|
179
|
+
throw new EntropyError('network', `SuperRand WebSocket failed after ${MAX_RECONNECTS} reconnect attempts`, { provider: INFO.name, cause: error });
|
|
180
|
+
}
|
|
181
|
+
await sleep(reconnectBaseDelayMs * 2 ** (failures - 1), opts.signal);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
// TS narrows `connection` to never after the infinite loop; widen it.
|
|
187
|
+
;
|
|
188
|
+
connection?.close();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return defineProvider({
|
|
192
|
+
...INFO,
|
|
193
|
+
defaultChunkBytes: DEFAULT_STREAM_CHUNK,
|
|
194
|
+
async getBytes(length, reqOpts) {
|
|
195
|
+
const chunks = [];
|
|
196
|
+
for (let remaining = length; remaining > 0;) {
|
|
197
|
+
const n = Math.min(MAX_PER_REQUEST, remaining);
|
|
198
|
+
const res = await fetchJson(`${baseUrl}?key=${encodeURIComponent(apiKey)}`, {
|
|
199
|
+
provider: INFO.name,
|
|
200
|
+
method: 'POST',
|
|
201
|
+
body: integerRequest(n),
|
|
202
|
+
signal: reqOpts?.signal,
|
|
203
|
+
fetchImpl,
|
|
204
|
+
});
|
|
205
|
+
chunks.push(byteArrayFrom(normalizeRes(res?.res), n, INFO.name));
|
|
206
|
+
remaining -= n;
|
|
207
|
+
}
|
|
208
|
+
return { bytes: concatBytes(chunks), sources: [INFO] };
|
|
209
|
+
},
|
|
210
|
+
stream(opts = {}) {
|
|
211
|
+
return wsStream(opts);
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=superrand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superrand.js","sourceRoot":"","sources":["../../src/providers/superrand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EntropyProvider } from '../types.js';
|
|
2
|
+
export interface TezosBeaconOptions {
|
|
3
|
+
fetch?: typeof fetch;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
pollIntervalMs?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Tezos head block hashes as a PUBLIC crypto-beacon, read through the TzKT
|
|
9
|
+
* indexer — note the extra trust in a third-party indexer on top of the usual
|
|
10
|
+
* baker influence. Auditable public randomness only.
|
|
11
|
+
*/
|
|
12
|
+
export declare function tezosBeacon(opts?: TezosBeaconOptions): EntropyProvider;
|
|
13
|
+
//# sourceMappingURL=tezos.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tezos.d.ts","sourceRoot":"","sources":["../../src/providers/tezos.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAA;AAcrE,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAqCD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,eAAe,CAuD1E"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { concatBytes } from '../internal/bytes.js';
|
|
3
|
+
import { base58Decode } from '../internal/encoding.js';
|
|
4
|
+
import { fetchJson } from '../internal/http.js';
|
|
5
|
+
import { defineProvider } from '../internal/provider.js';
|
|
6
|
+
import { beaconStream } from '../internal/stream.js';
|
|
7
|
+
const INFO = Object.freeze({
|
|
8
|
+
name: 'tezos',
|
|
9
|
+
kind: 'beacon',
|
|
10
|
+
privacy: 'public',
|
|
11
|
+
});
|
|
12
|
+
const HASH_BYTES = 32;
|
|
13
|
+
const PREFIX_BYTES = 2; // Tezos block hashes: base58check payload starts [1, 52]
|
|
14
|
+
const CHECKSUM_BYTES = 4;
|
|
15
|
+
const DEFAULT_BASE_URL = 'https://api.tzkt.io';
|
|
16
|
+
// ~8 s blocks.
|
|
17
|
+
const DEFAULT_POLL_INTERVAL_MS = 10_000;
|
|
18
|
+
/** base58check-decode a Tezos block hash and return its 32 payload bytes. */
|
|
19
|
+
async function decodeBlockHash(hash) {
|
|
20
|
+
let full;
|
|
21
|
+
try {
|
|
22
|
+
full = base58Decode(hash);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw new EntropyError('bad_response', 'invalid base58 block hash', {
|
|
26
|
+
provider: INFO.name,
|
|
27
|
+
cause: error,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (full.length !== PREFIX_BYTES + HASH_BYTES + CHECKSUM_BYTES) {
|
|
31
|
+
throw new EntropyError('bad_response', `block hash decodes to ${full.length} bytes`, {
|
|
32
|
+
provider: INFO.name,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const payload = full.slice(0, PREFIX_BYTES + HASH_BYTES);
|
|
36
|
+
const checksum = full.slice(PREFIX_BYTES + HASH_BYTES);
|
|
37
|
+
const first = new Uint8Array(await crypto.subtle.digest('SHA-256', payload));
|
|
38
|
+
const second = new Uint8Array(await crypto.subtle.digest('SHA-256', first));
|
|
39
|
+
for (let i = 0; i < CHECKSUM_BYTES; i++) {
|
|
40
|
+
if (checksum[i] !== second[i]) {
|
|
41
|
+
throw new EntropyError('bad_response', 'block hash checksum mismatch', {
|
|
42
|
+
provider: INFO.name,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return payload.slice(PREFIX_BYTES);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Tezos head block hashes as a PUBLIC crypto-beacon, read through the TzKT
|
|
50
|
+
* indexer — note the extra trust in a third-party indexer on top of the usual
|
|
51
|
+
* baker influence. Auditable public randomness only.
|
|
52
|
+
*/
|
|
53
|
+
export function tezosBeacon(opts = {}) {
|
|
54
|
+
const { baseUrl = DEFAULT_BASE_URL, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, fetch: fetchImpl, } = opts;
|
|
55
|
+
async function fetchBlock(path, signal) {
|
|
56
|
+
const res = await fetchJson(`${baseUrl}${path}`, {
|
|
57
|
+
provider: INFO.name,
|
|
58
|
+
signal,
|
|
59
|
+
fetchImpl,
|
|
60
|
+
});
|
|
61
|
+
if (typeof res?.hash !== 'string' || !Number.isInteger(res?.level)) {
|
|
62
|
+
throw new EntropyError('bad_response', 'missing block hash/level', { provider: INFO.name });
|
|
63
|
+
}
|
|
64
|
+
return { level: res.level, bytes: await decodeBlockHash(res.hash) };
|
|
65
|
+
}
|
|
66
|
+
return defineProvider({
|
|
67
|
+
...INFO,
|
|
68
|
+
defaultChunkBytes: HASH_BYTES,
|
|
69
|
+
async getBytes(length, reqOpts) {
|
|
70
|
+
const blocksNeeded = Math.ceil(length / HASH_BYTES);
|
|
71
|
+
const head = await fetchBlock('/v1/head', reqOpts?.signal);
|
|
72
|
+
const chunks = [head.bytes];
|
|
73
|
+
for (let i = 1; i < blocksNeeded; i++) {
|
|
74
|
+
const level = head.level - i;
|
|
75
|
+
if (level < 1) {
|
|
76
|
+
throw new EntropyError('insufficient_entropy', 'not enough blocks for request', {
|
|
77
|
+
provider: INFO.name,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
chunks.push((await fetchBlock(`/v1/blocks/${level}`, reqOpts?.signal)).bytes);
|
|
81
|
+
}
|
|
82
|
+
return { bytes: concatBytes(chunks).slice(0, length), sources: [INFO] };
|
|
83
|
+
},
|
|
84
|
+
stream(streamOpts = {}) {
|
|
85
|
+
return beaconStream(async (signal) => {
|
|
86
|
+
const head = await fetchBlock('/v1/head', signal);
|
|
87
|
+
return { id: head.level, bytes: head.bytes };
|
|
88
|
+
}, pollIntervalMs, HASH_BYTES, INFO.name, streamOpts);
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=tezos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tezos.js","sourceRoot":"","sources":["../../src/providers/tezos.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type NistFamilyOptions } from '../internal/nist-pulse.js';
|
|
2
|
+
import type { EntropyProvider } from '../types.js';
|
|
3
|
+
export interface UchileOptions extends NistFamilyOptions {
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Random UChile (CLCERT, Universidad de Chile) — hybrid beacon mixing a local
|
|
7
|
+
* quantum device with seismic, radio and other public inputs; 512 bits every
|
|
8
|
+
* 60 seconds. PUBLIC randomness.
|
|
9
|
+
*/
|
|
10
|
+
export declare function uchile(opts?: UchileOptions): EntropyProvider;
|
|
11
|
+
//# sourceMappingURL=uchile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uchile.d.ts","sourceRoot":"","sources":["../../src/providers/uchile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAmB,MAAM,2BAA2B,CAAA;AACnF,OAAO,KAAK,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAA;AAQrE,MAAM,WAAW,aAAc,SAAQ,iBAAiB;CAAG;AAE3D;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,IAAI,GAAE,aAAkB,GAAG,eAAe,CAYhE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { nistPulseBeacon } from '../internal/nist-pulse.js';
|
|
2
|
+
const INFO = Object.freeze({
|
|
3
|
+
name: 'uchile',
|
|
4
|
+
kind: 'beacon',
|
|
5
|
+
privacy: 'public',
|
|
6
|
+
});
|
|
7
|
+
/**
|
|
8
|
+
* Random UChile (CLCERT, Universidad de Chile) — hybrid beacon mixing a local
|
|
9
|
+
* quantum device with seismic, radio and other public inputs; 512 bits every
|
|
10
|
+
* 60 seconds. PUBLIC randomness.
|
|
11
|
+
*/
|
|
12
|
+
export function uchile(opts = {}) {
|
|
13
|
+
return nistPulseBeacon({
|
|
14
|
+
info: INFO,
|
|
15
|
+
defaultBaseUrl: 'https://random.uchile.cl/beacon/2.1-beta',
|
|
16
|
+
// NB: their '/pulse/last' route is a genuine 404 — the query form is
|
|
17
|
+
// the working "latest" endpoint.
|
|
18
|
+
latestPath: '/pulse?chainId=last&pulseId=last',
|
|
19
|
+
defaultPollIntervalMs: 60_000,
|
|
20
|
+
}, opts);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=uchile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uchile.js","sourceRoot":"","sources":["../../src/providers/uchile.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAGnF,MAAM,IAAI,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,QAAQ;CAClB,CAAC,CAAA;AAIF;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,OAAsB,EAAE;IAC7C,OAAO,eAAe,CACpB;QACE,IAAI,EAAE,IAAI;QACV,cAAc,EAAE,0CAA0C;QAC1D,qEAAqE;QACrE,iCAAiC;QACjC,UAAU,EAAE,kCAAkC;QAC9C,qBAAqB,EAAE,MAAM;KAC9B,EACD,IAAI,CACL,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EntropyProvider } from '../types.js';
|
|
2
|
+
export interface FallbackOptions {
|
|
3
|
+
/** Budget per member attempt before moving to the next. Default 10_000. */
|
|
4
|
+
attemptTimeoutMs?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Try providers strictly in array order; the first success wins. An attempt
|
|
8
|
+
* that fails or exceeds `attemptTimeoutMs` moves the chain along; a caller
|
|
9
|
+
* abort stops the chain immediately. Attribution reports the winner's sources.
|
|
10
|
+
*/
|
|
11
|
+
export declare function fallback(providers: EntropyProvider[], opts?: FallbackOptions): EntropyProvider;
|
|
12
|
+
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/strategies/fallback.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,eAAe,EAAE,EAC5B,IAAI,GAAE,eAAoB,GACzB,eAAe,CAkCjB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { abortedError, commonKind, compositeName, pessimisticPrivacy, requireProviders, toEntropyError, } from '../internal/composite.js';
|
|
3
|
+
import { defineProvider } from '../internal/provider.js';
|
|
4
|
+
/**
|
|
5
|
+
* Try providers strictly in array order; the first success wins. An attempt
|
|
6
|
+
* that fails or exceeds `attemptTimeoutMs` moves the chain along; a caller
|
|
7
|
+
* abort stops the chain immediately. Attribution reports the winner's sources.
|
|
8
|
+
*/
|
|
9
|
+
export function fallback(providers, opts = {}) {
|
|
10
|
+
requireProviders(providers, 'fallback');
|
|
11
|
+
const members = [...providers];
|
|
12
|
+
const attemptTimeoutMs = opts.attemptTimeoutMs ?? 10_000;
|
|
13
|
+
const name = compositeName('fallback', members);
|
|
14
|
+
return defineProvider({
|
|
15
|
+
name,
|
|
16
|
+
kind: commonKind(members),
|
|
17
|
+
privacy: pessimisticPrivacy(members),
|
|
18
|
+
// Leave room for every member to use its full attempt budget by default.
|
|
19
|
+
defaultTimeoutMs: attemptTimeoutMs * members.length + 1000,
|
|
20
|
+
async getBytes(length, reqOpts) {
|
|
21
|
+
const errors = [];
|
|
22
|
+
for (const member of members) {
|
|
23
|
+
if (reqOpts?.signal?.aborted)
|
|
24
|
+
throw abortedError(name);
|
|
25
|
+
try {
|
|
26
|
+
return await member.getBytes(length, {
|
|
27
|
+
signal: reqOpts?.signal,
|
|
28
|
+
timeoutMs: attemptTimeoutMs,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const entropyError = toEntropyError(error, member.name);
|
|
33
|
+
if (entropyError.code === 'aborted')
|
|
34
|
+
throw entropyError;
|
|
35
|
+
errors.push(entropyError);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw new EntropyError('insufficient_entropy', `all ${members.length} providers failed`, {
|
|
39
|
+
provider: name,
|
|
40
|
+
cause: new AggregateError(errors, `all providers in ${name} failed`),
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/strategies/fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EACL,YAAY,EACZ,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,GACf,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAQxD;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,SAA4B,EAC5B,OAAwB,EAAE;IAE1B,gBAAgB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;IACvC,MAAM,OAAO,GAAG,CAAC,GAAG,SAAS,CAAC,CAAA;IAC9B,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,MAAM,CAAA;IACxD,MAAM,IAAI,GAAG,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IAE/C,OAAO,cAAc,CAAC;QACpB,IAAI;QACJ,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC;QACzB,OAAO,EAAE,kBAAkB,CAAC,OAAO,CAAC;QACpC,yEAAyE;QACzE,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI;QAE1D,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO;YAC5B,MAAM,MAAM,GAAmB,EAAE,CAAA;YACjC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,OAAO,EAAE,MAAM,EAAE,OAAO;oBAAE,MAAM,YAAY,CAAC,IAAI,CAAC,CAAA;gBACtD,IAAI,CAAC;oBACH,OAAO,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE;wBACnC,MAAM,EAAE,OAAO,EAAE,MAAM;wBACvB,SAAS,EAAE,gBAAgB;qBAC5B,CAAC,CAAA;gBACJ,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,YAAY,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;oBACvD,IAAI,YAAY,CAAC,IAAI,KAAK,SAAS;wBAAE,MAAM,YAAY,CAAA;oBACvD,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;gBAC3B,CAAC;YACH,CAAC;YACD,MAAM,IAAI,YAAY,CAAC,sBAAsB,EAAE,OAAO,OAAO,CAAC,MAAM,mBAAmB,EAAE;gBACvF,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,IAAI,cAAc,CAAC,MAAM,EAAE,oBAAoB,IAAI,SAAS,CAAC;aACrE,CAAC,CAAA;QACJ,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EntropyProvider } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Start ALL members simultaneously; the first fulfillment wins and the losers
|
|
4
|
+
* are cancelled via AbortSignal. Attribution reports only the winner.
|
|
5
|
+
*/
|
|
6
|
+
export declare function race(providers: EntropyProvider[]): EntropyProvider;
|
|
7
|
+
//# sourceMappingURL=race.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"race.d.ts","sourceRoot":"","sources":["../../src/strategies/race.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD;;;GAGG;AACH,wBAAgB,IAAI,CAAC,SAAS,EAAE,eAAe,EAAE,GAAG,eAAe,CAsClE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { abortedError, commonKind, compositeName, pessimisticPrivacy, requireProviders, toEntropyError, } from '../internal/composite.js';
|
|
3
|
+
import { defineProvider } from '../internal/provider.js';
|
|
4
|
+
/**
|
|
5
|
+
* Start ALL members simultaneously; the first fulfillment wins and the losers
|
|
6
|
+
* are cancelled via AbortSignal. Attribution reports only the winner.
|
|
7
|
+
*/
|
|
8
|
+
export function race(providers) {
|
|
9
|
+
requireProviders(providers, 'race');
|
|
10
|
+
const members = [...providers];
|
|
11
|
+
const name = compositeName('race', members);
|
|
12
|
+
return defineProvider({
|
|
13
|
+
name,
|
|
14
|
+
kind: commonKind(members),
|
|
15
|
+
privacy: pessimisticPrivacy(members),
|
|
16
|
+
async getBytes(length, reqOpts) {
|
|
17
|
+
const controller = new AbortController();
|
|
18
|
+
const signal = reqOpts?.signal
|
|
19
|
+
? AbortSignal.any([reqOpts.signal, controller.signal])
|
|
20
|
+
: controller.signal;
|
|
21
|
+
const tasks = members.map((member) => member.getBytes(length, { signal, timeoutMs: reqOpts?.timeoutMs }).catch((error) => {
|
|
22
|
+
throw toEntropyError(error, member.name);
|
|
23
|
+
}));
|
|
24
|
+
try {
|
|
25
|
+
return await Promise.any(tasks);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (reqOpts?.signal?.aborted)
|
|
29
|
+
throw abortedError(name);
|
|
30
|
+
throw new EntropyError('insufficient_entropy', `all ${members.length} providers failed in ${name}`, {
|
|
31
|
+
provider: name,
|
|
32
|
+
cause: error,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
controller.abort(); // cancel the losers (no-op if everything settled)
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=race.js.map
|