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 +38 -10
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +56 -7
- package/dist/index.esm.js +1 -1
- package/package.json +1 -1
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
|
|
7
|
+
> A minimal, type-safe utility that extends the native `fetch` API with runtime schema validation.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
[](https://bundlephobia.com/result?p=prapti)
|
|
11
|
+
[](#license)
|
|
12
|
+
[](https://github.com/kiranojhanp/prapti/issues)
|
|
8
13
|
|
|
9
14
|
```typescript
|
|
10
15
|
// Without Prapti
|
|
11
|
-
const response = await
|
|
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
|
-
|
|
123
|
+
Extended fetch options with validation schemas:
|
|
95
124
|
|
|
96
|
-
- `requestSchema
|
|
97
|
-
- `responseSchema
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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};
|