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.
- package/README.md +273 -0
- package/dist/circuit.d.ts +68 -0
- package/dist/circuit.d.ts.map +1 -0
- package/dist/circuit.js +119 -0
- package/dist/circuit.js.map +1 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +63 -0
- package/dist/client.js.map +1 -0
- package/dist/core.d.ts +29 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +280 -0
- package/dist/core.js.map +1 -0
- package/dist/dedup.d.ts +29 -0
- package/dist/dedup.d.ts.map +1 -0
- package/dist/dedup.js +70 -0
- package/dist/dedup.js.map +1 -0
- package/dist/index.cjs +29 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +31 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +48 -0
- package/dist/middleware.js.map +1 -0
- package/dist/mock.d.ts +30 -0
- package/dist/mock.d.ts.map +1 -0
- package/dist/mock.js +56 -0
- package/dist/mock.js.map +1 -0
- package/dist/retry.d.ts +31 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +51 -0
- package/dist/retry.js.map +1 -0
- package/dist/timeout.d.ts +34 -0
- package/dist/timeout.d.ts.map +1 -0
- package/dist/timeout.js +87 -0
- package/dist/timeout.js.map +1 -0
- package/dist/types.d.ts +214 -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 +38 -0
package/dist/retry.d.ts
ADDED
|
@@ -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"}
|
package/dist/timeout.js
ADDED
|
@@ -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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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
+
}
|