fetchvault 1.0.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.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Determines whether a failed request should be retried.
3
+ *
4
+ * @param statusCode HTTP status code when available; `null` indicates network failure.
5
+ * @param attempt Current retry attempt number starting at 1.
6
+ * @param maxRetries Maximum retry attempts allowed.
7
+ * @returns `true` when retry conditions are met.
8
+ * @example
9
+ * const retry = shouldRetry(503, 1, 3)
10
+ */
11
+ export declare function shouldRetry(statusCode: number | null, attempt: number, maxRetries: number): boolean;
12
+ /**
13
+ * Calculates an exponential backoff delay with jitter.
14
+ *
15
+ * @param attempt Current retry attempt number starting at 1.
16
+ * @param randomFn Random number generator for deterministic testing.
17
+ * @returns Delay in milliseconds.
18
+ * @example
19
+ * const delayMs = calculateRetryDelay(2)
20
+ */
21
+ export declare function calculateRetryDelay(attempt: number, randomFn?: () => number): number;
22
+ /**
23
+ * Waits for a retry delay interval.
24
+ *
25
+ * @param delayMs Delay duration in milliseconds.
26
+ * @returns Promise that resolves after the delay.
27
+ * @example
28
+ * await waitForRetryDelay(1_000)
29
+ */
30
+ export declare function waitForRetryDelay(delayMs: number): Promise<void>;
31
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAKA;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,OAAO,CAUT;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,MAAoB,GACnC,MAAM,CAIR;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE"}
package/dist/retry.js ADDED
@@ -0,0 +1,51 @@
1
+ const BASE_RETRY_DELAY_MS = 1000;
2
+ const MAX_JITTER_MS = 500;
3
+ const SERVER_ERROR_MIN = 500;
4
+ const SERVER_ERROR_MAX = 599;
5
+ /**
6
+ * Determines whether a failed request should be retried.
7
+ *
8
+ * @param statusCode HTTP status code when available; `null` indicates network failure.
9
+ * @param attempt Current retry attempt number starting at 1.
10
+ * @param maxRetries Maximum retry attempts allowed.
11
+ * @returns `true` when retry conditions are met.
12
+ * @example
13
+ * const retry = shouldRetry(503, 1, 3)
14
+ */
15
+ export function shouldRetry(statusCode, attempt, maxRetries) {
16
+ if (attempt > maxRetries) {
17
+ return false;
18
+ }
19
+ if (statusCode === null) {
20
+ return true;
21
+ }
22
+ return statusCode >= SERVER_ERROR_MIN && statusCode <= SERVER_ERROR_MAX;
23
+ }
24
+ /**
25
+ * Calculates an exponential backoff delay with jitter.
26
+ *
27
+ * @param attempt Current retry attempt number starting at 1.
28
+ * @param randomFn Random number generator for deterministic testing.
29
+ * @returns Delay in milliseconds.
30
+ * @example
31
+ * const delayMs = calculateRetryDelay(2)
32
+ */
33
+ export function calculateRetryDelay(attempt, randomFn = Math.random) {
34
+ const exponentDelay = 2 ** attempt * BASE_RETRY_DELAY_MS;
35
+ const jitter = Math.floor(randomFn() * (MAX_JITTER_MS + 1));
36
+ return exponentDelay + jitter;
37
+ }
38
+ /**
39
+ * Waits for a retry delay interval.
40
+ *
41
+ * @param delayMs Delay duration in milliseconds.
42
+ * @returns Promise that resolves after the delay.
43
+ * @example
44
+ * await waitForRetryDelay(1_000)
45
+ */
46
+ export function waitForRetryDelay(delayMs) {
47
+ return new Promise((resolve) => {
48
+ setTimeout(resolve, delayMs);
49
+ });
50
+ }
51
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,MAAM,mBAAmB,GAAG,IAAK,CAAA;AACjC,MAAM,aAAa,GAAG,GAAG,CAAA;AACzB,MAAM,gBAAgB,GAAG,GAAG,CAAA;AAC5B,MAAM,gBAAgB,GAAG,GAAG,CAAA;AAE5B;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CACzB,UAAyB,EACzB,OAAe,EACf,UAAkB;IAElB,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;QACzB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,UAAU,IAAI,gBAAgB,IAAI,UAAU,IAAI,gBAAgB,CAAA;AACzE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAe,EACf,WAAyB,IAAI,CAAC,MAAM;IAEpC,MAAM,aAAa,GAAG,CAAC,IAAI,OAAO,GAAG,mBAAmB,CAAA;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAA;IAC3D,OAAO,aAAa,GAAG,MAAM,CAAA;AAC/B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC9B,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Parses a timeout value into milliseconds.
3
+ *
4
+ * @param input Human-readable timeout (e.g. `"500ms"`, `"1.5s"`) or numeric milliseconds.
5
+ * @returns Timeout duration in milliseconds.
6
+ * @example
7
+ * const timeoutMs = parseTimeout('5s')
8
+ */
9
+ export declare function parseTimeout(input: string | number): number;
10
+ /**
11
+ * Creates a timeout-backed abort signal and merges it with an optional external signal.
12
+ *
13
+ * @param timeout Timeout value in human-readable format or milliseconds.
14
+ * @param externalSignal Optional user-provided abort signal.
15
+ * @returns Signal, cleanup callback, and timeout-state accessor.
16
+ * @example
17
+ * const controller = createTimeoutSignal('5s', options.signal)
18
+ */
19
+ export declare function createTimeoutSignal(timeout: string | number | undefined, externalSignal?: AbortSignal): {
20
+ signal: AbortSignal | undefined;
21
+ cleanup: () => void;
22
+ didTimeout: () => boolean;
23
+ };
24
+ /**
25
+ * Merges two abort signals into a single composite signal.
26
+ *
27
+ * @param primary Primary abort signal.
28
+ * @param secondary Optional secondary abort signal.
29
+ * @returns Composite abort signal that aborts when either source aborts.
30
+ * @example
31
+ * const signal = mergeAbortSignals(internal.signal, options.signal)
32
+ */
33
+ export declare function mergeAbortSignals(primary: AbortSignal, secondary?: AbortSignal): AbortSignal;
34
+ //# sourceMappingURL=timeout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../src/timeout.ts"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAuB3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EACpC,cAAc,CAAC,EAAE,WAAW,GAC3B;IACD,MAAM,EAAE,WAAW,GAAG,SAAS,CAAA;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,UAAU,EAAE,MAAM,OAAO,CAAA;CAC1B,CAmBA;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,SAAS,CAAC,EAAE,WAAW,GACtB,WAAW,CAoBb"}
@@ -0,0 +1,87 @@
1
+ const MILLISECONDS_SUFFIX = 'ms';
2
+ const SECONDS_SUFFIX = 's';
3
+ const MS_MULTIPLIER = 1;
4
+ const SECONDS_TO_MS = 1000;
5
+ const DURATION_PATTERN = /^(\d*\.?\d+)(ms|s)$/i;
6
+ /**
7
+ * Parses a timeout value into milliseconds.
8
+ *
9
+ * @param input Human-readable timeout (e.g. `"500ms"`, `"1.5s"`) or numeric milliseconds.
10
+ * @returns Timeout duration in milliseconds.
11
+ * @example
12
+ * const timeoutMs = parseTimeout('5s')
13
+ */
14
+ export function parseTimeout(input) {
15
+ if (typeof input === 'number' && Number.isFinite(input) && input >= 0) {
16
+ return input;
17
+ }
18
+ if (typeof input !== 'string') {
19
+ throw new TypeError('Timeout must be a non-negative number or a duration string like "500ms" or "5s".');
20
+ }
21
+ const normalized = input.trim().toLowerCase();
22
+ const match = normalized.match(DURATION_PATTERN);
23
+ if (!match) {
24
+ throw new TypeError(`Invalid timeout format "${input}". Use values like "500ms", "5s", or 300.`);
25
+ }
26
+ const amount = Number(match[1]);
27
+ const unit = match[2];
28
+ if (!Number.isFinite(amount) || amount < 0) {
29
+ throw new TypeError(`Invalid timeout value "${input}". Timeout must be non-negative.`);
30
+ }
31
+ const multiplier = unit === MILLISECONDS_SUFFIX ? MS_MULTIPLIER : SECONDS_TO_MS;
32
+ return Math.round(amount * multiplier);
33
+ }
34
+ /**
35
+ * Creates a timeout-backed abort signal and merges it with an optional external signal.
36
+ *
37
+ * @param timeout Timeout value in human-readable format or milliseconds.
38
+ * @param externalSignal Optional user-provided abort signal.
39
+ * @returns Signal, cleanup callback, and timeout-state accessor.
40
+ * @example
41
+ * const controller = createTimeoutSignal('5s', options.signal)
42
+ */
43
+ export function createTimeoutSignal(timeout, externalSignal) {
44
+ if (timeout === undefined) {
45
+ return { signal: externalSignal, cleanup: () => undefined, didTimeout: () => false };
46
+ }
47
+ const timeoutMs = parseTimeout(timeout);
48
+ const timeoutController = new AbortController();
49
+ let timedOut = false;
50
+ const timer = setTimeout(() => {
51
+ timedOut = true;
52
+ timeoutController.abort(new Error('FetchVault timeout exceeded'));
53
+ }, timeoutMs);
54
+ const signal = mergeAbortSignals(timeoutController.signal, externalSignal);
55
+ return {
56
+ signal,
57
+ cleanup: () => clearTimeout(timer),
58
+ didTimeout: () => timedOut
59
+ };
60
+ }
61
+ /**
62
+ * Merges two abort signals into a single composite signal.
63
+ *
64
+ * @param primary Primary abort signal.
65
+ * @param secondary Optional secondary abort signal.
66
+ * @returns Composite abort signal that aborts when either source aborts.
67
+ * @example
68
+ * const signal = mergeAbortSignals(internal.signal, options.signal)
69
+ */
70
+ export function mergeAbortSignals(primary, secondary) {
71
+ if (!secondary) {
72
+ return primary;
73
+ }
74
+ if (typeof AbortSignal.any === 'function') {
75
+ return AbortSignal.any([primary, secondary]);
76
+ }
77
+ const compositeController = new AbortController();
78
+ const abortComposite = () => compositeController.abort();
79
+ if (primary.aborted || secondary.aborted) {
80
+ abortComposite();
81
+ return compositeController.signal;
82
+ }
83
+ primary.addEventListener('abort', abortComposite, { once: true });
84
+ secondary.addEventListener('abort', abortComposite, { once: true });
85
+ return compositeController.signal;
86
+ }
87
+ //# sourceMappingURL=timeout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeout.js","sourceRoot":"","sources":["../src/timeout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Machine-readable error codes emitted by FetchVault.
3
+ *
4
+ * @example
5
+ * const code: VaultErrorCode = 'TIMEOUT'
6
+ */
7
+ export type VaultErrorCode = 'NETWORK_ERROR' | 'TIMEOUT' | 'PARSE_ERROR' | 'VALIDATION_ERROR' | 'CIRCUIT_OPEN' | 'MIDDLEWARE_ERROR' | 'HTTP_ERROR' | 'UNKNOWN';
8
+ /**
9
+ * Structured error object returned on failed requests.
10
+ *
11
+ * @example
12
+ * const error: VaultError = { code: 'HTTP_ERROR', message: 'Request failed' }
13
+ */
14
+ export interface VaultError {
15
+ /** Machine-readable error code */
16
+ code: VaultErrorCode;
17
+ /** Human-readable message */
18
+ message: string;
19
+ /** The original thrown value, if any */
20
+ cause?: unknown;
21
+ }
22
+ /**
23
+ * Successful FetchVault response shape.
24
+ *
25
+ * @example
26
+ * const success: VaultSuccess<{ ok: true }> = { data: { ok: true }, error: null, statusCode: 200 }
27
+ */
28
+ export interface VaultSuccess<T> {
29
+ /** The parsed and validated response data */
30
+ data: T;
31
+ /** Always null on success */
32
+ error: null;
33
+ /** HTTP status code of the response */
34
+ statusCode: number;
35
+ }
36
+ /**
37
+ * Failed FetchVault response shape.
38
+ *
39
+ * @example
40
+ * const failure: VaultFailure = { data: null, error: { code: 'NETWORK_ERROR', message: 'Offline' }, statusCode: null }
41
+ */
42
+ export interface VaultFailure {
43
+ /** Always null on failure */
44
+ data: null;
45
+ /** Structured error object describing what went wrong */
46
+ error: VaultError;
47
+ /** HTTP status code if available, null for network-level failures */
48
+ statusCode: number | null;
49
+ }
50
+ /**
51
+ * The standard result object returned by every FetchVault call.
52
+ * Uses a discriminated union to enforce explicit error handling.
53
+ * A vault always gives you back exactly what you put in � or tells
54
+ * you clearly why it couldn't.
55
+ *
56
+ * @example
57
+ * const result: VaultResult<{ id: string }> = await fetchvault('/users/1')
58
+ */
59
+ export type VaultResult<T> = VaultSuccess<T> | VaultFailure;
60
+ /**
61
+ * Structural schema contract compatible with Zod, Valibot, Yup, and others.
62
+ *
63
+ * @example
64
+ * const schema: VaultSchema<{ id: string }> = { parse: (input) => input as { id: string } }
65
+ */
66
+ export interface VaultSchema<T> {
67
+ /** Parses and validates unknown input into typed output */
68
+ parse: (data: unknown) => T;
69
+ }
70
+ /**
71
+ * Infers the output type from a structural schema.
72
+ *
73
+ * @example
74
+ * type User = InferSchema<{ parse: (input: unknown) => { id: string } }>
75
+ */
76
+ export type InferSchema<S> = S extends VaultSchema<infer Output> ? Output : never;
77
+ /**
78
+ * Internal request model used by middleware pipelines.
79
+ *
80
+ * @example
81
+ * const req: VaultRequest = { url: '/users', method: 'GET', headers: {} }
82
+ */
83
+ export interface VaultRequest {
84
+ /** Final request URL */
85
+ url: string;
86
+ /** HTTP method in uppercase */
87
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
88
+ /** Request headers as plain object */
89
+ headers: Record<string, string>;
90
+ /** Request body before fetch boundary serialization */
91
+ body?: unknown;
92
+ /** Optional merged abort signal */
93
+ signal?: AbortSignal;
94
+ }
95
+ /**
96
+ * Internal response model used by middleware pipelines.
97
+ *
98
+ * @example
99
+ * const res: VaultResponse = { statusCode: 200, headers: { 'content-type': 'application/json' }, raw: { ok: true }, data: { ok: true } }
100
+ */
101
+ export interface VaultResponse {
102
+ /** HTTP status code */
103
+ statusCode: number;
104
+ /** Response headers as plain object */
105
+ headers: Record<string, string>;
106
+ /** Raw payload before schema validation */
107
+ raw: unknown;
108
+ /** Current transformed payload */
109
+ data: unknown;
110
+ }
111
+ /**
112
+ * Hook executed before a request is sent.
113
+ *
114
+ * @example
115
+ * const hook: VaultRequestHook = async (req) => ({ ...req, headers: { ...req.headers, Authorization: 'Bearer token' } })
116
+ */
117
+ export type VaultRequestHook = (req: VaultRequest) => VaultRequest | Promise<VaultRequest>;
118
+ /**
119
+ * Hook executed after a response is received.
120
+ *
121
+ * @example
122
+ * const hook: VaultResponseHook = (res) => ({ ...res, data: res.raw })
123
+ */
124
+ export type VaultResponseHook = (res: VaultResponse) => VaultResponse | Promise<VaultResponse>;
125
+ /**
126
+ * Mock response configuration.
127
+ *
128
+ * @example
129
+ * const mock: VaultMockConfig = { data: { ok: true }, status: 200, delay: '200ms' }
130
+ */
131
+ export interface VaultMockConfig {
132
+ /** Data to return as the mock response */
133
+ data: unknown;
134
+ /** Mock HTTP status code � default: 200 */
135
+ status?: number;
136
+ /** Simulated network delay e.g. "200ms" */
137
+ delay?: string | number;
138
+ }
139
+ /**
140
+ * Request options for a FetchVault call.
141
+ *
142
+ * @example
143
+ * const options: VaultOptions<{ id: string }, never> = { method: 'GET', timeout: '5s' }
144
+ */
145
+ export interface VaultOptions<T, S> {
146
+ /** HTTP method � defaults to GET */
147
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
148
+ /** Request headers merged with instance-level headers */
149
+ headers?: Record<string, string>;
150
+ /** Request body � will be JSON.stringify'd automatically */
151
+ body?: unknown;
152
+ /** Human-readable timeout e.g. "5s" or "500ms" */
153
+ timeout?: string | number;
154
+ /** Number of retries on 5xx or network failure � default: 3 */
155
+ retries?: number;
156
+ /** Optional validation schema (Zod, Valibot, Yup compatible) */
157
+ schema?: S;
158
+ /** User-provided AbortSignal � merged with internal timeout signal */
159
+ signal?: AbortSignal;
160
+ /** Deduplicate concurrent GET requests � default: true */
161
+ deduplicate?: boolean;
162
+ /** If provided, bypasses network and returns mock data */
163
+ mock?: VaultMockConfig;
164
+ /** Hooks that run before the request is sent */
165
+ beforeRequest?: Array<VaultRequestHook>;
166
+ /** Hooks that run after a response is received */
167
+ afterResponse?: Array<VaultResponseHook>;
168
+ }
169
+ /**
170
+ * Client-level defaults used by `createClient`.
171
+ *
172
+ * @example
173
+ * const config: VaultClientConfig = { baseURL: 'https://api.example.com', timeout: '10s' }
174
+ */
175
+ export interface VaultClientConfig {
176
+ /** Base URL prefixed to every method path */
177
+ baseURL: string;
178
+ /** Default headers merged into each request */
179
+ headers?: Record<string, string>;
180
+ /** Default timeout inherited by each request */
181
+ timeout?: string | number;
182
+ /** Default retry count inherited by each request */
183
+ retries?: number;
184
+ /** Default before-request hooks */
185
+ beforeRequest?: Array<VaultRequestHook>;
186
+ /** Default after-response hooks */
187
+ afterResponse?: Array<VaultResponseHook>;
188
+ }
189
+ /**
190
+ * Callable method contract used by each HTTP verb helper.
191
+ *
192
+ * @example
193
+ * const get: VaultClientMethod = (path) => fetchvault(path)
194
+ */
195
+ export type VaultClientMethod = <T, S extends VaultSchema<T> = never>(path: string, options?: VaultOptions<T, S>) => Promise<VaultResult<S extends never ? T : InferSchema<S>>>;
196
+ /**
197
+ * Pre-configured FetchVault client interface.
198
+ *
199
+ * @example
200
+ * const client: VaultClient = createClient({ baseURL: 'https://api.example.com' })
201
+ */
202
+ export interface VaultClient {
203
+ /** Executes a GET request */
204
+ get: VaultClientMethod;
205
+ /** Executes a POST request */
206
+ post: VaultClientMethod;
207
+ /** Executes a PUT request */
208
+ put: VaultClientMethod;
209
+ /** Executes a PATCH request */
210
+ patch: VaultClientMethod;
211
+ /** Executes a DELETE request */
212
+ delete: VaultClientMethod;
213
+ }
214
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "fetchvault",
3
+ "version": "1.0.0",
4
+ "description": "A resilient, typed, zero-dependency fetch wrapper with circuit-breaker, retry, and deduplication.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc && node scripts/build-cjs.mjs",
21
+ "test": "vitest run",
22
+ "prepublishOnly": "npm run test && npm run build"
23
+ },
24
+ "keywords": [
25
+ "fetch",
26
+ "resilience",
27
+ "circuit-breaker",
28
+ "retry",
29
+ "deduplication",
30
+ "typescript"
31
+ ],
32
+ "author": "",
33
+ "license": "MIT",
34
+ "devDependencies": {
35
+ "typescript": "^5.4.0",
36
+ "vitest": "^1.6.0"
37
+ }
38
+ }