enlace 0.0.1-beta.15 → 0.0.1-beta.17

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
@@ -30,9 +30,7 @@ type ApiSchema = {
30
30
  };
31
31
 
32
32
  // Pass global error type as second generic
33
- const useAPI = enlaceHookReact<ApiSchema, ApiError>(
34
- "https://api.example.com"
35
- );
33
+ const useAPI = enlaceHookReact<ApiSchema, ApiError>("https://api.example.com");
36
34
  ```
37
35
 
38
36
  ## Schema Conventions
@@ -98,11 +96,11 @@ import { Endpoint } from "enlace";
98
96
 
99
97
  type ApiSchema = {
100
98
  posts: {
101
- $get: Post[]; // Direct type (simplest)
102
- $post: Endpoint<Post, CreatePost>; // Data + Body
99
+ $get: Post[]; // Direct type (simplest)
100
+ $post: Endpoint<Post, CreatePost>; // Data + Body
103
101
  $put: Endpoint<Post, UpdatePost, ValidationError>; // Data + Body + Error
104
- $delete: void; // void response
105
- $patch: Endpoint<Post, never, NotFoundError>; // Custom error without body
102
+ $delete: void; // void response
103
+ $patch: Endpoint<Post, never, NotFoundError>; // Custom error without body
106
104
  };
107
105
  };
108
106
  ```
@@ -116,15 +114,24 @@ import { EndpointWithQuery } from "enlace";
116
114
 
117
115
  type ApiSchema = {
118
116
  users: {
119
- $get: EndpointWithQuery<User[], { page: number; limit: number; search?: string }>;
117
+ $get: EndpointWithQuery<
118
+ User[],
119
+ { page: number; limit: number; search?: string }
120
+ >;
120
121
  };
121
122
  posts: {
122
- $get: EndpointWithQuery<Post[], { status: "draft" | "published" }, ApiError>;
123
+ $get: EndpointWithQuery<
124
+ Post[],
125
+ { status: "draft" | "published" },
126
+ ApiError
127
+ >;
123
128
  };
124
129
  };
125
130
 
126
131
  // Usage - query params are fully typed
127
- const { data } = useAPI((api) => api.users.$get({ query: { page: 1, limit: 10 } }));
132
+ const { data } = useAPI((api) =>
133
+ api.users.$get({ query: { page: 1, limit: 10 } })
134
+ );
128
135
  // api.users.$get({ query: { foo: "bar" } }); // ✗ Error: 'foo' does not exist
129
136
  ```
130
137
 
@@ -148,22 +155,22 @@ type ApiSchema = {
148
155
  const { trigger } = useAPI((api) => api.uploads.$post);
149
156
  trigger({
150
157
  formData: {
151
- file: selectedFile, // File object
152
- name: "document.pdf", // String - converted automatically
153
- }
158
+ file: selectedFile, // File object
159
+ name: "document.pdf", // String - converted automatically
160
+ },
154
161
  });
155
162
  // → Sends as multipart/form-data
156
163
  ```
157
164
 
158
165
  **FormData conversion rules:**
159
166
 
160
- | Type | Conversion |
161
- |------|------------|
162
- | `File` / `Blob` | Appended directly |
163
- | `string` / `number` / `boolean` | Converted to string |
164
- | `object` (nested) | JSON stringified |
165
- | `array` of primitives | Each item appended separately |
166
- | `array` of files | Each file appended with same key |
167
+ | Type | Conversion |
168
+ | ------------------------------- | -------------------------------- |
169
+ | `File` / `Blob` | Appended directly |
170
+ | `string` / `number` / `boolean` | Converted to string |
171
+ | `object` (nested) | JSON stringified |
172
+ | `array` of primitives | Each item appended separately |
173
+ | `array` of files | Each file appended with same key |
167
174
 
168
175
  #### `EndpointFull<T>`
169
176
 
