@spoosh/core 0.3.0 → 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 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<{ data: { id: string }; urlEncoded: { amount: number; currency: string } }>;
39
+ $post: Endpoint<{
40
+ data: { id: string };
41
+ urlEncoded: { amount: number; currency: string };
42
+ }>;
40
43
  };
41
44
  };
42
45
  ```
@@ -173,16 +176,16 @@ const updatedContext = await applyMiddlewares(context, middlewares, "before");
173
176
 
174
177
  ## Schema Types
175
178
 
176
- | Type | Description | Example |
177
- | --------------------------------------- | ----------------------------- | --------------------------------------------------------------------- |
178
- | `Endpoint<{ data }>` | Endpoint with data only | `$get: Endpoint<{ data: User[] }>` |
179
- | `Endpoint<{ data; body }>` | Endpoint with JSON body | `$post: Endpoint<{ data: User; body: CreateUserBody }>` |
180
- | `Endpoint<{ data; query }>` | Endpoint with query params | `$get: Endpoint<{ data: User[]; query: { page: number } }>` |
181
- | `Endpoint<{ data; formData }>` | Endpoint with multipart form | `$post: Endpoint<{ data: Result; formData: { file: File } }>` |
182
- | `Endpoint<{ data; urlEncoded }>` | Endpoint with URL-encoded | `$post: Endpoint<{ data: Result; urlEncoded: { amount: number } }>` |
183
- | `Endpoint<{ data; error }>` | Endpoint with typed error | `$get: Endpoint<{ data: User; error: ApiError }>` |
184
- | `void` | No response body | `$delete: void` |
185
- | `_` | Dynamic path segment | `users: { _: { $get: Endpoint<{ data: User }> } }` |
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 }> } }` |
186
189
 
187
190
  ## API Reference
188
191
 
@@ -206,11 +209,8 @@ import { cachePlugin } from "@spoosh/plugin-cache";
206
209
  import { retryPlugin } from "@spoosh/plugin-retry";
207
210
 
208
211
  const client = new Spoosh<ApiSchema, Error>("/api", {
209
- headers: { Authorization: "Bearer token" }
210
- }).use([
211
- cachePlugin({ staleTime: 5000 }),
212
- retryPlugin({ retries: 3 })
213
- ]);
212
+ headers: { Authorization: "Bearer token" },
213
+ }).use([cachePlugin({ staleTime: 5000 }), retryPlugin({ retries: 3 })]);
214
214
 
215
215
  const { api } = client;
216
216
  const { data } = await api.users.$get();
@@ -218,26 +218,25 @@ const { data } = await api.users.$get();
218
218
 
219
219
  **Constructor Parameters:**
220
220
 
221
- | Parameter | Type | Description |
222
- | ---------------- | --------------- | --------------------------------------- |
223
- | `baseUrl` | `string` | Base URL for all API requests |
224
- | `defaultOptions` | `RequestInit` | (Optional) Default fetch options |
221
+ | Parameter | Type | Description |
222
+ | ---------------- | ------------- | -------------------------------- |
223
+ | `baseUrl` | `string` | Base URL for all API requests |
224
+ | `defaultOptions` | `RequestInit` | (Optional) Default fetch options |
225
225
 
226
226
  **Methods:**
227
227
 
228
- | Method | Description |
229
- | ------ | ----------- |
228
+ | Method | Description |
229
+ | --------------- | --------------------------------------------------------------------- |
230
230
  | `.use(plugins)` | Add plugins to the client. Returns a new instance with updated types. |
231
231
 
232
232
  **Properties:**
233
233
 
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 |
240
-
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 |
241
240
 
242
241
  ## Creating Plugins
243
242
 
