Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@blimu/fetch

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@blimu/fetch - npm Package Compare versions

Comparing version
0.3.0
to
0.4.0
+28
-14
dist/index.cjs

@@ -302,3 +302,3 @@ "use strict";

}
this.hooks.get(stage).push(hook);
this.hooks.get(stage)?.push(hook);
}

@@ -349,3 +349,10 @@ /**

has(stage) {
return this.hooks.has(stage) && this.hooks.get(stage).length > 0;
if (!this.hooks.has(stage)) {
return false;
}
const hooks = this.hooks.get(stage);
if (!hooks) {
return false;
}
return hooks.length > 0;
}

@@ -469,3 +476,3 @@ };

yield parsed;
} catch (error) {
} catch {
console.warn("Skipping invalid JSON line:", trimmed);

@@ -479,3 +486,3 @@ }

yield parsed;
} catch (error) {
} catch {
console.warn("Skipping invalid JSON in buffer:", buffer.trim());

@@ -727,4 +734,5 @@ }

*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async request(init) {
let url = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const url = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const headers = new Headers(this.cfg.headers || {});

@@ -766,3 +774,3 @@ if (init.headers) {

try {
let attemptUrl = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const attemptUrl = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const attemptHeaders = new Headers(headers);

@@ -776,2 +784,3 @@ await this.applyAuthentication(attemptHeaders, attemptUrl);

if (this.hookRegistry.has("beforeRetry")) {
const serializedBody = serializeBody(init.body, bodyContentType);
await this.hookRegistry.execute("beforeRetry", {

@@ -781,3 +790,4 @@ url: url.toString(),

...init,
headers
headers,
body: serializedBody
},

@@ -792,2 +802,3 @@ attempt,

if (this.hookRegistry.has("afterRetry")) {
const serializedBody = serializeBody(init.body, bodyContentType);
await this.hookRegistry.execute("afterRetry", {

@@ -797,3 +808,4 @@ url: url.toString(),

...init,
headers
headers,
body: serializedBody
},

@@ -817,3 +829,3 @@ attempt,

async *requestStream(init) {
let url = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const url = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const headers = new Headers(this.cfg.headers || {});

@@ -892,3 +904,4 @@ if (init.headers) {

const parsed = await parseResponse(res);
const error = createFetchError(res.status, parsed?.message || `HTTP ${res.status}`, parsed, res.headers);
const message = parsed?.message;
const error = createFetchError(res.status, message || `HTTP ${res.status}`, parsed, res.headers);
if (this.hookRegistry.has("onError")) {

@@ -915,3 +928,3 @@ await this.hookRegistry.execute("onError", {

for await (const chunk of parseSSEStream(res)) {
let transformedChunk = chunk;
const transformedChunk = chunk;
if (this.hookRegistry.has("onStreamChunk")) {

@@ -929,3 +942,3 @@ await this.hookRegistry.execute("onStreamChunk", {

for await (const chunk of parseNDJSONStream(res)) {
let transformedChunk = chunk;
const transformedChunk = chunk;
if (this.hookRegistry.has("onStreamChunk")) {

@@ -943,3 +956,3 @@ await this.hookRegistry.execute("onStreamChunk", {

for await (const chunk of parseChunkedStream(res)) {
let transformedChunk = chunk;
const transformedChunk = chunk;
if (this.hookRegistry.has("onStreamChunk")) {

@@ -1049,3 +1062,4 @@ await this.hookRegistry.execute("onStreamChunk", {

if (!isSuccessResponse(res)) {
const error = createFetchError(res.status, parsed?.message || `HTTP ${res.status}`, parsed, res.headers);
const message = parsed?.message;
const error = createFetchError(res.status, message || `HTTP ${res.status}`, parsed, res.headers);
if (this.hookRegistry.has("onError")) {

@@ -1052,0 +1066,0 @@ await this.hookRegistry.execute("onError", {

/**
* Lifecycle stages for request execution
*/
type HookStage = "beforeRequest" | "afterRequest" | "afterResponse" | "onError" | "beforeRetry" | "afterRetry" | "onTimeout" | "onStreamStart" | "onStreamChunk" | "onStreamEnd";
/**
* Base context available to all hooks
*/
interface BaseHookContext {
url: string;
init: RequestInit & {
path?: string;
method: string;
query?: Record<string, any>;
headers?: Headers;
};
attempt: number;
}
/**
* Context for beforeRequest hook
*/
interface BeforeRequestHookContext extends BaseHookContext {
}
/**
* Context for afterRequest hook (before parsing)
*/
interface AfterRequestHookContext extends BaseHookContext {
response: Response;
}
/**
* Context for afterResponse hook (after parsing)
*/
interface AfterResponseHookContext extends BaseHookContext {
response: Response;
data: unknown;
}
/**
* Context for onError hook
*/
interface OnErrorHookContext extends BaseHookContext {
error: unknown;
}
/**
* Context for beforeRetry hook
*/
interface BeforeRetryHookContext extends BaseHookContext {
error: unknown;
retryCount: number;
}
/**
* Context for afterRetry hook
*/
interface AfterRetryHookContext extends BaseHookContext {
error: unknown;
retryCount: number;
success: boolean;
}
/**
* Context for onTimeout hook
*/
interface OnTimeoutHookContext extends BaseHookContext {
timeoutMs: number;
}
/**
* Context for onStreamStart hook
*/
interface OnStreamStartHookContext extends BaseHookContext {
response: Response;
}
/**
* Context for onStreamChunk hook
*/
interface OnStreamChunkHookContext extends BaseHookContext {
chunk: unknown;
}
/**
* Context for onStreamEnd hook
*/
interface OnStreamEndHookContext extends BaseHookContext {
response: Response;
}
/**
* Union type of all hook contexts
*/
type HookContext = BeforeRequestHookContext | AfterRequestHookContext | AfterResponseHookContext | OnErrorHookContext | BeforeRetryHookContext | AfterRetryHookContext | OnTimeoutHookContext | OnStreamStartHookContext | OnStreamChunkHookContext | OnStreamEndHookContext;
/**
* Hook function type
*/
type Hook = (context: HookContext) => void | Promise<void>;
/**
* Hook configuration interface
*/
interface HooksConfig {
beforeRequest?: Hook[];
afterRequest?: Hook[];
afterResponse?: Hook[];
onError?: Hook[];
beforeRetry?: Hook[];
afterRetry?: Hook[];
onTimeout?: Hook[];
onStreamStart?: Hook[];
onStreamChunk?: Hook[];
onStreamEnd?: Hook[];
}
/**
* Hook registry that manages hooks for different lifecycle stages

@@ -134,3 +30,3 @@ */