@@ -311,28 +318,30 @@ function OrderStatus({ orderId }: { orderId: string }) {
311
318
  ```
312
319
 
313
320
  The function receives `(data, error)` and should return:
321
+
314
322
  - `number`: Interval in milliseconds
315
323
  - `false`: Stop polling
316
324
 
317
325
  ```typescript
318
326
  // Poll faster when there's an error (retry), slower otherwise
319
- { pollingInterval: (data, error) => error ? 1000 : 10000 }
327
+ {
328
+ pollingInterval: (data, error) => (error ? 1000 : 10000);
329
+ }
320
330
 
321
331
  // Stop polling once data meets a condition
322
- { pollingInterval: (order) => order?.status === "completed" ? false : 3000 }
332
+ {
333
+ pollingInterval: (order) => (order?.status === "completed" ? false : 3000);
334
+ }
323
335
  ```
324
336
 
325
337
  **Combined with conditional fetching:**
326
338
 
327
339
  ```typescript
328
340
  function OrderStatus({ orderId }: { orderId: string | undefined }) {
329
- const { data } = useAPI(
330
- (api) => api.orders[orderId!].$get(),
331
- {
332
- enabled: !!orderId,
333
- pollingInterval: 10000, // Poll every 10 seconds
334
- }
335
- );
341
+ const { data } = useAPI((api) => api.orders[orderId!].$get(), {
342
+ enabled: !!orderId,
343
+ pollingInterval: 10000, // Poll every 10 seconds
344
+ });
336
345
  // Polling only runs when orderId is defined
337
346
  }
338
347
  ```
@@ -651,21 +660,15 @@ type EnlaceErrorCallbackPayload<T> =
651
660
  const result = useAPI((api) => api.posts.$get());
652
661
 
653
662
  // With options
654
- const result = useAPI(
655
- (api) => api.posts.$get(),
656
- {
657
- enabled: true, // Skip fetching when false
658
- pollingInterval: 5000 // Refetch every 5s after previous request completes
659
- }
660
- );
663
+ const result = useAPI((api) => api.posts.$get(), {
664
+ enabled: true, // Skip fetching when false
665
+ pollingInterval: 5000, // Refetch every 5s after previous request completes
666
+ });
661
667
 
662
668
  // With dynamic polling
663
- const result = useAPI(
664
- (api) => api.orders[id].$get(),
665
- {
666
- pollingInterval: (order) => order?.status === "pending" ? 2000 : false
667
- }
668
- );
669
+ const result = useAPI((api) => api.orders[id].$get(), {
670
+ pollingInterval: (order) => (order?.status === "pending" ? 2000 : false),
671
+ });
669
672
 
670
673
  type UseEnlaceQueryResult<TData, TError> = {
671
674
  loading: boolean; // No cached data and fetching
@@ -750,9 +753,7 @@ import { enlaceHookNext } from "enlace/hook";
750
753
 
751
754
  type ApiError = { message: string };
752
755
 
753
- const useAPI = enlaceHookNext<ApiSchema, ApiError>(
754
- "https://api.example.com"
755
- );
756
+ const useAPI = enlaceHookNext<ApiSchema, ApiError>("https://api.example.com");
756
757
  ```
757
758
 
758
759
  ### Server-Side Revalidation
@@ -922,6 +923,28 @@ npm install enlace-openapi
922
923
  enlace-openapi --schema ./types/APISchema.ts --output ./openapi.json
923
924
  ```
924
925
 
926
+ ## Framework Adapters
927
+
928
+ ### Hono
929
+
930
+ Use [`enlace-hono`](../hono/README.md) to automatically generate Enlace schemas from your Hono app:
931
+
932
+ ```typescript
933
+ import { Hono } from "hono";
934
+ import type { HonoToEnlace } from "enlace-hono";
935
+
936
+ const app = new Hono()
937
+ .basePath("/api")
938
+ .get("/posts", (c) => c.json([{ id: 1, title: "Hello" }]))
939
+ .get("/posts/:id", (c) => c.json({ id: c.req.param("id") }));
940
+
941
+ // Auto-generate schema from Hono types
942
+ type ApiSchema = HonoToEnlace<typeof app>;
943
+
944
+ // Use with Enlace
945
+ const client = enlace<ApiSchema["api"]>("http://localhost:3000/api");
946
+ ```
947
+
925
948
  ## License
926
949
 
927
950
  MIT
@@ -68,7 +68,7 @@ type TrackedCall = {
68
68
  method: string;
69
69
  options: unknown;
70
70
  };
71
- declare const HTTP_METHODS: readonly ["get", "post", "put", "patch", "delete"];
71
+ declare const HTTP_METHODS: readonly ["$get", "$post", "$put", "$patch", "$delete"];
72
72
  type ExtractData<T> = T extends (...args: any[]) => Promise<EnlaceResponse<infer D, unknown>> ? D : never;
73
73
  type ExtractError<T> = T extends (...args: any[]) => Promise<EnlaceResponse<unknown, infer E>> ? E : never;
74
74
  /** Discriminated union for hook response state - enables type narrowing via error check */
@@ -68,7 +68,7 @@ type TrackedCall = {
68
68
  method: string;
69
69
  options: unknown;
70
70
  };
71
- declare const HTTP_METHODS: readonly ["get", "post", "put", "patch", "delete"];
71
+ declare const HTTP_METHODS: readonly ["$get", "$post", "$put", "$patch", "$delete"];
72
72
  type ExtractData<T> = T extends (...args: any[]) => Promise<EnlaceResponse<infer D, unknown>> ? D : never;
73
73
  type ExtractError<T> = T extends (...args: any[]) => Promise<EnlaceResponse<unknown, infer E>> ? E : never;
74
74
  /** Discriminated union for hook response state - enables type narrowing via error check */
@@ -329,7 +329,7 @@ function useQueryMode(api, trackedCall, options) {
329
329
  }
330
330
 
331
331
  // src/react/types.ts
332
- var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
332
+ var HTTP_METHODS = ["$get", "$post", "$put", "$patch", "$delete"];
333
333
 
334
334
  // src/react/trackingProxy.ts
335
335
  function createTrackingProxy(onTrack) {
@@ -304,7 +304,7 @@ function useQueryMode(api, trackedCall, options) {
304
304
  }
305
305
 
306
306
  // src/react/types.ts
307
- var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
307
+ var HTTP_METHODS = ["$get", "$post", "$put", "$patch", "$delete"];
308
308
 
309
309
  // src/react/trackingProxy.ts
310
310
  function createTrackingProxy(onTrack) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enlace",
3
- "version": "0.0.1-beta.15",
3
+ "version": "0.0.1-beta.17",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "dist"