enlace 0.0.0-alpha.2 → 0.0.0-alpha.4

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/dist/index.d.mts CHANGED
@@ -1,5 +1,106 @@
1
- import { E as EnlaceOptions, F as FetchExecutor, R as RequestOptions, H as HttpMethod, a as EnlaceResponse, W as WildcardClient, b as EnlaceClient } from './types-B-6qIYag.mjs';
2
- export { c as Endpoint, M as MethodDefinition, S as SchemaMethod } from './types-B-6qIYag.mjs';
1
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
2
+ type SchemaMethod = "$get" | "$post" | "$put" | "$patch" | "$delete";
3
+ type EnlaceResponse<TData, TError> = {
4
+ ok: true;
5
+ status: number;
6
+ data: TData;
7
+ error?: never;
8
+ } | {
9
+ ok: false;
10
+ status: number;
11
+ data?: never;
12
+ error: TError;
13
+ };
14
+ type EnlaceOptions = Omit<RequestInit, "method" | "body">;
15
+ type FetchExecutor<TOptions = EnlaceOptions, TRequestOptions = RequestOptions<unknown>> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions) => Promise<EnlaceResponse<TData, TError>>;
16
+ type RequestOptions<TBody = never> = {
17
+ body?: TBody;
18
+ query?: Record<string, string | number | boolean | undefined>;
19
+ headers?: HeadersInit;
20
+ cache?: RequestCache;
21
+ };
22
+ type MethodDefinition = {
23
+ data: unknown;
24
+ error: unknown;
25
+ body?: unknown;
26
+ };
27
+ /**
28
+ * Helper to define an endpoint with proper typing.
29
+ * Provides cleaner syntax than writing { data, error, body } manually.
30
+ *
31
+ * @example
32
+ * type MyApi = {
33
+ * posts: {
34
+ * $get: Endpoint<Post[], ApiError>;
35
+ * $post: Endpoint<Post, ApiError, CreatePost>; // with body
36
+ * _: {
37
+ * $get: Endpoint<Post, NotFoundError>;
38
+ * };
39
+ * };
40
+ * };
41
+ */
42
+ type Endpoint<TData, TError, TBody = never> = [TBody] extends [never] ? {
43
+ data: TData;
44
+ error: TError;
45
+ } : {
46
+ data: TData;
47
+ error: TError;
48
+ body: TBody;
49
+ };
50
+ type ExtractMethodDef<TSchema, TMethod extends SchemaMethod> = TSchema extends {
51
+ [K in TMethod]: infer M;
52
+ } ? M extends MethodDefinition ? M : never : never;
53
+ type ExtractData<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
54
+ data: infer D;
55
+ } ? D : never;
56
+ type ExtractError<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
57
+ error: infer E;
58
+ } ? E : never;
59
+ type ExtractBody<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
60
+ body: infer B;
61
+ } ? B : never;
62
+ type HasMethod<TSchema, TMethod extends SchemaMethod> = TSchema extends {
63
+ [K in TMethod]: MethodDefinition;
64
+ } ? true : false;
65
+ type MethodFn<TSchema, TMethod extends SchemaMethod, TRequestOptionsBase = object> = HasMethod<TSchema, TMethod> extends true ? ExtractBody<TSchema, TMethod> extends never ? (options?: RequestOptions<never> & TRequestOptionsBase) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : (options: RequestOptions<ExtractBody<TSchema, TMethod>> & TRequestOptionsBase) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : never;
66
+ type IsSpecialKey<K> = K extends SchemaMethod | "_" ? true : false;
67
+ type StaticPathKeys<TSchema> = {
68
+ [K in keyof TSchema as IsSpecialKey<K> extends true ? never : K extends string ? K : never]: TSchema[K];
69
+ };
70
+ type ExtractDynamicSchema<TSchema> = TSchema extends {
71
+ _: infer D;
72
+ } ? D : never;
73
+ type MethodOrPath<TSchema, TMethodName extends string, TSchemaMethod extends SchemaMethod, TRequestOptionsBase = object> = TMethodName extends keyof TSchema ? EnlaceClient<TSchema[TMethodName], TRequestOptionsBase> : MethodFn<TSchema, TSchemaMethod, TRequestOptionsBase>;
74
+ type HttpMethods<TSchema, TRequestOptionsBase = object> = {
75
+ get: MethodOrPath<TSchema, "get", "$get", TRequestOptionsBase>;
76
+ post: MethodOrPath<TSchema, "post", "$post", TRequestOptionsBase>;
77
+ put: MethodOrPath<TSchema, "put", "$put", TRequestOptionsBase>;
78
+ patch: MethodOrPath<TSchema, "patch", "$patch", TRequestOptionsBase>;
79
+ delete: MethodOrPath<TSchema, "delete", "$delete", TRequestOptionsBase>;
80
+ };
81
+ type DynamicAccess<TSchema, TRequestOptionsBase = object> = ExtractDynamicSchema<TSchema> extends never ? object : {
82
+ [key: string]: EnlaceClient<ExtractDynamicSchema<TSchema>, TRequestOptionsBase>;
83
+ [key: number]: EnlaceClient<ExtractDynamicSchema<TSchema>, TRequestOptionsBase>;
84
+ };
85
+ type MethodNameKeys = "get" | "post" | "put" | "patch" | "delete";
86
+ type EnlaceClient<TSchema, TRequestOptionsBase = object> = HttpMethods<TSchema, TRequestOptionsBase> & DynamicAccess<TSchema, TRequestOptionsBase> & {
87
+ [K in keyof StaticPathKeys<TSchema> as K extends MethodNameKeys ? never : K]: EnlaceClient<TSchema[K], TRequestOptionsBase>;
88
+ };
89
+ type WildcardMethodFn<TRequestOptionsBase = object> = (options?: RequestOptions<unknown> & TRequestOptionsBase) => Promise<EnlaceResponse<unknown, unknown>>;
90
+ /**
91
+ * Wildcard client type - allows any path access when no schema is provided.
92
+ * All methods are available at every level and return unknown types.
93
+ */
94
+ type WildcardClient<TRequestOptionsBase = object> = {
95
+ get: WildcardMethodFn<TRequestOptionsBase>;
96
+ post: WildcardMethodFn<TRequestOptionsBase>;
97
+ put: WildcardMethodFn<TRequestOptionsBase>;
98
+ patch: WildcardMethodFn<TRequestOptionsBase>;
99
+ delete: WildcardMethodFn<TRequestOptionsBase>;
100
+ } & {
101
+ [key: string]: WildcardClient<TRequestOptionsBase>;
102
+ [key: number]: WildcardClient<TRequestOptionsBase>;
103
+ };
3
104
 
4
105
  declare function createProxyHandler<TSchema extends object, TOptions = EnlaceOptions>(baseUrl: string, defaultOptions: TOptions, path?: string[], fetchExecutor?: FetchExecutor<TOptions, RequestOptions<unknown>>): TSchema;
5
106
 
@@ -17,4 +118,4 @@ declare function executeFetch<TData, TError>(baseUrl: string, path: string[], me
17
118
  */
18
119
  declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: EnlaceOptions): unknown extends TSchema ? WildcardClient : EnlaceClient<TSchema>;
19
120
 