*/
execute(stage: HookStage, context: any): Promise<void>;
execute(stage: HookStage, context: HookContext): Promise<void>;
/**

@@ -160,3 +56,3 @@ * Check if any hooks are registered for a stage

*/
strategy: "exponential" | "linear" | RetryStrategyFunction;
strategy: 'exponential' | 'linear' | RetryStrategyFunction;
/**

@@ -189,7 +85,7 @@ * Base backoff time in milliseconds (used for exponential and linear strategies)

*/
declare function getRetryStrategy(strategy: "exponential" | "linear" | RetryStrategyFunction): RetryStrategyFunction;
declare function getRetryStrategy(strategy: 'exponential' | 'linear' | RetryStrategyFunction): RetryStrategyFunction;
/**
* Calculate the delay for a retry attempt
*/
declare function calculateRetryDelay(attempt: number, strategy: "exponential" | "linear" | RetryStrategyFunction, baseBackoffMs: number): number;
declare function calculateRetryDelay(attempt: number, strategy: 'exponential' | 'linear' | RetryStrategyFunction, baseBackoffMs: number): number;

@@ -201,2 +97,3 @@ /**

declare function parseSSEStream(response: Response): AsyncGenerator<string, void, unknown>;
type AnyParsedNDJSON = string | object | ArrayBuffer | unknown;
/**

@@ -206,3 +103,3 @@ * Parse NDJSON (Newline-Delimited JSON) stream

*/
declare function parseNDJSONStream<T = any>(response: Response): AsyncGenerator<T, void, unknown>;
declare function parseNDJSONStream<T = AnyParsedNDJSON>(response: Response): AsyncGenerator<T, void, unknown>;
/**

@@ -217,3 +114,3 @@ * Parse generic chunked stream

*/
type StreamingFormat = "sse" | "ndjson" | "chunked";
type StreamingFormat = 'sse' | 'ndjson' | 'chunked';

