prapti 0.0.2 → 0.0.3

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 CHANGED
@@ -4,11 +4,16 @@
4
4
 
5
5
  _"प्राप्ति" (Prapti) - Sanskrit for "fetch" or "obtain"_
6
6
 
7
- > A minimal, type-safe HTTP client that extends the native `fetch` API with runtime schema validation.
7
+ > A minimal, type-safe utility that extends the native `fetch` API with runtime schema validation.
8
+
9
+ ![NPM Version](https://img.shields.io/npm/v/prapti)
10
+ [![npm min + gzip size](https://badgen.net/bundlephobia/minzip/prapti)](https://bundlephobia.com/result?p=prapti)
11
+ [![License](https://img.shields.io/badge/License-MIT-blue)](#license)
12
+ [![issues - prapti](https://img.shields.io/github/issues/kiranojhanp/prapti)](https://github.com/kiranojhanp/prapti/issues)
8
13
 
9
14
  ```typescript
10
15
  // Without Prapti
11
- const response = await safeFetch("/api/users");
16
+ const response = await fetch("/api/users");
12
17
  const data = await response.json(); // any type
13
18
  const validatedData = UserSchema.parse(data); // manual validation
14
19
 
@@ -73,6 +78,30 @@ const newUser = await safeFetch("/api/users", {
73
78
  requestSchema: CreateUserSchema,
74
79
  responseSchema: UserSchema,
75
80
  });
81
+
82
+ // With header validation
83
+ const RequestHeadersSchema = z.object({
84
+ authorization: z.string().startsWith("Bearer "),
85
+ "content-type": z.literal("application/json"),
86
+ });
87
+
88
+ const ResponseHeadersSchema = z.object({
89
+ "content-type": z.string().includes("json"),
90
+ "x-rate-limit-remaining": z.string().transform(Number).pipe(z.number()),
91
+ });
92
+
93
+ const response = await safeFetch("/api/users", {
94
+ headers: {
95
+ Authorization: "Bearer token123",
96
+ "Content-Type": "application/json",
97
+ },
98
+ requestHeadersSchema: RequestHeadersSchema,
99
+ responseHeadersSchema: ResponseHeadersSchema,
100
+ });
101
+
102
+ // Get typed and validated headers
103
+ const headers = response.getValidatedHeaders();
104
+ console.log(`Rate limit remaining: ${headers["x-rate-limit-remaining"]}`);
76
105
  ```
77
106
 
78
107
  ## API
@@ -91,10 +120,12 @@ Main client class. Pass a validation adapter for your schema library.
91
120
 
92
121
  ### `PraptiOptions`
93
122
 
94
- Standard fetch options plus:
123
+ Extended fetch options with validation schemas:
95
124
 
96
- - `requestSchema?` - Schema to validate request body
97
- - `responseSchema?` - Schema to validate response data
125
+ - `requestSchema` - Schema to validate request body
126
+ - `responseSchema` - Schema to validate response data
127
+ - `requestHeadersSchema` - Schema to validate request headers
128
+ - `responseHeadersSchema` - Schema to validate response headers
98
129
 
99
130
  ### `ValidatedResponse`
100
131
 
@@ -105,6 +136,7 @@ Enhanced Response with validation:
105
136
  - `blob()` - Get blob (no validation)
106
137
  - `arrayBuffer()` - Get buffer (no validation)
107
138
  - `formData()` - Parse and validate form data
139
+ - `getValidatedHeaders()` - Get validated headers as typed object
108
140
 
109
141
  ## Custom Adapters
110
142
 
@@ -136,15 +168,11 @@ try {
136
168
 
137
169
  - 🔄 **Built-in adapters for Valibot, Yup, Joi, AJV**
138
170
  - 🎨 **Custom adapter utilities and helpers**
139
- - 🛡️ **Enhanced error types with validation details**
140
- - 🎯 **Header validation with schemas**
141
- - ⚡ **Zero-config TypeScript integration**
142
- - 📦 **FormData and URLSearchParams validation**
143
171
  - 🔄 **Streaming response validation**
144
172
 
145
173
  ## License
146
174
 
147
- MIT
175
+ Released under [MIT](/LICENSE) by [@kiranojhanp](https://github.com/kiranojhanp).
148
176
 
149
177
  ---
150
178
 
package/dist/index.cjs.js CHANGED
@@ -1 +1 @@
1
- var{defineProperty:H,getOwnPropertyNames:Q,getOwnPropertyDescriptor:T}=Object,U=Object.prototype.hasOwnProperty;var K=new WeakMap,W=(g)=>{var j=K.get(g),k;if(j)return j;if(j=H({},"__esModule",{value:!0}),g&&typeof g==="object"||typeof g==="function")Q(g).map((w)=>!U.call(j,w)&&H(j,w,{get:()=>g[w],enumerable:!(k=T(g,w))||k.enumerable}));return K.set(g,j),j};var X=(g,j)=>{for(var k in j)H(g,k,{get:j[k],enumerable:!0,configurable:!0,set:(w)=>j[k]=()=>w})};var _={};X(_,{zodAdapter:()=>Z,createPrapti:()=>Y,ValidatedResponse:()=>D,Prapti:()=>I});module.exports=W(_);class D extends Response{adapter;responseSchema;constructor(g,j,k){super(g.body,g);this.adapter=j;this.responseSchema=k;Object.setPrototypeOf(this,D.prototype)}async json(){let g=await super.json();return this.responseSchema?this.adapter.parse(this.responseSchema,g):g}async text(){let g=await super.text();return this.responseSchema?this.adapter.parse(this.responseSchema,g):g}async blob(){return super.blob()}async arrayBuffer(){return super.arrayBuffer()}async formData(){let g=await super.formData();if(this.responseSchema)this.adapter.parse(this.responseSchema,Object.fromEntries(g.entries()));return g}}class I{adapter;constructor(g){this.adapter=g}async fetch(g,j){let{requestSchema:k,responseSchema:w,body:C,...J}=j||{},F,G=new Headers(J.headers);if(k&&C!==void 0&&C!==null){let M=typeof C==="string"?JSON.parse(C):C,N=this.adapter.parse(k,M);if(F=JSON.stringify(N),!G.has("Content-Type"))G.set("Content-Type","application/json")}else F=C;let L=await fetch(g,{...J,headers:G,body:F});return new D(L,this.adapter,w)}}function Y(g){return new I(g)}var Z={parse:(g,j)=>g.parse(j)};
1
+ var{defineProperty:Y,getOwnPropertyNames:x,getOwnPropertyDescriptor:q}=Object,F=Object.prototype.hasOwnProperty;var _=new WeakMap,R=(w)=>{var g=_.get(w),z;if(g)return g;if(g=Y({},"__esModule",{value:!0}),w&&typeof w==="object"||typeof w==="function")x(w).map((A)=>!F.call(g,A)&&Y(g,A,{get:()=>w[A],enumerable:!(z=q(w,A))||z.enumerable}));return _.set(w,g),g};var V=(w,g)=>{for(var z in g)Y(w,z,{get:g[z],enumerable:!0,configurable:!0,set:(A)=>g[z]=()=>A})};var B={};V(B,{createPrapti:()=>O,adapters:()=>S,ValidatedResponse:()=>W,Prapti:()=>Z});module.exports=R(B);class W extends Response{adapter;responseSchema;responseHeadersSchema;constructor(w,g,z,A){super(w.body,w);this.adapter=g;this.responseSchema=z;this.responseHeadersSchema=A;if(Object.setPrototypeOf(this,W.prototype),A)this.validateResponseHeaders()}validateResponseHeaders(){if(!this.responseHeadersSchema)return;let w={};this.headers.forEach((g,z)=>{w[z.toLowerCase()]=g}),this.adapter.parse(this.responseHeadersSchema,w)}getValidatedHeaders(){let w={};return this.headers.forEach((g,z)=>{w[z.toLowerCase()]=g}),this.responseHeadersSchema?this.adapter.parse(this.responseHeadersSchema,w):w}async json(){let w=await super.json();return this.responseSchema?this.adapter.parse(this.responseSchema,w):w}async text(){let w=await super.text();return this.responseSchema?this.adapter.parse(this.responseSchema,w):w}async blob(){return super.blob()}async arrayBuffer(){return super.arrayBuffer()}async formData(){let w=await super.formData();if(this.responseSchema){let g={};w.forEach((A,C)=>{if(g[C]!==void 0)if(Array.isArray(g[C]))g[C].push(A);else g[C]=[g[C],A];else g[C]=A});let z=this.adapter.parse(this.responseSchema,g);if(z&&typeof z==="object"){let A=new FormData;return Object.entries(z).forEach(([C,I])=>{if(Array.isArray(I))I.forEach((G)=>A.append(C,G));else A.append(C,I)}),A}}return w}async urlSearchParams(){let w=await super.text(),g=new URLSearchParams(w);if(this.responseSchema){let z={};g.forEach((C,I)=>{if(z[I]!==void 0)if(Array.isArray(z[I]))z[I].push(C);else z[I]=[z[I],C];else z[I]=C});let A=this.adapter.parse(this.responseSchema,z);if(A&&typeof A==="object"){let C=new URLSearchParams;return Object.entries(A).forEach(([I,G])=>{if(Array.isArray(G))G.forEach((U)=>C.append(I,String(U)));else C.append(I,String(G))}),C}}return g}}class Z{adapter;constructor(w){this.adapter=w}headersToObject(w){if(!w)return{};if(w instanceof Headers){let z={};return w.forEach((A,C)=>{z[C.toLowerCase()]=A}),z}if(Array.isArray(w)){let z={};return w.forEach(([A,C])=>{z[A.toLowerCase()]=String(C)}),z}let g={};return Object.entries(w).forEach(([z,A])=>{g[z.toLowerCase()]=String(A)}),g}formDataToObject(w){let g={};return w.forEach((z,A)=>{if(g[A]!==void 0)if(Array.isArray(g[A]))g[A].push(z);else g[A]=[g[A],z];else g[A]=z}),g}urlSearchParamsToObject(w){let g={};return w.forEach((z,A)=>{if(g[A]!==void 0)if(Array.isArray(g[A]))g[A].push(z);else g[A]=[g[A],z];else g[A]=z}),g}async fetch(w,g){let{requestSchema:z,responseSchema:A,requestHeadersSchema:C,responseHeadersSchema:I,body:G,headers:U,...$}=g||{},Q,T=new Headers;if(U){let K=this.headersToObject(U);if(C){let L=this.adapter.parse(C,K);if(L&&typeof L==="object")Object.entries(L).forEach(([J,M])=>{T.set(J,String(M))})}else Object.entries(K).forEach(([L,J])=>{T.set(L,J)})}if(z&&G!==void 0&&G!==null){let K;if(typeof G==="string")try{K=JSON.parse(G)}catch{K=G}else if(G instanceof FormData)K=this.formDataToObject(G);else if(G instanceof URLSearchParams)K=this.urlSearchParamsToObject(G);else K=G;let L=this.adapter.parse(z,K);if(G instanceof FormData){let J=new FormData;Object.entries(L).forEach(([M,N])=>{if(Array.isArray(N))N.forEach((X)=>J.append(M,X));else J.append(M,N)}),Q=J}else if(G instanceof URLSearchParams){let J=new URLSearchParams;Object.entries(L).forEach(([M,N])=>{if(Array.isArray(N))N.forEach((X)=>J.append(M,String(X)));else J.append(M,String(N))}),Q=J}else if(Q=JSON.stringify(L),!T.has("Content-Type"))T.set("Content-Type","application/json")}else Q=G;let E=await fetch(w,{...$,headers:T,body:Q});return new W(E,this.adapter,A,I)}}function O(w){return new Z(w)}var S={zod:{parse:(w,g)=>w.parse(g)}};
package/dist/index.d.ts CHANGED
@@ -20,6 +20,7 @@ export interface ValidationAdapter<TSchema = unknown> {
20
20
  }
21
21
  /**
22
22
  * Type helper to infer output type from various schema formats
23
+ * Supports multiple validation library conventions
23
24
  */
24
25
  export type InferOutput<T> = T extends {
25
26
  _output: infer U;
@@ -30,22 +31,43 @@ export type InferOutput<T> = T extends {
30
31
  * Extended fetch options with optional validation schemas
31
32
  * Maintains compatibility with native RequestInit while adding validation
32
33
  */
33
- export interface PraptiOptions<TRequestSchema = unknown, TResponseSchema = unknown> extends Omit<RequestInit, "body"> {
34
+ export interface PraptiOptions<TRequestSchema = unknown, TResponseSchema = unknown, TRequestHeadersSchema = unknown, TResponseHeadersSchema = unknown> extends Omit<RequestInit, "body" | "headers"> {
34
35
  /** Request body - can be any serializable data when using requestSchema */
35
36
  body?: BodyInit | null | unknown;
37
+ /** Request headers - can be HeadersInit or plain object when using requestHeadersSchema */
38
+ headers?: HeadersInit | Record<string, unknown>;
36
39
  /** Schema to validate request body against */
37
40
  requestSchema?: TRequestSchema;
38
41
  /** Schema to validate response data against */
39
42
  responseSchema?: TResponseSchema;
43
+ /** Schema to validate request headers against */
44
+ requestHeadersSchema?: TRequestHeadersSchema;
45
+ /** Schema to validate response headers against */
46
+ responseHeadersSchema?: TResponseHeadersSchema;
40
47
  }
41
48
  /**
42
49
  * Enhanced Response class with validation-aware methods
43
50
  * Extends native Response to provide type-safe data parsing
44
51
  */
45
- export declare class ValidatedResponse<T = unknown> extends Response {
52
+ export declare class ValidatedResponse<T = unknown, THeadersSchema = any> extends Response {
46
53
  private adapter;
47
54
  private responseSchema?;
48
- constructor(response: Response, adapter: ValidationAdapter<any>, responseSchema?: any | undefined);
55
+ private responseHeadersSchema?;
56
+ constructor(response: Response, adapter: ValidationAdapter<any>, responseSchema?: any | undefined, responseHeadersSchema?: THeadersSchema | undefined);
57
+ /**
58
+ * Validate response headers against schema
59
+ * @private
60
+ */
61
+ private validateResponseHeaders;
62
+ /**
63
+ * Get validated headers as typed object
64
+ * @returns Validated headers object if schema provided, otherwise plain object
65
+ */
66
+ getValidatedHeaders<T = THeadersSchema extends {
67
+ _output: infer U;
68
+ } ? U : THeadersSchema extends {
69
+ _type: infer U;
70
+ } ? U : Record<string, string>>(): T;
49
71
  /**
50
72
  * Parse JSON response and validate with schema if provided
51
73
  * @returns Promise resolving to validated JSON data
@@ -68,8 +90,15 @@ export declare class ValidatedResponse<T = unknown> extends Response {
68
90
  arrayBuffer(): Promise<ArrayBuffer>;
69
91
  /**
70
92
  * Parse form data response and validate with schema if provided
93
+ * Note: Validation converts FormData to plain object for schema checking
94
+ * @returns Promise resolving to FormData
71
95
  */
72
96
  formData(): Promise<FormData>;
97
+ /**
98
+ * Parse URLSearchParams response and validate with schema if provided
99
+ * @returns Promise resolving to URLSearchParams
100
+ */
101
+ urlSearchParams(): Promise<URLSearchParams>;
73
102
  }
74
103
  /**
75
104
  * Type-safe HTTP client with validation capabilities
@@ -82,13 +111,28 @@ export declare class Prapti<TSchema = unknown> {
82
111
  * @param adapter - Validation adapter for chosen schema library
83
112
  */
84
113
  constructor(adapter: ValidationAdapter<TSchema>);
114
+ /**
115
+ * Convert Headers object or HeadersInit to plain object
116
+ * @private
117
+ */
118
+ private headersToObject;
119
+ /**
120
+ * Convert FormData object to plain object for validation
121
+ * @private
122
+ */
123
+ private formDataToObject;
124
+ /**
125
+ * Convert URLSearchParams to plain object for validation
126
+ * @private
127
+ */
128
+ private urlSearchParamsToObject;
85
129
  /**
86
130
  * Make an HTTP request with optional request/response validation
87
131
  * @param input - Request URL or Request object
88
132
  * @param options - Extended fetch options with validation schemas
89
133
  * @returns Promise resolving to ValidatedResponse with inferred types
90
134
  */
91
- fetch<TResponseSchema extends TSchema = never>(input: RequestInfo | URL, options?: PraptiOptions<TSchema, TResponseSchema>): Promise<ValidatedResponse<TResponseSchema extends never ? unknown : InferOutput<TResponseSchema>>>;
135
+ fetch<TResponseSchema extends TSchema = never, TResponseHeadersSchema extends TSchema = never>(input: RequestInfo | URL, options?: PraptiOptions<TSchema, TResponseSchema, TSchema, TResponseHeadersSchema>): Promise<ValidatedResponse<TResponseSchema extends never ? unknown : InferOutput<TResponseSchema>, TResponseHeadersSchema>>;
92
136
  }
93
137
  /**
94
138
  * Convenience function to create Prapti instance
@@ -97,9 +141,14 @@ export declare class Prapti<TSchema = unknown> {
97
141
  */
98
142
  export declare function createPrapti<TSchema>(adapter: ValidationAdapter<TSchema>): Prapti<TSchema>;
99
143
  /**
100
- * Pre-built Zod adapter
101
- * Import only when using Zod to keep bundle size minimal
144
+ * Pre-built adapters for popular validation libraries
102
145
  */
103
- export declare const zodAdapter: ValidationAdapter;
146
+ export declare const adapters: {
147
+ /**
148
+ * Zod adapter
149
+ * @returns ValidationAdapter for Zod schemas
150
+ */
151
+ zod: ValidationAdapter;
152
+ };
104
153
 
105
154
  export {};
package/dist/index.esm.js CHANGED
@@ -1 +1 @@
1
- class F extends Response{adapter;responseSchema;constructor(g,k,w){super(g.body,g);this.adapter=k;this.responseSchema=w;Object.setPrototypeOf(this,F.prototype)}async json(){let g=await super.json();return this.responseSchema?this.adapter.parse(this.responseSchema,g):g}async text(){let g=await super.text();return this.responseSchema?this.adapter.parse(this.responseSchema,g):g}async blob(){return super.blob()}async arrayBuffer(){return super.arrayBuffer()}async formData(){let g=await super.formData();if(this.responseSchema)this.adapter.parse(this.responseSchema,Object.fromEntries(g.entries()));return g}}class H{adapter;constructor(g){this.adapter=g}async fetch(g,k){let{requestSchema:w,responseSchema:I,body:j,...G}=k||{},C,D=new Headers(G.headers);if(w&&j!==void 0&&j!==null){let K=typeof j==="string"?JSON.parse(j):j,L=this.adapter.parse(w,K);if(C=JSON.stringify(L),!D.has("Content-Type"))D.set("Content-Type","application/json")}else C=j;let J=await fetch(g,{...G,headers:D,body:C});return new F(J,this.adapter,I)}}function M(g){return new H(g)}var N={parse:(g,k)=>g.parse(k)};export{N as zodAdapter,M as createPrapti,F as ValidatedResponse,H as Prapti};
1
+ class X extends Response{adapter;responseSchema;responseHeadersSchema;constructor(g,w,z,A){super(g.body,g);this.adapter=w;this.responseSchema=z;this.responseHeadersSchema=A;if(Object.setPrototypeOf(this,X.prototype),A)this.validateResponseHeaders()}validateResponseHeaders(){if(!this.responseHeadersSchema)return;let g={};this.headers.forEach((w,z)=>{g[z.toLowerCase()]=w}),this.adapter.parse(this.responseHeadersSchema,g)}getValidatedHeaders(){let g={};return this.headers.forEach((w,z)=>{g[z.toLowerCase()]=w}),this.responseHeadersSchema?this.adapter.parse(this.responseHeadersSchema,g):g}async json(){let g=await super.json();return this.responseSchema?this.adapter.parse(this.responseSchema,g):g}async text(){let g=await super.text();return this.responseSchema?this.adapter.parse(this.responseSchema,g):g}async blob(){return super.blob()}async arrayBuffer(){return super.arrayBuffer()}async formData(){let g=await super.formData();if(this.responseSchema){let w={};g.forEach((A,C)=>{if(w[C]!==void 0)if(Array.isArray(w[C]))w[C].push(A);else w[C]=[w[C],A];else w[C]=A});let z=this.adapter.parse(this.responseSchema,w);if(z&&typeof z==="object"){let A=new FormData;return Object.entries(z).forEach(([C,I])=>{if(Array.isArray(I))I.forEach((G)=>A.append(C,G));else A.append(C,I)}),A}}return g}async urlSearchParams(){let g=await super.text(),w=new URLSearchParams(g);if(this.responseSchema){let z={};w.forEach((C,I)=>{if(z[I]!==void 0)if(Array.isArray(z[I]))z[I].push(C);else z[I]=[z[I],C];else z[I]=C});let A=this.adapter.parse(this.responseSchema,z);if(A&&typeof A==="object"){let C=new URLSearchParams;return Object.entries(A).forEach(([I,G])=>{if(Array.isArray(G))G.forEach((U)=>C.append(I,String(U)));else C.append(I,String(G))}),C}}return w}}class Y{adapter;constructor(g){this.adapter=g}headersToObject(g){if(!g)return{};if(g instanceof Headers){let z={};return g.forEach((A,C)=>{z[C.toLowerCase()]=A}),z}if(Array.isArray(g)){let z={};return g.forEach(([A,C])=>{z[A.toLowerCase()]=String(C)}),z}let w={};return Object.entries(g).forEach(([z,A])=>{w[z.toLowerCase()]=String(A)}),w}formDataToObject(g){let w={};return g.forEach((z,A)=>{if(w[A]!==void 0)if(Array.isArray(w[A]))w[A].push(z);else w[A]=[w[A],z];else w[A]=z}),w}urlSearchParamsToObject(g){let w={};return g.forEach((z,A)=>{if(w[A]!==void 0)if(Array.isArray(w[A]))w[A].push(z);else w[A]=[w[A],z];else w[A]=z}),w}async fetch(g,w){let{requestSchema:z,responseSchema:A,requestHeadersSchema:C,responseHeadersSchema:I,body:G,headers:U,...Z}=w||{},Q,T=new Headers;if(U){let K=this.headersToObject(U);if(C){let L=this.adapter.parse(C,K);if(L&&typeof L==="object")Object.entries(L).forEach(([J,M])=>{T.set(J,String(M))})}else Object.entries(K).forEach(([L,J])=>{T.set(L,J)})}if(z&&G!==void 0&&G!==null){let K;if(typeof G==="string")try{K=JSON.parse(G)}catch{K=G}else if(G instanceof FormData)K=this.formDataToObject(G);else if(G instanceof URLSearchParams)K=this.urlSearchParamsToObject(G);else K=G;let L=this.adapter.parse(z,K);if(G instanceof FormData){let J=new FormData;Object.entries(L).forEach(([M,N])=>{if(Array.isArray(N))N.forEach((W)=>J.append(M,W));else J.append(M,N)}),Q=J}else if(G instanceof URLSearchParams){let J=new URLSearchParams;Object.entries(L).forEach(([M,N])=>{if(Array.isArray(N))N.forEach((W)=>J.append(M,String(W)));else J.append(M,String(N))}),Q=J}else if(Q=JSON.stringify(L),!T.has("Content-Type"))T.set("Content-Type","application/json")}else Q=G;let _=await fetch(g,{...Z,headers:T,body:Q});return new X(_,this.adapter,A,I)}}function $(g){return new Y(g)}var E={zod:{parse:(g,w)=>g.parse(w)}};export{$ as createPrapti,E as adapters,X as ValidatedResponse,Y as Prapti};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prapti",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Type-safe HTTP client with runtime schema validation support for Zod, Valibot, Yup, and more",
5
5
  "files": [
6
6
  "dist/*",