20
- export { EnlaceClient, EnlaceOptions, EnlaceResponse, FetchExecutor, HttpMethod, RequestOptions, WildcardClient, createEnlace, createProxyHandler, executeFetch };
121
+ export { type Endpoint, type EnlaceClient, type EnlaceOptions, type EnlaceResponse, type FetchExecutor, type HttpMethod, type MethodDefinition, type RequestOptions, type SchemaMethod, type WildcardClient, createEnlace, createProxyHandler, executeFetch };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,106 @@
1
- import { E as EnlaceOptions, F as FetchExecutor, R as RequestOptions, H as HttpMethod, a as EnlaceResponse, W as WildcardClient, b as EnlaceClient } from './types-B-6qIYag.js';
2
- export { c as Endpoint, M as MethodDefinition, S as SchemaMethod } from './types-B-6qIYag.js';
1
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
2
+ type SchemaMethod = "$get" | "$post" | "$put" | "$patch" | "$delete";
3
+ type EnlaceResponse<TData, TError> = {
4
+ ok: true;
5
+ status: number;
6
+ data: TData;
7
+ error?: never;
8
+ } | {
9
+ ok: false;
10
+ status: number;
11
+ data?: never;
12
+ error: TError;
13
+ };
14
+ type EnlaceOptions = Omit<RequestInit, "method" | "body">;
15
+ type FetchExecutor<TOptions = EnlaceOptions, TRequestOptions = RequestOptions<unknown>> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions) => Promise<EnlaceResponse<TData, TError>>;
16
+ type RequestOptions<TBody = never> = {
17
+ body?: TBody;
18
+ query?: Record<string, string | number | boolean | undefined>;
19
+ headers?: HeadersInit;
20
+ cache?: RequestCache;
21
+ };
22
+ type MethodDefinition = {
23
+ data: unknown;
24
+ error: unknown;
25
+ body?: unknown;
26
+ };
27
+ /**
28
+ * Helper to define an endpoint with proper typing.
29
+ * Provides cleaner syntax than writing { data, error, body } manually.
30
+ *
31
+ * @example
32
+ * type MyApi = {
33
+ * posts: {
34
+ * $get: Endpoint<Post[], ApiError>;
35
+ * $post: Endpoint<Post, ApiError, CreatePost>; // with body
36
+ * _: {
37
+ * $get: Endpoint<Post, NotFoundError>;
38
+ * };
39
+ * };
40
+ * };
41
+ */
42
+ type Endpoint<TData, TError, TBody = never> = [TBody] extends [never] ? {
43
+ data: TData;
44
+ error: TError;
45
+ } : {
46
+ data: TData;
47
+ error: TError;
48
+ body: TBody;
49
+ };
50
+ type ExtractMethodDef<TSchema, TMethod extends SchemaMethod> = TSchema extends {
51
+ [K in TMethod]: infer M;
52
+ } ? M extends MethodDefinition ? M : never : never;
53
+ type ExtractData<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
54
+ data: infer D;
55
+ } ? D : never;
56
+ type ExtractError<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
57
+ error: infer E;
58
+ } ? E : never;
59
+ type ExtractBody<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
60
+ body: infer B;
61
+ } ? B : never;
62
+ type HasMethod<TSchema, TMethod extends SchemaMethod> = TSchema extends {
63
+ [K in TMethod]: MethodDefinition;
64
+ } ? true : false;
65
+ type MethodFn<TSchema, TMethod extends SchemaMethod, TRequestOptionsBase = object> = HasMethod<TSchema, TMethod> extends true ? ExtractBody<TSchema, TMethod> extends never ? (options?: RequestOptions<never> & TRequestOptionsBase) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : (options: RequestOptions<ExtractBody<TSchema, TMethod>> & TRequestOptionsBase) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : never;
66
+ type IsSpecialKey<K> = K extends SchemaMethod | "_" ? true : false;
67
+ type StaticPathKeys<TSchema> = {
68
+ [K in keyof TSchema as IsSpecialKey<K> extends true ? never : K extends string ? K : never]: TSchema[K];
69
+ };
70
+ type ExtractDynamicSchema<TSchema> = TSchema extends {
71
+ _: infer D;
72
+ } ? D : never;
73
+ type MethodOrPath<TSchema, TMethodName extends string, TSchemaMethod extends SchemaMethod, TRequestOptionsBase = object> = TMethodName extends keyof TSchema ? EnlaceClient<TSchema[TMethodName], TRequestOptionsBase> : MethodFn<TSchema, TSchemaMethod, TRequestOptionsBase>;
74
+ type HttpMethods<TSchema, TRequestOptionsBase = object> = {
75
+ get: MethodOrPath<TSchema, "get", "$get", TRequestOptionsBase>;
76
+ post: MethodOrPath<TSchema, "post", "$post", TRequestOptionsBase>;
77
+ put: MethodOrPath<TSchema, "put", "$put", TRequestOptionsBase>;
78
+ patch: MethodOrPath<TSchema, "patch", "$patch", TRequestOptionsBase>;
79
+ delete: MethodOrPath<TSchema, "delete", "$delete", TRequestOptionsBase>;
80
+ };
81
+ type DynamicAccess<TSchema, TRequestOptionsBase = object> = ExtractDynamicSchema<TSchema> extends never ? object : {
82
+ [key: string]: EnlaceClient<ExtractDynamicSchema<TSchema>, TRequestOptionsBase>;
83
+ [key: number]: EnlaceClient<ExtractDynamicSchema<TSchema>, TRequestOptionsBase>;
84
+ };
85
+ type MethodNameKeys = "get" | "post" | "put" | "patch" | "delete";
86
+ type EnlaceClient<TSchema, TRequestOptionsBase = object> = HttpMethods<TSchema, TRequestOptionsBase> & DynamicAccess<TSchema, TRequestOptionsBase> & {
87
+ [K in keyof StaticPathKeys<TSchema> as K extends MethodNameKeys ? never : K]: EnlaceClient<TSchema[K], TRequestOptionsBase>;
88
+ };
89
+ type WildcardMethodFn<TRequestOptionsBase = object> = (options?: RequestOptions<unknown> & TRequestOptionsBase) => Promise<EnlaceResponse<unknown, unknown>>;
90
+ /**
91
+ * Wildcard client type - allows any path access when no schema is provided.
92
+ * All methods are available at every level and return unknown types.
93
+ */
94
+ type WildcardClient<TRequestOptionsBase = object> = {
95
+ get: WildcardMethodFn<TRequestOptionsBase>;
96
+ post: WildcardMethodFn<TRequestOptionsBase>;
97
+ put: WildcardMethodFn<TRequestOptionsBase>;
98
+ patch: WildcardMethodFn<TRequestOptionsBase>;
99
+ delete: WildcardMethodFn<TRequestOptionsBase>;
100
+ } & {
101
+ [key: string]: WildcardClient<TRequestOptionsBase>;
102
+ [key: number]: WildcardClient<TRequestOptionsBase>;
103
+ };
3
104
 
4
105
  declare function createProxyHandler<TSchema extends object, TOptions = EnlaceOptions>(baseUrl: string, defaultOptions: TOptions, path?: string[], fetchExecutor?: FetchExecutor<TOptions, RequestOptions<unknown>>): TSchema;
5
106
 
@@ -17,4 +118,4 @@ declare function executeFetch<TData, TError>(baseUrl: string, path: string[], me
17
118
  */
18
119
  declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: EnlaceOptions): unknown extends TSchema ? WildcardClient : EnlaceClient<TSchema>;