@@ -294,2 +191,25 @@ /**

/**
* Query parameter value types
* Query parameters can be strings, numbers, booleans, arrays of strings (including string literal unions), or null/undefined
*
* Note: Arrays of string literal unions (e.g., ('active' | 'inactive')[]) are accepted
* because they are assignable to arrays of string-like values at runtime. The runtime
* serialization converts all values to strings using String().
*
* We use a more permissive array type to accept arrays of string literals, which TypeScript
* doesn't automatically widen to string[] due to array covariance rules. The type accepts
* arrays of any primitive type since they all get stringified at runtime.
*/
type QueryParamValue = any;
/**
* Query parameters object
* Maps parameter names to their values
*
* The runtime implementation uses Object.entries() which works with any object type.
* TypeScript interfaces are not directly assignable to Record types, so when passing
* interface types (like generated SDK query interfaces), a type assertion may be needed.
* The runtime serialization code safely handles the conversion.
*/
type QueryParams = Record<string, QueryParamValue>;
/**
* Body type that can be serialized by the fetch client

@@ -314,7 +234,7 @@ * Includes standard BodyInit types plus plain objects/arrays that will be JSON serialized

*/
query?: Record<string, any>;
query?: QueryParams | undefined;
/**
* Request body - can be BodyInit, plain object/array (will be JSON serialized), or null/undefined
*/
body?: SerializableBody;
body?: SerializableBody | undefined;
}

@@ -336,2 +256,107 @@ /**

/**
* Lifecycle stages for request execution
*/
type HookStage = 'beforeRequest' | 'afterRequest' | 'afterResponse' | 'onError' | 'beforeRetry' | 'afterRetry' | 'onTimeout' | 'onStreamStart' | 'onStreamChunk' | 'onStreamEnd';
/**
* Base context available to all hooks
*/
interface BaseHookContext {
url: string;
init: Omit<RequestInit, 'body'> & {
path?: string | undefined;
method: string;
query?: QueryParams | undefined;
headers?: Headers | undefined;
body?: RequestInit['body'] | null | undefined;
};
attempt: number;
}
/**
* Context for beforeRequest hook
*/
interface BeforeRequestHookContext extends BaseHookContext {
}
/**
* Context for afterRequest hook (before parsing)
*/
interface AfterRequestHookContext extends BaseHookContext {
response: Response;
}
/**
* Context for afterResponse hook (after parsing)
*/
interface AfterResponseHookContext extends BaseHookContext {
response: Response;
data: unknown;
}
/**
* Context for onError hook
*/
interface OnErrorHookContext extends BaseHookContext {
error: unknown;
}
/**
* Context for beforeRetry hook
*/
interface BeforeRetryHookContext extends BaseHookContext {
error: unknown;
retryCount: number;
}
/**
* Context for afterRetry hook
*/
interface AfterRetryHookContext extends BaseHookContext {
error: unknown;
retryCount: number;
success: boolean;
}
/**
* Context for onTimeout hook
*/
interface OnTimeoutHookContext extends BaseHookContext {
timeoutMs: number;
}
/**
* Context for onStreamStart hook
*/
interface OnStreamStartHookContext extends BaseHookContext {
response: Response;
}
/**
* Context for onStreamChunk hook
*/
interface OnStreamChunkHookContext extends BaseHookContext {
chunk: unknown;
}
/**
* Context for onStreamEnd hook
*/
interface OnStreamEndHookContext extends BaseHookContext {
response: Response;
}
/**
* Union type of all hook contexts
*/
type HookContext = BeforeRequestHookContext | AfterRequestHookContext | AfterResponseHookContext | OnErrorHookContext | BeforeRetryHookContext | AfterRetryHookContext | OnTimeoutHookContext | OnStreamStartHookContext | OnStreamChunkHookContext | OnStreamEndHookContext;
/**
* Hook function type
*/
type Hook = (context: HookContext) => void | Promise<void>;
/**
* Hook configuration interface
*/
interface HooksConfig {
beforeRequest?: Hook[];
afterRequest?: Hook[];
afterResponse?: Hook[];
onError?: Hook[];
beforeRetry?: Hook[];
afterRetry?: Hook[];
onTimeout?: Hook[];
onStreamStart?: Hook[];
onStreamChunk?: Hook[];
onStreamEnd?: Hook[];
}
/**
* Universal HTTP fetch client with hooks, retries, and streaming support

@@ -354,11 +379,11 @@ */

