@spoosh/core 0.2.2 → 0.4.0
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 +40 -44
- package/dist/index.d.mts +18 -74
- package/dist/index.d.ts +18 -74
- package/dist/index.js +4 -6
- package/dist/index.mjs +4 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,10 @@ type ApiSchema = {
|
|
|
36
36
|
$post: Endpoint<{ data: { url: string }; formData: { file: File } }>;
|
|
37
37
|
};
|
|
38
38
|
payments: {
|
|
39
|
-
$post: Endpoint<{
|
|
39
|
+
$post: Endpoint<{
|
|
40
|
+
data: { id: string };
|
|
41
|
+
urlEncoded: { amount: number; currency: string };
|
|
42
|
+
}>;
|
|
40
43
|
};
|
|
41
44
|
};
|
|
42
45
|
```
|
|
@@ -65,25 +68,22 @@ const { data: newUser } = await api.users.$post({
|
|
|
65
68
|
body: { name: "John", email: "john@example.com" },
|
|
66
69
|
});
|
|
67
70
|
|
|
68
|
-
// GET /api/users/123 (
|
|
69
|
-
const { data: user } = await api.users
|
|
71
|
+
// GET /api/users/123 (direct usage - simplest)
|
|
72
|
+
const { data: user } = await api.users(123).$get();
|
|
70
73
|
|
|
71
|
-
//
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// Alternative bracket syntaxes (less recommended):
|
|
77
|
-
// api.users[":userId"].$get() - works but no type inference for params
|
|
78
|
-
// api.users[userId].$get() - works with variable, no type inference
|
|
79
|
-
|
|
80
|
-
// PUT /api/users/123
|
|
81
|
-
const { data: updated } = await api.users[123].$put({
|
|
74
|
+
// PUT /api/users/123 (with variable)
|
|
75
|
+
const userId = 123;
|
|
76
|
+
const { data: updated } = await api.users(userId).$put({
|
|
82
77
|
body: { name: "John Updated" },
|
|
83
78
|
});
|
|
84
79
|
|
|
85
80
|
// DELETE /api/users/123
|
|
86
|
-
await api.users
|
|
81
|
+
await api.users(123).$delete();
|
|
82
|
+
|
|
83
|
+
// Typed params (advanced - when you need explicit param names)
|
|
84
|
+
const { data } = await api.users(":userId").$get({
|
|
85
|
+
params: { userId: 123 },
|
|
86
|
+
});
|
|
87
87
|
|
|
88
88
|
// POST with FormData
|
|
89
89
|
const { data: uploaded } = await api.upload.$post({
|
|
@@ -124,7 +124,7 @@ const api = createClient<ApiSchema>({ baseUrl: process.env.API_URL! });
|
|
|
124
124
|
const { data: posts } = await api.posts.$get();
|
|
125
125
|
|
|
126
126
|
// Auto-generates next: { tags: ['users', 'users/123', 'users/123/posts'] }
|
|
127
|
-
const { data: userPosts } = await api.users
|
|
127
|
+
const { data: userPosts } = await api.users(123).posts.$get();
|
|
128
128
|
```
|
|
129
129
|
|
|
130
130
|
This enables automatic cache invalidation with `revalidateTag()` in Next.js.
|
|
@@ -176,16 +176,16 @@ const updatedContext = await applyMiddlewares(context, middlewares, "before");
|
|
|
176
176
|
|
|
177
177
|
## Schema Types
|
|
178
178
|
|
|
179
|
-
| Type
|
|
180
|
-
|
|
|
181
|
-
| `Endpoint<{ data }>`
|
|
182
|
-
| `Endpoint<{ data; body }>`
|
|
183
|
-
| `Endpoint<{ data; query }>`
|
|
184
|
-
| `Endpoint<{ data; formData }>`
|
|
185
|
-
| `Endpoint<{ data; urlEncoded }>`
|
|
186
|
-
| `Endpoint<{ data; error }>`
|
|
187
|
-
| `void`
|
|
188
|
-
| `_`
|
|
179
|
+
| Type | Description | Example |
|
|
180
|
+
| -------------------------------- | ---------------------------- | ------------------------------------------------------------------- |
|
|
181
|
+
| `Endpoint<{ data }>` | Endpoint with data only | `$get: Endpoint<{ data: User[] }>` |
|
|
182
|
+
| `Endpoint<{ data; body }>` | Endpoint with JSON body | `$post: Endpoint<{ data: User; body: CreateUserBody }>` |
|
|
183
|
+
| `Endpoint<{ data; query }>` | Endpoint with query params | `$get: Endpoint<{ data: User[]; query: { page: number } }>` |
|
|
184
|
+
| `Endpoint<{ data; formData }>` | Endpoint with multipart form | `$post: Endpoint<{ data: Result; formData: { file: File } }>` |
|
|
185
|
+
| `Endpoint<{ data; urlEncoded }>` | Endpoint with URL-encoded | `$post: Endpoint<{ data: Result; urlEncoded: { amount: number } }>` |
|
|
186
|
+
| `Endpoint<{ data; error }>` | Endpoint with typed error | `$get: Endpoint<{ data: User; error: ApiError }>` |
|
|
187
|
+
| `void` | No response body | `$delete: void` |
|
|
188
|
+
| `_` | Dynamic path segment | `users: { _: { $get: Endpoint<{ data: User }> } }` |
|
|
189
189
|
|
|
190
190
|
## API Reference
|
|
191
191
|
|
|
@@ -209,11 +209,8 @@ import { cachePlugin } from "@spoosh/plugin-cache";
|
|
|
209
209
|
import { retryPlugin } from "@spoosh/plugin-retry";
|
|
210
210
|
|
|
211
211
|
const client = new Spoosh<ApiSchema, Error>("/api", {
|
|
212
|
-
headers: { Authorization: "Bearer token" }
|
|
213
|
-
}).use([
|
|
214
|
-
cachePlugin({ staleTime: 5000 }),
|
|
215
|
-
retryPlugin({ retries: 3 })
|
|
216
|
-
]);
|
|
212
|
+
headers: { Authorization: "Bearer token" },
|
|
213
|
+
}).use([cachePlugin({ staleTime: 5000 }), retryPlugin({ retries: 3 })]);
|
|
217
214
|
|
|
218
215
|
const { api } = client;
|
|
219
216
|
const { data } = await api.users.$get();
|
|
@@ -221,26 +218,25 @@ const { data } = await api.users.$get();
|
|
|
221
218
|
|
|
222
219
|
**Constructor Parameters:**
|
|
223
220
|
|
|
224
|
-
| Parameter | Type
|
|
225
|
-
| ---------------- |
|
|
226
|
-
| `baseUrl` | `string`
|
|
227
|
-
| `defaultOptions` | `RequestInit`
|
|
221
|
+
| Parameter | Type | Description |
|
|
222
|
+
| ---------------- | ------------- | -------------------------------- |
|
|
223
|
+
| `baseUrl` | `string` | Base URL for all API requests |
|
|
224
|
+
| `defaultOptions` | `RequestInit` | (Optional) Default fetch options |
|
|
228
225
|
|
|
229
226
|
**Methods:**
|
|
230
227
|
|
|
231
|
-
| Method
|
|
232
|
-
|
|
|
228
|
+
| Method | Description |
|
|
229
|
+
| --------------- | --------------------------------------------------------------------- |
|
|
233
230
|
| `.use(plugins)` | Add plugins to the client. Returns a new instance with updated types. |
|
|
234
231
|
|
|
235
232
|
**Properties:**
|
|
236
233
|
|
|
237
|
-
| Property
|
|
238
|
-
|
|
|
239
|
-
| `.api`
|
|
240
|
-
| `.stateManager`
|
|
241
|
-
| `.eventEmitter`
|
|
242
|
-
| `.pluginExecutor` | Plugin lifecycle management
|
|
243
|
-
|
|
234
|
+
| Property | Description |
|
|
235
|
+
| ----------------- | ---------------------------------------- |
|
|
236
|
+
| `.api` | Type-safe API client for making requests |
|
|
237
|
+
| `.stateManager` | Cache and state management |
|
|
238
|
+
| `.eventEmitter` | Event system for refetch/invalidation |
|
|
239
|
+
| `.pluginExecutor` | Plugin lifecycle management |
|
|
244
240
|
|
|
245
241
|
## Creating Plugins
|
|
246
242
|
|
package/dist/index.d.mts
CHANGED
|
@@ -787,7 +787,7 @@ type HasMethod<TSchema, TMethod extends SchemaMethod> = TSchema extends {
|
|
|
787
787
|
} ? true : false;
|
|
788
788
|
type HasRequiredOptions<TSchema, TMethod extends SchemaMethod, TDefaultError = unknown> = [ExtractBody<TSchema, TMethod, TDefaultError>] extends [never] ? [ExtractFormData<TSchema, TMethod, TDefaultError>] extends [never] ? [ExtractUrlEncoded<TSchema, TMethod, TDefaultError>] extends [never] ? false : true : true : true;
|
|
789
789
|
|
|
790
|
-
type ExtractParamName$1<S
|
|
790
|
+
type ExtractParamName$1<S> = S extends `:${infer P}` ? P : never;
|
|
791
791
|
type MethodRequestOptions<TSchema, TMethod extends SchemaMethod, TDefaultError, TOptionsMap, TParamNames extends string, TRequired extends boolean> = TRequired extends true ? RequestOptions<ExtractBody<TSchema, TMethod, TDefaultError>, ExtractQuery<TSchema, TMethod, TDefaultError>, ExtractFormData<TSchema, TMethod, TDefaultError>, ExtractUrlEncoded<TSchema, TMethod, TDefaultError>> & ComputeRequestOptions<ExtractMethodOptions<TOptionsMap, TMethod>, TParamNames> : RequestOptions<never, ExtractQuery<TSchema, TMethod, TDefaultError>, never, never> & ComputeRequestOptions<ExtractMethodOptions<TOptionsMap, TMethod>, TParamNames>;
|
|
792
792
|
type MethodFn<TSchema, TMethod extends SchemaMethod, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = HasMethod<TSchema, TMethod> extends true ? HasRequiredOptions<TSchema, TMethod, TDefaultError> extends true ? (options: MethodRequestOptions<TSchema, TMethod, TDefaultError, TOptionsMap, TParamNames, true>) => Promise<SpooshResponse<ExtractData<TSchema, TMethod, TDefaultError>, ExtractError<TSchema, TMethod, TDefaultError>, MethodRequestOptions<TSchema, TMethod, TDefaultError, TOptionsMap, TParamNames, true>, ExtractQuery<TSchema, TMethod, TDefaultError>, ExtractBody<TSchema, TMethod, TDefaultError>, ExtractFormData<TSchema, TMethod, TDefaultError>, ExtractUrlEncoded<TSchema, TMethod, TDefaultError>, TParamNames>> : (options?: MethodRequestOptions<TSchema, TMethod, TDefaultError, TOptionsMap, TParamNames, false>) => Promise<SpooshResponse<ExtractData<TSchema, TMethod, TDefaultError>, ExtractError<TSchema, TMethod, TDefaultError>, MethodRequestOptions<TSchema, TMethod, TDefaultError, TOptionsMap, TParamNames, false>, ExtractQuery<TSchema, TMethod, TDefaultError>, ExtractBody<TSchema, TMethod, TDefaultError>, ExtractFormData<TSchema, TMethod, TDefaultError>, ExtractUrlEncoded<TSchema, TMethod, TDefaultError>, TParamNames>> : never;
|
|
793
793
|
type IsSpecialKey<K> = K extends SchemaMethod | "_" ? true : false;
|
|
@@ -801,35 +801,22 @@ type HttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamN
|
|
|
801
801
|
[K in SchemaMethod as K extends keyof TSchema ? K : never]: MethodFn<TSchema, K, TDefaultError, TOptionsMap, TParamNames>;
|
|
802
802
|
};
|
|
803
803
|
type DynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = ExtractDynamicSchema<TSchema> extends never ? object : {
|
|
804
|
-
[key: string]: SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
805
|
-
[key: number]: SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
806
804
|
/**
|
|
807
805
|
* Dynamic path segment with typed param name.
|
|
808
806
|
* Use `:paramName` format to get typed params in the response.
|
|
809
807
|
*
|
|
810
808
|
* @example
|
|
811
809
|
* ```ts
|
|
812
|
-
* //
|
|
810
|
+
* // With number
|
|
811
|
+
* await api.users(123).$get()
|
|
812
|
+
*
|
|
813
|
+
* // With typed params
|
|
813
814
|
* const { data, params } = await api.users(':userId').$get({ params: { userId: 123 } })
|
|
814
815
|
* ```
|
|
815
816
|
*/
|
|
816
|
-
<TKey extends string>(key: TKey): SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | ExtractParamName$1<TKey>, TRootSchema>;
|
|
817
|
+
<TKey extends string | number>(key: TKey): SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | ExtractParamName$1<TKey>, TRootSchema>;
|
|
817
818
|
};
|
|
818
|
-
type
|
|
819
|
-
_: infer D;
|
|
820
|
-
} ? {
|
|
821
|
-
/**
|
|
822
|
-
* Dynamic path segment placeholder for routes like `/posts/:id`.
|
|
823
|
-
*
|
|
824
|
-
* @example
|
|
825
|
-
* ```ts
|
|
826
|
-
* // Direct client usage
|
|
827
|
-
* const { data } = await api.posts._.$get({ params: { id: 123 } })
|
|
828
|
-
* ```
|
|
829
|
-
*/
|
|
830
|
-
_: SpooshClient<D, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
831
|
-
} : object;
|
|
832
|
-
type SpooshClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = HttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & DynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & DynamicKey<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & {
|
|
819
|
+
type SpooshClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = HttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & DynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & {
|
|
833
820
|
[K in keyof StaticPathKeys<TSchema> as K extends SchemaMethod ? never : K]: SpooshClient<TSchema[K], TDefaultError, TOptionsMap, TParamNames, TRootSchema>;
|
|
834
821
|
};
|
|
835
822
|
|
|
@@ -852,29 +839,13 @@ type HasMutationMethods<TSchema> = TSchema extends object ? MutationMethod exten
|
|
|
852
839
|
type QueryHttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = {
|
|
853
840
|
[K in QueryMethod as K extends keyof TSchema ? K : never]: MethodFn<TSchema, K, TDefaultError, TOptionsMap, TParamNames>;
|
|
854
841
|
};
|
|
855
|
-
type ExtractParamName<S
|
|
842
|
+
type ExtractParamName<S> = S extends `:${infer P}` ? P : never;
|
|
856
843
|
type QueryDynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = TSchema extends {
|
|
857
844
|
_: infer D;
|
|
858
845
|
} ? HasQueryMethods<D> extends true ? {
|
|
859
|
-
|
|
860
|
-
[key: number]: QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
861
|
-
<TKey extends string>(key: TKey): QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>, TRootSchema>;
|
|
846
|
+
<TKey extends string | number>(key: TKey): QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>, TRootSchema>;
|
|
862
847
|
} : object : object;
|
|
863
|
-
type
|
|
864
|
-
_: infer D;
|
|
865
|
-
} ? HasQueryMethods<D> extends true ? {
|
|
866
|
-
/**
|
|
867
|
-
* Dynamic path segment placeholder for routes like `/posts/:id`.
|
|
868
|
-
*
|
|
869
|
-
* @example
|
|
870
|
-
* ```ts
|
|
871
|
-
* useRead((api) => api.posts[123].$get())
|
|
872
|
-
* useRead((api) => api.posts(':id').$get({ params: { id: 123 } }))
|
|
873
|
-
* ```
|
|
874
|
-
*/
|
|
875
|
-
_: QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
876
|
-
} : object : object;
|
|
877
|
-
type QueryOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = QueryHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & QueryDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & QueryDynamicKey<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & {
|
|
848
|
+
type QueryOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = QueryHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & QueryDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & {
|
|
878
849
|
[K in keyof StaticPathKeys<TSchema> as K extends SchemaMethod ? never : HasQueryMethods<TSchema[K]> extends true ? K : never]: QueryOnlyClient<TSchema[K], TDefaultError, TOptionsMap, TParamNames, TRootSchema>;
|
|
879
850
|
};
|
|
880
851
|
type MutationHttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = {
|
|
@@ -883,25 +854,9 @@ type MutationHttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object,
|
|
|
883
854
|
type MutationDynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = TSchema extends {
|
|
884
855
|
_: infer D;
|
|
885
856
|
} ? HasMutationMethods<D> extends true ? {
|
|
886
|
-
|
|
887
|
-
[key: number]: MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string>;
|
|
888
|
-
<TKey extends string>(key: TKey): MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>>;
|
|
889
|
-
} : object : object;
|
|
890
|
-
type MutationDynamicKey<TSchema, TDefaultError, TOptionsMap, TParamNames extends string = never> = TSchema extends {
|
|
891
|
-
_: infer D;
|
|
892
|
-
} ? HasMutationMethods<D> extends true ? {
|
|
893
|
-
/**
|
|
894
|
-
* Dynamic path segment placeholder for routes like `/posts/:id`.
|
|
895
|
-
*
|
|
896
|
-
* @example
|
|
897
|
-
* ```ts
|
|
898
|
-
* const { trigger } = useWrite((api) => api.posts(':id').$delete)
|
|
899
|
-
* trigger({ params: { id: 123 } })
|
|
900
|
-
* ```
|
|
901
|
-
*/
|
|
902
|
-
_: MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string>;
|
|
857
|
+
<TKey extends string | number>(key: TKey): MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>>;
|
|
903
858
|
} : object : object;
|
|
904
|
-
type MutationOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = MutationHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & MutationDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames> &
|
|
859
|
+
type MutationOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = MutationHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & MutationDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames> & {
|
|
905
860
|
[K in keyof StaticPathKeys<TSchema> as K extends SchemaMethod ? never : HasMutationMethods<TSchema[K]> extends true ? K : never]: MutationOnlyClient<TSchema[K], TDefaultError, TOptionsMap, TParamNames>;
|
|
906
861
|
};
|
|
907
862
|
|
|
@@ -948,7 +903,7 @@ type EndpointToMethod<T> = (options?: ExtractEndpointRequestOptions<T>) => Promi
|
|
|
948
903
|
* trigger({
|
|
949
904
|
* myCallback: (api) => [
|
|
950
905
|
* api.posts.$get, // ✓ Valid
|
|
951
|
-
* api.users
|
|
906
|
+
* api.users(1).$get, // ✓ Dynamic segment
|
|
952
907
|
* api.nonexistent.$get, // ✗ Type error
|
|
953
908
|
* ],
|
|
954
909
|
* });
|
|
@@ -961,18 +916,7 @@ type QuerySchemaHelper<TSchema> = {
|
|
|
961
916
|
} & (TSchema extends {
|
|
962
917
|
_: infer D;
|
|
963
918
|
} ? HasQueryMethods<D> extends true ? {
|
|
964
|
-
|
|
965
|
-
* Dynamic path segment placeholder for routes like `/posts/:id`.
|
|
966
|
-
*
|
|
967
|
-
* @example
|
|
968
|
-
* ```ts
|
|
969
|
-
* // In plugin callback - reference the endpoint
|
|
970
|
-
* myCallback: (api) => api.posts._.$get
|
|
971
|
-
* ```
|
|
972
|
-
*/
|
|
973
|
-
_: QuerySchemaHelper<D>;
|
|
974
|
-
[key: string]: QuerySchemaHelper<D>;
|
|
975
|
-
[key: number]: QuerySchemaHelper<D>;
|
|
919
|
+
<TKey extends string | number>(key: TKey): QuerySchemaHelper<D>;
|
|
976
920
|
} : object : object);
|
|
977
921
|
|
|
978
922
|
type PluginArray = readonly SpooshPlugin<PluginTypeConfig>[];
|
|
@@ -1119,7 +1063,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1119
1063
|
* const { data } = await api.posts.$post({ body: { title: 'Hello' } });
|
|
1120
1064
|
*
|
|
1121
1065
|
* // Dynamic path parameters
|
|
1122
|
-
* const { data } = await api.posts
|
|
1066
|
+
* const { data } = await api.posts(postId).$get();
|
|
1123
1067
|
* ```
|
|
1124
1068
|
*/
|
|
1125
1069
|
get api(): SpooshClient<TSchema, TError, CoreRequestOptionsBase>;
|
|
@@ -1240,7 +1184,7 @@ type SpooshClientConfig = {
|
|
|
1240
1184
|
*
|
|
1241
1185
|
* // Type-safe API calls
|
|
1242
1186
|
* const { data } = await api.posts.$get();
|
|
1243
|
-
* const { data: post } = await api.posts
|
|
1187
|
+
* const { data: post } = await api.posts(123).$get();
|
|
1244
1188
|
* ```
|
|
1245
1189
|
*/
|
|
1246
1190
|
declare function createClient<TSchema, TDefaultError = unknown>(config: SpooshClientConfig): SpooshClient<TSchema, TDefaultError>;
|
|
@@ -1313,9 +1257,9 @@ type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
|
1313
1257
|
* await api.posts.$get();
|
|
1314
1258
|
*
|
|
1315
1259
|
* // Dynamic segments via function call:
|
|
1316
|
-
* // api.posts
|
|
1260
|
+
* // api.posts(123).$get() or api.posts('123').$get()
|
|
1317
1261
|
* // Executes: GET /api/posts/123
|
|
1318
|
-
* await api.posts
|
|
1262
|
+
* await api.posts(123).$get();
|
|
1319
1263
|
* ```
|
|
1320
1264
|
*/
|
|
1321
1265
|
declare function createProxyHandler<TSchema extends object, TOptions = SpooshOptions>(config: ProxyHandlerConfig<TOptions>): TSchema;
|
package/dist/index.d.ts
CHANGED
|
@@ -787,7 +787,7 @@ type HasMethod<TSchema, TMethod extends SchemaMethod> = TSchema extends {
|
|
|
787
787
|
} ? true : false;
|
|
788
788
|
type HasRequiredOptions<TSchema, TMethod extends SchemaMethod, TDefaultError = unknown> = [ExtractBody<TSchema, TMethod, TDefaultError>] extends [never] ? [ExtractFormData<TSchema, TMethod, TDefaultError>] extends [never] ? [ExtractUrlEncoded<TSchema, TMethod, TDefaultError>] extends [never] ? false : true : true : true;
|
|
789
789
|
|
|
790
|
-
type ExtractParamName$1<S
|
|
790
|
+
type ExtractParamName$1<S> = S extends `:${infer P}` ? P : never;
|
|
791
791
|
type MethodRequestOptions<TSchema, TMethod extends SchemaMethod, TDefaultError, TOptionsMap, TParamNames extends string, TRequired extends boolean> = TRequired extends true ? RequestOptions<ExtractBody<TSchema, TMethod, TDefaultError>, ExtractQuery<TSchema, TMethod, TDefaultError>, ExtractFormData<TSchema, TMethod, TDefaultError>, ExtractUrlEncoded<TSchema, TMethod, TDefaultError>> & ComputeRequestOptions<ExtractMethodOptions<TOptionsMap, TMethod>, TParamNames> : RequestOptions<never, ExtractQuery<TSchema, TMethod, TDefaultError>, never, never> & ComputeRequestOptions<ExtractMethodOptions<TOptionsMap, TMethod>, TParamNames>;
|
|
792
792
|
type MethodFn<TSchema, TMethod extends SchemaMethod, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = HasMethod<TSchema, TMethod> extends true ? HasRequiredOptions<TSchema, TMethod, TDefaultError> extends true ? (options: MethodRequestOptions<TSchema, TMethod, TDefaultError, TOptionsMap, TParamNames, true>) => Promise<SpooshResponse<ExtractData<TSchema, TMethod, TDefaultError>, ExtractError<TSchema, TMethod, TDefaultError>, MethodRequestOptions<TSchema, TMethod, TDefaultError, TOptionsMap, TParamNames, true>, ExtractQuery<TSchema, TMethod, TDefaultError>, ExtractBody<TSchema, TMethod, TDefaultError>, ExtractFormData<TSchema, TMethod, TDefaultError>, ExtractUrlEncoded<TSchema, TMethod, TDefaultError>, TParamNames>> : (options?: MethodRequestOptions<TSchema, TMethod, TDefaultError, TOptionsMap, TParamNames, false>) => Promise<SpooshResponse<ExtractData<TSchema, TMethod, TDefaultError>, ExtractError<TSchema, TMethod, TDefaultError>, MethodRequestOptions<TSchema, TMethod, TDefaultError, TOptionsMap, TParamNames, false>, ExtractQuery<TSchema, TMethod, TDefaultError>, ExtractBody<TSchema, TMethod, TDefaultError>, ExtractFormData<TSchema, TMethod, TDefaultError>, ExtractUrlEncoded<TSchema, TMethod, TDefaultError>, TParamNames>> : never;
|
|
793
793
|
type IsSpecialKey<K> = K extends SchemaMethod | "_" ? true : false;
|
|
@@ -801,35 +801,22 @@ type HttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamN
|
|
|
801
801
|
[K in SchemaMethod as K extends keyof TSchema ? K : never]: MethodFn<TSchema, K, TDefaultError, TOptionsMap, TParamNames>;
|
|
802
802
|
};
|
|
803
803
|
type DynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = ExtractDynamicSchema<TSchema> extends never ? object : {
|
|
804
|
-
[key: string]: SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
805
|
-
[key: number]: SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
806
804
|
/**
|
|
807
805
|
* Dynamic path segment with typed param name.
|
|
808
806
|
* Use `:paramName` format to get typed params in the response.
|
|
809
807
|
*
|
|
810
808
|
* @example
|
|
811
809
|
* ```ts
|
|
812
|
-
* //
|
|
810
|
+
* // With number
|
|
811
|
+
* await api.users(123).$get()
|
|
812
|
+
*
|
|
813
|
+
* // With typed params
|
|
813
814
|
* const { data, params } = await api.users(':userId').$get({ params: { userId: 123 } })
|
|
814
815
|
* ```
|
|
815
816
|
*/
|
|
816
|
-
<TKey extends string>(key: TKey): SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | ExtractParamName$1<TKey>, TRootSchema>;
|
|
817
|
+
<TKey extends string | number>(key: TKey): SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | ExtractParamName$1<TKey>, TRootSchema>;
|
|
817
818
|
};
|
|
818
|
-
type
|
|
819
|
-
_: infer D;
|
|
820
|
-
} ? {
|
|
821
|
-
/**
|
|
822
|
-
* Dynamic path segment placeholder for routes like `/posts/:id`.
|
|
823
|
-
*
|
|
824
|
-
* @example
|
|
825
|
-
* ```ts
|
|
826
|
-
* // Direct client usage
|
|
827
|
-
* const { data } = await api.posts._.$get({ params: { id: 123 } })
|
|
828
|
-
* ```
|
|
829
|
-
*/
|
|
830
|
-
_: SpooshClient<D, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
831
|
-
} : object;
|
|
832
|
-
type SpooshClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = HttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & DynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & DynamicKey<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & {
|
|
819
|
+
type SpooshClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = HttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & DynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & {
|
|
833
820
|
[K in keyof StaticPathKeys<TSchema> as K extends SchemaMethod ? never : K]: SpooshClient<TSchema[K], TDefaultError, TOptionsMap, TParamNames, TRootSchema>;
|
|
834
821
|
};
|
|
835
822
|
|
|
@@ -852,29 +839,13 @@ type HasMutationMethods<TSchema> = TSchema extends object ? MutationMethod exten
|
|
|
852
839
|
type QueryHttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = {
|
|
853
840
|
[K in QueryMethod as K extends keyof TSchema ? K : never]: MethodFn<TSchema, K, TDefaultError, TOptionsMap, TParamNames>;
|
|
854
841
|
};
|
|
855
|
-
type ExtractParamName<S
|
|
842
|
+
type ExtractParamName<S> = S extends `:${infer P}` ? P : never;
|
|
856
843
|
type QueryDynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = TSchema extends {
|
|
857
844
|
_: infer D;
|
|
858
845
|
} ? HasQueryMethods<D> extends true ? {
|
|
859
|
-
|
|
860
|
-
[key: number]: QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
861
|
-
<TKey extends string>(key: TKey): QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>, TRootSchema>;
|
|
846
|
+
<TKey extends string | number>(key: TKey): QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>, TRootSchema>;
|
|
862
847
|
} : object : object;
|
|
863
|
-
type
|
|
864
|
-
_: infer D;
|
|
865
|
-
} ? HasQueryMethods<D> extends true ? {
|
|
866
|
-
/**
|
|
867
|
-
* Dynamic path segment placeholder for routes like `/posts/:id`.
|
|
868
|
-
*
|
|
869
|
-
* @example
|
|
870
|
-
* ```ts
|
|
871
|
-
* useRead((api) => api.posts[123].$get())
|
|
872
|
-
* useRead((api) => api.posts(':id').$get({ params: { id: 123 } }))
|
|
873
|
-
* ```
|
|
874
|
-
*/
|
|
875
|
-
_: QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
|
|
876
|
-
} : object : object;
|
|
877
|
-
type QueryOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = QueryHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & QueryDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & QueryDynamicKey<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & {
|
|
848
|
+
type QueryOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = QueryHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & QueryDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames, TRootSchema> & {
|
|
878
849
|
[K in keyof StaticPathKeys<TSchema> as K extends SchemaMethod ? never : HasQueryMethods<TSchema[K]> extends true ? K : never]: QueryOnlyClient<TSchema[K], TDefaultError, TOptionsMap, TParamNames, TRootSchema>;
|
|
879
850
|
};
|
|
880
851
|
type MutationHttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = {
|
|
@@ -883,25 +854,9 @@ type MutationHttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object,
|
|
|
883
854
|
type MutationDynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = TSchema extends {
|
|
884
855
|
_: infer D;
|
|
885
856
|
} ? HasMutationMethods<D> extends true ? {
|
|
886
|
-
|
|
887
|
-
[key: number]: MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string>;
|
|
888
|
-
<TKey extends string>(key: TKey): MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>>;
|
|
889
|
-
} : object : object;
|
|
890
|
-
type MutationDynamicKey<TSchema, TDefaultError, TOptionsMap, TParamNames extends string = never> = TSchema extends {
|
|
891
|
-
_: infer D;
|
|
892
|
-
} ? HasMutationMethods<D> extends true ? {
|
|
893
|
-
/**
|
|
894
|
-
* Dynamic path segment placeholder for routes like `/posts/:id`.
|
|
895
|
-
*
|
|
896
|
-
* @example
|
|
897
|
-
* ```ts
|
|
898
|
-
* const { trigger } = useWrite((api) => api.posts(':id').$delete)
|
|
899
|
-
* trigger({ params: { id: 123 } })
|
|
900
|
-
* ```
|
|
901
|
-
*/
|
|
902
|
-
_: MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string>;
|
|
857
|
+
<TKey extends string | number>(key: TKey): MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>>;
|
|
903
858
|
} : object : object;
|
|
904
|
-
type MutationOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = MutationHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & MutationDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames> &
|
|
859
|
+
type MutationOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = MutationHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & MutationDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames> & {
|
|
905
860
|
[K in keyof StaticPathKeys<TSchema> as K extends SchemaMethod ? never : HasMutationMethods<TSchema[K]> extends true ? K : never]: MutationOnlyClient<TSchema[K], TDefaultError, TOptionsMap, TParamNames>;
|
|
906
861
|
};
|
|
907
862
|
|
|
@@ -948,7 +903,7 @@ type EndpointToMethod<T> = (options?: ExtractEndpointRequestOptions<T>) => Promi
|
|
|
948
903
|
* trigger({
|
|
949
904
|
* myCallback: (api) => [
|
|
950
905
|
* api.posts.$get, // ✓ Valid
|
|
951
|
-
* api.users
|
|
906
|
+
* api.users(1).$get, // ✓ Dynamic segment
|
|
952
907
|
* api.nonexistent.$get, // ✗ Type error
|
|
953
908
|
* ],
|
|
954
909
|
* });
|
|
@@ -961,18 +916,7 @@ type QuerySchemaHelper<TSchema> = {
|
|
|
961
916
|
} & (TSchema extends {
|
|
962
917
|
_: infer D;
|
|
963
918
|
} ? HasQueryMethods<D> extends true ? {
|
|
964
|
-
|
|
965
|
-
* Dynamic path segment placeholder for routes like `/posts/:id`.
|
|
966
|
-
*
|
|
967
|
-
* @example
|
|
968
|
-
* ```ts
|
|
969
|
-
* // In plugin callback - reference the endpoint
|
|
970
|
-
* myCallback: (api) => api.posts._.$get
|
|
971
|
-
* ```
|
|
972
|
-
*/
|
|
973
|
-
_: QuerySchemaHelper<D>;
|
|
974
|
-
[key: string]: QuerySchemaHelper<D>;
|
|
975
|
-
[key: number]: QuerySchemaHelper<D>;
|
|
919
|
+
<TKey extends string | number>(key: TKey): QuerySchemaHelper<D>;
|
|
976
920
|
} : object : object);
|
|
977
921
|
|
|
978
922
|
type PluginArray = readonly SpooshPlugin<PluginTypeConfig>[];
|
|
@@ -1119,7 +1063,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1119
1063
|
* const { data } = await api.posts.$post({ body: { title: 'Hello' } });
|
|
1120
1064
|
*
|
|
1121
1065
|
* // Dynamic path parameters
|
|
1122
|
-
* const { data } = await api.posts
|
|
1066
|
+
* const { data } = await api.posts(postId).$get();
|
|
1123
1067
|
* ```
|
|
1124
1068
|
*/
|
|
1125
1069
|
get api(): SpooshClient<TSchema, TError, CoreRequestOptionsBase>;
|
|
@@ -1240,7 +1184,7 @@ type SpooshClientConfig = {
|
|
|
1240
1184
|
*
|
|
1241
1185
|
* // Type-safe API calls
|
|
1242
1186
|
* const { data } = await api.posts.$get();
|
|
1243
|
-
* const { data: post } = await api.posts
|
|
1187
|
+
* const { data: post } = await api.posts(123).$get();
|
|
1244
1188
|
* ```
|
|
1245
1189
|
*/
|
|
1246
1190
|
declare function createClient<TSchema, TDefaultError = unknown>(config: SpooshClientConfig): SpooshClient<TSchema, TDefaultError>;
|
|
@@ -1313,9 +1257,9 @@ type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
|
1313
1257
|
* await api.posts.$get();
|
|
1314
1258
|
*
|
|
1315
1259
|
* // Dynamic segments via function call:
|
|
1316
|
-
* // api.posts
|
|
1260
|
+
* // api.posts(123).$get() or api.posts('123').$get()
|
|
1317
1261
|
* // Executes: GET /api/posts/123
|
|
1318
|
-
* await api.posts
|
|
1262
|
+
* await api.posts(123).$get();
|
|
1319
1263
|
* ```
|
|
1320
1264
|
*/
|
|
1321
1265
|
declare function createProxyHandler<TSchema extends object, TOptions = SpooshOptions>(config: ProxyHandlerConfig<TOptions>): TSchema;
|
package/dist/index.js
CHANGED
|
@@ -476,11 +476,9 @@ function createProxyHandler(config) {
|
|
|
476
476
|
nextTags
|
|
477
477
|
});
|
|
478
478
|
},
|
|
479
|
-
// Handles function call syntax for dynamic segments: api.posts(
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
// Eg. api.posts[":id"].$get() <-- TypeScript sees this as bracket notation with a string literal, can't infer param types
|
|
483
|
-
// But api.posts(":id").$get() <-- TypeScript can capture ":id" as a template literal type, enabling params: { id: string } inference
|
|
479
|
+
// Handles function call syntax for dynamic segments: api.posts(123), api.posts(":id"), api.users(userId)
|
|
480
|
+
// This is the only way to access dynamic segments in Spoosh.
|
|
481
|
+
// The function call syntax allows TypeScript to capture the literal type, enabling params: { id: string } inference.
|
|
484
482
|
apply(_target, _thisArg, args) {
|
|
485
483
|
const [segment] = args;
|
|
486
484
|
return createProxyHandler({
|
|
@@ -989,7 +987,7 @@ var Spoosh = class _Spoosh {
|
|
|
989
987
|
* const { data } = await api.posts.$post({ body: { title: 'Hello' } });
|
|
990
988
|
*
|
|
991
989
|
* // Dynamic path parameters
|
|
992
|
-
* const { data } = await api.posts
|
|
990
|
+
* const { data } = await api.posts(postId).$get();
|
|
993
991
|
* ```
|
|
994
992
|
*/
|
|
995
993
|
get api() {
|
package/dist/index.mjs
CHANGED
|
@@ -421,11 +421,9 @@ function createProxyHandler(config) {
|
|
|
421
421
|
nextTags
|
|
422
422
|
});
|
|
423
423
|
},
|
|
424
|
-
// Handles function call syntax for dynamic segments: api.posts(
|
|
425
|
-
//
|
|
426
|
-
//
|
|
427
|
-
// Eg. api.posts[":id"].$get() <-- TypeScript sees this as bracket notation with a string literal, can't infer param types
|
|
428
|
-
// But api.posts(":id").$get() <-- TypeScript can capture ":id" as a template literal type, enabling params: { id: string } inference
|
|
424
|
+
// Handles function call syntax for dynamic segments: api.posts(123), api.posts(":id"), api.users(userId)
|
|
425
|
+
// This is the only way to access dynamic segments in Spoosh.
|
|
426
|
+
// The function call syntax allows TypeScript to capture the literal type, enabling params: { id: string } inference.
|
|
429
427
|
apply(_target, _thisArg, args) {
|
|
430
428
|
const [segment] = args;
|
|
431
429
|
return createProxyHandler({
|
|
@@ -934,7 +932,7 @@ var Spoosh = class _Spoosh {
|
|
|
934
932
|
* const { data } = await api.posts.$post({ body: { title: 'Hello' } });
|
|
935
933
|
*
|
|
936
934
|
* // Dynamic path parameters
|
|
937
|
-
* const { data } = await api.posts
|
|
935
|
+
* const { data } = await api.posts(postId).$get();
|
|
938
936
|
* ```
|
|
939
937
|
*/
|
|
940
938
|
get api() {
|