19
120
 
20
- export { EnlaceClient, EnlaceOptions, EnlaceResponse, FetchExecutor, HttpMethod, RequestOptions, WildcardClient, createEnlace, createProxyHandler, executeFetch };
121
+ export { type Endpoint, type EnlaceClient, type EnlaceOptions, type EnlaceResponse, type FetchExecutor, type HttpMethod, type MethodDefinition, type RequestOptions, type SchemaMethod, type WildcardClient, createEnlace, createProxyHandler, executeFetch };
package/dist/index.js CHANGED
@@ -28,18 +28,19 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
33
  createEnlace: () => createEnlace,
34
34
  createProxyHandler: () => createProxyHandler,
35
35
  executeFetch: () => executeFetch
36
36
  });
37
- module.exports = __toCommonJS(index_exports);
37
+ module.exports = __toCommonJS(src_exports);
38
38
 
39
39
  // src/utils/buildUrl.ts
40
40
  var import_query_string = __toESM(require("query-string"));
41
41
  function buildUrl(baseUrl, path, query) {
42
- const url = new URL(path.join("/"), baseUrl);
42
+ const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
43
+ const url = new URL(path.join("/"), normalizedBase);
43
44
  if (query) {
44
45
  url.search = import_query_string.default.stringify(query, { skipNull: true, skipEmptyString: true });
45
46
  }
package/dist/index.mjs CHANGED
@@ -1,7 +1,97 @@
1
- import {
2
- createProxyHandler,
3
- executeFetch
4
- } from "./chunk-PNORT7RX.mjs";
1
+ // src/utils/buildUrl.ts
2
+ import qs from "query-string";
3
+ function buildUrl(baseUrl, path, query) {
4
+ const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
5
+ const url = new URL(path.join("/"), normalizedBase);
6
+ if (query) {
7
+ url.search = qs.stringify(query, { skipNull: true, skipEmptyString: true });
8
+ }
9
+ return url.toString();
10
+ }
11
+
12
+ // src/utils/isJsonBody.ts
13
+ function isJsonBody(body) {
14
+ if (body === null || body === void 0) return false;
15
+ if (body instanceof FormData) return false;
16
+ if (body instanceof Blob) return false;
17
+ if (body instanceof ArrayBuffer) return false;
18
+ if (body instanceof URLSearchParams) return false;
19
+ if (body instanceof ReadableStream) return false;
20
+ if (typeof body === "string") return false;
21
+ return typeof body === "object";
22
+ }
23
+
24
+ // src/utils/mergeHeaders.ts
25
+ function mergeHeaders(defaultHeaders, requestHeaders) {
26
+ if (!defaultHeaders && !requestHeaders) return void 0;
27
+ if (!defaultHeaders) return requestHeaders;
28
+ if (!requestHeaders) return defaultHeaders;
29
+ return {
30
+ ...Object.fromEntries(new Headers(defaultHeaders)),
31
+ ...Object.fromEntries(new Headers(requestHeaders))
32
+ };
33
+ }
34
+
35
+ // src/core/fetch.ts
36
+ async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions) {
37
+ const url = buildUrl(baseUrl, path, requestOptions?.query);
38
+ let headers = mergeHeaders(defaultOptions.headers, requestOptions?.headers);
39
+ const fetchOptions = {
40
+ ...defaultOptions,
41
+ method
42
+ };
43
+ if (headers) {
44
+ fetchOptions.headers = headers;
45
+ }
46
+ if (requestOptions?.body !== void 0) {
47
+ if (isJsonBody(requestOptions.body)) {
48
+ fetchOptions.body = JSON.stringify(requestOptions.body);
49
+ headers = mergeHeaders(headers, { "Content-Type": "application/json" });
50
+ if (headers) {
51
+ fetchOptions.headers = headers;
52
+ }
53
+ } else {
54
+ fetchOptions.body = requestOptions.body;
55
+ }
56
+ }
57
+ const response = await fetch(url, fetchOptions);
58
+ const contentType = response.headers.get("content-type");
59
+ const isJson = contentType?.includes("application/json");
60
+ if (response.ok) {
61
+ return {
62
+ ok: true,
63
+ status: response.status,
64
+ data: isJson ? await response.json() : response
65
+ };
66
+ }
67
+ return {
68
+ ok: false,
69
+ status: response.status,
70
+ error: isJson ? await response.json() : response
71
+ };
72
+ }
73
+
74
+ // src/core/proxy.ts
75
+ var HTTP_METHODS = {
76
+ get: "GET",
77
+ post: "POST",
78
+ put: "PUT",
79
+ patch: "PATCH",
80
+ delete: "DELETE"
81
+ };
82
+ function createProxyHandler(baseUrl, defaultOptions, path = [], fetchExecutor = executeFetch) {
83
+ const handler = {
84
+ get(_target, prop) {
85
+ if (typeof prop === "symbol") return void 0;
86
+ const method = HTTP_METHODS[prop];
87
+ if (method) {
88
+ return (options) => fetchExecutor(baseUrl, path, method, defaultOptions, options);
89
+ }
90
+ return createProxyHandler(baseUrl, defaultOptions, [...path, prop], fetchExecutor);
91
+ }
92
+ };
93
+ return new Proxy({}, handler);
94
+ }
5
95
 
6
96
  // src/core/index.ts