*/
useHook(stage: string, hook: any): void;
useHook(stage: HookStage, hook: Hook): void;
/**
* Remove a hook
*/
removeHook(stage: string, hook: any): boolean;
removeHook(stage: HookStage, hook: Hook): boolean;
/**
* Clear hooks for a stage or all hooks
*/
clearHooks(stage?: string): void;
clearHooks(stage?: HookStage): void;
/**

@@ -371,3 +396,3 @@ * Make an HTTP request

*/
requestStream<T = any>(init: StreamingRequestOptions): AsyncGenerator<T, void, unknown>;
requestStream<T = unknown>(init: StreamingRequestOptions): AsyncGenerator<T, void, unknown>;
/**

@@ -487,3 +512,3 @@ * Internal method to execute a single request attempt

*/
declare function serializeQueryParams(query: Record<string, any>): URLSearchParams;
declare function serializeQueryParams(query: QueryParams): URLSearchParams;
/**

@@ -496,3 +521,3 @@ * Builds a URL with query parameters

*/
declare function buildUrl(baseUrl: string, path: string, query?: Record<string, any>): URL;
declare function buildUrl(baseUrl: string, path: string, query?: QueryParams | undefined): URL;
/**

@@ -517,3 +542,4 @@ * Determines the content type from a body value

*/
declare function parseResponse(response: Response): Promise<any>;
type AnyPromiseResponse = Promise<string | object | ArrayBuffer | unknown>;
declare function parseResponse(response: Response): AnyPromiseResponse;
/**

@@ -526,2 +552,2 @@ * Checks if a response indicates success (status 200-299)

export { type AfterRequestHookContext, type AfterResponseHookContext, type AfterRetryHookContext, type ApiKeyAuthStrategy, type AuthStrategy, BadGatewayError, BadRequestError, type BaseHookContext, type BasicAuthStrategy, type BearerAuthStrategy, type BeforeRequestHookContext, type BeforeRetryHookContext, ClientError, ConflictError, type CustomAuthStrategy, FetchClient, type FetchClientConfig, FetchError, ForbiddenError, GatewayTimeoutError, type Hook, type HookContext, HookRegistry, type HookStage, type HooksConfig, InternalServerError, MethodNotAllowedError, NotFoundError, type OnErrorHookContext, type OnStreamChunkHookContext, type OnStreamEndHookContext, type OnStreamStartHookContext, type OnTimeoutHookContext, type RequestOptions, type RetryConfig, type RetryStrategyFunction, type SerializableBody, ServerError, ServiceUnavailableError, type StreamingFormat, type StreamingRequestOptions, TooManyRequestsError, UnauthorizedError, UnprocessableEntityError, buildUrl, calculateRetryDelay, createFetchError, encodeBase64, exponentialStrategy, getContentType, getFetchErrorMessage, getRetryStrategy, isAbortControllerAvailable, isBrowser, isFetchAvailable, isNode, isSuccessResponse, linearStrategy, parseChunkedStream, parseNDJSONStream, parseResponse, parseSSEStream, serializeBody, serializeQueryParams };
export { type AfterRequestHookContext, type AfterResponseHookContext, type AfterRetryHookContext, type ApiKeyAuthStrategy, type AuthStrategy, BadGatewayError, BadRequestError, type BaseHookContext, type BasicAuthStrategy, type BearerAuthStrategy, type BeforeRequestHookContext, type BeforeRetryHookContext, ClientError, ConflictError, type CustomAuthStrategy, FetchClient, type FetchClientConfig, FetchError, ForbiddenError, GatewayTimeoutError, type Hook, type HookContext, HookRegistry, type HookStage, type HooksConfig, InternalServerError, MethodNotAllowedError, NotFoundError, type OnErrorHookContext, type OnStreamChunkHookContext, type OnStreamEndHookContext, type OnStreamStartHookContext, type OnTimeoutHookContext, type QueryParamValue, type QueryParams, type RequestOptions, type RetryConfig, type RetryStrategyFunction, type SerializableBody, ServerError, ServiceUnavailableError, type StreamingFormat, type StreamingRequestOptions, TooManyRequestsError, UnauthorizedError, UnprocessableEntityError, buildUrl, calculateRetryDelay, createFetchError, encodeBase64, exponentialStrategy, getContentType, getFetchErrorMessage, getRetryStrategy, isAbortControllerAvailable, isBrowser, isFetchAvailable, isNode, isSuccessResponse, linearStrategy, parseChunkedStream, parseNDJSONStream, parseResponse, parseSSEStream, serializeBody, serializeQueryParams };
/**
* Lifecycle stages for request execution
*/
type HookStage = "beforeRequest" | "afterRequest" | "afterResponse" | "onError" | "beforeRetry" | "afterRetry" | "onTimeout" | "onStreamStart" | "onStreamChunk" | "onStreamEnd";
/**
* Base context available to all hooks
*/
interface BaseHookContext {
url: string;
init: RequestInit & {
path?: string;
method: string;
query?: Record<string, any>;
headers?: Headers;
};
attempt: number;
}
/**
* Context for beforeRequest hook
*/
interface BeforeRequestHookContext extends BaseHookContext {
}
/**
* Context for afterRequest hook (before parsing)
*/
interface AfterRequestHookContext extends BaseHookContext {
response: Response;
}
/**
* Context for afterResponse hook (after parsing)
*/
interface AfterResponseHookContext extends BaseHookContext {
response: Response;
data: unknown;
}
/**
* Context for onError hook
*/
interface OnErrorHookContext extends BaseHookContext {
error: unknown;
}
/**
* Context for beforeRetry hook
*/
interface BeforeRetryHookContext extends BaseHookContext {
error: unknown;
retryCount: number;
}
/**
* Context for afterRetry hook
*/
interface AfterRetryHookContext extends BaseHookContext {
error: unknown;
retryCount: number;
success: boolean;
}
/**
* Context for onTimeout hook
*/
interface OnTimeoutHookContext extends BaseHookContext {
timeoutMs: number;
}
/**
* Context for onStreamStart hook
*/
interface OnStreamStartHookContext extends BaseHookContext {
response: Response;
}
/**
* Context for onStreamChunk hook
*/
interface OnStreamChunkHookContext extends BaseHookContext {
chunk: unknown;
}
/**
* Context for onStreamEnd hook
*/
interface OnStreamEndHookContext extends BaseHookContext {
response: Response;
}
/**
* Union type of all hook contexts
*/
type HookContext = BeforeRequestHookContext | AfterRequestHookContext | AfterResponseHookContext | OnErrorHookContext | BeforeRetryHookContext | AfterRetryHookContext | OnTimeoutHookContext | OnStreamStartHookContext | OnStreamChunkHookContext | OnStreamEndHookContext;
/**
* Hook function type
*/
type Hook = (context: HookContext) => void | Promise<void>;
/**
* Hook configuration interface
*/
interface HooksConfig {
beforeRequest?: Hook[];
afterRequest?: Hook[];
afterResponse?: Hook[];
onError?: Hook[];
beforeRetry?: Hook[];
afterRetry?: Hook[];
onTimeout?: Hook[];
onStreamStart?: Hook[];
onStreamChunk?: Hook[];
onStreamEnd?: Hook[];
}
/**
* Hook registry that manages hooks for different lifecycle stages

@@ -134,3 +30,3 @@ */

