@oyaprotocol/utils 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 John Shutt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @oyaprotocol/utils
2
+
3
+ Small shared utilities for hardened Oya kernel packages.
4
+
5
+ ## Public Entrypoint
6
+
7
+ - `@oyaprotocol/utils`
8
+
9
+ ## Validation Helpers
10
+
11
+ - `assertAsciiBytes(bytes, message)`
12
+ - `assertBytes32HexString(value, label)`
13
+ - `assertCanonicalCid(value, label)`: requires CIDv1, lowercase unpadded Base32, and a SHA-256 multihash with a 32-byte digest. Returns the original string; rejects whitespace, paths, URIs, CIDv0, alternate bases, nonminimal integer encodings, and invalid Base32 padding bits. Checks codec encoding without maintaining a codec registry allowlist. This validates identifier structure, not content or availability.
14
+ - `assertNonEmptyString(value, label)`
15
+ - `assertHexData(value, label)`
16
+ - `assertHexString(value, label)`
17
+ - `assertPositiveInteger(value, label)`
18
+ - `assertNonNegativeInteger(value, label)`
19
+ - `assertUint256(value, name)`: requires a bigint from `0n` through `(1n << 256n) - 1n`, returning the original value.
20
+ - `assertHeadersObject(headers, label, options)`
21
+ - `isPlainObject(value)`
22
+ - `parseBytes(value, name, size?)`: validates `0x`-prefixed, byte-aligned hex, optionally requiring an exact byte count. Returns the original string without trimming; accepts `0x` when no size is required.
23
+
24
+ ## HTTP Utilities
25
+
26
+ - `CreateHttpConfigOptions`
27
+ - `HttpConfig`
28
+ - `createHttpConfig(options, normalizeUrl?)`
29
+ - `HttpFetchLike<TOptions, TResponse>`
30
+ - `HttpPostFetchLike<TBody, TResponse>`
31
+ - `HttpPostFetchOptions<TBody>`
32
+ - `HttpStatusError`
33
+ - `HttpStatusErrorOptions`
34
+ - `HttpTextResponse`
35
+ - `RETRYABLE_HTTP_NETWORK_ERROR_CODES` (runtime-immutable `ReadonlySet<string>` with the full ES2025 Set algebra API)
36
+ - `hasRetryableNetworkErrorCode(error)`
37
+ - `readErrorStringChain(error, key)`
38
+
39
+ ## Async Utilities
40
+
41
+ - `assertTimerMs(value, name)`: validates a positive integer duration up to 2,147,483,647 ms, avoiding timer overflow.
42
+ - `AbortSignalHandle`
43
+ - `RunWithRetryAttemptContext`
44
+ - `RunWithRetryOptions`
45
+ - `createTimeoutSignal(timeoutMs)`
46
+ - `combineAbortSignals(signals)`
47
+ - `invokeWithAbort(createPromise, signal)`
48
+ - `runWithRetry(options)`
49
+ - `throwIfSignalAborted(signal, message, cause)`
50
+ - `waitForRetryDelay(options)`
@@ -0,0 +1,31 @@
1
+ interface AbortSignalHandle {
2
+ signal: AbortSignal | undefined;
3
+ cleanup: (() => void) | null;
4
+ }
5
+ interface RunWithRetryAttemptContext {
6
+ attempt: number;
7
+ signal: AbortSignal | undefined;
8
+ }
9
+ interface RunWithRetryOptions<TResult> {
10
+ maxRetries: number;
11
+ retryDelayMs: number;
12
+ timeoutMs: number;
13
+ signal?: AbortSignal | undefined;
14
+ abortErrorMessage: string;
15
+ shouldRetry(error: unknown): boolean;
16
+ normalizeError(error: unknown): Error;
17
+ run(context: RunWithRetryAttemptContext): Promise<TResult>;
18
+ }
19
+ declare function assertTimerMs(value: unknown, name: string): number;
20
+ declare function createTimeoutSignal(timeoutMs: number): AbortSignalHandle;
21
+ declare function combineAbortSignals(signals: Array<AbortSignal | undefined>): AbortSignalHandle;
22
+ declare function invokeWithAbort<T>(createPromise: () => Promise<T>, signal: AbortSignal | undefined): Promise<T>;
23
+ declare function throwIfSignalAborted(signal: AbortSignal | undefined, message: string, cause: unknown): void;
24
+ declare function waitForRetryDelay({ retryDelayMs, signal, abortErrorMessage, }: {
25
+ retryDelayMs: number;
26
+ signal: AbortSignal | undefined;
27
+ abortErrorMessage: string;
28
+ }): Promise<void>;
29
+ declare function runWithRetry<TResult>({ maxRetries, retryDelayMs, timeoutMs, signal, abortErrorMessage, shouldRetry, normalizeError, run, }: RunWithRetryOptions<TResult>): Promise<TResult>;
30
+ export { assertTimerMs, combineAbortSignals, createTimeoutSignal, invokeWithAbort, runWithRetry, throwIfSignalAborted, waitForRetryDelay, };
31
+ export type { AbortSignalHandle, RunWithRetryAttemptContext, RunWithRetryOptions, };
@@ -0,0 +1,186 @@
1
+ import { assertNonEmptyString, assertNonNegativeInteger, assertPositiveInteger, } from './validation-utils.js';
2
+ function assertTimerMs(value, name) {
3
+ const duration = assertPositiveInteger(value, name);
4
+ if (duration > 2_147_483_647) {
5
+ throw new Error(`${name} must not exceed 2147483647 ms.`);
6
+ }
7
+ return duration;
8
+ }
9
+ function createTimeoutSignal(timeoutMs) {
10
+ const controller = new AbortController();
11
+ const timer = setTimeout(() => controller.abort(new Error('Request timed out.')), timeoutMs);
12
+ controller.signal.addEventListener('abort', () => clearTimeout(timer), { once: true });
13
+ return {
14
+ signal: controller.signal,
15
+ cleanup: () => clearTimeout(timer),
16
+ };
17
+ }
18
+ function combineAbortSignals(signals) {
19
+ const presentSignals = signals.filter((signal) => signal !== undefined);
20
+ if (presentSignals.length === 0) {
21
+ return {
22
+ signal: undefined,
23
+ cleanup: null,
24
+ };
25
+ }
26
+ if (presentSignals.length === 1) {
27
+ return {
28
+ signal: presentSignals[0],
29
+ cleanup: null,
30
+ };
31
+ }
32
+ if (typeof AbortSignal.any === 'function') {
33
+ return {
34
+ signal: AbortSignal.any(presentSignals),
35
+ cleanup: null,
36
+ };
37
+ }
38
+ const controller = new AbortController();
39
+ const abortedSignal = presentSignals.find((signal) => signal.aborted);
40
+ if (abortedSignal) {
41
+ controller.abort(abortedSignal.reason);
42
+ return {
43
+ signal: controller.signal,
44
+ cleanup: null,
45
+ };
46
+ }
47
+ const listeners = [];
48
+ for (const signal of presentSignals) {
49
+ const listener = () => {
50
+ controller.abort(signal.reason);
51
+ };
52
+ signal.addEventListener('abort', listener, { once: true });
53
+ listeners.push({ signal, listener });
54
+ }
55
+ return {
56
+ signal: controller.signal,
57
+ cleanup: () => {
58
+ for (const { signal, listener } of listeners) {
59
+ signal.removeEventListener('abort', listener);
60
+ }
61
+ },
62
+ };
63
+ }
64
+ async function invokeWithAbort(createPromise, signal) {
65
+ if (!signal) {
66
+ return await createPromise();
67
+ }
68
+ if (signal.aborted) {
69
+ throw signal.reason ?? new Error('Operation aborted.');
70
+ }
71
+ return await new Promise((resolve, reject) => {
72
+ let settled = false;
73
+ const finishResolve = (value) => {
74
+ if (settled) {
75
+ return;
76
+ }
77
+ settled = true;
78
+ signal.removeEventListener('abort', onAbort);
79
+ resolve(value);
80
+ };
81
+ const finishReject = (error) => {
82
+ if (settled) {
83
+ return;
84
+ }
85
+ settled = true;
86
+ signal.removeEventListener('abort', onAbort);
87
+ reject(error);
88
+ };
89
+ const onAbort = () => {
90
+ finishReject(signal.reason ?? new Error('Operation aborted.'));
91
+ };
92
+ signal.addEventListener('abort', onAbort, { once: true });
93
+ let promise;
94
+ try {
95
+ promise = createPromise();
96
+ }
97
+ catch (error) {
98
+ finishReject(error);
99
+ return;
100
+ }
101
+ promise.then(finishResolve, finishReject);
102
+ });
103
+ }
104
+ function throwIfSignalAborted(signal, message, cause) {
105
+ if (signal?.aborted) {
106
+ throw new Error(message, { cause });
107
+ }
108
+ }
109
+ async function waitForRetryDelay({ retryDelayMs, signal, abortErrorMessage, }) {
110
+ if (retryDelayMs <= 0) {
111
+ return;
112
+ }
113
+ throwIfSignalAborted(signal, abortErrorMessage, signal?.reason);
114
+ await new Promise((resolve) => {
115
+ if (!signal) {
116
+ setTimeout(resolve, retryDelayMs);
117
+ return;
118
+ }
119
+ let settled = false;
120
+ let timer = null;
121
+ const finish = () => {
122
+ if (settled) {
123
+ return;
124
+ }
125
+ settled = true;
126
+ if (timer !== null) {
127
+ clearTimeout(timer);
128
+ }
129
+ signal.removeEventListener('abort', finish);
130
+ resolve();
131
+ };
132
+ signal.addEventListener('abort', finish, { once: true });
133
+ if (signal.aborted) {
134
+ finish();
135
+ return;
136
+ }
137
+ timer = setTimeout(finish, retryDelayMs);
138
+ });
139
+ throwIfSignalAborted(signal, abortErrorMessage, signal?.reason);
140
+ }
141
+ async function runWithRetry({ maxRetries, retryDelayMs, timeoutMs, signal, abortErrorMessage, shouldRetry, normalizeError, run, }) {
142
+ const retryLimit = assertNonNegativeInteger(maxRetries, 'maxRetries');
143
+ const retryDelay = assertNonNegativeInteger(retryDelayMs, 'retryDelayMs');
144
+ const requestTimeoutMs = assertPositiveInteger(timeoutMs, 'timeoutMs');
145
+ const callerAbortErrorMessage = assertNonEmptyString(abortErrorMessage, 'abortErrorMessage');
146
+ if (typeof shouldRetry !== 'function') {
147
+ throw new Error('shouldRetry must be provided as a function.');
148
+ }
149
+ if (typeof normalizeError !== 'function') {
150
+ throw new Error('normalizeError must be provided as a function.');
151
+ }
152
+ if (typeof run !== 'function') {
153
+ throw new Error('run must be provided as a function.');
154
+ }
155
+ let lastError = null;
156
+ for (let attempt = 1; attempt <= retryLimit + 1; attempt += 1) {
157
+ const timeoutSignal = createTimeoutSignal(requestTimeoutMs);
158
+ const requestSignal = combineAbortSignals([signal, timeoutSignal.signal]);
159
+ try {
160
+ return await invokeWithAbort(() => run({
161
+ attempt,
162
+ signal: requestSignal.signal,
163
+ }), requestSignal.signal);
164
+ }
165
+ catch (error) {
166
+ lastError = error;
167
+ throwIfSignalAborted(signal, callerAbortErrorMessage, error);
168
+ if (attempt <= retryLimit && shouldRetry(error)) {
169
+ await waitForRetryDelay({
170
+ retryDelayMs: retryDelay,
171
+ signal,
172
+ abortErrorMessage: callerAbortErrorMessage,
173
+ });
174
+ continue;
175
+ }
176
+ break;
177
+ }
178
+ finally {
179
+ requestSignal.cleanup?.();
180
+ timeoutSignal.cleanup?.();
181
+ }
182
+ }
183
+ throw normalizeError(lastError);
184
+ }
185
+ export { assertTimerMs, combineAbortSignals, createTimeoutSignal, invokeWithAbort, runWithRetry, throwIfSignalAborted, waitForRetryDelay, };
186
+ //# sourceMappingURL=async-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-utils.js","sourceRoot":"","sources":["../src/async-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** Validate the kernel's CIDv1 / lowercase unpadded Base32 / SHA-256 format. */
2
+ declare function assertCanonicalCid(value: unknown, label: string): string;
3
+ export { assertCanonicalCid };
@@ -0,0 +1,43 @@
1
+ const BASE32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';
2
+ /** Validate the kernel's CIDv1 / lowercase unpadded Base32 / SHA-256 format. */
3
+ function assertCanonicalCid(value, label) {
4
+ const invalid = () => new TypeError(`${label} must be a canonical CIDv1 in lowercase unpadded Base32 with a 32-byte SHA-256 digest.`);
5
+ // One-byte version/hash/length, a 1–9-byte codec varint, and a 32-byte digest.
6
+ // Bound the input before decoding; no trimming, URLs, paths, or alternate bases.
7
+ if (typeof value !== 'string' || value.length < 59 || value.length > 72 || !/^b[a-z2-7]+$/.test(value)) {
8
+ throw invalid();
9
+ }
10
+ const bytes = [];
11
+ let accumulator = 0;
12
+ let bitCount = 0;
13
+ for (const character of value.slice(1)) {
14
+ accumulator = (accumulator << 5) | BASE32_ALPHABET.indexOf(character);
15
+ bitCount += 5;
16
+ if (bitCount >= 8) {
17
+ bitCount -= 8;
18
+ bytes.push(accumulator >> bitCount);
19
+ accumulator &= (1 << bitCount) - 1;
20
+ }
21
+ }
22
+ // Reject redundant symbols and nonzero unused bits, not just '=' padding.
23
+ if (bitCount >= 5 || accumulator !== 0 || bytes[0] !== 1) {
24
+ throw invalid();
25
+ }
26
+ // A codec is an unsigned, minimally encoded varint of at most 63 bits.
27
+ // Do not freeze the evolving codec registry into this format validator.
28
+ let offset = 1;
29
+ while (offset <= 9 && (bytes[offset] & 0x80) !== 0) {
30
+ offset += 1;
31
+ }
32
+ if (offset > 9 || (offset > 1 && bytes[offset] === 0)) {
33
+ throw invalid();
34
+ }
35
+ offset += 1;
36
+ // Requiring these exact bytes also rejects nonminimal hash/length varints.
37
+ if (bytes[offset] !== 0x12 || bytes[offset + 1] !== 0x20 || bytes.length !== offset + 2 + 32) {
38
+ throw invalid();
39
+ }
40
+ return value;
41
+ }
42
+ export { assertCanonicalCid };
43
+ //# sourceMappingURL=cid-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cid-utils.js","sourceRoot":"","sources":["../src/cid-utils.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,kCAAkC,CAAC;AAE3D,gFAAgF;AAChF,SAAS,kBAAkB,CAAC,KAAc,EAAE,KAAa;IACrD,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,SAAS,CAC/B,GAAG,KAAK,wFAAwF,CACnG,CAAC;IACF,+EAA+E;IAC/E,iFAAiF;IACjF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrG,MAAM,OAAO,EAAE,CAAC;IACpB,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACrC,WAAW,GAAG,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACtE,QAAQ,IAAI,CAAC,CAAC;QACd,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAChB,QAAQ,IAAI,CAAC,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,QAAQ,CAAC,CAAC;YACpC,WAAW,IAAI,CAAC,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;IACL,CAAC;IACD,0EAA0E;IAC1E,IAAI,QAAQ,IAAI,CAAC,IAAI,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,OAAO,EAAE,CAAC;IACpB,CAAC;IAED,uEAAuE;IACvE,wEAAwE;IACxE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,CAAC,CAAC;IAChB,CAAC;IACD,IAAI,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QACpD,MAAM,OAAO,EAAE,CAAC;IACpB,CAAC;IACD,MAAM,IAAI,CAAC,CAAC;IACZ,2EAA2E;IAC3E,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC;QAC3F,MAAM,OAAO,EAAE,CAAC;IACpB,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,47 @@
1
+ interface CreateHttpConfigOptions {
2
+ url: string;
3
+ headers: Record<string, string>;
4
+ timeoutMs: number;
5
+ maxRetries: number;
6
+ retryDelayMs: number;
7
+ }
8
+ interface HttpConfig {
9
+ readonly url: string;
10
+ readonly headers: Readonly<Record<string, string>>;
11
+ readonly timeoutMs: number;
12
+ readonly maxRetries: number;
13
+ readonly retryDelayMs: number;
14
+ }
15
+ interface HttpPostFetchOptions<TBody> {
16
+ method: 'POST';
17
+ headers: Readonly<Record<string, string>>;
18
+ body: TBody;
19
+ signal?: AbortSignal | undefined;
20
+ }
21
+ interface HttpTextResponse {
22
+ ok: boolean;
23
+ status: number;
24
+ statusText: string;
25
+ text(): Promise<string>;
26
+ }
27
+ interface HttpStatusErrorOptions {
28
+ operation: string;
29
+ status: number;
30
+ statusText?: string;
31
+ responseText?: string;
32
+ }
33
+ type HttpFetchLike<TOptions, TResponse> = (url: string, options: TOptions) => Promise<TResponse>;
34
+ type HttpPostFetchLike<TBody, TResponse = HttpTextResponse> = HttpFetchLike<HttpPostFetchOptions<TBody>, TResponse>;
35
+ declare const RETRYABLE_HTTP_NETWORK_ERROR_CODES: ReadonlySet<string>;
36
+ declare class HttpStatusError extends Error {
37
+ readonly operation: string;
38
+ readonly status: number;
39
+ readonly statusText: string;
40
+ readonly responseText: string | undefined;
41
+ constructor({ operation, status, statusText, responseText }: HttpStatusErrorOptions);
42
+ }
43
+ declare function createHttpConfig({ url, headers, timeoutMs, maxRetries, retryDelayMs }: CreateHttpConfigOptions, normalizeConfigUrl?: (url: string) => string): HttpConfig;
44
+ declare function readErrorStringChain(error: unknown, key: string): string[];
45
+ declare function hasRetryableNetworkErrorCode(error: unknown): boolean;
46
+ export { HttpStatusError, RETRYABLE_HTTP_NETWORK_ERROR_CODES, createHttpConfig, hasRetryableNetworkErrorCode, readErrorStringChain, };
47
+ export type { CreateHttpConfigOptions, HttpConfig, HttpFetchLike, HttpPostFetchLike, HttpPostFetchOptions, HttpStatusErrorOptions, HttpTextResponse, };
@@ -0,0 +1,119 @@
1
+ import { assertHeadersObject, assertNonEmptyString, assertNonNegativeInteger, assertPositiveInteger, } from './validation-utils.js';
2
+ function createReadonlySet(values) {
3
+ const set = new Set(values);
4
+ const readonlySet = {
5
+ get size() {
6
+ return set.size;
7
+ },
8
+ has(value) {
9
+ return set.has(value);
10
+ },
11
+ entries() {
12
+ return set.entries();
13
+ },
14
+ keys() {
15
+ return set.keys();
16
+ },
17
+ values() {
18
+ return set.values();
19
+ },
20
+ union(other) {
21
+ return set.union(other);
22
+ },
23
+ intersection(other) {
24
+ return set.intersection(other);
25
+ },
26
+ difference(other) {
27
+ return set.difference(other);
28
+ },
29
+ symmetricDifference(other) {
30
+ return set.symmetricDifference(other);
31
+ },
32
+ isSubsetOf(other) {
33
+ return set.isSubsetOf(other);
34
+ },
35
+ isSupersetOf(other) {
36
+ return set.isSupersetOf(other);
37
+ },
38
+ isDisjointFrom(other) {
39
+ return set.isDisjointFrom(other);
40
+ },
41
+ forEach(callback, thisArg) {
42
+ for (const value of set) {
43
+ callback.call(thisArg, value, value, readonlySet);
44
+ }
45
+ },
46
+ [Symbol.iterator]() {
47
+ return set.values();
48
+ },
49
+ };
50
+ return Object.freeze(readonlySet);
51
+ }
52
+ const RETRYABLE_HTTP_NETWORK_ERROR_CODES = createReadonlySet([
53
+ 'ECONNREFUSED',
54
+ 'ECONNRESET',
55
+ 'EAI_AGAIN',
56
+ 'ENOTFOUND',
57
+ 'EPIPE',
58
+ 'ETIMEDOUT',
59
+ 'UND_ERR_BODY_TIMEOUT',
60
+ 'UND_ERR_CONNECT_TIMEOUT',
61
+ 'UND_ERR_HEADERS_TIMEOUT',
62
+ 'UND_ERR_SOCKET',
63
+ ]);
64
+ class HttpStatusError extends Error {
65
+ operation;
66
+ status;
67
+ statusText;
68
+ responseText;
69
+ constructor({ operation, status, statusText, responseText }) {
70
+ const normalizedOperation = assertNonEmptyString(operation, 'operation');
71
+ const normalizedStatus = assertNonNegativeInteger(status, 'status');
72
+ const normalizedStatusText = typeof statusText === 'string' && statusText.trim()
73
+ ? statusText.trim()
74
+ : 'Unknown Status';
75
+ super(`${normalizedOperation} failed with ${normalizedStatus} ${normalizedStatusText}.`);
76
+ this.name = 'HttpStatusError';
77
+ this.operation = normalizedOperation;
78
+ this.status = normalizedStatus;
79
+ this.statusText = normalizedStatusText;
80
+ this.responseText = responseText;
81
+ }
82
+ }
83
+ function normalizeUrl(url) {
84
+ return url.replace(/\/+$/, '');
85
+ }
86
+ function createHttpConfig({ url, headers, timeoutMs, maxRetries, retryDelayMs }, normalizeConfigUrl = normalizeUrl) {
87
+ const normalizedUrl = assertNonEmptyString(normalizeConfigUrl(assertNonEmptyString(url, 'config.url')), 'config.url');
88
+ return Object.freeze({
89
+ url: normalizedUrl,
90
+ headers: assertHeadersObject(headers, 'config.headers', {
91
+ disallowedNames: ['content-type'],
92
+ }),
93
+ timeoutMs: assertPositiveInteger(timeoutMs, 'config.timeoutMs'),
94
+ maxRetries: assertNonNegativeInteger(maxRetries, 'config.maxRetries'),
95
+ retryDelayMs: assertNonNegativeInteger(retryDelayMs, 'config.retryDelayMs'),
96
+ });
97
+ }
98
+ function readErrorStringChain(error, key) {
99
+ const values = [];
100
+ let current = error;
101
+ while (current && typeof current === 'object') {
102
+ const value = current[key];
103
+ if (typeof value === 'string' && value) {
104
+ values.push(value);
105
+ }
106
+ current = current.cause;
107
+ }
108
+ return values;
109
+ }
110
+ function hasRetryableNetworkErrorCode(error) {
111
+ for (const code of readErrorStringChain(error, 'code')) {
112
+ if (RETRYABLE_HTTP_NETWORK_ERROR_CODES.has(code.toUpperCase())) {
113
+ return true;
114
+ }
115
+ }
116
+ return false;
117
+ }
118
+ export { HttpStatusError, RETRYABLE_HTTP_NETWORK_ERROR_CODES, createHttpConfig, hasRetryableNetworkErrorCode, readErrorStringChain, };
119
+ //# sourceMappingURL=http-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-utils.js","sourceRoot":"","sources":["../src/http-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,GACxB,MAAM,uBAAuB,CAAC;AAiD/B,SAAS,iBAAiB,CAAI,MAAmB;IAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,WAAW,GAAmB;QAChC,IAAI,IAAI;YACJ,OAAO,GAAG,CAAC,IAAI,CAAC;QACpB,CAAC;QACD,GAAG,CAAC,KAAQ;YACR,OAAO,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,OAAO;YACH,OAAO,GAAG,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC;QACD,IAAI;YACA,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;QACD,MAAM;YACF,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC;QACD,KAAK,CAAI,KAAyB;YAC9B,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;QACD,YAAY,CAAI,KAAyB;YACrC,OAAO,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QACD,UAAU,CAAI,KAAyB;YACnC,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QACD,mBAAmB,CAAI,KAAyB;YAC5C,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC;QACD,UAAU,CAAC,KAA+B;YACtC,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QACD,YAAY,CAAC,KAA+B;YACxC,OAAO,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QACD,cAAc,CAAC,KAA+B;YAC1C,OAAO,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,CACH,QAAoE,EACpE,OAAiB;YAEjB,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;gBACtB,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;YACtD,CAAC;QACL,CAAC;QACD,CAAC,MAAM,CAAC,QAAQ,CAAC;YACb,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC;KACJ,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,kCAAkC,GAAwB,iBAAiB,CAAC;IAC9E,cAAc;IACd,YAAY;IACZ,WAAW;IACX,WAAW;IACX,OAAO;IACP,WAAW;IACX,sBAAsB;IACtB,yBAAyB;IACzB,yBAAyB;IACzB,gBAAgB;CACnB,CAAC,CAAC;AAEH,MAAM,eAAgB,SAAQ,KAAK;IACtB,SAAS,CAAS;IAClB,MAAM,CAAS;IACf,UAAU,CAAS;IACnB,YAAY,CAAqB;IAE1C,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAA0B;QAC/E,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACzE,MAAM,gBAAgB,GAAG,wBAAwB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACpE,MAAM,oBAAoB,GACtB,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,EAAE;YAC/C,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE;YACnB,CAAC,CAAC,gBAAgB,CAAC;QAC3B,KAAK,CAAC,GAAG,mBAAmB,gBAAgB,gBAAgB,IAAI,oBAAoB,GAAG,CAAC,CAAC;QACzF,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,SAAS,GAAG,mBAAmB,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,gBAAgB,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,oBAAoB,CAAC;QACvC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;CACJ;AAED,SAAS,YAAY,CAAC,GAAW;IAC7B,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,gBAAgB,CACrB,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAA2B,EAC9E,qBAA8C,YAAY;IAE1D,MAAM,aAAa,GAAG,oBAAoB,CACtC,kBAAkB,CAAC,oBAAoB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,EAC3D,YAAY,CACf,CAAC;IAEF,OAAO,MAAM,CAAC,MAAM,CAAC;QACjB,GAAG,EAAE,aAAa;QAClB,OAAO,EAAE,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE;YACpD,eAAe,EAAE,CAAC,cAAc,CAAC;SACpC,CAAC;QACF,SAAS,EAAE,qBAAqB,CAAC,SAAS,EAAE,kBAAkB,CAAC;QAC/D,UAAU,EAAE,wBAAwB,CAAC,UAAU,EAAE,mBAAmB,CAAC;QACrE,YAAY,EAAE,wBAAwB,CAAC,YAAY,EAAE,qBAAqB,CAAC;KAC9E,CAAC,CAAC;AACP,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAc,EAAE,GAAW;IACrD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,OAAO,GAAY,KAAK,CAAC;IAC7B,OAAO,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAI,OAAmC,CAAC,GAAG,CAAC,CAAC;QACxD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,GAAI,OAAmC,CAAC,KAAK,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,SAAS,4BAA4B,CAAC,KAAc;IAChD,KAAK,MAAM,IAAI,IAAI,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;QACrD,IAAI,kCAAkC,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC7D,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,OAAO,EACH,eAAe,EACf,kCAAkC,EAClC,gBAAgB,EAChB,4BAA4B,EAC5B,oBAAoB,GACvB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { assertCanonicalCid } from './cid-utils.js';
2
+ export { assertTimerMs, combineAbortSignals, createTimeoutSignal, invokeWithAbort, runWithRetry, throwIfSignalAborted, waitForRetryDelay, } from './async-utils.js';
3
+ export type { AbortSignalHandle, RunWithRetryAttemptContext, RunWithRetryOptions, } from './async-utils.js';
4
+ export { HttpStatusError, RETRYABLE_HTTP_NETWORK_ERROR_CODES, createHttpConfig, hasRetryableNetworkErrorCode, readErrorStringChain, } from './http-utils.js';
5
+ export type { CreateHttpConfigOptions, HttpConfig, HttpFetchLike, HttpPostFetchLike, HttpPostFetchOptions, HttpStatusErrorOptions, HttpTextResponse, } from './http-utils.js';
6
+ export { assertAsciiBytes, assertBytes32HexString, assertHeadersObject, assertHexData, assertHexString, assertNonEmptyString, assertNonNegativeInteger, assertPositiveInteger, assertUint256, isPlainObject, parseBytes, } from './validation-utils.js';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { assertCanonicalCid } from './cid-utils.js';
2
+ export { assertTimerMs, combineAbortSignals, createTimeoutSignal, invokeWithAbort, runWithRetry, throwIfSignalAborted, waitForRetryDelay, } from './async-utils.js';
3
+ export { HttpStatusError, RETRYABLE_HTTP_NETWORK_ERROR_CODES, createHttpConfig, hasRetryableNetworkErrorCode, readErrorStringChain, } from './http-utils.js';
4
+ export { assertAsciiBytes, assertBytes32HexString, assertHeadersObject, assertHexData, assertHexString, assertNonEmptyString, assertNonNegativeInteger, assertPositiveInteger, assertUint256, isPlainObject, parseBytes, } from './validation-utils.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EACH,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,GACpB,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EACH,eAAe,EACf,kCAAkC,EAClC,gBAAgB,EAChB,4BAA4B,EAC5B,oBAAoB,GACvB,MAAM,iBAAiB,CAAC;AAWzB,OAAO,EACH,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACb,UAAU,GACb,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,14 @@
1
+ declare function assertNonEmptyString(value: unknown, label: string): string;
2
+ declare function assertPositiveInteger(value: unknown, label: string): number;
3
+ declare function assertNonNegativeInteger(value: unknown, label: string): number;
4
+ declare function assertUint256(value: unknown, name: string): bigint;
5
+ declare function isPlainObject(value: unknown): value is Record<string, unknown>;
6
+ declare function assertHeadersObject(headers: unknown, label: string, options?: {
7
+ disallowedNames?: string[];
8
+ }): Readonly<Record<string, string>>;
9
+ declare function assertAsciiBytes(bytes: Uint8Array, message: string): void;
10
+ declare function assertHexString(value: unknown, label: string): string;
11
+ declare function assertHexData(value: unknown, label: string): string;
12
+ declare function assertBytes32HexString(value: unknown, label: string): string;
13
+ declare function parseBytes(value: unknown, name: string, size?: number): string;
14
+ export { assertAsciiBytes, assertBytes32HexString, assertHeadersObject, assertHexData, assertHexString, assertNonEmptyString, assertNonNegativeInteger, assertPositiveInteger, assertUint256, isPlainObject, parseBytes, };
@@ -0,0 +1,87 @@
1
+ const UINT256_MAX = (1n << 256n) - 1n;
2
+ function assertNonEmptyString(value, label) {
3
+ if (typeof value !== 'string' || !value.trim()) {
4
+ throw new Error(`${label} must be a non-empty string.`);
5
+ }
6
+ return value.trim();
7
+ }
8
+ function assertPositiveInteger(value, label) {
9
+ if (typeof value !== 'number' || !Number.isInteger(value) || value < 1) {
10
+ throw new Error(`${label} must be a positive integer.`);
11
+ }
12
+ return value;
13
+ }
14
+ function assertNonNegativeInteger(value, label) {
15
+ if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
16
+ throw new Error(`${label} must be a non-negative integer.`);
17
+ }
18
+ return value;
19
+ }
20
+ function assertUint256(value, name) {
21
+ if (typeof value !== 'bigint' || value < 0n || value > UINT256_MAX) {
22
+ throw new Error(`${name} must be a non-negative bigint fitting in 256 bits.`);
23
+ }
24
+ return value;
25
+ }
26
+ function isPlainObject(value) {
27
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
28
+ return false;
29
+ }
30
+ const prototype = Object.getPrototypeOf(value);
31
+ return prototype === Object.prototype || prototype === null;
32
+ }
33
+ function assertHeadersObject(headers, label, options = {}) {
34
+ if (!isPlainObject(headers)) {
35
+ throw new Error(`${label} must be a plain object.`);
36
+ }
37
+ const disallowedNames = new Set((options.disallowedNames ?? []).map((name) => name.toLowerCase()));
38
+ const validated = {};
39
+ for (const [key, value] of Object.entries(headers)) {
40
+ if (disallowedNames.has(key.toLowerCase())) {
41
+ throw new Error(`${label} must not include ${key.toLowerCase()}.`);
42
+ }
43
+ if (typeof value !== 'string') {
44
+ throw new Error(`${label}.${key} must be a string.`);
45
+ }
46
+ validated[key] = value;
47
+ }
48
+ return Object.freeze(validated);
49
+ }
50
+ function assertAsciiBytes(bytes, message) {
51
+ for (const byte of bytes) {
52
+ if (byte > 0x7f) {
53
+ throw new Error(message);
54
+ }
55
+ }
56
+ }
57
+ function assertHexString(value, label) {
58
+ const validated = assertNonEmptyString(value, label);
59
+ if (!/^0x[0-9a-fA-F]*$/.test(validated)) {
60
+ throw new Error(`${label} must be a 0x-prefixed hex string.`);
61
+ }
62
+ return validated;
63
+ }
64
+ function assertHexData(value, label) {
65
+ const validated = assertHexString(value, label);
66
+ if (validated.length === 2 || validated.length % 2 !== 0) {
67
+ throw new Error(`${label} must be non-empty byte-aligned hex data.`);
68
+ }
69
+ return validated;
70
+ }
71
+ function assertBytes32HexString(value, label) {
72
+ const validated = assertHexString(value, label);
73
+ if (validated.length !== 66) {
74
+ throw new Error(`${label} must be a 32-byte hex string.`);
75
+ }
76
+ return validated;
77
+ }
78
+ function parseBytes(value, name, size) {
79
+ if (typeof value !== 'string' ||
80
+ !/^0x(?:[0-9a-fA-F]{2})*$/.test(value) ||
81
+ (size !== undefined && value.length !== 2 + size * 2)) {
82
+ throw new Error(`${name} must be ${size === undefined ? 'byte-aligned' : `${size}-byte`} hex data.`);
83
+ }
84
+ return value;
85
+ }
86
+ export { assertAsciiBytes, assertBytes32HexString, assertHeadersObject, assertHexData, assertHexString, assertNonEmptyString, assertNonNegativeInteger, assertPositiveInteger, assertUint256, isPlainObject, parseBytes, };
87
+ //# sourceMappingURL=validation-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-utils.js","sourceRoot":"","sources":["../src/validation-utils.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@oyaprotocol/utils",
3
+ "version": "0.1.0",
4
+ "description": "Oya utilities package.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/oyaprotocol/oya-commitments.git",
9
+ "directory": "packages/utils"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public",
13
+ "registry": "https://registry.npmjs.org/"
14
+ },
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist/**/*.js",
26
+ "dist/**/*.d.ts",
27
+ "dist/**/*.js.map",
28
+ "README.md",
29
+ "LICENSE"
30
+ ]
31
+ }