7
97
  function createEnlace(baseUrl, defaultOptions = {}) {
@@ -1,18 +1,121 @@
1
- import { R as RequestOptions, W as WildcardClient, b as EnlaceClient } from '../types-B-6qIYag.mjs';
2
- export { c as Endpoint, E as EnlaceOptions, a as EnlaceResponse, F as FetchExecutor, H as HttpMethod, M as MethodDefinition, S as SchemaMethod } from '../types-B-6qIYag.mjs';
1
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
2
+ type SchemaMethod = "$get" | "$post" | "$put" | "$patch" | "$delete";
3
+ type EnlaceResponse<TData, TError> = {
4
+ ok: true;
5
+ status: number;
6
+ data: TData;
7
+ error?: never;
8
+ } | {
9
+ ok: false;
10
+ status: number;
11
+ data?: never;
12
+ error: TError;
13
+ };
14
+ type EnlaceOptions = Omit<RequestInit, "method" | "body">;
15
+ type FetchExecutor<TOptions = EnlaceOptions, TRequestOptions = RequestOptions<unknown>> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions) => Promise<EnlaceResponse<TData, TError>>;
16
+ type RequestOptions<TBody = never> = {
17
+ body?: TBody;
18
+ query?: Record<string, string | number | boolean | undefined>;
19
+ headers?: HeadersInit;
20
+ cache?: RequestCache;
21
+ };
22
+ type MethodDefinition = {
23
+ data: unknown;
24
+ error: unknown;
25
+ body?: unknown;
26
+ };
27
+ /**
28
+ * Helper to define an endpoint with proper typing.
29
+ * Provides cleaner syntax than writing { data, error, body } manually.
30
+ *
31
+ * @example
32
+ * type MyApi = {
33
+ * posts: {
34
+ * $get: Endpoint<Post[], ApiError>;
35
+ * $post: Endpoint<Post, ApiError, CreatePost>; // with body
36
+ * _: {
37
+ * $get: Endpoint<Post, NotFoundError>;
38
+ * };
39
+ * };
40
+ * };
41
+ */
42
+ type Endpoint<TData, TError, TBody = never> = [TBody] extends [never] ? {
43
+ data: TData;
44
+ error: TError;
45
+ } : {
46
+ data: TData;
47
+ error: TError;
48
+ body: TBody;
49
+ };
50
+ type ExtractMethodDef<TSchema, TMethod extends SchemaMethod> = TSchema extends {
51
+ [K in TMethod]: infer M;
52
+ } ? M extends MethodDefinition ? M : never : never;
53
+ type ExtractData<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
54
+ data: infer D;
55
+ } ? D : never;
56
+ type ExtractError<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
57
+ error: infer E;
58
+ } ? E : never;
59
+ type ExtractBody<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
60
+ body: infer B;
61
+ } ? B : never;
62
+ type HasMethod<TSchema, TMethod extends SchemaMethod> = TSchema extends {
63
+ [K in TMethod]: MethodDefinition;
64
+ } ? true : false;
65
+ type MethodFn<TSchema, TMethod extends SchemaMethod, TRequestOptionsBase = object> = HasMethod<TSchema, TMethod> extends true ? ExtractBody<TSchema, TMethod> extends never ? (options?: RequestOptions<never> & TRequestOptionsBase) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : (options: RequestOptions<ExtractBody<TSchema, TMethod>> & TRequestOptionsBase) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : never;
66
+ type IsSpecialKey<K> = K extends SchemaMethod | "_" ? true : false;
67
+ type StaticPathKeys<TSchema> = {
68
+ [K in keyof TSchema as IsSpecialKey<K> extends true ? never : K extends string ? K : never]: TSchema[K];
69
+ };
70
+ type ExtractDynamicSchema<TSchema> = TSchema extends {
71
+ _: infer D;
72
+ } ? D : never;
73
+ type MethodOrPath<TSchema, TMethodName extends string, TSchemaMethod extends SchemaMethod, TRequestOptionsBase = object> = TMethodName extends keyof TSchema ? EnlaceClient<TSchema[TMethodName], TRequestOptionsBase> : MethodFn<TSchema, TSchemaMethod, TRequestOptionsBase>;
74
+ type HttpMethods<TSchema, TRequestOptionsBase = object> = {
75
+ get: MethodOrPath<TSchema, "get", "$get", TRequestOptionsBase>;
76
+ post: MethodOrPath<TSchema, "post", "$post", TRequestOptionsBase>;
77
+ put: MethodOrPath<TSchema, "put", "$put", TRequestOptionsBase>;
78
+ patch: MethodOrPath<TSchema, "patch", "$patch", TRequestOptionsBase>;
79
+ delete: MethodOrPath<TSchema, "delete", "$delete", TRequestOptionsBase>;
80
+ };
81
+ type DynamicAccess<TSchema, TRequestOptionsBase = object> = ExtractDynamicSchema<TSchema> extends never ? object : {
82
+ [key: string]: EnlaceClient<ExtractDynamicSchema<TSchema>, TRequestOptionsBase>;
83
+ [key: number]: EnlaceClient<ExtractDynamicSchema<TSchema>, TRequestOptionsBase>;
84
+ };
85
+ type MethodNameKeys = "get" | "post" | "put" | "patch" | "delete";
86
+ type EnlaceClient<TSchema, TRequestOptionsBase = object> = HttpMethods<TSchema, TRequestOptionsBase> & DynamicAccess<TSchema, TRequestOptionsBase> & {
87
+ [K in keyof StaticPathKeys<TSchema> as K extends MethodNameKeys ? never : K]: EnlaceClient<TSchema[K], TRequestOptionsBase>;
88
+ };
89
+ type WildcardMethodFn<TRequestOptionsBase = object> = (options?: RequestOptions<unknown> & TRequestOptionsBase) => Promise<EnlaceResponse<unknown, unknown>>;
90
+ /**
91
+ * Wildcard client type - allows any path access when no schema is provided.
92
+ * All methods are available at every level and return unknown types.
93
+ */
94
+ type WildcardClient<TRequestOptionsBase = object> = {
95
+ get: WildcardMethodFn<TRequestOptionsBase>;
96
+ post: WildcardMethodFn<TRequestOptionsBase>;
97
+ put: WildcardMethodFn<TRequestOptionsBase>;
98
+ patch: WildcardMethodFn<TRequestOptionsBase>;
99
+ delete: WildcardMethodFn<TRequestOptionsBase>;
100
+ } & {
101
+ [key: string]: WildcardClient<TRequestOptionsBase>;
102
+ [key: number]: WildcardClient<TRequestOptionsBase>;
103
+ };
3
104
 
4
105
  type NextFetchOptions = {
5
106
  revalidate?: number | false;
6
107
  tags?: string[];
7
108
  };
8
- type NextEnlaceOptions = Omit<RequestInit, 'method' | 'body'> & {
9
- next?: NextFetchOptions;
109
+ type RevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
110
+ type NextEnlaceOptions = EnlaceOptions & {
111
+ revalidate?: RevalidateHandler;
10
112
  };
11
- type NextRequestOptions<TBody = never> = RequestOptions<TBody> & {
12
- cache?: 'force-cache' | 'no-store';
113
+ type NextRequestOptionsBase = {
13
114
  next?: NextFetchOptions;
115
+ revalidateTags?: string[];
116
+ revalidatePaths?: string[];
14
117
  };
15
118
 
16
- declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: NextEnlaceOptions): unknown extends TSchema ? WildcardClient : EnlaceClient<TSchema>;
119
+ declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: NextEnlaceOptions): unknown extends TSchema ? WildcardClient<NextRequestOptionsBase> : EnlaceClient<TSchema, NextRequestOptionsBase>;
17
120
 
