UNPKG

zodsei

Version:

Contract-first type-safe HTTP client with Zod validation

1,172 lines (1,156 loc) 34.1 kB
var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/errors.ts var ZodseiError, ValidationError, HttpError, NetworkError, ConfigError, TimeoutError; var init_errors = __esm({ "src/errors.ts"() { "use strict"; ZodseiError = class extends Error { constructor(message, code) { super(message); this.code = code; this.name = "ZodseiError"; } }; ValidationError = class _ValidationError extends ZodseiError { constructor(message, issues, type = "request") { super(message, "VALIDATION_ERROR"); this.issues = issues; this.type = type; this.name = "ValidationError"; } static fromZodError(error, type = "request") { const message = `${type} validation failed: ${error.issues.map( (issue) => `${issue.path.join(".")}: ${issue.message}` ).join(", ")}`; return new _ValidationError(message, error.issues, type); } }; HttpError = class _HttpError extends ZodseiError { constructor(message, status, statusText, response) { super(message, "HTTP_ERROR"); this.status = status; this.statusText = statusText; this.response = response; this.name = "HttpError"; } static fromResponse(response, data) { const message = `HTTP ${response.status}: ${response.statusText}`; return new _HttpError(message, response.status, response.statusText, data); } }; NetworkError = class extends ZodseiError { constructor(message, originalError) { super(message, "NETWORK_ERROR"); this.originalError = originalError; this.name = "NetworkError"; } }; ConfigError = class extends ZodseiError { constructor(message) { super(message, "CONFIG_ERROR"); this.name = "ConfigError"; } }; TimeoutError = class extends ZodseiError { constructor(timeout) { super(`Request timeout after ${timeout}ms`, "TIMEOUT_ERROR"); this.name = "TimeoutError"; } }; } }); // src/adapters/fetch.ts var fetch_exports = {}; __export(fetch_exports, { FetchAdapter: () => FetchAdapter }); var FetchAdapter; var init_fetch = __esm({ "src/adapters/fetch.ts"() { "use strict"; init_errors(); FetchAdapter = class { constructor(config = {}) { this.name = "fetch"; this.config = { timeout: 3e4, ...config }; } async request(context) { try { const init = this.createRequestInit(context); const response = await fetch(context.url, init); const data = await this.parseResponseData(response); const responseHeaders = {}; response.headers.forEach((value, key) => { responseHeaders[key] = value; }); const responseContext = { status: response.status, statusText: response.statusText, headers: responseHeaders, data }; if (!response.ok) { throw HttpError.fromResponse(response, data); } return responseContext; } catch (error) { if (error instanceof HttpError) { throw error; } if (error instanceof Error) { if (error.name === "AbortError") { throw new TimeoutError(this.config.timeout || 0); } throw new NetworkError(`Fetch request failed: ${error.message}`, error); } throw new NetworkError("Unknown fetch error", error); } } createRequestInit(context) { const init = { method: context.method.toUpperCase(), headers: { "Content-Type": "application/json", ...context.headers }, credentials: this.config.credentials, mode: this.config.mode, cache: this.config.cache, redirect: this.config.redirect, referrer: this.config.referrer, referrerPolicy: this.config.referrerPolicy, integrity: this.config.integrity }; if (context.body !== void 0 && !["GET", "HEAD"].includes(context.method.toUpperCase())) { if (typeof context.body === "object") { init.body = JSON.stringify(context.body); } else { init.body = context.body; } } if (this.config.timeout && this.config.timeout > 0) { const controller = new AbortController(); setTimeout(() => controller.abort(), this.config.timeout); init.signal = controller.signal; } return init; } async parseResponseData(response) { const contentType = response.headers.get("content-type"); if (contentType?.includes("application/json")) { return response.json(); } else if (contentType?.includes("text/")) { return response.text(); } else if (contentType?.includes("application/octet-stream") || contentType?.includes("application/pdf")) { return response.blob(); } else { try { return await response.json(); } catch { return response.text(); } } } }; } }); // src/adapters/axios.ts var axios_exports = {}; __export(axios_exports, { AxiosAdapter: () => AxiosAdapter }); var AxiosAdapter; var init_axios = __esm({ "src/adapters/axios.ts"() { "use strict"; init_errors(); AxiosAdapter = class { constructor(config = {}) { this.name = "axios"; this.config = { timeout: 3e4, validateStatus: () => true, // We handle status validation ourselves ...config }; } async getAxios() { if (!this.axios) { try { const axiosModule = await import("axios"); this.axios = axiosModule.default || axiosModule; } catch (_error) { throw new Error("axios is not installed. Please install it with: npm install axios"); } } return this.axios; } // Interceptors are not supported. Use middleware in the client instead. async request(context) { try { const axios = await this.getAxios(); const axiosConfig = this.createAxiosConfig(context); const response = await axios.request(axiosConfig); const responseContext = { status: response.status, statusText: response.statusText, headers: response.headers || {}, data: response.data }; if (response.status >= 400) { throw new HttpError( `HTTP ${response.status}: ${response.statusText}`, response.status, response.statusText, response.data ); } return responseContext; } catch (error) { if (error instanceof HttpError) { throw error; } if (error.isAxiosError) { if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") { throw new TimeoutError(this.config.timeout || 0); } if (error.response) { throw new HttpError( `HTTP ${error.response.status}: ${error.response.statusText}`, error.response.status, error.response.statusText, error.response.data ); } else if (error.request) { throw new NetworkError(`Network request failed: ${error.message}`, error); } } throw new NetworkError(`Axios request failed: ${error.message}`, error); } } createAxiosConfig(context) { const config = { url: context.url, method: context.method.toLowerCase(), headers: { "Content-Type": "application/json", ...context.headers }, timeout: this.config.timeout, maxRedirects: this.config.maxRedirects, validateStatus: this.config.validateStatus, maxContentLength: this.config.maxContentLength, maxBodyLength: this.config.maxBodyLength, withCredentials: this.config.withCredentials, auth: this.config.auth, proxy: this.config.proxy }; if (context.body !== void 0 && !["GET", "HEAD"].includes(context.method.toUpperCase())) { config.data = context.body; } if (context.query && Object.keys(context.query).length > 0) { config.params = context.query; } return config; } }; } }); // src/adapters/ky.ts var ky_exports = {}; __export(ky_exports, { KyAdapter: () => KyAdapter }); var KyAdapter; var init_ky = __esm({ "src/adapters/ky.ts"() { "use strict"; init_errors(); KyAdapter = class { constructor(config = {}) { this.name = "ky"; this.config = { timeout: 3e4, throwHttpErrors: false, // We handle errors ourselves ...config }; } async getKy() { if (!this.ky) { try { const kyModule = await import("ky"); this.ky = kyModule.default || kyModule; } catch (_error) { throw new Error("ky is not installed. Please install it with: npm install ky"); } } return this.ky; } async request(context) { try { const ky = await this.getKy(); const kyOptions = this.createKyOptions(context); const response = await ky(context.url, kyOptions); const data = await this.parseResponseData(response); const responseHeaders = {}; response.headers.forEach((value, key) => { responseHeaders[key] = value; }); const responseContext = { status: response.status, statusText: response.statusText, headers: responseHeaders, data }; if (!response.ok) { throw new HttpError( `HTTP ${response.status}: ${response.statusText}`, response.status, response.statusText, data ); } return responseContext; } catch (error) { if (error instanceof HttpError) { throw error; } if (error.name === "HTTPError") { const response = error.response; const data = await this.parseResponseData(response).catch(() => null); throw new HttpError( `HTTP ${response.status}: ${response.statusText}`, response.status, response.statusText, data ); } if (error.name === "TimeoutError") { throw new TimeoutError(this.config.timeout || 0); } throw new NetworkError(`Ky request failed: ${error.message}`, error); } } createKyOptions(context) { const options = { method: context.method.toLowerCase(), headers: { "Content-Type": "application/json", ...context.headers }, timeout: this.config.timeout, retry: this.config.retry, throwHttpErrors: this.config.throwHttpErrors, credentials: this.config.credentials, mode: this.config.mode, cache: this.config.cache, redirect: this.config.redirect, referrer: this.config.referrer, referrerPolicy: this.config.referrerPolicy, integrity: this.config.integrity }; if (context.body !== void 0 && !["GET", "HEAD"].includes(context.method.toUpperCase())) { if (typeof context.body === "object") { options.json = context.body; } else { options.body = context.body; } } if (context.query && Object.keys(context.query).length > 0) { options.searchParams = context.query; } return options; } async parseResponseData(response) { const contentType = response.headers.get("content-type"); if (contentType?.includes("application/json")) { return response.json(); } else if (contentType?.includes("text/")) { return response.text(); } else if (contentType?.includes("application/octet-stream") || contentType?.includes("application/pdf")) { return response.blob(); } else { try { return await response.json(); } catch { return response.text(); } } } }; } }); // src/validation.ts init_errors(); import { z } from "zod"; function validateRequest(schema, data) { if (!schema) { return data; } try { return schema.parse(data); } catch (error) { if (error instanceof z.ZodError) { throw ValidationError.fromZodError(error, "request"); } throw error; } } function validateResponse(schema, data) { if (!schema) { return data; } try { return schema.parse(data); } catch (error) { if (error instanceof z.ZodError) { throw ValidationError.fromZodError(error, "response"); } throw error; } } function safeParseRequest(schema, data) { if (!schema) { return { success: true, data }; } try { const result = schema.parse(data); return { success: true, data: result }; } catch (error) { if (error instanceof z.ZodError) { return { success: false, error: ValidationError.fromZodError(error, "request") }; } return { success: false, error: new ValidationError("Unknown validation error", [], "request") }; } } function safeParseResponse(schema, data) { if (!schema) { return { success: true, data }; } try { const result = schema.parse(data); return { success: true, data: result }; } catch (error) { if (error instanceof z.ZodError) { return { success: false, error: ValidationError.fromZodError(error, "response") }; } return { success: false, error: new ValidationError("Unknown validation error", [], "response") }; } } function createValidator(schema, enabled) { return { validateRequest: enabled ? (data) => validateRequest(schema, data) : (data) => data, validateResponse: enabled ? (data) => validateResponse(schema, data) : (data) => data, safeParseRequest: (data) => safeParseRequest(schema, data), safeParseResponse: (data) => safeParseResponse(schema, data) }; } // src/utils/path.ts function extractPathParamNames(path) { const matches = path.match(/:([^/]+)/g); return matches ? matches.map((match) => match.slice(1)) : []; } function replacePath(path, params) { let result = path; for (const [key, value] of Object.entries(params)) { result = result.replace(`:${key}`, encodeURIComponent(value)); } return result; } function buildQueryString(params) { const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value !== void 0 && value !== null) { if (Array.isArray(value)) { value.forEach((item) => searchParams.append(key, String(item))); } else { searchParams.append(key, String(value)); } } } const queryString = searchParams.toString(); return queryString ? `?${queryString}` : ""; } function buildUrl(baseUrl, path, query) { const cleanBaseUrl = baseUrl.replace(/\/$/, ""); const cleanPath = path.startsWith("/") ? path : `/${path}`; const queryString = query ? buildQueryString(query) : ""; return `${cleanBaseUrl}${cleanPath}${queryString}`; } function separateParams(path, data) { const pathParamNames = extractPathParamNames(path); const pathParams = {}; const queryParams = {}; if (!data) { return { pathParams, queryParams }; } for (const [key, value] of Object.entries(data)) { if (pathParamNames.includes(key)) { pathParams[key] = String(value); } else { queryParams[key] = value; } } return { pathParams, queryParams }; } function shouldHaveBody(method) { return !["GET", "HEAD", "DELETE"].includes(method.toUpperCase()); } // src/middleware/index.ts var MiddlewareExecutor = class { constructor(middleware = []) { this.middleware = middleware; } // Execute middleware chain async execute(request, finalHandler) { if (this.middleware.length === 0) { return finalHandler(request); } let index = 0; const next = async (req) => { if (index >= this.middleware.length) { return finalHandler(req); } const middleware = this.middleware[index++]; return middleware(req, next); }; return next(request); } // Add middleware use(middleware) { this.middleware.push(middleware); } // Get middleware list getMiddleware() { return [...this.middleware]; } }; function createMiddlewareExecutor(middleware = []) { return new MiddlewareExecutor(middleware); } function composeMiddleware(...middleware) { return async (request, next) => { const executor = new MiddlewareExecutor(middleware); return executor.execute(request, next); }; } // src/adapters/index.ts async function createAdapter(type, config) { switch (type) { case "fetch": { const { FetchAdapter: FetchAdapter2 } = await Promise.resolve().then(() => (init_fetch(), fetch_exports)); return new FetchAdapter2(config); } case "axios": { const { AxiosAdapter: AxiosAdapter2 } = await Promise.resolve().then(() => (init_axios(), axios_exports)); return new AxiosAdapter2(config); } case "ky": { const { KyAdapter: KyAdapter2 } = await Promise.resolve().then(() => (init_ky(), ky_exports)); return new KyAdapter2(config); } default: throw new Error(`Unsupported adapter type: ${type}`); } } async function isAdapterAvailable(type) { try { switch (type) { case "fetch": return typeof fetch !== "undefined"; case "axios": await import("axios"); return true; case "ky": await import("ky"); return true; default: return false; } } catch { return false; } } async function getDefaultAdapter(config) { if (await isAdapterAvailable("fetch")) { return createAdapter("fetch", config); } if (await isAdapterAvailable("axios")) { return createAdapter("axios", config); } if (await isAdapterAvailable("ky")) { return createAdapter("ky", config); } throw new Error( "No HTTP adapter available. Please install axios or ky, or ensure fetch is available." ); } // src/schema.ts import { z as z2 } from "zod"; var SchemaExtractor = class _SchemaExtractor { constructor(contract) { this.contract = contract; } /** * Get endpoint definition by path */ getEndpoint(path) { const endpoint = this.contract[path]; if (this.isEndpointDefinition(endpoint)) { return endpoint; } throw new Error(`Endpoint "${String(path)}" not found or is not a valid endpoint`); } /** * Get nested contract by path */ getNested(path) { const nested = this.contract[path]; if (this.isNestedContract(nested)) { return new _SchemaExtractor(nested); } throw new Error(`Nested contract "${String(path)}" not found or is not a valid contract`); } /** * Get request schema for an endpoint */ getRequestSchema(path) { const endpoint = this.getEndpoint(path); return endpoint.request; } /** * Get response schema for an endpoint */ getResponseSchema(path) { const endpoint = this.getEndpoint(path); return endpoint.response; } /** * Get all schemas for an endpoint */ getEndpointSchemas(path) { const endpoint = this.getEndpoint(path); const result = { request: endpoint.request, response: endpoint.response, endpoint }; return result; } /** * Get all endpoint paths in the contract */ getEndpointPaths() { return Object.keys(this.contract).filter( (key) => this.isEndpointDefinition(this.contract[key]) ); } /** * Get all nested contract paths */ getNestedPaths() { return Object.keys(this.contract).filter( (key) => this.isNestedContract(this.contract[key]) ); } /** * Generate OpenAPI-like schema description */ describeEndpoint(path) { const endpoint = this.getEndpoint(path); const result = { path: endpoint.path, method: endpoint.method, requestSchema: endpoint.request, responseSchema: endpoint.response, requestType: endpoint.request ? this.getSchemaDescription(endpoint.request) : "void", responseType: endpoint.response ? this.getSchemaDescription(endpoint.response) : "unknown" }; return result; } /** * Generate schema description for documentation */ getSchemaDescription(schema) { if (!schema) { return "undefined"; } try { if (schema instanceof z2.ZodObject) { const shape = schema.shape; const fields = Object.keys(shape).map((key) => { const field = shape[key]; return `${key}: ${this.getZodTypeDescription(field)}`; }); return `{ ${fields.join(", ")} }`; } return this.getZodTypeDescription(schema); } catch { return "unknown"; } } /** * Get basic Zod type description */ getZodTypeDescription(schema) { const def = schema._def; if (def?.typeName) { switch (def.typeName) { case "ZodString": return "string"; case "ZodNumber": return "number"; case "ZodBoolean": return "boolean"; case "ZodArray": return def.type ? `${this.getZodTypeDescription(def.type)}[]` : "array"; case "ZodOptional": return def.innerType ? `${this.getZodTypeDescription(def.innerType)}?` : "optional"; case "ZodNullable": return def.innerType ? `${this.getZodTypeDescription(def.innerType)} | null` : "nullable"; case "ZodObject": return "object"; case "ZodUnion": return "union"; case "ZodLiteral": return `literal(${JSON.stringify(def.value)})`; case "ZodEnum": return "enum"; default: return def.typeName.replace("Zod", "").toLowerCase(); } } try { if (schema instanceof z2.ZodString) return "string"; if (schema instanceof z2.ZodNumber) return "number"; if (schema instanceof z2.ZodBoolean) return "boolean"; if (schema instanceof z2.ZodArray) { const element = schema.element; return element ? `${this.getZodTypeDescription(element)}[]` : "array"; } if (schema instanceof z2.ZodOptional) { const inner = schema.unwrap(); return inner ? `${this.getZodTypeDescription(inner)}?` : "optional"; } if (schema instanceof z2.ZodNullable) { const inner = schema.unwrap(); return inner ? `${this.getZodTypeDescription(inner)} | null` : "nullable"; } if (schema instanceof z2.ZodObject) return "object"; } catch { } return "unknown"; } /** * Check if a value is an endpoint definition */ isEndpointDefinition(value) { return Boolean(value) && typeof value === "object" && value !== null && "path" in value && "method" in value; } /** * Check if a value is a nested contract */ isNestedContract(value) { return Boolean(value) && typeof value === "object" && value !== null && !this.isEndpointDefinition(value); } }; function createSchemaExtractor(contract) { return new SchemaExtractor(contract); } function extractTypeInfo(endpoint) { return { requestSchema: endpoint.request, responseSchema: endpoint.response, method: endpoint.method, path: endpoint.path, hasRequestSchema: Boolean(endpoint.request), hasResponseSchema: Boolean(endpoint.response) }; } // src/client.ts var ZodseiClient = class { constructor(contract, config) { this.adapter = null; this.contract = contract; this.config = this.normalizeConfig(config); this.middlewareExecutor = createMiddlewareExecutor(this.config.middleware); this.$schema = createSchemaExtractor(contract); return new Proxy(this, { get: (target, prop) => { if (typeof prop === "string") { if (prop in this.contract && this.isEndpointDefinition(this.contract[prop])) { return this.createEndpointMethod(prop); } if (prop in this.contract && this.isNestedContract(this.contract[prop])) { return this.createNestedClient(this.contract[prop]); } } return target[prop]; } }); } /** * Normalize configuration */ normalizeConfig(config) { return { baseUrl: config.baseUrl.replace(/\/$/, ""), validateRequest: config.validateRequest ?? true, validateResponse: config.validateResponse ?? true, headers: config.headers ?? {}, timeout: config.timeout ?? 3e4, retries: config.retries ?? 0, middleware: config.middleware ?? [], adapter: config.adapter ?? "fetch", adapterConfig: config.adapterConfig ?? {} }; } /** * Check if a value is an endpoint definition */ isEndpointDefinition(value) { return value && typeof value === "object" && "path" in value && "method" in value; } /** * Check if a value is a nested contract */ isNestedContract(value) { return value && typeof value === "object" && !this.isEndpointDefinition(value); } /** * Create nested client for sub-contracts */ createNestedClient(nestedContract) { return new Proxy( {}, { get: (_target, prop) => { if (typeof prop === "string") { if (prop in nestedContract && this.isEndpointDefinition(nestedContract[prop])) { return this.createEndpointMethod( `${prop}`, nestedContract[prop] ); } if (prop in nestedContract && this.isNestedContract(nestedContract[prop])) { return this.createNestedClient(nestedContract[prop]); } } return void 0; } } ); } /** * Create endpoint method with schema access */ createEndpointMethod(endpointName, endpoint) { const targetEndpoint = endpoint || this.contract[endpointName]; const method = async (...args) => { const data = targetEndpoint.request ? args[0] : void 0; return this.executeEndpoint(targetEndpoint, data); }; method.schema = { request: targetEndpoint.request, response: targetEndpoint.response, endpoint: targetEndpoint }; method.infer = { request: targetEndpoint.request ? {} : void 0, response: targetEndpoint.response ? {} : {} }; return method; } /** * Execute endpoint request */ async executeEndpoint(endpoint, data) { const validatedData = this.config.validateRequest ? validateRequest(endpoint.request, data) : data; const requestContext = this.buildRequestContext(endpoint, validatedData); const response = await this.middlewareExecutor.execute( requestContext, (ctx) => this.executeHttpRequest(ctx) ); const validatedResponse = this.config.validateResponse ? validateResponse(endpoint.response, response.data) : response.data; return validatedResponse; } /** * Build request context */ buildRequestContext(endpoint, data) { const { path, method } = endpoint; const { pathParams, queryParams } = separateParams(path, data); const finalPath = replacePath(path, pathParams); const url = method.toLowerCase() === "get" ? buildUrl(this.config.baseUrl, finalPath, queryParams) : buildUrl(this.config.baseUrl, finalPath); const body = shouldHaveBody(method) ? method.toLowerCase() === "get" ? void 0 : data : void 0; return { url, method, headers: { ...this.config.headers }, body, params: pathParams, query: method.toLowerCase() === "get" ? queryParams : void 0 }; } /** * Get adapter */ async getAdapter() { if (!this.adapter) { const adapterConfig = { timeout: this.config.timeout, ...this.config.adapterConfig }; if (typeof this.config.adapter === "string") { this.adapter = await createAdapter(this.config.adapter, adapterConfig); } else { this.adapter = await createAdapter("fetch", adapterConfig); } } return this.adapter; } /** * Execute HTTP request */ async executeHttpRequest(context) { const adapter = await this.getAdapter(); return adapter.request(context); } /** * Get configuration */ getConfig() { return { ...this.config }; } /** * Get contract */ getContract() { return { ...this.contract }; } /** * Add middleware */ use(middleware) { this.middlewareExecutor.use(middleware); } }; function createClient(contract, config) { return new ZodseiClient(contract, config); } // src/types.ts function defineContract(contract) { return contract; } // src/index.ts init_errors(); // src/middleware/retry.ts init_errors(); function defaultRetryCondition(error) { if (error instanceof HttpError) { return error.status >= 500 || error.status === 408 || error.status === 429; } return true; } function calculateDelay(attempt, baseDelay, backoff) { switch (backoff) { case "exponential": return baseDelay * Math.pow(2, attempt); case "linear": default: return baseDelay * (attempt + 1); } } function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } function retryMiddleware(config) { const { retries, delay: baseDelay, backoff = "exponential", retryCondition = defaultRetryCondition, onRetry } = config; return async (request, next) => { let lastError; for (let attempt = 0; attempt <= retries; attempt++) { try { return await next(request); } catch (error) { lastError = error; if (attempt === retries) { throw lastError; } if (!retryCondition(lastError)) { throw lastError; } if (onRetry) { onRetry(attempt + 1, lastError); } const delayTime = calculateDelay(attempt, baseDelay, backoff); await delay(delayTime); } } throw lastError; }; } function simpleRetry(retries, delay2 = 1e3) { return retryMiddleware({ retries, delay: delay2, backoff: "exponential" }); } // src/middleware/cache.ts var MemoryCacheStorage = class { constructor() { this.cache = /* @__PURE__ */ new Map(); } async get(key) { const entry = this.cache.get(key); if (!entry) { return null; } if (Date.now() - entry.timestamp > entry.ttl) { this.cache.delete(key); return null; } return entry; } async set(key, entry) { this.cache.set(key, entry); } async delete(key) { this.cache.delete(key); } async clear() { this.cache.clear(); } // Get cache size size() { return this.cache.size; } // Clean expired cache cleanup() { const now = Date.now(); for (const [key, entry] of this.cache.entries()) { if (now - entry.timestamp > entry.ttl) { this.cache.delete(key); } } } }; function defaultKeyGenerator(request) { const { url, method, body, query } = request; const parts = [method.toUpperCase(), url]; if (query && Object.keys(query).length > 0) { parts.push(JSON.stringify(query)); } if (body) { parts.push(JSON.stringify(body)); } return parts.join("|"); } function defaultShouldCache(request, response) { return request.method.toLowerCase() === "get" && response.status >= 200 && response.status < 300; } function cacheMiddleware(config) { const { ttl, keyGenerator = defaultKeyGenerator, shouldCache = defaultShouldCache, storage = new MemoryCacheStorage() } = config; return async (request, next) => { const cacheKey = keyGenerator(request); const cachedEntry = await storage.get(cacheKey); if (cachedEntry) { return cachedEntry.data; } const response = await next(request); if (shouldCache(request, response)) { const entry = { data: response, timestamp: Date.now(), ttl }; await storage.set(cacheKey, entry); } return response; }; } function simpleCache(ttl) { return cacheMiddleware({ ttl }); } // src/utils/request.ts function mergeHeaders(defaultHeaders, requestHeaders) { return { ...defaultHeaders, ...requestHeaders }; } // src/index.ts init_fetch(); init_axios(); init_ky(); import { z as z3 } from "zod"; export { AxiosAdapter, ConfigError, FetchAdapter, HttpError, KyAdapter, MemoryCacheStorage, NetworkError, SchemaExtractor, TimeoutError, ValidationError, ZodseiClient, ZodseiError, buildQueryString, buildUrl, cacheMiddleware, composeMiddleware, createAdapter, createClient, createMiddlewareExecutor, createSchemaExtractor, createValidator, defineContract, extractPathParamNames, extractTypeInfo, getDefaultAdapter, isAdapterAvailable, mergeHeaders, replacePath, retryMiddleware, safeParseRequest, safeParseResponse, separateParams, shouldHaveBody, simpleCache, simpleRetry, validateRequest, validateResponse, z3 as z }; //# sourceMappingURL=index.mjs.map