*/
execute(stage: HookStage, context: any): Promise<void>;
execute(stage: HookStage, context: HookContext): Promise<void>;
/**

@@ -160,3 +56,3 @@ * Check if any hooks are registered for a stage

*/
strategy: "exponential" | "linear" | RetryStrategyFunction;
strategy: 'exponential' | 'linear' | RetryStrategyFunction;
/**

@@ -189,7 +85,7 @@ * Base backoff time in milliseconds (used for exponential and linear strategies)

*/
declare function getRetryStrategy(strategy: "exponential" | "linear" | RetryStrategyFunction): RetryStrategyFunction;
declare function getRetryStrategy(strategy: 'exponential' | 'linear' | RetryStrategyFunction): RetryStrategyFunction;
/**
* Calculate the delay for a retry attempt
*/
declare function calculateRetryDelay(attempt: number, strategy: "exponential" | "linear" | RetryStrategyFunction, baseBackoffMs: number): number;
declare function calculateRetryDelay(attempt: number, strategy: 'exponential' | 'linear' | RetryStrategyFunction, baseBackoffMs: number): number;

@@ -201,2 +97,3 @@ /**

declare function parseSSEStream(response: Response): AsyncGenerator<string, void, unknown>;
type AnyParsedNDJSON = string | object | ArrayBuffer | unknown;
/**

@@ -206,3 +103,3 @@ * Parse NDJSON (Newline-Delimited JSON) stream

*/
declare function parseNDJSONStream<T = any>(response: Response): AsyncGenerator<T, void, unknown>;
declare function parseNDJSONStream<T = AnyParsedNDJSON>(response: Response): AsyncGenerator<T, void, unknown>;
/**

@@ -217,3 +114,3 @@ * Parse generic chunked stream

*/
type StreamingFormat = "sse" | "ndjson" | "chunked";
type StreamingFormat = 'sse' | 'ndjson' | 'chunked';