18
- export { EnlaceClient, type NextEnlaceOptions, type NextFetchOptions, type NextRequestOptions, RequestOptions, WildcardClient, createEnlace };
121
+ export { type Endpoint, type EnlaceClient, type EnlaceOptions, type EnlaceResponse, type FetchExecutor, type HttpMethod, type MethodDefinition, type NextEnlaceOptions, type NextFetchOptions, type NextRequestOptionsBase, type RequestOptions, type RevalidateHandler, type SchemaMethod, type WildcardClient, createEnlace };
@@ -1,18 +1,121 @@
1
- import { R as RequestOptions, W as WildcardClient, b as EnlaceClient } from '../types-B-6qIYag.js';
2
- export { c as Endpoint, E as EnlaceOptions, a as EnlaceResponse, F as FetchExecutor, H as HttpMethod, M as MethodDefinition, S as SchemaMethod } from '../types-B-6qIYag.js';
1
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
2
+ type SchemaMethod = "$get" | "$post" | "$put" | "$patch" | "$delete";
3
+ type EnlaceResponse<TData, TError> = {
4
+ ok: true;
5
+ status: number;
6
+ data: TData;
7
+ error?: never;
8
+ } | {
9
+ ok: false;
10
+ status: number;
11
+ data?: never;
12
+ error: TError;
13
+ };
14
+ type EnlaceOptions = Omit<RequestInit, "method" | "body">;
15
+ type FetchExecutor<TOptions = EnlaceOptions, TRequestOptions = RequestOptions<unknown>> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions) => Promise<EnlaceResponse<TData, TError>>;
16
+ type RequestOptions<TBody = never> = {
17
+ body?: TBody;
18
+ query?: Record<string, string | number | boolean | undefined>;
19
+ headers?: HeadersInit;
20
+ cache?: RequestCache;
21
+ };
22
+ type MethodDefinition = {
23
+ data: unknown;
24
+ error: unknown;
25
+ body?: unknown;
26
+ };
27
+ /**
28
+ * Helper to define an endpoint with proper typing.
29
+ * Provides cleaner syntax than writing { data, error, body } manually.
30
+ *
31
+ * @example
32
+ * type MyApi = {
33
+ * posts: {
34
+ * $get: Endpoint<Post[], ApiError>;
35
+ * $post: Endpoint<Post, ApiError, CreatePost>; // with body
36
+ * _: {
37
+ * $get: Endpoint<Post, NotFoundError>;
38
+ * };
39
+ * };
40
+ * };
41
+ */
42
+ type Endpoint<TData, TError, TBody = never> = [TBody] extends [never] ? {
43
+ data: TData;
44
+ error: TError;
45
+ } : {
46
+ data: TData;
47
+ error: TError;
48
+ body: TBody;
49
+ };
50
+ type ExtractMethodDef<TSchema, TMethod extends SchemaMethod> = TSchema extends {
51
+ [K in TMethod]: infer M;
52
+ } ? M extends MethodDefinition ? M : never : never;
53
+ type ExtractData<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
54
+ data: infer D;
55
+ } ? D : never;
56
+ type ExtractError<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
57
+ error: infer E;
58
+ } ? E : never;
59
+ type ExtractBody<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
60
+ body: infer B;
61
+ } ? B : never;
62
+ type HasMethod<TSchema, TMethod extends SchemaMethod> = TSchema extends {
63
+ [K in TMethod]: MethodDefinition;
64
+ } ? true : false;
65
+ type MethodFn<TSchema, TMethod extends SchemaMethod, TRequestOptionsBase = object> = HasMethod<TSchema, TMethod> extends true ? ExtractBody<TSchema, TMethod> extends never ? (options?: RequestOptions<never> & TRequestOptionsBase) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : (options: RequestOptions<ExtractBody<TSchema, TMethod>> & TRequestOptionsBase) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : never;
66
+ type IsSpecialKey<K> = K extends SchemaMethod | "_" ? true : false;
67
+ type StaticPathKeys<TSchema> = {
68
+ [K in keyof TSchema as IsSpecialKey<K> extends true ? never : K extends string ? K : never]: TSchema[K];
69
+ };
70
+ type ExtractDynamicSchema<TSchema> = TSchema extends {
71
+ _: infer D;
72
+ } ? D : never;
73
+ type MethodOrPath<TSchema, TMethodName extends string, TSchemaMethod extends SchemaMethod, TRequestOptionsBase = object> = TMethodName extends keyof TSchema ? EnlaceClient<TSchema[TMethodName], TRequestOptionsBase> : MethodFn<TSchema, TSchemaMethod, TRequestOptionsBase>;
74
+ type HttpMethods<TSchema, TRequestOptionsBase = object> = {
75
+ get: MethodOrPath<TSchema, "get", "$get", TRequestOptionsBase>;
76
+ post: MethodOrPath<TSchema, "post", "$post", TRequestOptionsBase>;
77
+ put: MethodOrPath<TSchema, "put", "$put", TRequestOptionsBase>;
78
+ patch: MethodOrPath<TSchema, "patch", "$patch", TRequestOptionsBase>;
79
+ delete: MethodOrPath<TSchema, "delete", "$delete", TRequestOptionsBase>;
80
+ };
81
+ type DynamicAccess<TSchema, TRequestOptionsBase = object> = ExtractDynamicSchema<TSchema> extends never ? object : {
82
+ [key: string]: EnlaceClient<ExtractDynamicSchema<TSchema>, TRequestOptionsBase>;
83
+ [key: number]: EnlaceClient<ExtractDynamicSchema<TSchema>, TRequestOptionsBase>;
84
+ };
85
+ type MethodNameKeys = "get" | "post" | "put" | "patch" | "delete";
86
+ type EnlaceClient<TSchema, TRequestOptionsBase = object> = HttpMethods<TSchema, TRequestOptionsBase> & DynamicAccess<TSchema, TRequestOptionsBase> & {
87
+ [K in keyof StaticPathKeys<TSchema> as K extends MethodNameKeys ? never : K]: EnlaceClient<TSchema[K], TRequestOptionsBase>;
88
+ };
89
+ type WildcardMethodFn<TRequestOptionsBase = object> = (options?: RequestOptions<unknown> & TRequestOptionsBase) => Promise<EnlaceResponse<unknown, unknown>>;
90
+ /**
91
+ * Wildcard client type - allows any path access when no schema is provided.
92
+ * All methods are available at every level and return unknown types.
93
+ */
94
+ type WildcardClient<TRequestOptionsBase = object> = {
95
+ get: WildcardMethodFn<TRequestOptionsBase>;
96
+ post: WildcardMethodFn<TRequestOptionsBase>;
97
+ put: WildcardMethodFn<TRequestOptionsBase>;
98
+ patch: WildcardMethodFn<TRequestOptionsBase>;
99
+ delete: WildcardMethodFn<TRequestOptionsBase>;
100
+ } & {
101
+ [key: string]: WildcardClient<TRequestOptionsBase>;
102
+ [key: number]: WildcardClient<TRequestOptionsBase>;
103
+ };
3
104
 
4
105
  type NextFetchOptions = {
5
106
  revalidate?: number | false;
6
107
  tags?: string[];
7
108
  };
8
- type NextEnlaceOptions = Omit<RequestInit, 'method' | 'body'> & {
9
- next?: NextFetchOptions;
109
+ type RevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
110
+ type NextEnlaceOptions = EnlaceOptions & {
111
+ revalidate?: RevalidateHandler;
10
112
  };
11
- type NextRequestOptions<TBody = never> = RequestOptions<TBody> & {
12
- cache?: 'force-cache' | 'no-store';
113
+ type NextRequestOptionsBase = {
13
114
  next?: NextFetchOptions;
115
+ revalidateTags?: string[];
116
+ revalidatePaths?: string[];
14
117
  };
15
118
 
16
- declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: NextEnlaceOptions): unknown extends TSchema ? WildcardClient : EnlaceClient<TSchema>;
119
+ declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: NextEnlaceOptions): unknown extends TSchema ? WildcardClient<NextRequestOptionsBase> : EnlaceClient<TSchema, NextRequestOptionsBase>;
17
120
 
18
- export { EnlaceClient, type NextEnlaceOptions, type NextFetchOptions, type NextRequestOptions, RequestOptions, WildcardClient, createEnlace };
121
+ export { type Endpoint, type EnlaceClient, type EnlaceOptions, type EnlaceResponse, type FetchExecutor, type HttpMethod, type MethodDefinition, type NextEnlaceOptions, type NextFetchOptions, type NextRequestOptionsBase, type RequestOptions, type RevalidateHandler, type SchemaMethod, type WildcardClient, createEnlace };
@@ -37,7 +37,8 @@ module.exports = __toCommonJS(next_exports);
37
37
  // src/utils/buildUrl.ts
38
38
  var import_query_string = __toESM(require("query-string"));
