@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,76 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
function defaultErrorFor(status, body, response, provider) {
|
|
3
|
+
if (status === 429) {
|
|
4
|
+
const retryAfter = Number.parseFloat(response.headers.get('retry-after') ?? '');
|
|
5
|
+
return new EntropyError('rate_limited', `HTTP 429: ${body.slice(0, 200)}`, {
|
|
6
|
+
provider,
|
|
7
|
+
retryAfterMs: Number.isFinite(retryAfter) ? retryAfter * 1000 : undefined,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
if (status === 401 || status === 403) {
|
|
11
|
+
return new EntropyError('auth', `HTTP ${status}: ${body.slice(0, 200)}`, { provider });
|
|
12
|
+
}
|
|
13
|
+
return new EntropyError('network', `HTTP ${status}: ${body.slice(0, 200)}`, { provider });
|
|
14
|
+
}
|
|
15
|
+
function isAbort(error, signal) {
|
|
16
|
+
return signal?.aborted === true || (error instanceof DOMException && error.name === 'AbortError');
|
|
17
|
+
}
|
|
18
|
+
/** Shared transport + error taxonomy: resolve to an OK Response or throw EntropyError. */
|
|
19
|
+
async function fetchOk(url, opts, accept) {
|
|
20
|
+
const { provider, signal } = opts;
|
|
21
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
22
|
+
const headers = { accept, ...opts.headers };
|
|
23
|
+
const init = { method: opts.method ?? 'GET', headers, signal };
|
|
24
|
+
if (opts.body !== undefined) {
|
|
25
|
+
headers['content-type'] = 'application/json';
|
|
26
|
+
init.body = JSON.stringify(opts.body);
|
|
27
|
+
}
|
|
28
|
+
let response;
|
|
29
|
+
try {
|
|
30
|
+
response = await fetchImpl(url, init);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (isAbort(error, signal))
|
|
34
|
+
throw error;
|
|
35
|
+
throw new EntropyError('network', `request to ${url} failed: ${error.message}`, {
|
|
36
|
+
provider,
|
|
37
|
+
cause: error,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
const body = await response.text().catch(() => '');
|
|
42
|
+
throw (opts.onErrorResponse?.(response.status, body) ??
|
|
43
|
+
defaultErrorFor(response.status, body, response, provider));
|
|
44
|
+
}
|
|
45
|
+
return response;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* fetch + JSON parse with the library's uniform error taxonomy. Abort/timeout
|
|
49
|
+
* reasons pass through unwrapped so `defineProvider` can classify them.
|
|
50
|
+
*/
|
|
51
|
+
export async function fetchJson(url, opts) {
|
|
52
|
+
const response = await fetchOk(url, opts, 'application/json');
|
|
53
|
+
try {
|
|
54
|
+
return (await response.json());
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new EntropyError('bad_response', `invalid JSON from ${url}`, {
|
|
58
|
+
provider: opts.provider,
|
|
59
|
+
cause: error,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** fetch returning the raw text body (e.g. Bitcoin tip-hash endpoints). */
|
|
64
|
+
export async function fetchText(url, opts) {
|
|
65
|
+
const response = await fetchOk(url, opts, 'text/plain');
|
|
66
|
+
try {
|
|
67
|
+
return await response.text();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
throw new EntropyError('bad_response', `unreadable body from ${url}`, {
|
|
71
|
+
provider: opts.provider,
|
|
72
|
+
cause: error,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/internal/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { EntropyProvider, EntropySourceInfo } from '../types.js';
|
|
2
|
+
/** Caller-facing options every NIST-2.0-format beacon provider exposes. */
|
|
3
|
+
export interface NistFamilyOptions {
|
|
4
|
+
fetch?: typeof fetch;
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
/** Stream poll cadence. Default: the beacon's pulse period. */
|
|
7
|
+
pollIntervalMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface NistFamilySpec {
|
|
10
|
+
info: EntropySourceInfo;
|
|
11
|
+
defaultBaseUrl: string;
|
|
12
|
+
/**
|
|
13
|
+
* Appended to baseUrl for the newest pulse — may carry a query string
|
|
14
|
+
* (UChile) or rely on an HTTP redirect that fetch follows (NQSN's 303).
|
|
15
|
+
*/
|
|
16
|
+
latestPath: string;
|
|
17
|
+
/** Historical pulse path. Default `/chain/{chain}/pulse/{i}`. */
|
|
18
|
+
pulsePath?: (chainIndex: number, pulseIndex: number) => string;
|
|
19
|
+
/** outputValue size in bytes. Default 64 (512-bit). */
|
|
20
|
+
pulseBytes?: number;
|
|
21
|
+
/** Pulse period, doubling as the default stream poll cadence. */
|
|
22
|
+
defaultPollIntervalMs: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Shared machinery for the NIST IR 8213 beacon family (NIST, NQSN Singapore,
|
|
26
|
+
* Random UChile, Inmetro Brazil): fetch latest pulse (wrapped or flat JSON),
|
|
27
|
+
* walk the chain backwards for larger requests, poll-dedupe streaming.
|
|
28
|
+
* All of these emit PUBLIC randomness — never use it as secret key material.
|
|
29
|
+
*/
|
|
30
|
+
export declare function nistPulseBeacon(spec: NistFamilySpec, opts?: NistFamilyOptions): EntropyProvider;
|
|
31
|
+
//# sourceMappingURL=nist-pulse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nist-pulse.d.ts","sourceRoot":"","sources":["../../src/internal/nist-pulse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAOrE,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,iBAAiB,CAAA;IACvB,cAAc,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,MAAM,CAAA;IAC9D,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iEAAiE;IACjE,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAQD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE,iBAAsB,GAC3B,eAAe,CAiEjB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { concatBytes } from './bytes.js';
|
|
3
|
+
import { fetchJson } from './http.js';
|
|
4
|
+
import { defineProvider } from './provider.js';
|
|
5
|
+
import { beaconStream } from './stream.js';
|
|
6
|
+
import { bytesFromHexField } from './validate.js';
|
|
7
|
+
/**
|
|
8
|
+
* Shared machinery for the NIST IR 8213 beacon family (NIST, NQSN Singapore,
|
|
9
|
+
* Random UChile, Inmetro Brazil): fetch latest pulse (wrapped or flat JSON),
|
|
10
|
+
* walk the chain backwards for larger requests, poll-dedupe streaming.
|
|
11
|
+
* All of these emit PUBLIC randomness — never use it as secret key material.
|
|
12
|
+
*/
|
|
13
|
+
export function nistPulseBeacon(spec, opts = {}) {
|
|
14
|
+
const { info, pulseBytes = 64 } = spec;
|
|
15
|
+
const pulsePath = spec.pulsePath ?? ((chain, i) => `/chain/${chain}/pulse/${i}`);
|
|
16
|
+
const { baseUrl = spec.defaultBaseUrl, pollIntervalMs = spec.defaultPollIntervalMs, fetch: fetchImpl, } = opts;
|
|
17
|
+
async function fetchPulse(path, signal) {
|
|
18
|
+
const res = await fetchJson(`${baseUrl}${path}`, {
|
|
19
|
+
provider: info.name,
|
|
20
|
+
signal,
|
|
21
|
+
fetchImpl,
|
|
22
|
+
});
|
|
23
|
+
const pulse = res?.pulse ?? res;
|
|
24
|
+
const { chainIndex, pulseIndex } = pulse ?? {};
|
|
25
|
+
if (!Number.isInteger(chainIndex) || !Number.isInteger(pulseIndex)) {
|
|
26
|
+
throw new EntropyError('bad_response', 'malformed beacon pulse', { provider: info.name });
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
chainIndex: chainIndex,
|
|
30
|
+
pulseIndex: pulseIndex,
|
|
31
|
+
bytes: bytesFromHexField(pulse?.outputValue, pulseBytes, info.name),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return defineProvider({
|
|
35
|
+
...info,
|
|
36
|
+
defaultChunkBytes: pulseBytes,
|
|
37
|
+
async getBytes(length, reqOpts) {
|
|
38
|
+
const pulsesNeeded = Math.ceil(length / pulseBytes);
|
|
39
|
+
const latest = await fetchPulse(spec.latestPath, reqOpts?.signal);
|
|
40
|
+
const chunks = [latest.bytes];
|
|
41
|
+
// Past pulses are equally public — walk the chain backwards.
|
|
42
|
+
for (let i = 1; i < pulsesNeeded; i++) {
|
|
43
|
+
const pulseIndex = latest.pulseIndex - i;
|
|
44
|
+
if (pulseIndex < 1) {
|
|
45
|
+
throw new EntropyError('insufficient_entropy', 'beacon chain too short for request', {
|
|
46
|
+
provider: info.name,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const prior = await fetchPulse(pulsePath(latest.chainIndex, pulseIndex), reqOpts?.signal);
|
|
50
|
+
chunks.push(prior.bytes);
|
|
51
|
+
}
|
|
52
|
+
return { bytes: concatBytes(chunks).slice(0, length), sources: [info] };
|
|
53
|
+
},
|
|
54
|
+
stream(streamOpts = {}) {
|
|
55
|
+
return beaconStream(async (signal) => {
|
|
56
|
+
const pulse = await fetchPulse(spec.latestPath, signal);
|
|
57
|
+
return { id: pulse.pulseIndex, bytes: pulse.bytes };
|
|
58
|
+
}, pollIntervalMs, pulseBytes, info.name, streamOpts);
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=nist-pulse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nist-pulse.js","sourceRoot":"","sources":["../../src/internal/nist-pulse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { EntropyProvider, EntropyRequestOptions, EntropyResult, EntropySourceInfo, EntropyStreamOptions } from '../types.js';
|
|
2
|
+
export declare const DEFAULT_TIMEOUT_MS = 10000;
|
|
3
|
+
export interface ProviderSpec extends EntropySourceInfo {
|
|
4
|
+
getBytes(length: number, opts?: EntropyRequestOptions): Promise<EntropyResult>;
|
|
5
|
+
stream?(opts?: EntropyStreamOptions): AsyncIterable<Uint8Array>;
|
|
6
|
+
/** Natural chunk size for the default poll-based stream. */
|
|
7
|
+
defaultChunkBytes?: number;
|
|
8
|
+
/** Whole-call timeout when the caller passes none (composites need more room). */
|
|
9
|
+
defaultTimeoutMs?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Wraps a provider implementation with the cross-cutting contract every
|
|
13
|
+
* provider must honor: length validation (`invalid_request`), abort handling
|
|
14
|
+
* (`aborted`), a whole-call timeout budget (`timeout`), and a default
|
|
15
|
+
* poll-based `stream()`. The impl receives a composite AbortSignal that fires
|
|
16
|
+
* on either caller abort or timeout expiry.
|
|
17
|
+
*/
|
|
18
|
+
export declare function defineProvider(spec: ProviderSpec): EntropyProvider;
|
|
19
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/internal/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,aAAa,CAAA;AAGpB,eAAO,MAAM,kBAAkB,QAAS,CAAA;AAExC,MAAM,WAAW,YAAa,SAAQ,iBAAiB;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAC9E,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,oBAAoB,GAAG,aAAa,CAAC,UAAU,CAAC,CAAA;IAC/D,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,CAyDlE"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { pollStream } from './stream.js';
|
|
3
|
+
export const DEFAULT_TIMEOUT_MS = 10_000;
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a provider implementation with the cross-cutting contract every
|
|
6
|
+
* provider must honor: length validation (`invalid_request`), abort handling
|
|
7
|
+
* (`aborted`), a whole-call timeout budget (`timeout`), and a default
|
|
8
|
+
* poll-based `stream()`. The impl receives a composite AbortSignal that fires
|
|
9
|
+
* on either caller abort or timeout expiry.
|
|
10
|
+
*/
|
|
11
|
+
export function defineProvider(spec) {
|
|
12
|
+
const { name, kind, privacy, defaultChunkBytes } = spec;
|
|
13
|
+
const provider = {
|
|
14
|
+
name,
|
|
15
|
+
kind,
|
|
16
|
+
privacy,
|
|
17
|
+
async getBytes(length, opts = {}) {
|
|
18
|
+
if (!Number.isInteger(length) || length < 1) {
|
|
19
|
+
throw new EntropyError('invalid_request', `requested length must be a positive integer, got ${length}`, {
|
|
20
|
+
provider: name,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
if (opts.signal?.aborted) {
|
|
24
|
+
throw new EntropyError('aborted', 'request aborted before start', { provider: name });
|
|
25
|
+
}
|
|
26
|
+
const timeoutMs = opts.timeoutMs ?? spec.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
27
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
28
|
+
const signal = opts.signal ? AbortSignal.any([opts.signal, timeoutSignal]) : timeoutSignal;
|
|
29
|
+
try {
|
|
30
|
+
return await spec.getBytes(length, { ...opts, signal, timeoutMs });
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error instanceof EntropyError) {
|
|
34
|
+
// A nested provider saw our composite signal abort and reported
|
|
35
|
+
// 'aborted' — if that abort was our own timeout, reclassify.
|
|
36
|
+
if (error.code === 'aborted' && !opts.signal?.aborted && timeoutSignal.aborted) {
|
|
37
|
+
throw new EntropyError('timeout', `request exceeded ${timeoutMs}ms`, {
|
|
38
|
+
provider: name,
|
|
39
|
+
cause: error,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
if (opts.signal?.aborted) {
|
|
45
|
+
throw new EntropyError('aborted', 'request aborted', { provider: name, cause: error });
|
|
46
|
+
}
|
|
47
|
+
if (timeoutSignal.aborted) {
|
|
48
|
+
throw new EntropyError('timeout', `request exceeded ${timeoutMs}ms`, {
|
|
49
|
+
provider: name,
|
|
50
|
+
cause: error,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
stream(opts = {}) {
|
|
57
|
+
return spec.stream ? spec.stream(opts) : pollStream(provider, opts, defaultChunkBytes);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
return Object.freeze(provider);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/internal/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
2
|
+
/**
|
|
3
|
+
* Client-side throttle: guarantees at least `intervalMs` between the moments
|
|
4
|
+
* consecutive `wait()` calls resolve. Callers reserve their slot at call time,
|
|
5
|
+
* so concurrent waiters are served FIFO, one interval apart. `defer(ms)`
|
|
6
|
+
* pushes the next free slot into the future — used for server-directed
|
|
7
|
+
* backoff like RANDOM.ORG's `advisoryDelay`.
|
|
8
|
+
*/
|
|
9
|
+
export declare class MinIntervalGate {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(intervalMs: number);
|
|
12
|
+
wait(signal?: AbortSignal): Promise<void>;
|
|
13
|
+
defer(ms: number): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/internal/rate-limit.ts"],"names":[],"mappings":"AAMA,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAYrE;AAED;;;;;;GAMG;AACH,qBAAa,eAAe;;gBAId,UAAU,EAAE,MAAM;IAIxB,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/C,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;CAGxB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function abortError(signal) {
|
|
2
|
+
const reason = signal.reason;
|
|
3
|
+
if (reason instanceof Error)
|
|
4
|
+
return reason;
|
|
5
|
+
return new DOMException('The operation was aborted.', 'AbortError');
|
|
6
|
+
}
|
|
7
|
+
export function sleep(ms, signal) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const timer = setTimeout(() => {
|
|
10
|
+
signal?.removeEventListener('abort', onAbort);
|
|
11
|
+
resolve();
|
|
12
|
+
}, ms);
|
|
13
|
+
const onAbort = () => {
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
reject(signal ? abortError(signal) : new DOMException('aborted', 'AbortError'));
|
|
16
|
+
};
|
|
17
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Client-side throttle: guarantees at least `intervalMs` between the moments
|
|
22
|
+
* consecutive `wait()` calls resolve. Callers reserve their slot at call time,
|
|
23
|
+
* so concurrent waiters are served FIFO, one interval apart. `defer(ms)`
|
|
24
|
+
* pushes the next free slot into the future — used for server-directed
|
|
25
|
+
* backoff like RANDOM.ORG's `advisoryDelay`.
|
|
26
|
+
*/
|
|
27
|
+
export class MinIntervalGate {
|
|
28
|
+
#nextAt = 0;
|
|
29
|
+
#intervalMs;
|
|
30
|
+
constructor(intervalMs) {
|
|
31
|
+
this.#intervalMs = intervalMs;
|
|
32
|
+
}
|
|
33
|
+
async wait(signal) {
|
|
34
|
+
if (signal?.aborted)
|
|
35
|
+
throw abortError(signal);
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const startAt = Math.max(now, this.#nextAt);
|
|
38
|
+
this.#nextAt = startAt + this.#intervalMs;
|
|
39
|
+
const delay = startAt - now;
|
|
40
|
+
if (delay > 0)
|
|
41
|
+
await sleep(delay, signal);
|
|
42
|
+
}
|
|
43
|
+
defer(ms) {
|
|
44
|
+
this.#nextAt = Math.max(this.#nextAt, Date.now() + ms);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/internal/rate-limit.ts"],"names":[],"mappings":"AAAA,SAAS,UAAU,CAAC,MAAmB;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;IAC5B,IAAI,MAAM,YAAY,KAAK;QAAE,OAAO,MAAM,CAAA;IAC1C,OAAO,IAAI,YAAY,CAAC,4BAA4B,EAAE,YAAY,CAAC,CAAA;AACrE,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB;IACpD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC7C,OAAO,EAAE,CAAA;QACX,CAAC,EAAE,EAAE,CAAC,CAAA;QACN,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAA;QACjF,CAAC,CAAA;QACD,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5D,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,eAAe;IAC1B,OAAO,GAAG,CAAC,CAAA;IACF,WAAW,CAAQ;IAE5B,YAAY,UAAkB;QAC5B,IAAI,CAAC,WAAW,GAAG,UAAU,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAoB;QAC7B,IAAI,MAAM,EAAE,OAAO;YAAE,MAAM,UAAU,CAAC,MAAM,CAAC,CAAA;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QAC3C,IAAI,CAAC,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC,WAAW,CAAA;QACzC,MAAM,KAAK,GAAG,OAAO,GAAG,GAAG,CAAA;QAC3B,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC3C,CAAC;IAED,KAAK,CAAC,EAAU;QACd,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;IACxD,CAAC;CACF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EntropyProvider, EntropySourceInfo } from '../types.js';
|
|
2
|
+
import { type ConditioningOptions } from './condition.js';
|
|
3
|
+
export interface SampledSpec extends EntropySourceInfo {
|
|
4
|
+
/**
|
|
5
|
+
* Open a raw-sample session. The returned iterable must stop sampling and
|
|
6
|
+
* release the underlying hardware when the iterator is closed (return())
|
|
7
|
+
* or the signal aborts — put cleanup in the generator's finally block.
|
|
8
|
+
*/
|
|
9
|
+
open(signal?: AbortSignal): AsyncIterable<Uint8Array>;
|
|
10
|
+
/** Conservative assessed min-entropy in bits per raw sample byte. */
|
|
11
|
+
defaultMinEntropyPerSample: number;
|
|
12
|
+
defaultSafetyFactor: number;
|
|
13
|
+
/** Stricter H for health-testing than for crediting (see ConditionerConfig). */
|
|
14
|
+
defaultHealthMinEntropyPerSample?: number;
|
|
15
|
+
defaultTimeoutMs?: number;
|
|
16
|
+
defaultChunkBytes?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The convergence point for all local physical-noise providers: wires a raw
|
|
20
|
+
* sample session through health tests + conditioning (or raw passthrough)
|
|
21
|
+
* into the standard EntropyProvider contract. getBytes opens one session per
|
|
22
|
+
* call; stream() holds one lazy session for the iterator's lifetime.
|
|
23
|
+
*/
|
|
24
|
+
export declare function sampledProvider(spec: SampledSpec, opts?: ConditioningOptions): EntropyProvider;
|
|
25
|
+
//# sourceMappingURL=sampled.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampled.d.ts","sourceRoot":"","sources":["../../src/internal/sampled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAwB,MAAM,aAAa,CAAA;AAE3F,OAAO,EAEL,KAAK,mBAAmB,EAGzB,MAAM,gBAAgB,CAAA;AAIvB,MAAM,WAAW,WAAY,SAAQ,iBAAiB;IACpD;;;;OAIG;IACH,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC,CAAA;IACrD,qEAAqE;IACrE,0BAA0B,EAAE,MAAM,CAAA;IAClC,mBAAmB,EAAE,MAAM,CAAA;IAC3B,gFAAgF;IAChF,gCAAgC,CAAC,EAAE,MAAM,CAAA;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,WAAW,EACjB,IAAI,GAAE,mBAAwB,GAC7B,eAAe,CAuCjB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { iterateBytes } from './byte-source.js';
|
|
2
|
+
import { collectBytes, condition, } from './condition.js';
|
|
3
|
+
import { defineProvider } from './provider.js';
|
|
4
|
+
import { rechunk } from './stream.js';
|
|
5
|
+
const CONDITIONED_BLOCK_BYTES = 32;
|
|
6
|
+
/**
|
|
7
|
+
* The convergence point for all local physical-noise providers: wires a raw
|
|
8
|
+
* sample session through health tests + conditioning (or raw passthrough)
|
|
9
|
+
* into the standard EntropyProvider contract. getBytes opens one session per
|
|
10
|
+
* call; stream() holds one lazy session for the iterator's lifetime.
|
|
11
|
+
*/
|
|
12
|
+
export function sampledProvider(spec, opts = {}) {
|
|
13
|
+
const mode = opts.conditioning ?? 'conditioned';
|
|
14
|
+
// Raw mode is visible in the name — and therefore in attribution — so a
|
|
15
|
+
// result can always be traced to whitened vs unprocessed physical bits.
|
|
16
|
+
const name = mode === 'raw' ? `${spec.name}(raw)` : spec.name;
|
|
17
|
+
const info = Object.freeze({ name, kind: spec.kind, privacy: spec.privacy });
|
|
18
|
+
const config = {
|
|
19
|
+
provider: name,
|
|
20
|
+
minEntropyPerSample: opts.minEntropyPerSample ?? spec.defaultMinEntropyPerSample,
|
|
21
|
+
safetyFactor: opts.safetyFactor ?? spec.defaultSafetyFactor,
|
|
22
|
+
healthMinEntropyPerSample: spec.defaultHealthMinEntropyPerSample,
|
|
23
|
+
mode,
|
|
24
|
+
};
|
|
25
|
+
return defineProvider({
|
|
26
|
+
...info,
|
|
27
|
+
defaultTimeoutMs: spec.defaultTimeoutMs,
|
|
28
|
+
defaultChunkBytes: spec.defaultChunkBytes ?? CONDITIONED_BLOCK_BYTES,
|
|
29
|
+
async getBytes(length, reqOpts) {
|
|
30
|
+
// iterateBytes races every pull against the composite signal so even a
|
|
31
|
+
// non-cooperative source honors abort/timeout; collectBytes returns
|
|
32
|
+
// early once satisfied and its close propagates through condition()
|
|
33
|
+
// into the source's finally block.
|
|
34
|
+
const raw = iterateBytes(spec.open(reqOpts?.signal), reqOpts?.signal);
|
|
35
|
+
const stream = condition(raw, config);
|
|
36
|
+
return { bytes: await collectBytes(stream, length, name), sources: [info] };
|
|
37
|
+
},
|
|
38
|
+
stream(streamOpts = {}) {
|
|
39
|
+
const chunkBytes = streamOpts.chunkBytes ?? spec.defaultChunkBytes ?? CONDITIONED_BLOCK_BYTES;
|
|
40
|
+
async function* session() {
|
|
41
|
+
yield* condition(iterateBytes(spec.open(streamOpts.signal), streamOpts.signal), config);
|
|
42
|
+
}
|
|
43
|
+
const natural = mode === 'conditioned' ? CONDITIONED_BLOCK_BYTES : -1;
|
|
44
|
+
const inner = session();
|
|
45
|
+
return chunkBytes === natural ? inner : rechunk(inner, chunkBytes);
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=sampled.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampled.js","sourceRoot":"","sources":["../../src/internal/sampled.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAGL,YAAY,EACZ,SAAS,GACV,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAkBrC,MAAM,uBAAuB,GAAG,EAAE,CAAA;AAElC;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAiB,EACjB,OAA4B,EAAE;IAE9B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,IAAI,aAAa,CAAA;IAC/C,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,IAAI,GAAG,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;IAC7D,MAAM,IAAI,GAAsB,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;IAC/F,MAAM,MAAM,GAAsB;QAChC,QAAQ,EAAE,IAAI;QACd,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,0BAA0B;QAChF,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,mBAAmB;QAC3D,yBAAyB,EAAE,IAAI,CAAC,gCAAgC;QAChE,IAAI;KACL,CAAA;IAED,OAAO,cAAc,CAAC;QACpB,GAAG,IAAI;QACP,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,uBAAuB;QAEpE,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO;YAC5B,uEAAuE;YACvE,oEAAoE;YACpE,oEAAoE;YACpE,mCAAmC;YACnC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;YACrE,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;YACrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,CAAA;QAC7E,CAAC;QAED,MAAM,CAAC,aAAmC,EAAE;YAC1C,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,IAAI,CAAC,iBAAiB,IAAI,uBAAuB,CAAA;YAC7F,KAAK,SAAS,CAAC,CAAC,OAAO;gBACrB,KAAK,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;YACzF,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACrE,MAAM,KAAK,GAAG,OAAO,EAAE,CAAA;YACvB,OAAO,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;QACpE,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { EntropyRequestOptions, EntropyResult, EntropyStreamOptions } from '../types.js';
|
|
2
|
+
export declare const DEFAULT_CHUNK_BYTES = 32;
|
|
3
|
+
interface ByteSource {
|
|
4
|
+
getBytes(length: number, opts?: EntropyRequestOptions): Promise<EntropyResult>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Default stream implementation: one `getBytes` call per pull. Lazy (no I/O
|
|
8
|
+
* before the first `next()`), backpressured by construction, and it inherits
|
|
9
|
+
* whatever rate limiting the underlying `getBytes` applies.
|
|
10
|
+
*/
|
|
11
|
+
export declare function pollStream(source: ByteSource, opts?: EntropyStreamOptions, defaultChunkBytes?: number): AsyncIterable<Uint8Array>;
|
|
12
|
+
/**
|
|
13
|
+
* Re-slice an inner stream into fixed-size chunks. When the source ends, any
|
|
14
|
+
* buffered tail is flushed as a final (short) chunk rather than discarded.
|
|
15
|
+
*/
|
|
16
|
+
export declare function rechunk(source: AsyncIterable<Uint8Array>, size: number): AsyncGenerator<Uint8Array>;
|
|
17
|
+
export interface BeaconPoll {
|
|
18
|
+
/** Monotone identifier of the round/pulse, used for deduplication. */
|
|
19
|
+
id: number;
|
|
20
|
+
bytes: Uint8Array;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Round-aware beacon stream: polls `fetchLatest` on a fixed cadence and
|
|
24
|
+
* yields the payload only when the round/pulse id advances. Public beacons
|
|
25
|
+
* emit a small fixed payload per round, so `chunkBytes` is served via rechunk.
|
|
26
|
+
*/
|
|
27
|
+
export declare function beaconStream(fetchLatest: (signal?: AbortSignal) => Promise<BeaconPoll>, pollIntervalMs: number, naturalChunkBytes: number, provider: string, opts?: EntropyStreamOptions): AsyncIterable<Uint8Array>;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/internal/stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAI7F,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,UAAU,UAAU;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;CAC/E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE,oBAAyB,EAC/B,iBAAiB,GAAE,MAA4B,GAC9C,aAAa,CAAC,UAAU,CAAC,CAW3B;AAED;;;GAGG;AACH,wBAAuB,OAAO,CAC5B,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,EACjC,IAAI,EAAE,MAAM,GACX,cAAc,CAAC,UAAU,CAAC,CAiB5B;AAED,MAAM,WAAW,UAAU;IACzB,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,UAAU,CAAA;CAClB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC,EAC1D,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,MAAM,EACzB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,oBAAyB,GAC9B,aAAa,CAAC,UAAU,CAAC,CA0B3B"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { concatBytes } from './bytes.js';
|
|
3
|
+
import { sleep } from './rate-limit.js';
|
|
4
|
+
export const DEFAULT_CHUNK_BYTES = 32;
|
|
5
|
+
/**
|
|
6
|
+
* Default stream implementation: one `getBytes` call per pull. Lazy (no I/O
|
|
7
|
+
* before the first `next()`), backpressured by construction, and it inherits
|
|
8
|
+
* whatever rate limiting the underlying `getBytes` applies.
|
|
9
|
+
*/
|
|
10
|
+
export function pollStream(source, opts = {}, defaultChunkBytes = DEFAULT_CHUNK_BYTES) {
|
|
11
|
+
const chunkBytes = opts.chunkBytes ?? defaultChunkBytes;
|
|
12
|
+
const requestOpts = { signal: opts.signal, timeoutMs: opts.timeoutMs };
|
|
13
|
+
return {
|
|
14
|
+
async *[Symbol.asyncIterator]() {
|
|
15
|
+
while (true) {
|
|
16
|
+
const { bytes } = await source.getBytes(chunkBytes, requestOpts);
|
|
17
|
+
yield bytes;
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Re-slice an inner stream into fixed-size chunks. When the source ends, any
|
|
24
|
+
* buffered tail is flushed as a final (short) chunk rather than discarded.
|
|
25
|
+
*/
|
|
26
|
+
export async function* rechunk(source, size) {
|
|
27
|
+
let buffer = [];
|
|
28
|
+
let buffered = 0;
|
|
29
|
+
for await (const chunk of source) {
|
|
30
|
+
buffer.push(chunk);
|
|
31
|
+
buffered += chunk.length;
|
|
32
|
+
if (buffered >= size) {
|
|
33
|
+
let all = concatBytes(buffer);
|
|
34
|
+
while (all.length >= size) {
|
|
35
|
+
yield all.slice(0, size);
|
|
36
|
+
all = all.slice(size);
|
|
37
|
+
}
|
|
38
|
+
buffer = all.length > 0 ? [all] : [];
|
|
39
|
+
buffered = all.length;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (buffered > 0)
|
|
43
|
+
yield concatBytes(buffer);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Round-aware beacon stream: polls `fetchLatest` on a fixed cadence and
|
|
47
|
+
* yields the payload only when the round/pulse id advances. Public beacons
|
|
48
|
+
* emit a small fixed payload per round, so `chunkBytes` is served via rechunk.
|
|
49
|
+
*/
|
|
50
|
+
export function beaconStream(fetchLatest, pollIntervalMs, naturalChunkBytes, provider, opts = {}) {
|
|
51
|
+
async function* rounds() {
|
|
52
|
+
let lastId = Number.NEGATIVE_INFINITY;
|
|
53
|
+
try {
|
|
54
|
+
while (true) {
|
|
55
|
+
const timeoutMs = opts.timeoutMs ?? 10_000;
|
|
56
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
57
|
+
const signal = opts.signal ? AbortSignal.any([opts.signal, timeoutSignal]) : timeoutSignal;
|
|
58
|
+
const { id, bytes } = await fetchLatest(signal);
|
|
59
|
+
if (id > lastId) {
|
|
60
|
+
lastId = id;
|
|
61
|
+
yield bytes;
|
|
62
|
+
}
|
|
63
|
+
await sleep(pollIntervalMs, opts.signal);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (!(error instanceof EntropyError) && opts.signal?.aborted) {
|
|
68
|
+
throw new EntropyError('aborted', 'stream aborted', { provider, cause: error });
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const inner = rounds();
|
|
74
|
+
return opts.chunkBytes && opts.chunkBytes !== naturalChunkBytes
|
|
75
|
+
? rechunk(inner, opts.chunkBytes)
|
|
76
|
+
: inner;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/internal/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Validate a provider's JSON number array as exactly `expected` bytes. */
|
|
2
|
+
export declare function byteArrayFrom(values: unknown, expected: number, provider: string): Uint8Array;
|
|
3
|
+
/** Validate a provider's hex string field as exactly `expected` bytes. */
|
|
4
|
+
export declare function bytesFromHexField(hex: unknown, expected: number, provider: string): Uint8Array;
|
|
5
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/internal/validate.ts"],"names":[],"mappings":"AAGA,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAmB7F;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAgB9F"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { hexToBytes } from './bytes.js';
|
|
3
|
+
/** Validate a provider's JSON number array as exactly `expected` bytes. */
|
|
4
|
+
export function byteArrayFrom(values, expected, provider) {
|
|
5
|
+
if (!Array.isArray(values) || values.length !== expected) {
|
|
6
|
+
throw new EntropyError('bad_response', `expected an array of ${expected} byte values, got ${Array.isArray(values) ? values.length : typeof values}`, { provider });
|
|
7
|
+
}
|
|
8
|
+
const out = new Uint8Array(expected);
|
|
9
|
+
for (let i = 0; i < expected; i++) {
|
|
10
|
+
const value = values[i];
|
|
11
|
+
if (!Number.isInteger(value) || value < 0 || value > 255) {
|
|
12
|
+
throw new EntropyError('bad_response', `value at index ${i} is not a byte: ${value}`, {
|
|
13
|
+
provider,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
out[i] = value;
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
/** Validate a provider's hex string field as exactly `expected` bytes. */
|
|
21
|
+
export function bytesFromHexField(hex, expected, provider) {
|
|
22
|
+
if (typeof hex !== 'string') {
|
|
23
|
+
throw new EntropyError('bad_response', `expected a hex string, got ${typeof hex}`, { provider });
|
|
24
|
+
}
|
|
25
|
+
let bytes;
|
|
26
|
+
try {
|
|
27
|
+
bytes = hexToBytes(hex);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
throw new EntropyError('bad_response', `invalid hex in response`, { provider, cause: error });
|
|
31
|
+
}
|
|
32
|
+
if (bytes.length !== expected) {
|
|
33
|
+
throw new EntropyError('bad_response', `expected ${expected} bytes, got ${bytes.length}`, {
|
|
34
|
+
provider,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return bytes;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/internal/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,2EAA2E;AAC3E,MAAM,UAAU,aAAa,CAAC,MAAe,EAAE,QAAgB,EAAE,QAAgB;IAC/E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACzD,MAAM,IAAI,YAAY,CACpB,cAAc,EACd,wBAAwB,QAAQ,qBAAqB,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,EAC5G,EAAE,QAAQ,EAAE,CACb,CAAA;IACH,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAA;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;QACvB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAK,KAAgB,GAAG,CAAC,IAAK,KAAgB,GAAG,GAAG,EAAE,CAAC;YACjF,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,kBAAkB,CAAC,mBAAmB,KAAK,EAAE,EAAE;gBACpF,QAAQ;aACT,CAAC,CAAA;QACJ,CAAC;QACD,GAAG,CAAC,CAAC,CAAC,GAAG,KAAe,CAAA;IAC1B,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,iBAAiB,CAAC,GAAY,EAAE,QAAgB,EAAE,QAAgB;IAChF,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,8BAA8B,OAAO,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAA;IAClG,CAAC;IACD,IAAI,KAAiB,CAAA;IACrB,IAAI,CAAC;QACH,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,yBAAyB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;IAC/F,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,YAAY,QAAQ,eAAe,KAAK,CAAC,MAAM,EAAE,EAAE;YACxF,QAAQ;SACT,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
|