@@ -294,2 +191,25 @@ /**

/**
* Query parameter value types
* Query parameters can be strings, numbers, booleans, arrays of strings (including string literal unions), or null/undefined
*
* Note: Arrays of string literal unions (e.g., ('active' | 'inactive')[]) are accepted
* because they are assignable to arrays of string-like values at runtime. The runtime
* serialization converts all values to strings using String().
*
* We use a more permissive array type to accept arrays of string literals, which TypeScript
* doesn't automatically widen to string[] due to array covariance rules. The type accepts
* arrays of any primitive type since they all get stringified at runtime.
*/
type QueryParamValue = any;
/**
* Query parameters object
* Maps parameter names to their values
*
* The runtime implementation uses Object.entries() which works with any object type.
* TypeScript interfaces are not directly assignable to Record types, so when passing
* interface types (like generated SDK query interfaces), a type assertion may be needed.
* The runtime serialization code safely handles the conversion.
*/
type QueryParams = Record<string, QueryParamValue>;
/**
* Body type that can be serialized by the fetch client

@@ -314,7 +234,7 @@ * Includes standard BodyInit types plus plain objects/arrays that will be JSON serialized

*/
query?: Record<string, any>;
query?: QueryParams | undefined;
/**
* Request body - can be BodyInit, plain object/array (will be JSON serialized), or null/undefined
*/
body?: SerializableBody;
body?: SerializableBody | undefined;
}

@@ -336,2 +256,107 @@ /**

/**
* Lifecycle stages for request execution
*/
type HookStage = 'beforeRequest' | 'afterRequest' | 'afterResponse' | 'onError' | 'beforeRetry' | 'afterRetry' | 'onTimeout' | 'onStreamStart' | 'onStreamChunk' | 'onStreamEnd';
/**
* Base context available to all hooks
*/
interface BaseHookContext {
url: string;
init: Omit<RequestInit, 'body'> & {
path?: string | undefined;
method: string;
query?: QueryParams | undefined;
headers?: Headers | undefined;
body?: RequestInit['body'] | null | undefined;
};
attempt: number;
}
/**
* Context for beforeRequest hook
*/
interface BeforeRequestHookContext extends BaseHookContext {
}
/**
* Context for afterRequest hook (before parsing)
*/
interface AfterRequestHookContext extends BaseHookContext {
response: Response;
}
/**
* Context for afterResponse hook (after parsing)
*/
interface AfterResponseHookContext extends BaseHookContext {
response: Response;
data: unknown;
}
/**
* Context for onError hook
*/
interface OnErrorHookContext extends BaseHookContext {
error: unknown;
}
/**
* Context for beforeRetry hook
*/
interface BeforeRetryHookContext extends BaseHookContext {
error: unknown;
retryCount: number;
}
/**
* Context for afterRetry hook
*/
interface AfterRetryHookContext extends BaseHookContext {
error: unknown;
retryCount: number;
success: boolean;
}
/**
* Context for onTimeout hook
*/
interface OnTimeoutHookContext extends BaseHookContext {
timeoutMs: number;
}
/**
* Context for onStreamStart hook
*/
interface OnStreamStartHookContext extends BaseHookContext {
response: Response;
}
/**
* Context for onStreamChunk hook
*/
interface OnStreamChunkHookContext extends BaseHookContext {
chunk: unknown;
}
/**
* Context for onStreamEnd hook
*/
interface OnStreamEndHookContext extends BaseHookContext {
response: Response;
}
/**
* Union type of all hook contexts
*/
type HookContext = BeforeRequestHookContext | AfterRequestHookContext | AfterResponseHookContext | OnErrorHookContext | BeforeRetryHookContext | AfterRetryHookContext | OnTimeoutHookContext | OnStreamStartHookContext | OnStreamChunkHookContext | OnStreamEndHookContext;
/**
* Hook function type
*/
type Hook = (context: HookContext) => void | Promise<void>;
/**
* Hook configuration interface
*/
interface HooksConfig {
beforeRequest?: Hook[];
afterRequest?: Hook[];
afterResponse?: Hook[];
onError?: Hook[];
beforeRetry?: Hook[];
afterRetry?: Hook[];
onTimeout?: Hook[];
onStreamStart?: Hook[];
onStreamChunk?: Hook[];
onStreamEnd?: Hook[];
}
/**
* Universal HTTP fetch client with hooks, retries, and streaming support

@@ -354,11 +379,11 @@ */