39
39
  function buildUrl(baseUrl, path, query) {
40
- const url = new URL(path.join("/"), baseUrl);
40
+ const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
41
+ const url = new URL(path.join("/"), normalizedBase);
41
42
  if (query) {
42
43
  url.search = import_query_string.default.stringify(query, { skipNull: true, skipEmptyString: true });
43
44
  }
@@ -135,7 +136,7 @@ function generateTags(path) {
135
136
  async function executeNextFetch(baseUrl, path, method, defaultOptions, requestOptions) {
136
137
  const url = buildUrl(baseUrl, path, requestOptions?.query);
137
138
  let headers = mergeHeaders(defaultOptions.headers, requestOptions?.headers);
138
- const nextOptions = requestOptions?.next ?? defaultOptions.next;
139
+ const nextOptions = requestOptions?.next;
139
140
  const tags = nextOptions?.tags ?? generateTags(path);
140
141
  const fetchOptions = {
141
142
  ...defaultOptions,
@@ -167,6 +168,10 @@ async function executeNextFetch(baseUrl, path, method, defaultOptions, requestOp
167
168
  const contentType = response.headers.get("content-type");
168
169
  const isJson = contentType?.includes("application/json");
169
170
  if (response.ok) {
171
+ const { revalidateTags = [], revalidatePaths = [] } = requestOptions ?? {};
172
+ if (revalidateTags.length || revalidatePaths.length) {
173
+ defaultOptions.revalidate?.(revalidateTags, revalidatePaths);
174
+ }
170
175
  return {
171
176
  ok: true,
172
177
  status: response.status,
@@ -1,9 +1,97 @@
1
- import {
2
- buildUrl,
3
- createProxyHandler,
4
- isJsonBody,
5
- mergeHeaders
6
- } from "../chunk-PNORT7RX.mjs";
1
+ // src/utils/buildUrl.ts
2
+ import qs from "query-string";
3
+ function buildUrl(baseUrl, path, query) {
4
+ const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
5
+ const url = new URL(path.join("/"), normalizedBase);
6
+ if (query) {
7
+ url.search = qs.stringify(query, { skipNull: true, skipEmptyString: true });
8
+ }
9
+ return url.toString();
10
+ }
11
+
12
+ // src/utils/isJsonBody.ts
13
+ function isJsonBody(body) {
14
+ if (body === null || body === void 0) return false;
15
+ if (body instanceof FormData) return false;
16
+ if (body instanceof Blob) return false;
17
+ if (body instanceof ArrayBuffer) return false;
18
+ if (body instanceof URLSearchParams) return false;
19
+ if (body instanceof ReadableStream) return false;
20
+ if (typeof body === "string") return false;
21
+ return typeof body === "object";
22
+ }
23
+
24
+ // src/utils/mergeHeaders.ts
25
+ function mergeHeaders(defaultHeaders, requestHeaders) {
26
+ if (!defaultHeaders && !requestHeaders) return void 0;
27
+ if (!defaultHeaders) return requestHeaders;
28
+ if (!requestHeaders) return defaultHeaders;
29
+ return {
30
+ ...Object.fromEntries(new Headers(defaultHeaders)),
31
+ ...Object.fromEntries(new Headers(requestHeaders))
32
+ };
33
+ }
34
+
35
+ // src/core/fetch.ts
36
+ async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions) {
37
+ const url = buildUrl(baseUrl, path, requestOptions?.query);
38
+ let headers = mergeHeaders(defaultOptions.headers, requestOptions?.headers);
39
+ const fetchOptions = {
40
+ ...defaultOptions,
41
+ method
42
+ };
43
+ if (headers) {
44
+ fetchOptions.headers = headers;
45
+ }
46
+ if (requestOptions?.body !== void 0) {
47
+ if (isJsonBody(requestOptions.body)) {
48
+ fetchOptions.body = JSON.stringify(requestOptions.body);
49
+ headers = mergeHeaders(headers, { "Content-Type": "application/json" });
50
+ if (headers) {
51
+ fetchOptions.headers = headers;
52
+ }
53
+ } else {
54
+ fetchOptions.body = requestOptions.body;
55
+ }
56
+ }
57
+ const response = await fetch(url, fetchOptions);
58
+ const contentType = response.headers.get("content-type");
59
+ const isJson = contentType?.includes("application/json");
60
+ if (response.ok) {
61
+ return {
62
+ ok: true,
63
+ status: response.status,
64
+ data: isJson ? await response.json() : response
65
+ };
66
+ }
67
+ return {
68
+ ok: false,
69
+ status: response.status,
70
+ error: isJson ? await response.json() : response
71
+ };
72
+ }
73
+
74
+ // src/core/proxy.ts
75
+ var HTTP_METHODS = {
76
+ get: "GET",
77
+ post: "POST",
78
+ put: "PUT",
79
+ patch: "PATCH",
80
+ delete: "DELETE"
81
+ };
82
+ function createProxyHandler(baseUrl, defaultOptions, path = [], fetchExecutor = executeFetch) {
83
+ const handler = {
84
+ get(_target, prop) {
85
+ if (typeof prop === "symbol") return void 0;
86
+ const method = HTTP_METHODS[prop];
87
+ if (method) {
88
+ return (options) => fetchExecutor(baseUrl, path, method, defaultOptions, options);
89
+ }
90
+ return createProxyHandler(baseUrl, defaultOptions, [...path, prop], fetchExecutor);
91
+ }
92
+ };
93
+ return new Proxy({}, handler);
94
+ }
7
95
 
8
96
  // src/next/fetch.ts
9
97
  function generateTags(path) {
@@ -12,7 +100,7 @@ function generateTags(path) {
12
100
  async function executeNextFetch(baseUrl, path, method, defaultOptions, requestOptions) {
13
101
  const url = buildUrl(baseUrl, path, requestOptions?.query);
14
102
  let headers = mergeHeaders(defaultOptions.headers, requestOptions?.headers);
15
- const nextOptions = requestOptions?.next ?? defaultOptions.next;
103
+ const nextOptions = requestOptions?.next;
16
104
  const tags = nextOptions?.tags ?? generateTags(path);
17
105
  const fetchOptions = {
18
106
  ...defaultOptions,
@@ -44,6 +132,10 @@ async function executeNextFetch(baseUrl, path, method, defaultOptions, requestOp
44
132
  const contentType = response.headers.get("content-type");
45
133
  const isJson = contentType?.includes("application/json");
46
134
  if (response.ok) {
135
+ const { revalidateTags = [], revalidatePaths = [] } = requestOptions ?? {};
136
+ if (revalidateTags.length || revalidatePaths.length) {
137
+ defaultOptions.revalidate?.(revalidateTags, revalidatePaths);
138
+ }
47
139
  return {
48
140
  ok: true,
49
141
  status: response.status,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enlace",
3
- "version": "0.0.0-alpha.2",
3
+ "version": "0.0.0-alpha.4",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -21,7 +21,8 @@
21
21
  "dev": "tsup --watch",
22
22
  "build": "tsup",
23
23
  "typecheck": "tsc --noEmit",
24
- "lint": "eslint src --max-warnings 0"
24
+ "lint": "eslint src --max-warnings 0",
25
+ "prepublishOnly": "npm run build && npm run typecheck && npm run lint"
25
26
  },
26
27
  "peerDependencies": {
27
28
  "react": "^19"
@@ -33,6 +34,7 @@
33
34
  "eslint-plugin-react": "^7.37.5",
34
35
  "globals": "^16.5.0",
35
36
  "jiti": "^2.6.1",
37
+ "prettier": "^3.7.4",
36
38
  "tsup": "^8.5.1",
37
39
  "tsx": "^4.21.0",
38
40
  "typescript": "5.9.2",
@@ -1,101 +0,0 @@
1
- // src/utils/buildUrl.ts
2
- import qs from "query-string";
3
- function buildUrl(baseUrl, path, query) {
4
- const url = new URL(path.join("/"), baseUrl);
5
- if (query) {
6
- url.search = qs.stringify(query, { skipNull: true, skipEmptyString: true });
7
- }
8
- return url.toString();
9
- }
10
-
11
- // src/utils/isJsonBody.ts
12
- function isJsonBody(body) {
13
- if (body === null || body === void 0) return false;
14
- if (body instanceof FormData) return false;
15
- if (body instanceof Blob) return false;
16
- if (body instanceof ArrayBuffer) return false;
17
- if (body instanceof URLSearchParams) return false;
18
- if (body instanceof ReadableStream) return false;
19
- if (typeof body === "string") return false;
20
- return typeof body === "object";
21
- }
22
-
23
- // src/utils/mergeHeaders.ts
24
- function mergeHeaders(defaultHeaders, requestHeaders) {
25
- if (!defaultHeaders && !requestHeaders) return void 0;
26
- if (!defaultHeaders) return requestHeaders;
27
- if (!requestHeaders) return defaultHeaders;
28
- return {
29
- ...Object.fromEntries(new Headers(defaultHeaders)),
30
- ...Object.fromEntries(new Headers(requestHeaders))
31
- };
32
- }
33
-
34
- // src/core/fetch.ts
35
- async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions) {
36
- const url = buildUrl(baseUrl, path, requestOptions?.query);
37
- let headers = mergeHeaders(defaultOptions.headers, requestOptions?.headers);
38
- const fetchOptions = {
39
- ...defaultOptions,
40
- method
41
- };
42
- if (headers) {
43
- fetchOptions.headers = headers;
44
- }
45
- if (requestOptions?.body !== void 0) {
46
- if (isJsonBody(requestOptions.body)) {
47
- fetchOptions.body = JSON.stringify(requestOptions.body);
48
- headers = mergeHeaders(headers, { "Content-Type": "application/json" });
49
- if (headers) {
50
- fetchOptions.headers = headers;
51
- }
52
- } else {
53
- fetchOptions.body = requestOptions.body;
54
- }
55
- }
56
- const response = await fetch(url, fetchOptions);
57
- const contentType = response.headers.get("content-type");
58
- const isJson = contentType?.includes("application/json");
59
- if (response.ok) {
60
- return {
61
- ok: true,
62
- status: response.status,
63
- data: isJson ? await response.json() : response
64
- };
65
- }
66
- return {
67
- ok: false,
68
- status: response.status,
69
- error: isJson ? await response.json() : response
70
- };
71
- }
72
-
73
- // src/core/proxy.ts
74
- var HTTP_METHODS = {
75
- get: "GET",
76
- post: "POST",
77
- put: "PUT",
78
- patch: "PATCH",
79
- delete: "DELETE"
80
- };
81
- function createProxyHandler(baseUrl, defaultOptions, path = [], fetchExecutor = executeFetch) {
82
- const handler = {
83
- get(_target, prop) {
84
- if (typeof prop === "symbol") return void 0;
85
- const method = HTTP_METHODS[prop];
86
- if (method) {
87
- return (options) => fetchExecutor(baseUrl, path, method, defaultOptions, options);
88
- }
89
- return createProxyHandler(baseUrl, defaultOptions, [...path, prop], fetchExecutor);
90
- }
91
- };
92
- return new Proxy({}, handler);
93
- }
94
-
95
- export {
96
- buildUrl,
97
- isJsonBody,
98
- mergeHeaders,
99
- executeFetch,
100
- createProxyHandler
101
- };
@@ -1,104 +0,0 @@
1
- type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
2
- type SchemaMethod = '$get' | '$post' | '$put' | '$patch' | '$delete';
3
- type EnlaceResponse<TData, TError> = {
4
- ok: true;
5
- status: number;
6
- data: TData;
7
- error?: never;
8
- } | {
9
- ok: false;
10
- status: number;
11
- data?: never;
12
- error: TError;
13
- };
14
- type EnlaceOptions = Omit<RequestInit, 'method' | 'body'>;
15
- type FetchExecutor<TOptions = EnlaceOptions, TRequestOptions = RequestOptions<unknown>> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions) => Promise<EnlaceResponse<TData, TError>>;
16
- type RequestOptions<TBody = never> = {
17
- body?: TBody;
18
- query?: Record<string, string | number | boolean | undefined>;
19
- headers?: HeadersInit;
20
- };
21
- type MethodDefinition = {
22
- data: unknown;
23
- error: unknown;
24
- body?: unknown;
25
- };
26
- /**
27
- * Helper to define an endpoint with proper typing.
28
- * Provides cleaner syntax than writing { data, error, body } manually.
29
- *
30
- * @example
31
- * type MyApi = {
32
- * posts: {
33
- * $get: Endpoint<Post[], ApiError>;
34
- * $post: Endpoint<Post, ApiError, CreatePost>; // with body
35
- * _: {
36
- * $get: Endpoint<Post, NotFoundError>;
37
- * };
38
- * };
39
- * };
40
- */
41
- type Endpoint<TData, TError, TBody = never> = [TBody] extends [never] ? {
42
- data: TData;
43
- error: TError;
44
- } : {
45
- data: TData;
46
- error: TError;
47
- body: TBody;
48
- };
49
- type ExtractMethodDef<TSchema, TMethod extends SchemaMethod> = TSchema extends {
50
- [K in TMethod]: infer M;
51
- } ? M extends MethodDefinition ? M : never : never;
52
- type ExtractData<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
53
- data: infer D;
54
- } ? D : never;
55
- type ExtractError<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
56
- error: infer E;
57
- } ? E : never;
58
- type ExtractBody<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
59
- body: infer B;
60
- } ? B : never;
61
- type HasMethod<TSchema, TMethod extends SchemaMethod> = TSchema extends {
62
- [K in TMethod]: MethodDefinition;
63
- } ? true : false;
64
- type MethodFn<TSchema, TMethod extends SchemaMethod> = HasMethod<TSchema, TMethod> extends true ? ExtractBody<TSchema, TMethod> extends never ? (options?: RequestOptions<never>) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : (options: RequestOptions<ExtractBody<TSchema, TMethod>>) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : never;
65
- type IsSpecialKey<K> = K extends SchemaMethod | '_' ? true : false;
66
- type StaticPathKeys<TSchema> = {
67
- [K in keyof TSchema as IsSpecialKey<K> extends true ? never : K extends string ? K : never]: TSchema[K];
68
- };
69
- type ExtractDynamicSchema<TSchema> = TSchema extends {
70
- _: infer D;
71
- } ? D : never;
72
- type MethodOrPath<TSchema, TMethodName extends string, TSchemaMethod extends SchemaMethod> = TMethodName extends keyof TSchema ? EnlaceClient<TSchema[TMethodName]> : MethodFn<TSchema, TSchemaMethod>;
73
- type HttpMethods<TSchema> = {
74
- get: MethodOrPath<TSchema, 'get', '$get'>;
75
- post: MethodOrPath<TSchema, 'post', '$post'>;
76
- put: MethodOrPath<TSchema, 'put', '$put'>;
77
- patch: MethodOrPath<TSchema, 'patch', '$patch'>;
78
- delete: MethodOrPath<TSchema, 'delete', '$delete'>;
79
- };
80
- type DynamicAccess<TSchema> = ExtractDynamicSchema<TSchema> extends never ? object : {
81
- [key: string]: EnlaceClient<ExtractDynamicSchema<TSchema>>;
82
- [key: number]: EnlaceClient<ExtractDynamicSchema<TSchema>>;
83
- };
84
- type MethodNameKeys = 'get' | 'post' | 'put' | 'patch' | 'delete';
85
- type EnlaceClient<TSchema> = HttpMethods<TSchema> & DynamicAccess<TSchema> & {
86
- [K in keyof StaticPathKeys<TSchema> as K extends MethodNameKeys ? never : K]: EnlaceClient<TSchema[K]>;
87
- };
88
- type WildcardMethodFn = (options?: RequestOptions<unknown>) => Promise<EnlaceResponse<unknown, unknown>>;
89
- /**
90
- * Wildcard client type - allows any path access when no schema is provided.
91
- * All methods are available at every level and return unknown types.
92
- */
93
- type WildcardClient = {
94
- get: WildcardMethodFn;
95
- post: WildcardMethodFn;
96
- put: WildcardMethodFn;
97
- patch: WildcardMethodFn;
98
- delete: WildcardMethodFn;
99
- } & {
100
- [key: string]: WildcardClient;
101
- [key: number]: WildcardClient;
102
- };
103
-
104
- export type { EnlaceOptions as E, FetchExecutor as F, HttpMethod as H, MethodDefinition as M, RequestOptions as R, SchemaMethod as S, WildcardClient as W, EnlaceResponse as a, EnlaceClient as b, Endpoint as c };
@@ -1,104 +0,0 @@
1
- type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
2
- type SchemaMethod = '$get' | '$post' | '$put' | '$patch' | '$delete';
3
- type EnlaceResponse<TData, TError> = {
4
- ok: true;
5
- status: number;
6
- data: TData;
7
- error?: never;
8
- } | {
9
- ok: false;
10
- status: number;
11
- data?: never;
12
- error: TError;
13
- };
14
- type EnlaceOptions = Omit<RequestInit, 'method' | 'body'>;
15
- type FetchExecutor<TOptions = EnlaceOptions, TRequestOptions = RequestOptions<unknown>> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions) => Promise<EnlaceResponse<TData, TError>>;
16
- type RequestOptions<TBody = never> = {
17
- body?: TBody;
18
- query?: Record<string, string | number | boolean | undefined>;
19
- headers?: HeadersInit;
20
- };
21
- type MethodDefinition = {
22
- data: unknown;
23
- error: unknown;
24
- body?: unknown;
25
- };
26
- /**
27
- * Helper to define an endpoint with proper typing.
28
- * Provides cleaner syntax than writing { data, error, body } manually.
29
- *
30
- * @example
31
- * type MyApi = {
32
- * posts: {
33
- * $get: Endpoint<Post[], ApiError>;
34
- * $post: Endpoint<Post, ApiError, CreatePost>; // with body
35
- * _: {
36
- * $get: Endpoint<Post, NotFoundError>;
37
- * };
38
- * };
39
- * };
40
- */
41
- type Endpoint<TData, TError, TBody = never> = [TBody] extends [never] ? {
42
- data: TData;
43
- error: TError;
44
- } : {
45
- data: TData;
46
- error: TError;
47
- body: TBody;
48
- };
49
- type ExtractMethodDef<TSchema, TMethod extends SchemaMethod> = TSchema extends {
50
- [K in TMethod]: infer M;
51
- } ? M extends MethodDefinition ? M : never : never;
52
- type ExtractData<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
53
- data: infer D;
54
- } ? D : never;
55
- type ExtractError<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
56
- error: infer E;
57
- } ? E : never;
58
- type ExtractBody<TSchema, TMethod extends SchemaMethod> = ExtractMethodDef<TSchema, TMethod> extends {
59
- body: infer B;
60
- } ? B : never;
61
- type HasMethod<TSchema, TMethod extends SchemaMethod> = TSchema extends {
62
- [K in TMethod]: MethodDefinition;
63
- } ? true : false;
64
- type MethodFn<TSchema, TMethod extends SchemaMethod> = HasMethod<TSchema, TMethod> extends true ? ExtractBody<TSchema, TMethod> extends never ? (options?: RequestOptions<never>) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : (options: RequestOptions<ExtractBody<TSchema, TMethod>>) => Promise<EnlaceResponse<ExtractData<TSchema, TMethod>, ExtractError<TSchema, TMethod>>> : never;
65
- type IsSpecialKey<K> = K extends SchemaMethod | '_' ? true : false;
66
- type StaticPathKeys<TSchema> = {
67
- [K in keyof TSchema as IsSpecialKey<K> extends true ? never : K extends string ? K : never]: TSchema[K];
68
- };
69
- type ExtractDynamicSchema<TSchema> = TSchema extends {
70
- _: infer D;
71
- } ? D : never;
72
- type MethodOrPath<TSchema, TMethodName extends string, TSchemaMethod extends SchemaMethod> = TMethodName extends keyof TSchema ? EnlaceClient<TSchema[TMethodName]> : MethodFn<TSchema, TSchemaMethod>;
73
- type HttpMethods<TSchema> = {
74
- get: MethodOrPath<TSchema, 'get', '$get'>;
75
- post: MethodOrPath<TSchema, 'post', '$post'>;
76
- put: MethodOrPath<TSchema, 'put', '$put'>;
77
- patch: MethodOrPath<TSchema, 'patch', '$patch'>;
78
- delete: MethodOrPath<TSchema, 'delete', '$delete'>;
79
- };
80
- type DynamicAccess<TSchema> = ExtractDynamicSchema<TSchema> extends never ? object : {
81
- [key: string]: EnlaceClient<ExtractDynamicSchema<TSchema>>;
82
- [key: number]: EnlaceClient<ExtractDynamicSchema<TSchema>>;
83
- };
84
- type MethodNameKeys = 'get' | 'post' | 'put' | 'patch' | 'delete';
85
- type EnlaceClient<TSchema> = HttpMethods<TSchema> & DynamicAccess<TSchema> & {
86
- [K in keyof StaticPathKeys<TSchema> as K extends MethodNameKeys ? never : K]: EnlaceClient<TSchema[K]>;
87
- };
88
- type WildcardMethodFn = (options?: RequestOptions<unknown>) => Promise<EnlaceResponse<unknown, unknown>>;
89
- /**
90
- * Wildcard client type - allows any path access when no schema is provided.
91
- * All methods are available at every level and return unknown types.
92
- */
93
- type WildcardClient = {
94
- get: WildcardMethodFn;
95
- post: WildcardMethodFn;
96
- put: WildcardMethodFn;
97
- patch: WildcardMethodFn;
98
- delete: WildcardMethodFn;
99
- } & {
100
- [key: string]: WildcardClient;
101
- [key: number]: WildcardClient;
102
- };
103
-
104
- export type { EnlaceOptions as E, FetchExecutor as F, HttpMethod as H, MethodDefinition as M, RequestOptions as R, SchemaMethod as S, WildcardClient as W, EnlaceResponse as a, EnlaceClient as b, Endpoint as c };