package/dist/index.d.mts CHANGED
@@ -801,7 +801,6 @@ 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: number]: SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
805
804
  /**
806
805
  * Dynamic path segment with typed param name.
807
806
  * Use `:paramName` format to get typed params in the response.
@@ -844,7 +843,6 @@ type ExtractParamName<S> = S extends `:${infer P}` ? P : never;
844
843
  type QueryDynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = TSchema extends {
845
844
  _: infer D;
846
845
  } ? HasQueryMethods<D> extends true ? {
847
- [key: number]: QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
848
846
  <TKey extends string | number>(key: TKey): QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>, TRootSchema>;
849
847
  } : object : object;
850
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> & {
@@ -856,7 +854,6 @@ type MutationHttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object,
856
854
  type MutationDynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = TSchema extends {
857
855
  _: infer D;
858
856
  } ? HasMutationMethods<D> extends true ? {
859
- [key: number]: MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string>;
860
857
  <TKey extends string | number>(key: TKey): MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>>;
861
858
  } : object : object;
862
859
  type MutationOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = MutationHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & MutationDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames> & {
@@ -906,7 +903,7 @@ type EndpointToMethod<T> = (options?: ExtractEndpointRequestOptions<T>) => Promi
906
903
  * trigger({
907
904
  * myCallback: (api) => [
908
905
  * api.posts.$get, // ✓ Valid
909
- * api.users[1].$get, // ✓ Dynamic segment
906
+ * api.users(1).$get, // ✓ Dynamic segment
910
907
  * api.nonexistent.$get, // ✗ Type error
911
908
  * ],
912
909
  * });
@@ -919,7 +916,7 @@ type QuerySchemaHelper<TSchema> = {
919
916
  } & (TSchema extends {
920
917
  _: infer D;
921
918
  } ? HasQueryMethods<D> extends true ? {
922
- [key: number]: QuerySchemaHelper<D>;
919
+ <TKey extends string | number>(key: TKey): QuerySchemaHelper<D>;
923
920
  } : object : object);
924
921
 
925
922
  type PluginArray = readonly SpooshPlugin<PluginTypeConfig>[];
@@ -1066,7 +1063,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
1066
1063
  * const { data } = await api.posts.$post({ body: { title: 'Hello' } });
1067
1064
  *
1068
1065
  * // Dynamic path parameters
1069
- * const { data } = await api.posts[postId].$get();
1066
+ * const { data } = await api.posts(postId).$get();
1070
1067
  * ```
1071
1068
  */
1072
1069
  get api(): SpooshClient<TSchema, TError, CoreRequestOptionsBase>;