*/
useHook(stage: string, hook: any): void;
useHook(stage: HookStage, hook: Hook): void;
/**
* Remove a hook
*/
removeHook(stage: string, hook: any): boolean;
removeHook(stage: HookStage, hook: Hook): boolean;
/**
* Clear hooks for a stage or all hooks
*/
clearHooks(stage?: string): void;
clearHooks(stage?: HookStage): void;
/**

@@ -371,3 +396,3 @@ * Make an HTTP request

*/
requestStream<T = any>(init: StreamingRequestOptions): AsyncGenerator<T, void, unknown>;
requestStream<T = unknown>(init: StreamingRequestOptions): AsyncGenerator<T, void, unknown>;
/**

@@ -487,3 +512,3 @@ * Internal method to execute a single request attempt

*/
declare function serializeQueryParams(query: Record<string, any>): URLSearchParams;
declare function serializeQueryParams(query: QueryParams): URLSearchParams;
/**

@@ -496,3 +521,3 @@ * Builds a URL with query parameters

*/
declare function buildUrl(baseUrl: string, path: string, query?: Record<string, any>): URL;
declare function buildUrl(baseUrl: string, path: string, query?: QueryParams | undefined): URL;
/**

@@ -517,3 +542,4 @@ * Determines the content type from a body value

*/
declare function parseResponse(response: Response): Promise<any>;
type AnyPromiseResponse = Promise<string | object | ArrayBuffer | unknown>;
declare function parseResponse(response: Response): AnyPromiseResponse;
/**

@@ -526,2 +552,2 @@ * Checks if a response indicates success (status 200-299)

export { type AfterRequestHookContext, type AfterResponseHookContext, type AfterRetryHookContext, type ApiKeyAuthStrategy, type AuthStrategy, BadGatewayError, BadRequestError, type BaseHookContext, type BasicAuthStrategy, type BearerAuthStrategy, type BeforeRequestHookContext, type BeforeRetryHookContext, ClientError, ConflictError, type CustomAuthStrategy, FetchClient, type FetchClientConfig, FetchError, ForbiddenError, GatewayTimeoutError, type Hook, type HookContext, HookRegistry, type HookStage, type HooksConfig, InternalServerError, MethodNotAllowedError, NotFoundError, type OnErrorHookContext, type OnStreamChunkHookContext, type OnStreamEndHookContext, type OnStreamStartHookContext, type OnTimeoutHookContext, type RequestOptions, type RetryConfig, type RetryStrategyFunction, type SerializableBody, ServerError, ServiceUnavailableError, type StreamingFormat, type StreamingRequestOptions, TooManyRequestsError, UnauthorizedError, UnprocessableEntityError, buildUrl, calculateRetryDelay, createFetchError, encodeBase64, exponentialStrategy, getContentType, getFetchErrorMessage, getRetryStrategy, isAbortControllerAvailable, isBrowser, isFetchAvailable, isNode, isSuccessResponse, linearStrategy, parseChunkedStream, parseNDJSONStream, parseResponse, parseSSEStream, serializeBody, serializeQueryParams };
export { type AfterRequestHookContext, type AfterResponseHookContext, type AfterRetryHookContext, type ApiKeyAuthStrategy, type AuthStrategy, BadGatewayError, BadRequestError, type BaseHookContext, type BasicAuthStrategy, type BearerAuthStrategy, type BeforeRequestHookContext, type BeforeRetryHookContext, ClientError, ConflictError, type CustomAuthStrategy, FetchClient, type FetchClientConfig, FetchError, ForbiddenError, GatewayTimeoutError, type Hook, type HookContext, HookRegistry, type HookStage, type HooksConfig, InternalServerError, MethodNotAllowedError, NotFoundError, type OnErrorHookContext, type OnStreamChunkHookContext, type OnStreamEndHookContext, type OnStreamStartHookContext, type OnTimeoutHookContext, type QueryParamValue, type QueryParams, type RequestOptions, type RetryConfig, type RetryStrategyFunction, type SerializableBody, ServerError, ServiceUnavailableError, type StreamingFormat, type StreamingRequestOptions, TooManyRequestsError, UnauthorizedError, UnprocessableEntityError, buildUrl, calculateRetryDelay, createFetchError, encodeBase64, exponentialStrategy, getContentType, getFetchErrorMessage, getRetryStrategy, isAbortControllerAvailable, isBrowser, isFetchAvailable, isNode, isSuccessResponse, linearStrategy, parseChunkedStream, parseNDJSONStream, parseResponse, parseSSEStream, serializeBody, serializeQueryParams };

@@ -242,3 +242,3 @@ var __defProp = Object.defineProperty;

}
this.hooks.get(stage).push(hook);
this.hooks.get(stage)?.push(hook);
}

@@ -289,3 +289,10 @@ /**

has(stage) {
return this.hooks.has(stage) && this.hooks.get(stage).length > 0;
if (!this.hooks.has(stage)) {
return false;
}
const hooks = this.hooks.get(stage);
if (!hooks) {
return false;
}
return hooks.length > 0;
}

@@ -409,3 +416,3 @@ };

yield parsed;
} catch (error) {
} catch {
console.warn("Skipping invalid JSON line:", trimmed);

@@ -419,3 +426,3 @@ }

yield parsed;
} catch (error) {
} catch {
console.warn("Skipping invalid JSON in buffer:", buffer.trim());

@@ -667,4 +674,5 @@ }

*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async request(init) {
let url = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const url = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const headers = new Headers(this.cfg.headers || {});

@@ -706,3 +714,3 @@ if (init.headers) {

try {
let attemptUrl = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const attemptUrl = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const attemptHeaders = new Headers(headers);

@@ -716,2 +724,3 @@ await this.applyAuthentication(attemptHeaders, attemptUrl);

if (this.hookRegistry.has("beforeRetry")) {
const serializedBody = serializeBody(init.body, bodyContentType);
await this.hookRegistry.execute("beforeRetry", {

@@ -721,3 +730,4 @@ url: url.toString(),

...init,
headers
headers,
body: serializedBody
},

@@ -732,2 +742,3 @@ attempt,

if (this.hookRegistry.has("afterRetry")) {
const serializedBody = serializeBody(init.body, bodyContentType);
await this.hookRegistry.execute("afterRetry", {

@@ -737,3 +748,4 @@ url: url.toString(),

...init,
headers
headers,
body: serializedBody
},

@@ -757,3 +769,3 @@ attempt,

async *requestStream(init) {
let url = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const url = buildUrl(this.cfg.baseURL || "", init.path, init.query);
const headers = new Headers(this.cfg.headers || {});

@@ -832,3 +844,4 @@ if (init.headers) {

const parsed = await parseResponse(res);
const error = createFetchError(res.status, parsed?.message || `HTTP ${res.status}`, parsed, res.headers);
const message = parsed?.message;
const error = createFetchError(res.status, message || `HTTP ${res.status}`, parsed, res.headers);
if (this.hookRegistry.has("onError")) {

@@ -855,3 +868,3 @@ await this.hookRegistry.execute("onError", {

for await (const chunk of parseSSEStream(res)) {
let transformedChunk = chunk;
const transformedChunk = chunk;
if (this.hookRegistry.has("onStreamChunk")) {

@@ -869,3 +882,3 @@ await this.hookRegistry.execute("onStreamChunk", {

for await (const chunk of parseNDJSONStream(res)) {
let transformedChunk = chunk;
const transformedChunk = chunk;
if (this.hookRegistry.has("onStreamChunk")) {

@@ -883,3 +896,3 @@ await this.hookRegistry.execute("onStreamChunk", {

for await (const chunk of parseChunkedStream(res)) {
let transformedChunk = chunk;
const transformedChunk = chunk;
if (this.hookRegistry.has("onStreamChunk")) {

@@ -989,3 +1002,4 @@ await this.hookRegistry.execute("onStreamChunk", {

if (!isSuccessResponse(res)) {
const error = createFetchError(res.status, parsed?.message || `HTTP ${res.status}`, parsed, res.headers);
const message = parsed?.message;
const error = createFetchError(res.status, message || `HTTP ${res.status}`, parsed, res.headers);
if (this.hookRegistry.has("onError")) {

@@ -992,0 +1006,0 @@ await this.hookRegistry.execute("onError", {

{
"name": "@blimu/fetch",
"version": "0.3.0",
"version": "0.4.0",
"description": "Universal HTTP fetch client with hooks, retries, and streaming support for browser and Node.js",
"repository": "https://github.com/blimu-dev/packages",
"author": "viniciusdacal",
"license": "MIT",
"keywords": [
"fetch",
"http",
"client",
"retry",
"streaming",
"hooks",
"browser",
"nodejs"
],
"publishConfig": {
"access": "public"
},
"type": "module",

@@ -18,4 +34,5 @@ "main": "dist/index.cjs",

"files": [
"dist"
"dist/**/*"
],
"sideEffects": false,
"scripts": {

@@ -29,17 +46,2 @@ "build": "tsup",

},
"keywords": [
"fetch",
"http",
"client",
"retry",
"streaming",
"hooks",
"browser",
"nodejs"
],
"author": "",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"engines": {

@@ -49,3 +51,3 @@ "node": ">=22.0.0"

"devDependencies": {
"@types/node": "^25.0.8",
"@types/node": "^25.0.9",
"@vitest/browser": "^4.0.17",

@@ -52,0 +54,0 @@ "@vitest/coverage-v8": "^4.0.17",

@@ -452,3 +452,3 @@ # @blimu/fetch

method: string;
query?: Record<string, any>;
query?: Record<string, any> | undefined;
}

@@ -455,0 +455,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display