@@ -1187,7 +1184,7 @@ type SpooshClientConfig = {
1187
1184
  *
1188
1185
  * // Type-safe API calls
1189
1186
  * const { data } = await api.posts.$get();
1190
- * const { data: post } = await api.posts[123].$get();
1187
+ * const { data: post } = await api.posts(123).$get();
1191
1188
  * ```
1192
1189
  */
1193
1190
  declare function createClient<TSchema, TDefaultError = unknown>(config: SpooshClientConfig): SpooshClient<TSchema, TDefaultError>;
@@ -1260,9 +1257,9 @@ type ProxyHandlerConfig<TOptions = SpooshOptions> = {
1260
1257
  * await api.posts.$get();
1261
1258
  *
1262
1259
  * // Dynamic segments via function call:
1263
- * // api.posts[123].$get() or api.posts('123').$get()
1260
+ * // api.posts(123).$get() or api.posts('123').$get()
1264
1261
  * // Executes: GET /api/posts/123
1265
- * await api.posts[123].$get();
1262
+ * await api.posts(123).$get();
1266
1263
  * ```
1267
1264
  */
1268
1265
  declare function createProxyHandler<TSchema extends object, TOptions = SpooshOptions>(config: ProxyHandlerConfig<TOptions>): TSchema;
package/dist/index.d.ts CHANGED
@@ -801,7 +801,6 @@ 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: number]: SpooshClient<ExtractDynamicSchema<TSchema>, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
805
804
  /**
806
805
  * Dynamic path segment with typed param name.
807
806
  * Use `:paramName` format to get typed params in the response.
@@ -844,7 +843,6 @@ type ExtractParamName<S> = S extends `:${infer P}` ? P : never;
844
843
  type QueryDynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never, TRootSchema = TSchema> = TSchema extends {
845
844
  _: infer D;
846
845
  } ? HasQueryMethods<D> extends true ? {
847
- [key: number]: QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string, TRootSchema>;
848
846
  <TKey extends string | number>(key: TKey): QueryOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>, TRootSchema>;
849
847
  } : object : object;
850
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> & {
@@ -856,7 +854,6 @@ type MutationHttpMethods<TSchema, TDefaultError = unknown, TOptionsMap = object,
856
854
  type MutationDynamicAccess<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = TSchema extends {
857
855
  _: infer D;
858
856
  } ? HasMutationMethods<D> extends true ? {
859
- [key: number]: MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | string>;
860
857
  <TKey extends string | number>(key: TKey): MutationOnlyClient<D, TDefaultError, TOptionsMap, TParamNames | ExtractParamName<TKey>>;
861
858
  } : object : object;
862
859
  type MutationOnlyClient<TSchema, TDefaultError = unknown, TOptionsMap = object, TParamNames extends string = never> = MutationHttpMethods<TSchema, TDefaultError, TOptionsMap, TParamNames> & MutationDynamicAccess<TSchema, TDefaultError, TOptionsMap, TParamNames> & {
@@ -906,7 +903,7 @@ type EndpointToMethod<T> = (options?: ExtractEndpointRequestOptions<T>) => Promi
906
903
  * trigger({
907
904
  * myCallback: (api) => [
908
905
  * api.posts.$get, // ✓ Valid
909
- * api.users[1].$get, // ✓ Dynamic segment
906
+ * api.users(1).$get, // ✓ Dynamic segment
910
907
  * api.nonexistent.$get, // ✗ Type error
911
908
  * ],
912
909
  * });
@@ -919,7 +916,7 @@ type QuerySchemaHelper<TSchema> = {
919
916
  } & (TSchema extends {
920
917
  _: infer D;
921
918
  } ? HasQueryMethods<D> extends true ? {
922
- [key: number]: QuerySchemaHelper<D>;
919
+ <TKey extends string | number>(key: TKey): QuerySchemaHelper<D>;
923
920
  } : object : object);
924
921
 
925
922
  type PluginArray = readonly SpooshPlugin<PluginTypeConfig>[];
@@ -1066,7 +1063,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
1066
1063
  * const { data } = await api.posts.$post({ body: { title: 'Hello' } });
1067
1064
  *
1068
1065
  * // Dynamic path parameters
1069
- * const { data } = await api.posts[postId].$get();
1066
+ * const { data } = await api.posts(postId).$get();
1070
1067
  * ```
1071
1068
  */
1072
1069
  get api(): SpooshClient<TSchema, TError, CoreRequestOptionsBase>;
@@ -1187,7 +1184,7 @@ type SpooshClientConfig = {
1187
1184
  *
1188
1185
  * // Type-safe API calls
1189
1186
  * const { data } = await api.posts.$get();
1190
- * const { data: post } = await api.posts[123].$get();
1187
+ * const { data: post } = await api.posts(123).$get();
1191
1188
  * ```
1192
1189
  */
1193
1190
  declare function createClient<TSchema, TDefaultError = unknown>(config: SpooshClientConfig): SpooshClient<TSchema, TDefaultError>;
@@ -1260,9 +1257,9 @@ type ProxyHandlerConfig<TOptions = SpooshOptions> = {
1260
1257
  * await api.posts.$get();
1261
1258
  *
1262
1259
  * // Dynamic segments via function call:
1263
- * // api.posts[123].$get() or api.posts('123').$get()
1260
+ * // api.posts(123).$get() or api.posts('123').$get()
1264
1261
  * // Executes: GET /api/posts/123
1265
- * await api.posts[123].$get();
1262
+ * await api.posts(123).$get();
1266
1263
  * ```
1267
1264
  */
1268
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("123"), api.users(userId)
480
- // Q. Why allow this syntax?
481
- // A. To support dynamic type inference in frameworks where property access with variables is not possible.
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[postId].$get();
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("123"), api.users(userId)
425
- // Q. Why allow this syntax?
426
- // A. To support dynamic type inference in frameworks where property access with variables is not possible.
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[postId].$get();
935
+ * const { data } = await api.posts(postId).$get();
938
936
  * ```
939
937
  */
940
938
  get api() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spoosh/core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "license": "MIT",
5
5
  "description": "Type-safe API client with plugin middleware system",
6
6
  "keywords": [