@spoosh/core 0.10.1 → 0.11.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
@@ -47,8 +47,11 @@ type ApiSchema = {
47
47
  ```typescript
48
48
  import { createClient } from "@spoosh/core";
49
49
 
50
- const api = createClient<ApiSchema>({
51
- baseUrl: "/api",
50
+ const api = createClient<ApiSchema>("/api");
51
+
52
+ // With custom options
53
+ const apiWithAuth = createClient<ApiSchema>("/api", {
54
+ headers: { Authorization: "Bearer token" },
52
55
  });
53
56
 
54
57
  // Import body wrappers for explicit serialization
@@ -115,7 +118,7 @@ When using `createClient`, Next.js cache tags are automatically generated from t
115
118
  // Server component
116
119
  import { createClient } from "@spoosh/core";
117
120
 
118
- const api = createClient<ApiSchema>({ baseUrl: process.env.API_URL! });
121
+ const api = createClient<ApiSchema>(process.env.API_URL!);
119
122
 
120
123
  // Auto-generates next: { tags: ['posts'] }
121
124
  const { data: posts } = await api("posts").GET();
@@ -128,51 +131,6 @@ const { data: userPosts } = await api("users/:id/posts").GET({
128
131
 
129
132
  This enables automatic cache invalidation with `revalidateTag()` in Next.js.
130
133
 
131
- ### With Middlewares
132
-
133
- ```typescript
134
- import { createClient, createMiddleware } from "@spoosh/core";
135
-
136
- const authMiddleware = createMiddleware("auth", "before", async (ctx) => {
137
- ctx.requestOptions = {
138
- ...ctx.requestOptions,
139
- headers: { Authorization: `Bearer ${token}` },
140
- };
141
- return ctx;
142
- });
143
-
144
- const api = createClient<ApiSchema>({
145
- baseUrl: "/api",
146
- middlewares: [authMiddleware],
147
- });
148
- ```
149
-
150
- ### Middleware Utilities
151
-
152
- ```typescript
153
- import {
154
- createMiddleware,
155
- applyMiddlewares,
156
- composeMiddlewares,
157
- } from "@spoosh/core";
158
-
159
- // createMiddleware(name, phase, handler) - Create a named middleware
160
- const logMiddleware = createMiddleware("logger", "after", async (ctx) => {
161
- console.log(ctx.response?.status);
162
- return ctx;
163
- });
164
-
165
- // composeMiddlewares(...lists) - Combine multiple middleware arrays
166
- const allMiddlewares = composeMiddlewares(
167
- [authMiddleware],
168
- [logMiddleware],
169
- conditionalMiddlewares
170
- );
171
-
172
- // applyMiddlewares(context, middlewares, phase) - Run middlewares for a phase
173
- const updatedContext = await applyMiddlewares(context, middlewares, "before");
174
- ```
175
-
176
134
  ## Schema Types
177
135
 
178
136
  | Field | Description | Example |
@@ -186,15 +144,16 @@ Path parameters are defined using `:param` syntax in the path key (e.g., `"users
186
144
 
187
145
  ## API Reference
188
146
 
189
- ### createClient(config)
147
+ ### createClient(baseUrl, defaultOptions?)
190
148
 
191
149
  Creates a lightweight type-safe API instance.
192
150
 
193
- | Option | Type | Description |
194
- | ---------------- | -------------------- | -------------------------------------------------- |
195
- | `baseUrl` | `string` | Base URL for all API requests |
196
- | `defaultOptions` | `RequestInit` | Default fetch options (headers, credentials, etc.) |
197
- | `middlewares` | `SpooshMiddleware[]` | Request/response middlewares |
151
+ **Parameters:**
152
+
153
+ | Parameter | Type | Description |
154
+ | ---------------- | ------------- | ------------------------------------------------------------- |
155
+ | `baseUrl` | `string` | Base URL for all API requests |
156
+ | `defaultOptions` | `RequestInit` | (Optional) Default fetch options (headers, credentials, etc.) |
198
157
 
199
158
  ### Spoosh (class)
200
159
 
package/dist/index.d.mts CHANGED
@@ -4,24 +4,6 @@ type Simplify<T> = {
4
4
  [K in keyof T]: T[K];
5
5
  } & {};
6
6
 
7
- type MiddlewarePhase = "before" | "after";
8
- type MiddlewareContext<TData = unknown, TError = unknown> = {
9
- baseUrl: string;
10
- path: string[];
11
- method: HttpMethod;
12
- defaultOptions: SpooshOptions & SpooshOptionsExtra;
13
- requestOptions?: AnyRequestOptions;
14
- fetchInit?: RequestInit;
15
- response?: SpooshResponse<TData, TError>;
16
- metadata: Record<string, unknown>;
17
- };
18
- type MiddlewareHandler<TData = unknown, TError = unknown> = (context: MiddlewareContext<TData, TError>) => MiddlewareContext<TData, TError> | Promise<MiddlewareContext<TData, TError>>;
19
- type SpooshMiddleware<TData = unknown, TError = unknown> = {
20
- name: string;
21
- phase: MiddlewarePhase;
22
- handler: MiddlewareHandler<TData, TError>;
23
- };
24
-
25
7
  type QueryField<TQuery> = [TQuery] extends [never] ? object : {
26
8
  query: TQuery;
27
9
  };
@@ -54,15 +36,25 @@ type SpooshResponse<TData, TError, TRequestOptions = unknown, TQuery = never, TB
54
36
  aborted?: boolean;
55
37
  readonly __requestOptions?: TRequestOptions;
56
38
  } & InputFieldWrapper<TQuery, TBody, TParamNames>);
57
- type SpooshOptionsExtra<TData = unknown, TError = unknown> = {
58
- middlewares?: SpooshMiddleware<TData, TError>[];
59
- };
60
39
 
61
- type SpooshBody<T = unknown> = {
62
- readonly __spooshBody: true;
63
- readonly kind: "form" | "json" | "urlencoded";
64
- readonly value: T;
65
- };
40
+ /**
41
+ * Opaque type representing a transformed body. Create using `form()`, `json()`, or `urlencoded()` helpers.
42
+ * Do not create this type manually - use the helper functions instead.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * import { form, json, urlencoded } from "@spoosh/core";
47
+ *
48
+ * // Use helpers to create SpooshBody
49
+ * trigger({ body: form({ file: myFile }) });
50
+ * trigger({ body: json({ data: "value" }) });
51
+ * trigger({ body: urlencoded({ key: "value" }) });
52
+ * ```
53
+ */
54
+ declare class SpooshBodyClass<T> {
55
+ private __brand;
56
+ }
57
+ type SpooshBody<T = unknown> = SpooshBodyClass<T>;
66
58
  declare function isSpooshBody(value: unknown): value is SpooshBody;
67
59
  declare function form<T>(value: T): SpooshBody<T>;
68
60
  declare function json<T>(value: T): SpooshBody<T>;
@@ -231,7 +223,7 @@ type CacheEntryWithKey<TData = unknown, TError = unknown> = {
231
223
  declare function createInitialState<TData, TError>(): OperationState<TData, TError>;
232
224
  type StateManager = {
233
225
  createQueryKey: (params: {
234
- path: string[];
226
+ path: string;
235
227
  method: string;
236
228
  options?: unknown;
237
229
  }) => string;
@@ -249,6 +241,8 @@ type StateManager = {
249
241
  getAllCacheEntries: <TData, TError>() => CacheEntryWithKey<TData, TError>[];
250
242
  /** Get the number of cache entries */
251
243
  getSize: () => number;
244
+ /** Get the number of active subscribers for a cache key */
245
+ getSubscribersCount: (key: string) => number;
252
246
  /** Set a pending promise for a query key (for deduplication) */
253
247
  setPendingPromise: (key: string, promise: Promise<unknown> | undefined) => void;
254
248
  /** Get a pending promise for a query key */
@@ -275,9 +269,13 @@ type CacheEntry<TData = unknown, TError = unknown> = {
275
269
  /** Cache was invalidated while no subscriber was listening. Triggers refetch on next mount. */
276
270
  stale?: boolean;
277
271
  };
278
- type PluginContext<TData = unknown, TError = unknown> = {
272
+ /** RequestOptions in plugin context have headers already resolved to Record */
273
+ type PluginRequestOptions = Omit<AnyRequestOptions, "headers"> & {
274
+ headers: Record<string, string>;
275
+ };
276
+ type PluginContext = {
279
277
  readonly operationType: OperationType;
280
- readonly path: string[];
278
+ readonly path: string;
281
279
  readonly method: HttpMethod;
282
280
  readonly queryKey: string;
283
281
  readonly tags: string[];
@@ -285,17 +283,10 @@ type PluginContext<TData = unknown, TError = unknown> = {
285
283
  readonly requestTimestamp: number;
286
284
  /** Unique identifier for the hook instance. Persists across queryKey changes within the same hook. */
287
285
  readonly hookId?: string;
288
- requestOptions: AnyRequestOptions;
289
- state: OperationState<TData, TError>;
290
- response?: SpooshResponse<TData, TError>;
286
+ request: PluginRequestOptions;
291
287
  metadata: Map<string, unknown>;
292
- abort: () => void;
293
288
  stateManager: StateManager;
294
289
  eventEmitter: EventEmitter;
295
- /** Resolved headers as a plain object. Modify via setHeaders(). */
296
- headers: Record<string, string>;
297
- /** Add/update headers. Merges with existing headers. */
298
- setHeaders: (headers: Record<string, string>) => void;
299
290
  /** Access other plugins' exported APIs */
300
291
  plugins: PluginAccessor;
301
292
  /** Plugin-specific options passed from hooks (useRead/useWrite/useInfiniteRead) */
@@ -304,7 +295,7 @@ type PluginContext<TData = unknown, TError = unknown> = {
304
295
  forceRefetch?: boolean;
305
296
  };
306
297
  /** Input type for creating PluginContext (without injected properties) */
307
- type PluginContextInput<TData = unknown, TError = unknown> = Omit<PluginContext<TData, TError>, "plugins" | "setHeaders" | "headers">;
298
+ type PluginContextInput = Omit<PluginContext, "plugins">;
308
299
  /**
309
300
  * Middleware function that wraps the fetch flow.
310
301
  * Plugins use this for full control over request/response handling.
@@ -334,22 +325,22 @@ type PluginContextInput<TData = unknown, TError = unknown> = Omit<PluginContext<
334
325
  * }
335
326
  * ```
336
327
  */
337
- type PluginMiddleware<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, next: () => Promise<SpooshResponse<TData, TError>>) => Promise<SpooshResponse<TData, TError>>;
338
- type PluginHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>) => void | Promise<void>;
339
- type PluginUpdateHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, previousContext: PluginContext<TData, TError>) => void | Promise<void>;
328
+ type PluginMiddleware = (context: PluginContext, next: () => Promise<SpooshResponse<any, any>>) => Promise<SpooshResponse<any, any>>;
329
+ type PluginHandler = (context: PluginContext) => void | Promise<void>;
330
+ type PluginUpdateHandler = (context: PluginContext, previousContext: PluginContext) => void | Promise<void>;
340
331
  /**
341
332
  * Handler called after every response, regardless of early returns from middleware.
342
333
  * Can return a new response to transform it, or void for side effects only.
343
334
  * Returned responses are chained through plugins in order.
344
335
  */
345
- type PluginResponseHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, response: SpooshResponse<TData, TError>) => SpooshResponse<TData, TError> | void | Promise<SpooshResponse<TData, TError> | void>;
346
- type PluginLifecycle<TData = unknown, TError = unknown> = {
336
+ type PluginResponseHandler = (context: PluginContext, response: SpooshResponse<any, any>) => SpooshResponse<any, any> | void | Promise<SpooshResponse<any, any> | void>;
337
+ type PluginLifecycle = {
347
338
  /** Called on component mount */
348
- onMount?: PluginHandler<TData, TError>;
339
+ onMount?: PluginHandler;
349
340
  /** Called when options/query changes. Receives both new and previous context. */
350
- onUpdate?: PluginUpdateHandler<TData, TError>;
341
+ onUpdate?: PluginUpdateHandler;
351
342
  /** Called on component unmount */
352
- onUnmount?: PluginHandler<TData, TError>;
343
+ onUnmount?: PluginHandler;
353
344
  };
354
345
  /**
355
346
  * Configuration object for plugin type definitions.
@@ -606,8 +597,8 @@ type RefetchEvent = {
606
597
  * Avoids circular dependency with executor.ts.
607
598
  */
608
599
  type InstancePluginExecutor = {
609
- executeMiddleware: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, coreFetch: () => Promise<SpooshResponse<TData, TError>>) => Promise<SpooshResponse<TData, TError>>;
610
- createContext: <TData, TError>(input: PluginContextInput<TData, TError>) => PluginContext<TData, TError>;
600
+ executeMiddleware: <TData, TError>(operationType: OperationType, context: PluginContext, coreFetch: () => Promise<SpooshResponse<any, any>>) => Promise<SpooshResponse<TData, TError>>;
601
+ createContext: (input: PluginContextInput) => PluginContext;
611
602
  };
612
603
  /**
613
604
  * Context provided to plugin's instanceApi function.
@@ -622,14 +613,13 @@ type InstanceApiContext<TApi = unknown> = {
622
613
 
623
614
  type PluginExecutor = {
624
615
  /** Execute lifecycle hooks for onMount or onUnmount */
625
- executeLifecycle: <TData, TError>(phase: "onMount" | "onUnmount", operationType: OperationType, context: PluginContext<TData, TError>) => Promise<void>;
616
+ executeLifecycle: (phase: "onMount" | "onUnmount", operationType: OperationType, context: PluginContext) => Promise<void>;
626
617
  /** Execute onUpdate lifecycle with previous context */
627
- executeUpdateLifecycle: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, previousContext: PluginContext<TData, TError>) => Promise<void>;
628
- /** Execute middleware chain with a core fetch function, then run afterResponse handlers */
629
- executeMiddleware: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, coreFetch: () => Promise<SpooshResponse<TData, TError>>) => Promise<SpooshResponse<TData, TError>>;
618
+ executeUpdateLifecycle: (operationType: OperationType, context: PluginContext, previousContext: PluginContext) => Promise<void>;
619
+ executeMiddleware: (operationType: OperationType, context: PluginContext, coreFetch: () => Promise<SpooshResponse<any, any>>) => Promise<SpooshResponse<any, any>>;
630
620
  getPlugins: () => readonly SpooshPlugin[];
631
621
  /** Creates a full PluginContext with plugins accessor injected */
632
- createContext: <TData, TError>(input: PluginContextInput<TData, TError>) => PluginContext<TData, TError>;
622
+ createContext: (input: PluginContextInput) => PluginContext;
633
623
  };
634
624
  declare function createPluginExecutor(initialPlugins?: SpooshPlugin[]): PluginExecutor;
635
625
 
@@ -1346,18 +1336,14 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
1346
1336
  };
1347
1337
  }
1348
1338
 
1349
- type SpooshClientConfig = {
1350
- baseUrl: string;
1351
- defaultOptions?: SpooshOptionsInput;
1352
- middlewares?: SpooshMiddleware[];
1353
- };
1354
1339
  /**
1355
1340
  * Creates a lightweight type-safe API instance for vanilla JavaScript/TypeScript usage.
1356
1341
  *
1357
1342
  * This is a simpler alternative to `Spoosh` for users who don't need
1358
1343
  * the full plugin system, state management, or React integration.
1359
1344
  *
1360
- * @param config - Client configuration
1345
+ * @param baseUrl - Base URL for all API requests
1346
+ * @param defaultOptions - Default fetch options (headers, credentials, etc.)
1361
1347
  * @returns Type-safe API instance
1362
1348
  *
1363
1349
  * @example
@@ -1378,23 +1364,21 @@ type SpooshClientConfig = {
1378
1364
  * message: string;
1379
1365
  * }
1380
1366
  *
1381
- * const api = createClient<ApiSchema, ApiError>({
1382
- * baseUrl: "/api",
1383
- * });
1367
+ * const api = createClient<ApiSchema, ApiError>("/api");
1384
1368
  *
1385
1369
  * // Type-safe API calls with path strings
1386
1370
  * const { data } = await api("posts").GET();
1387
1371
  * const { data: post } = await api("posts/123").GET();
1388
1372
  * await api("posts/:id").GET({ params: { id: 123 } });
1389
1373
  *
1390
- * // With XHR transport
1391
- * const api = createClient<ApiSchema, ApiError>({
1392
- * baseUrl: "/api",
1393
- * defaultOptions: { transport: "xhr" },
1374
+ * // With custom options
1375
+ * const api = createClient<ApiSchema, ApiError>("/api", {
1376
+ * headers: { Authorization: "Bearer token" },
1377
+ * transport: "xhr",
1394
1378
  * });
1395
1379
  * ```
1396
1380
  */
1397
- declare function createClient<TSchema, TDefaultError = unknown>(config: SpooshClientConfig): SpooshClient<TSchema, TDefaultError>;
1381
+ declare function createClient<TSchema, TDefaultError = unknown>(baseUrl: string, defaultOptions?: SpooshOptionsInput): SpooshClient<TSchema, TDefaultError>;
1398
1382
 
1399
1383
  declare function buildUrl(baseUrl: string, path: string[], query?: Record<string, string | number | boolean | undefined>): string;
1400
1384
 
@@ -1593,11 +1577,7 @@ declare module "./types" {
1593
1577
  }
1594
1578
  declare const xhrTransport: Transport<XhrTransportOptions>;
1595
1579
 
1596
- declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions & SpooshOptionsExtra, requestOptions?: AnyRequestOptions, nextTags?: boolean): Promise<SpooshResponse<TData, TError>>;
1597
-
1598
- declare function createMiddleware<TData = unknown, TError = unknown>(name: string, phase: MiddlewarePhase, handler: SpooshMiddleware<TData, TError>["handler"]): SpooshMiddleware<TData, TError>;
1599
- declare function applyMiddlewares<TData = unknown, TError = unknown>(context: MiddlewareContext<TData, TError>, middlewares: SpooshMiddleware<TData, TError>[], phase: MiddlewarePhase): Promise<MiddlewareContext<TData, TError>>;
1600
- declare function composeMiddlewares<TData = unknown, TError = unknown>(...middlewareLists: (SpooshMiddleware<TData, TError>[] | undefined)[]): SpooshMiddleware<TData, TError>[];
1580
+ declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions, requestOptions?: AnyRequestOptions, nextTags?: boolean): Promise<SpooshResponse<TData, TError>>;
1601
1581
 
1602
1582
  type ExecuteOptions = {
1603
1583
  force?: boolean;
@@ -1613,15 +1593,15 @@ type OperationController<TData = unknown, TError = unknown> = {
1613
1593
  /** Called once when hook finally unmounts */
1614
1594
  unmount: () => void;
1615
1595
  /** Called when options/query changes. Pass previous context for cleanup. */
1616
- update: (previousContext: PluginContext<TData, TError>) => void;
1596
+ update: (previousContext: PluginContext) => void;
1617
1597
  /** Get current context (for passing to update as previousContext) */
1618
- getContext: () => PluginContext<TData, TError>;
1598
+ getContext: () => PluginContext;
1619
1599
  setPluginOptions: (options: unknown) => void;
1620
1600
  setMetadata: (key: string, value: unknown) => void;
1621
1601
  };
1622
1602
  type CreateOperationOptions<TData, TError> = {
1623
1603
  operationType: OperationType;
1624
- path: string[];
1604
+ path: string;
1625
1605
  method: HttpMethod;
1626
1606
  tags: string[];
1627
1607
  requestOptions?: AnyRequestOptions;
@@ -1663,12 +1643,12 @@ type InfiniteReadController<TData, TItem, TError> = {
1663
1643
  abort: () => void;
1664
1644
  mount: () => void;
1665
1645
  unmount: () => void;
1666
- update: (previousContext: PluginContext<TData, TError>) => void;
1667
- getContext: () => PluginContext<TData, TError>;
1646
+ update: (previousContext: PluginContext) => void;
1647
+ getContext: () => PluginContext;
1668
1648
  setPluginOptions: (options: unknown) => void;
1669
1649
  };
1670
1650
  type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
1671
- path: string[];
1651
+ path: string;
1672
1652
  method: HttpMethod;
1673
1653
  tags: string[];
1674
1654
  initialRequest: InfiniteRequestOptions;
@@ -1687,4 +1667,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
1687
1667
  };
1688
1668
  declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
1689
1669
 
1690
- export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type SpooshClientConfig as ClientConfig, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type MiddlewareContext, type MiddlewareHandler, type MiddlewarePhase, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, applyMiddlewares, buildUrl, composeMiddlewares, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createMiddleware, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
1670
+ export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginRequestOptions, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshOptions, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, buildUrl, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
package/dist/index.d.ts CHANGED
@@ -4,24 +4,6 @@ type Simplify<T> = {
4
4
  [K in keyof T]: T[K];
5
5
  } & {};
6
6
 
7
- type MiddlewarePhase = "before" | "after";
8
- type MiddlewareContext<TData = unknown, TError = unknown> = {
9
- baseUrl: string;
10
- path: string[];
11
- method: HttpMethod;
12
- defaultOptions: SpooshOptions & SpooshOptionsExtra;
13
- requestOptions?: AnyRequestOptions;
14
- fetchInit?: RequestInit;
15
- response?: SpooshResponse<TData, TError>;
16
- metadata: Record<string, unknown>;
17
- };
18
- type MiddlewareHandler<TData = unknown, TError = unknown> = (context: MiddlewareContext<TData, TError>) => MiddlewareContext<TData, TError> | Promise<MiddlewareContext<TData, TError>>;
19
- type SpooshMiddleware<TData = unknown, TError = unknown> = {
20
- name: string;
21
- phase: MiddlewarePhase;
22
- handler: MiddlewareHandler<TData, TError>;
23
- };
24
-
25
7
  type QueryField<TQuery> = [TQuery] extends [never] ? object : {
26
8
  query: TQuery;
27
9
  };
@@ -54,15 +36,25 @@ type SpooshResponse<TData, TError, TRequestOptions = unknown, TQuery = never, TB
54
36
  aborted?: boolean;
55
37
  readonly __requestOptions?: TRequestOptions;
56
38
  } & InputFieldWrapper<TQuery, TBody, TParamNames>);
57
- type SpooshOptionsExtra<TData = unknown, TError = unknown> = {
58
- middlewares?: SpooshMiddleware<TData, TError>[];
59
- };
60
39
 
61
- type SpooshBody<T = unknown> = {
62
- readonly __spooshBody: true;
63
- readonly kind: "form" | "json" | "urlencoded";
64
- readonly value: T;
65
- };
40
+ /**
41
+ * Opaque type representing a transformed body. Create using `form()`, `json()`, or `urlencoded()` helpers.
42
+ * Do not create this type manually - use the helper functions instead.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * import { form, json, urlencoded } from "@spoosh/core";
47
+ *
48
+ * // Use helpers to create SpooshBody
49
+ * trigger({ body: form({ file: myFile }) });
50
+ * trigger({ body: json({ data: "value" }) });
51
+ * trigger({ body: urlencoded({ key: "value" }) });
52
+ * ```
53
+ */
54
+ declare class SpooshBodyClass<T> {
55
+ private __brand;
56
+ }
57
+ type SpooshBody<T = unknown> = SpooshBodyClass<T>;
66
58
  declare function isSpooshBody(value: unknown): value is SpooshBody;
67
59
  declare function form<T>(value: T): SpooshBody<T>;
68
60
  declare function json<T>(value: T): SpooshBody<T>;
@@ -231,7 +223,7 @@ type CacheEntryWithKey<TData = unknown, TError = unknown> = {
231
223
  declare function createInitialState<TData, TError>(): OperationState<TData, TError>;
232
224
  type StateManager = {
233
225
  createQueryKey: (params: {
234
- path: string[];
226
+ path: string;
235
227
  method: string;
236
228
  options?: unknown;
237
229
  }) => string;
@@ -249,6 +241,8 @@ type StateManager = {
249
241
  getAllCacheEntries: <TData, TError>() => CacheEntryWithKey<TData, TError>[];
250
242
  /** Get the number of cache entries */
251
243
  getSize: () => number;
244
+ /** Get the number of active subscribers for a cache key */
245
+ getSubscribersCount: (key: string) => number;
252
246
  /** Set a pending promise for a query key (for deduplication) */
253
247
  setPendingPromise: (key: string, promise: Promise<unknown> | undefined) => void;
254
248
  /** Get a pending promise for a query key */
@@ -275,9 +269,13 @@ type CacheEntry<TData = unknown, TError = unknown> = {
275
269
  /** Cache was invalidated while no subscriber was listening. Triggers refetch on next mount. */
276
270
  stale?: boolean;
277
271
  };
278
- type PluginContext<TData = unknown, TError = unknown> = {
272
+ /** RequestOptions in plugin context have headers already resolved to Record */
273
+ type PluginRequestOptions = Omit<AnyRequestOptions, "headers"> & {
274
+ headers: Record<string, string>;
275
+ };
276
+ type PluginContext = {
279
277
  readonly operationType: OperationType;
280
- readonly path: string[];
278
+ readonly path: string;
281
279
  readonly method: HttpMethod;
282
280
  readonly queryKey: string;
283
281
  readonly tags: string[];
@@ -285,17 +283,10 @@ type PluginContext<TData = unknown, TError = unknown> = {
285
283
  readonly requestTimestamp: number;
286
284
  /** Unique identifier for the hook instance. Persists across queryKey changes within the same hook. */
287
285
  readonly hookId?: string;
288
- requestOptions: AnyRequestOptions;
289
- state: OperationState<TData, TError>;
290
- response?: SpooshResponse<TData, TError>;
286
+ request: PluginRequestOptions;
291
287
  metadata: Map<string, unknown>;
292
- abort: () => void;
293
288
  stateManager: StateManager;
294
289
  eventEmitter: EventEmitter;
295
- /** Resolved headers as a plain object. Modify via setHeaders(). */
296
- headers: Record<string, string>;
297
- /** Add/update headers. Merges with existing headers. */
298
- setHeaders: (headers: Record<string, string>) => void;
299
290
  /** Access other plugins' exported APIs */
300
291
  plugins: PluginAccessor;
301
292
  /** Plugin-specific options passed from hooks (useRead/useWrite/useInfiniteRead) */
@@ -304,7 +295,7 @@ type PluginContext<TData = unknown, TError = unknown> = {
304
295
  forceRefetch?: boolean;
305
296
  };
306
297
  /** Input type for creating PluginContext (without injected properties) */
307
- type PluginContextInput<TData = unknown, TError = unknown> = Omit<PluginContext<TData, TError>, "plugins" | "setHeaders" | "headers">;
298
+ type PluginContextInput = Omit<PluginContext, "plugins">;
308
299
  /**
309
300
  * Middleware function that wraps the fetch flow.
310
301
  * Plugins use this for full control over request/response handling.
@@ -334,22 +325,22 @@ type PluginContextInput<TData = unknown, TError = unknown> = Omit<PluginContext<
334
325
  * }
335
326
  * ```
336
327
  */
337
- type PluginMiddleware<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, next: () => Promise<SpooshResponse<TData, TError>>) => Promise<SpooshResponse<TData, TError>>;
338
- type PluginHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>) => void | Promise<void>;
339
- type PluginUpdateHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, previousContext: PluginContext<TData, TError>) => void | Promise<void>;
328
+ type PluginMiddleware = (context: PluginContext, next: () => Promise<SpooshResponse<any, any>>) => Promise<SpooshResponse<any, any>>;
329
+ type PluginHandler = (context: PluginContext) => void | Promise<void>;
330
+ type PluginUpdateHandler = (context: PluginContext, previousContext: PluginContext) => void | Promise<void>;
340
331
  /**
341
332
  * Handler called after every response, regardless of early returns from middleware.
342
333
  * Can return a new response to transform it, or void for side effects only.
343
334
  * Returned responses are chained through plugins in order.
344
335
  */
345
- type PluginResponseHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, response: SpooshResponse<TData, TError>) => SpooshResponse<TData, TError> | void | Promise<SpooshResponse<TData, TError> | void>;
346
- type PluginLifecycle<TData = unknown, TError = unknown> = {
336
+ type PluginResponseHandler = (context: PluginContext, response: SpooshResponse<any, any>) => SpooshResponse<any, any> | void | Promise<SpooshResponse<any, any> | void>;
337
+ type PluginLifecycle = {
347
338
  /** Called on component mount */
348
- onMount?: PluginHandler<TData, TError>;
339
+ onMount?: PluginHandler;
349
340
  /** Called when options/query changes. Receives both new and previous context. */
350
- onUpdate?: PluginUpdateHandler<TData, TError>;
341
+ onUpdate?: PluginUpdateHandler;
351
342
  /** Called on component unmount */
352
- onUnmount?: PluginHandler<TData, TError>;
343
+ onUnmount?: PluginHandler;
353
344
  };
354
345
  /**
355
346
  * Configuration object for plugin type definitions.
@@ -606,8 +597,8 @@ type RefetchEvent = {
606
597
  * Avoids circular dependency with executor.ts.
607
598
  */
608
599
  type InstancePluginExecutor = {
609
- executeMiddleware: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, coreFetch: () => Promise<SpooshResponse<TData, TError>>) => Promise<SpooshResponse<TData, TError>>;
610
- createContext: <TData, TError>(input: PluginContextInput<TData, TError>) => PluginContext<TData, TError>;
600
+ executeMiddleware: <TData, TError>(operationType: OperationType, context: PluginContext, coreFetch: () => Promise<SpooshResponse<any, any>>) => Promise<SpooshResponse<TData, TError>>;
601
+ createContext: (input: PluginContextInput) => PluginContext;
611
602
  };
612
603
  /**
613
604
  * Context provided to plugin's instanceApi function.
@@ -622,14 +613,13 @@ type InstanceApiContext<TApi = unknown> = {
622
613
 
623
614
  type PluginExecutor = {
624
615
  /** Execute lifecycle hooks for onMount or onUnmount */
625
- executeLifecycle: <TData, TError>(phase: "onMount" | "onUnmount", operationType: OperationType, context: PluginContext<TData, TError>) => Promise<void>;
616
+ executeLifecycle: (phase: "onMount" | "onUnmount", operationType: OperationType, context: PluginContext) => Promise<void>;
626
617
  /** Execute onUpdate lifecycle with previous context */
627
- executeUpdateLifecycle: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, previousContext: PluginContext<TData, TError>) => Promise<void>;
628
- /** Execute middleware chain with a core fetch function, then run afterResponse handlers */
629
- executeMiddleware: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, coreFetch: () => Promise<SpooshResponse<TData, TError>>) => Promise<SpooshResponse<TData, TError>>;
618
+ executeUpdateLifecycle: (operationType: OperationType, context: PluginContext, previousContext: PluginContext) => Promise<void>;
619
+ executeMiddleware: (operationType: OperationType, context: PluginContext, coreFetch: () => Promise<SpooshResponse<any, any>>) => Promise<SpooshResponse<any, any>>;
630
620
  getPlugins: () => readonly SpooshPlugin[];
631
621
  /** Creates a full PluginContext with plugins accessor injected */
632
- createContext: <TData, TError>(input: PluginContextInput<TData, TError>) => PluginContext<TData, TError>;
622
+ createContext: (input: PluginContextInput) => PluginContext;
633
623
  };
634
624
  declare function createPluginExecutor(initialPlugins?: SpooshPlugin[]): PluginExecutor;
635
625
 
@@ -1346,18 +1336,14 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
1346
1336
  };
1347
1337
  }
1348
1338
 
1349
- type SpooshClientConfig = {
1350
- baseUrl: string;
1351
- defaultOptions?: SpooshOptionsInput;
1352
- middlewares?: SpooshMiddleware[];
1353
- };
1354
1339
  /**
1355
1340
  * Creates a lightweight type-safe API instance for vanilla JavaScript/TypeScript usage.
1356
1341
  *
1357
1342
  * This is a simpler alternative to `Spoosh` for users who don't need
1358
1343
  * the full plugin system, state management, or React integration.
1359
1344
  *
1360
- * @param config - Client configuration
1345
+ * @param baseUrl - Base URL for all API requests
1346
+ * @param defaultOptions - Default fetch options (headers, credentials, etc.)
1361
1347
  * @returns Type-safe API instance
1362
1348
  *
1363
1349
  * @example
@@ -1378,23 +1364,21 @@ type SpooshClientConfig = {
1378
1364
  * message: string;
1379
1365
  * }
1380
1366
  *
1381
- * const api = createClient<ApiSchema, ApiError>({
1382
- * baseUrl: "/api",
1383
- * });
1367
+ * const api = createClient<ApiSchema, ApiError>("/api");
1384
1368
  *
1385
1369
  * // Type-safe API calls with path strings
1386
1370
  * const { data } = await api("posts").GET();
1387
1371
  * const { data: post } = await api("posts/123").GET();
1388
1372
  * await api("posts/:id").GET({ params: { id: 123 } });
1389
1373
  *
1390
- * // With XHR transport
1391
- * const api = createClient<ApiSchema, ApiError>({
1392
- * baseUrl: "/api",
1393
- * defaultOptions: { transport: "xhr" },
1374
+ * // With custom options
1375
+ * const api = createClient<ApiSchema, ApiError>("/api", {
1376
+ * headers: { Authorization: "Bearer token" },
1377
+ * transport: "xhr",
1394
1378
  * });
1395
1379
  * ```
1396
1380
  */
1397
- declare function createClient<TSchema, TDefaultError = unknown>(config: SpooshClientConfig): SpooshClient<TSchema, TDefaultError>;
1381
+ declare function createClient<TSchema, TDefaultError = unknown>(baseUrl: string, defaultOptions?: SpooshOptionsInput): SpooshClient<TSchema, TDefaultError>;
1398
1382
 
1399
1383
  declare function buildUrl(baseUrl: string, path: string[], query?: Record<string, string | number | boolean | undefined>): string;
1400
1384
 
@@ -1593,11 +1577,7 @@ declare module "./types" {
1593
1577
  }
1594
1578
  declare const xhrTransport: Transport<XhrTransportOptions>;
1595
1579
 
1596
- declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions & SpooshOptionsExtra, requestOptions?: AnyRequestOptions, nextTags?: boolean): Promise<SpooshResponse<TData, TError>>;
1597
-
1598
- declare function createMiddleware<TData = unknown, TError = unknown>(name: string, phase: MiddlewarePhase, handler: SpooshMiddleware<TData, TError>["handler"]): SpooshMiddleware<TData, TError>;
1599
- declare function applyMiddlewares<TData = unknown, TError = unknown>(context: MiddlewareContext<TData, TError>, middlewares: SpooshMiddleware<TData, TError>[], phase: MiddlewarePhase): Promise<MiddlewareContext<TData, TError>>;
1600
- declare function composeMiddlewares<TData = unknown, TError = unknown>(...middlewareLists: (SpooshMiddleware<TData, TError>[] | undefined)[]): SpooshMiddleware<TData, TError>[];
1580
+ declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions, requestOptions?: AnyRequestOptions, nextTags?: boolean): Promise<SpooshResponse<TData, TError>>;
1601
1581
 
1602
1582
  type ExecuteOptions = {
1603
1583
  force?: boolean;
@@ -1613,15 +1593,15 @@ type OperationController<TData = unknown, TError = unknown> = {
1613
1593
  /** Called once when hook finally unmounts */
1614
1594
  unmount: () => void;
1615
1595
  /** Called when options/query changes. Pass previous context for cleanup. */
1616
- update: (previousContext: PluginContext<TData, TError>) => void;
1596
+ update: (previousContext: PluginContext) => void;
1617
1597
  /** Get current context (for passing to update as previousContext) */
1618
- getContext: () => PluginContext<TData, TError>;
1598
+ getContext: () => PluginContext;
1619
1599
  setPluginOptions: (options: unknown) => void;
1620
1600
  setMetadata: (key: string, value: unknown) => void;
1621
1601
  };
1622
1602
  type CreateOperationOptions<TData, TError> = {
1623
1603
  operationType: OperationType;
1624
- path: string[];
1604
+ path: string;
1625
1605
  method: HttpMethod;
1626
1606
  tags: string[];
1627
1607
  requestOptions?: AnyRequestOptions;
@@ -1663,12 +1643,12 @@ type InfiniteReadController<TData, TItem, TError> = {
1663
1643
  abort: () => void;
1664
1644
  mount: () => void;
1665
1645
  unmount: () => void;
1666
- update: (previousContext: PluginContext<TData, TError>) => void;
1667
- getContext: () => PluginContext<TData, TError>;
1646
+ update: (previousContext: PluginContext) => void;
1647
+ getContext: () => PluginContext;
1668
1648
  setPluginOptions: (options: unknown) => void;
1669
1649
  };
1670
1650
  type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
1671
- path: string[];
1651
+ path: string;
1672
1652
  method: HttpMethod;
1673
1653
  tags: string[];
1674
1654
  initialRequest: InfiniteRequestOptions;
@@ -1687,4 +1667,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
1687
1667
  };
1688
1668
  declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
1689
1669
 
1690
- export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type SpooshClientConfig as ClientConfig, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type MiddlewareContext, type MiddlewareHandler, type MiddlewarePhase, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, applyMiddlewares, buildUrl, composeMiddlewares, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createMiddleware, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
1670
+ export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginRequestOptions, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshOptions, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, buildUrl, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
package/dist/index.js CHANGED
@@ -23,15 +23,12 @@ __export(src_exports, {
23
23
  HTTP_METHODS: () => HTTP_METHODS,
24
24
  Spoosh: () => Spoosh,
25
25
  __DEV__: () => __DEV__,
26
- applyMiddlewares: () => applyMiddlewares,
27
26
  buildUrl: () => buildUrl,
28
- composeMiddlewares: () => composeMiddlewares,
29
27
  containsFile: () => containsFile,
30
28
  createClient: () => createClient,
31
29
  createEventEmitter: () => createEventEmitter,
32
30
  createInfiniteReadController: () => createInfiniteReadController,
33
31
  createInitialState: () => createInitialState,
34
- createMiddleware: () => createMiddleware,
35
32
  createOperationController: () => createOperationController,
36
33
  createPluginExecutor: () => createPluginExecutor,
37
34
  createPluginRegistry: () => createPluginRegistry,
@@ -62,22 +59,6 @@ __export(src_exports, {
62
59
  });
63
60
  module.exports = __toCommonJS(src_exports);
64
61
 
65
- // src/middleware.ts
66
- function createMiddleware(name, phase, handler) {
67
- return { name, phase, handler };
68
- }
69
- async function applyMiddlewares(context, middlewares, phase) {
70
- const phaseMiddlewares = middlewares.filter((m) => m.phase === phase);
71
- let ctx = context;
72
- for (const middleware of phaseMiddlewares) {
73
- ctx = await middleware.handler(ctx);
74
- }
75
- return ctx;
76
- }
77
- function composeMiddlewares(...middlewareLists) {
78
- return middlewareLists.flat().filter(Boolean);
79
- }
80
-
81
62
  // src/utils/buildUrl.ts
82
63
  function stringifyQuery(query) {
83
64
  const parts = [];
@@ -288,19 +269,20 @@ function resolveRequestBody(rawBody) {
288
269
  return void 0;
289
270
  }
290
271
  if (isSpooshBody(rawBody)) {
291
- switch (rawBody.kind) {
272
+ const body = rawBody;
273
+ switch (body.kind) {
292
274
  case "form":
293
275
  return {
294
- body: objectToFormData(rawBody.value)
276
+ body: objectToFormData(body.value)
295
277
  };
296
278
  case "json":
297
279
  return {
298
- body: JSON.stringify(rawBody.value),
280
+ body: JSON.stringify(body.value),
299
281
  headers: { "Content-Type": "application/json" }
300
282
  };
301
283
  case "urlencoded":
302
284
  return {
303
- body: objectToUrlEncoded(rawBody.value),
285
+ body: objectToUrlEncoded(body.value),
304
286
  headers: { "Content-Type": "application/x-www-form-urlencoded" }
305
287
  };
306
288
  }
@@ -456,32 +438,15 @@ var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
456
438
  var isNetworkError = (err) => err instanceof TypeError;
457
439
  var isAbortError = (err) => err instanceof DOMException && err.name === "AbortError";
458
440
  async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions, nextTags) {
459
- const middlewares = defaultOptions.middlewares ?? [];
460
- let context = {
441
+ return executeCoreFetch({
461
442
  baseUrl,
462
443
  path,
463
444
  method,
464
445
  defaultOptions,
465
446
  requestOptions,
466
- metadata: {}
467
- };
468
- if (middlewares.length > 0) {
469
- context = await applyMiddlewares(context, middlewares, "before");
470
- }
471
- const response = await executeCoreFetch({
472
- baseUrl: context.baseUrl,
473
- path: context.path,
474
- method: context.method,
475
- defaultOptions: context.defaultOptions,
476
- requestOptions: context.requestOptions,
477
- middlewareFetchInit: context.fetchInit,
447
+ middlewareFetchInit: void 0,
478
448
  nextTags
479
449
  });
480
- context.response = response;
481
- if (middlewares.length > 0) {
482
- context = await applyMiddlewares(context, middlewares, "after");
483
- }
484
- return context.response;
485
450
  }
486
451
  function buildInputFields(requestOptions) {
487
452
  const fields = {};
@@ -516,12 +481,10 @@ async function executeCoreFetch(config) {
516
481
  nextTags
517
482
  } = config;
518
483
  const {
519
- middlewares: _,
520
484
  headers: defaultHeaders,
521
485
  transport: defaultTransport,
522
486
  ...fetchDefaults
523
487
  } = defaultOptions;
524
- void _;
525
488
  const inputFields = buildInputFields(requestOptions);
526
489
  const maxRetries = requestOptions?.retries ?? 3;
527
490
  const baseDelay = requestOptions?.retryDelay ?? 1e3;
@@ -712,7 +675,7 @@ function createInitialState() {
712
675
  function generateSelfTagFromKey(key) {
713
676
  try {
714
677
  const parsed = JSON.parse(key);
715
- return parsed.path?.join("/");
678
+ return parsed.path;
716
679
  } catch {
717
680
  return void 0;
718
681
  }
@@ -854,6 +817,9 @@ function createStateManager() {
854
817
  getSize() {
855
818
  return cache.size;
856
819
  },
820
+ getSubscribersCount(key) {
821
+ return subscribers.get(key)?.size ?? 0;
822
+ },
857
823
  setPendingPromise(key, promise) {
858
824
  if (promise === void 0) {
859
825
  pendingPromises.delete(key);
@@ -967,10 +933,7 @@ function createPluginExecutor(initialPlugins = []) {
967
933
  if (!handler) {
968
934
  continue;
969
935
  }
970
- await handler(
971
- context,
972
- previousContext
973
- );
936
+ await handler(context, previousContext);
974
937
  }
975
938
  };
976
939
  return {
@@ -990,6 +953,7 @@ function createPluginExecutor(initialPlugins = []) {
990
953
  return () => middleware(
991
954
  context,
992
955
  next
956
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
993
957
  );
994
958
  },
995
959
  coreFetch
@@ -1015,11 +979,6 @@ function createPluginExecutor(initialPlugins = []) {
1015
979
  createContext(input) {
1016
980
  const ctx = input;
1017
981
  ctx.plugins = createPluginAccessor(ctx);
1018
- ctx.headers = {};
1019
- ctx.setHeaders = (newHeaders) => {
1020
- ctx.headers = { ...ctx.headers, ...newHeaders };
1021
- ctx.requestOptions.headers = ctx.headers;
1022
- };
1023
982
  return ctx;
1024
983
  }
1025
984
  };
@@ -1253,15 +1212,10 @@ var Spoosh = class _Spoosh {
1253
1212
  };
1254
1213
 
1255
1214
  // src/createClient.ts
1256
- function createClient(config) {
1257
- const { baseUrl, defaultOptions = {}, middlewares = [] } = config;
1258
- const optionsWithMiddlewares = {
1259
- ...defaultOptions,
1260
- middlewares
1261
- };
1215
+ function createClient(baseUrl, defaultOptions) {
1262
1216
  return createProxyHandler({
1263
1217
  baseUrl,
1264
- defaultOptions: optionsWithMiddlewares,
1218
+ defaultOptions: defaultOptions || {},
1265
1219
  nextTags: true
1266
1220
  });
1267
1221
  }
@@ -1292,9 +1246,7 @@ function createOperationController(options) {
1292
1246
  let cachedState = initialState;
1293
1247
  let currentRequestTimestamp = Date.now();
1294
1248
  let isFirstExecute = true;
1295
- const createContext = (requestOptions = {}, requestTimestamp = Date.now()) => {
1296
- const cached = stateManager.getCache(queryKey);
1297
- const state = cached?.state ?? createInitialState();
1249
+ const createContext = (requestOptions = {}, requestTimestamp = Date.now(), resolvedHeaders) => {
1298
1250
  const resolvedTags = pluginOptions?.tags ?? tags;
1299
1251
  return pluginExecutor.createContext({
1300
1252
  operationType,
@@ -1304,11 +1256,13 @@ function createOperationController(options) {
1304
1256
  tags: resolvedTags,
1305
1257
  requestTimestamp,
1306
1258
  hookId,
1307
- requestOptions: { ...initialRequestOptions, ...requestOptions },
1308
- state,
1259
+ request: {
1260
+ ...initialRequestOptions,
1261
+ ...requestOptions,
1262
+ headers: resolvedHeaders ?? {}
1263
+ },
1309
1264
  metadata,
1310
1265
  pluginOptions,
1311
- abort: () => abortController?.abort(),
1312
1266
  stateManager,
1313
1267
  eventEmitter
1314
1268
  });
@@ -1336,21 +1290,24 @@ function createOperationController(options) {
1336
1290
  currentRequestTimestamp = Date.now();
1337
1291
  }
1338
1292
  isFirstExecute = false;
1339
- const context = createContext(opts, currentRequestTimestamp);
1293
+ const mergedOptions = { ...initialRequestOptions, ...opts };
1294
+ const resolvedHeaders = await resolveHeadersToRecord(
1295
+ mergedOptions.headers
1296
+ );
1297
+ const context = createContext(
1298
+ opts,
1299
+ currentRequestTimestamp,
1300
+ resolvedHeaders
1301
+ );
1340
1302
  if (force) {
1341
1303
  context.forceRefetch = true;
1342
1304
  }
1343
- context.headers = await resolveHeadersToRecord(
1344
- context.requestOptions.headers
1345
- );
1346
- context.requestOptions.headers = context.headers;
1347
1305
  const coreFetch = async () => {
1348
1306
  abortController = new AbortController();
1349
- context.requestOptions.signal = abortController.signal;
1307
+ context.request.signal = abortController.signal;
1350
1308
  const fetchPromise = (async () => {
1351
1309
  try {
1352
- const response = await fetchFn(context.requestOptions);
1353
- context.response = response;
1310
+ const response = await fetchFn(context.request);
1354
1311
  return response;
1355
1312
  } catch (err) {
1356
1313
  const errorResponse = {
@@ -1358,7 +1315,6 @@ function createOperationController(options) {
1358
1315
  error: err,
1359
1316
  data: void 0
1360
1317
  };
1361
- context.response = errorResponse;
1362
1318
  return errorResponse;
1363
1319
  }
1364
1320
  })();
@@ -1587,11 +1543,6 @@ function createInfiniteReadController(options) {
1587
1543
  );
1588
1544
  };
1589
1545
  const createContext = (pageKey) => {
1590
- const initialState = {
1591
- data: void 0,
1592
- error: void 0,
1593
- timestamp: 0
1594
- };
1595
1546
  return pluginExecutor.createContext({
1596
1547
  operationType: "infiniteRead",
1597
1548
  path,
@@ -1600,11 +1551,9 @@ function createInfiniteReadController(options) {
1600
1551
  tags,
1601
1552
  requestTimestamp: Date.now(),
1602
1553
  hookId,
1603
- requestOptions: {},
1604
- state: initialState,
1554
+ request: { headers: {} },
1605
1555
  metadata: /* @__PURE__ */ new Map(),
1606
1556
  pluginOptions,
1607
- abort: () => abortController?.abort(),
1608
1557
  stateManager,
1609
1558
  eventEmitter
1610
1559
  });
@@ -1631,7 +1580,6 @@ function createInfiniteReadController(options) {
1631
1580
  const fetchPromise = (async () => {
1632
1581
  try {
1633
1582
  const response = await fetchFn(mergedRequest, signal);
1634
- context.response = response;
1635
1583
  if (signal.aborted) {
1636
1584
  return {
1637
1585
  status: 0,
@@ -1653,7 +1601,6 @@ function createInfiniteReadController(options) {
1653
1601
  error: err,
1654
1602
  data: void 0
1655
1603
  };
1656
- context.response = errorResponse;
1657
1604
  latestError = err;
1658
1605
  return errorResponse;
1659
1606
  } finally {
package/dist/index.mjs CHANGED
@@ -1,19 +1,3 @@
1
- // src/middleware.ts
2
- function createMiddleware(name, phase, handler) {
3
- return { name, phase, handler };
4
- }
5
- async function applyMiddlewares(context, middlewares, phase) {
6
- const phaseMiddlewares = middlewares.filter((m) => m.phase === phase);
7
- let ctx = context;
8
- for (const middleware of phaseMiddlewares) {
9
- ctx = await middleware.handler(ctx);
10
- }
11
- return ctx;
12
- }
13
- function composeMiddlewares(...middlewareLists) {
14
- return middlewareLists.flat().filter(Boolean);
15
- }
16
-
17
1
  // src/utils/buildUrl.ts
18
2
  function stringifyQuery(query) {
19
3
  const parts = [];
@@ -224,19 +208,20 @@ function resolveRequestBody(rawBody) {
224
208
  return void 0;
225
209
  }
226
210
  if (isSpooshBody(rawBody)) {
227
- switch (rawBody.kind) {
211
+ const body = rawBody;
212
+ switch (body.kind) {
228
213
  case "form":
229
214
  return {
230
- body: objectToFormData(rawBody.value)
215
+ body: objectToFormData(body.value)
231
216
  };
232
217
  case "json":
233
218
  return {
234
- body: JSON.stringify(rawBody.value),
219
+ body: JSON.stringify(body.value),
235
220
  headers: { "Content-Type": "application/json" }
236
221
  };
237
222
  case "urlencoded":
238
223
  return {
239
- body: objectToUrlEncoded(rawBody.value),
224
+ body: objectToUrlEncoded(body.value),
240
225
  headers: { "Content-Type": "application/x-www-form-urlencoded" }
241
226
  };
242
227
  }
@@ -392,32 +377,15 @@ var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
392
377
  var isNetworkError = (err) => err instanceof TypeError;
393
378
  var isAbortError = (err) => err instanceof DOMException && err.name === "AbortError";
394
379
  async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions, nextTags) {
395
- const middlewares = defaultOptions.middlewares ?? [];
396
- let context = {
380
+ return executeCoreFetch({
397
381
  baseUrl,
398
382
  path,
399
383
  method,
400
384
  defaultOptions,
401
385
  requestOptions,
402
- metadata: {}
403
- };
404
- if (middlewares.length > 0) {
405
- context = await applyMiddlewares(context, middlewares, "before");
406
- }
407
- const response = await executeCoreFetch({
408
- baseUrl: context.baseUrl,
409
- path: context.path,
410
- method: context.method,
411
- defaultOptions: context.defaultOptions,
412
- requestOptions: context.requestOptions,
413
- middlewareFetchInit: context.fetchInit,
386
+ middlewareFetchInit: void 0,
414
387
  nextTags
415
388
  });
416
- context.response = response;
417
- if (middlewares.length > 0) {
418
- context = await applyMiddlewares(context, middlewares, "after");
419
- }
420
- return context.response;
421
389
  }
422
390
  function buildInputFields(requestOptions) {
423
391
  const fields = {};
@@ -452,12 +420,10 @@ async function executeCoreFetch(config) {
452
420
  nextTags
453
421
  } = config;
454
422
  const {
455
- middlewares: _,
456
423
  headers: defaultHeaders,
457
424
  transport: defaultTransport,
458
425
  ...fetchDefaults
459
426
  } = defaultOptions;
460
- void _;
461
427
  const inputFields = buildInputFields(requestOptions);
462
428
  const maxRetries = requestOptions?.retries ?? 3;
463
429
  const baseDelay = requestOptions?.retryDelay ?? 1e3;
@@ -648,7 +614,7 @@ function createInitialState() {
648
614
  function generateSelfTagFromKey(key) {
649
615
  try {
650
616
  const parsed = JSON.parse(key);
651
- return parsed.path?.join("/");
617
+ return parsed.path;
652
618
  } catch {
653
619
  return void 0;
654
620
  }
@@ -790,6 +756,9 @@ function createStateManager() {
790
756
  getSize() {
791
757
  return cache.size;
792
758
  },
759
+ getSubscribersCount(key) {
760
+ return subscribers.get(key)?.size ?? 0;
761
+ },
793
762
  setPendingPromise(key, promise) {
794
763
  if (promise === void 0) {
795
764
  pendingPromises.delete(key);
@@ -903,10 +872,7 @@ function createPluginExecutor(initialPlugins = []) {
903
872
  if (!handler) {
904
873
  continue;
905
874
  }
906
- await handler(
907
- context,
908
- previousContext
909
- );
875
+ await handler(context, previousContext);
910
876
  }
911
877
  };
912
878
  return {
@@ -926,6 +892,7 @@ function createPluginExecutor(initialPlugins = []) {
926
892
  return () => middleware(
927
893
  context,
928
894
  next
895
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
929
896
  );
930
897
  },
931
898
  coreFetch
@@ -951,11 +918,6 @@ function createPluginExecutor(initialPlugins = []) {
951
918
  createContext(input) {
952
919
  const ctx = input;
953
920
  ctx.plugins = createPluginAccessor(ctx);
954
- ctx.headers = {};
955
- ctx.setHeaders = (newHeaders) => {
956
- ctx.headers = { ...ctx.headers, ...newHeaders };
957
- ctx.requestOptions.headers = ctx.headers;
958
- };
959
921
  return ctx;
960
922
  }
961
923
  };
@@ -1189,15 +1151,10 @@ var Spoosh = class _Spoosh {
1189
1151
  };
1190
1152
 
1191
1153
  // src/createClient.ts
1192
- function createClient(config) {
1193
- const { baseUrl, defaultOptions = {}, middlewares = [] } = config;
1194
- const optionsWithMiddlewares = {
1195
- ...defaultOptions,
1196
- middlewares
1197
- };
1154
+ function createClient(baseUrl, defaultOptions) {
1198
1155
  return createProxyHandler({
1199
1156
  baseUrl,
1200
- defaultOptions: optionsWithMiddlewares,
1157
+ defaultOptions: defaultOptions || {},
1201
1158
  nextTags: true
1202
1159
  });
1203
1160
  }
@@ -1228,9 +1185,7 @@ function createOperationController(options) {
1228
1185
  let cachedState = initialState;
1229
1186
  let currentRequestTimestamp = Date.now();
1230
1187
  let isFirstExecute = true;
1231
- const createContext = (requestOptions = {}, requestTimestamp = Date.now()) => {
1232
- const cached = stateManager.getCache(queryKey);
1233
- const state = cached?.state ?? createInitialState();
1188
+ const createContext = (requestOptions = {}, requestTimestamp = Date.now(), resolvedHeaders) => {
1234
1189
  const resolvedTags = pluginOptions?.tags ?? tags;
1235
1190
  return pluginExecutor.createContext({
1236
1191
  operationType,
@@ -1240,11 +1195,13 @@ function createOperationController(options) {
1240
1195
  tags: resolvedTags,
1241
1196
  requestTimestamp,
1242
1197
  hookId,
1243
- requestOptions: { ...initialRequestOptions, ...requestOptions },
1244
- state,
1198
+ request: {
1199
+ ...initialRequestOptions,
1200
+ ...requestOptions,
1201
+ headers: resolvedHeaders ?? {}
1202
+ },
1245
1203
  metadata,
1246
1204
  pluginOptions,
1247
- abort: () => abortController?.abort(),
1248
1205
  stateManager,
1249
1206
  eventEmitter
1250
1207
  });
@@ -1272,21 +1229,24 @@ function createOperationController(options) {
1272
1229
  currentRequestTimestamp = Date.now();
1273
1230
  }
1274
1231
  isFirstExecute = false;
1275
- const context = createContext(opts, currentRequestTimestamp);
1232
+ const mergedOptions = { ...initialRequestOptions, ...opts };
1233
+ const resolvedHeaders = await resolveHeadersToRecord(
1234
+ mergedOptions.headers
1235
+ );
1236
+ const context = createContext(
1237
+ opts,
1238
+ currentRequestTimestamp,
1239
+ resolvedHeaders
1240
+ );
1276
1241
  if (force) {
1277
1242
  context.forceRefetch = true;
1278
1243
  }
1279
- context.headers = await resolveHeadersToRecord(
1280
- context.requestOptions.headers
1281
- );
1282
- context.requestOptions.headers = context.headers;
1283
1244
  const coreFetch = async () => {
1284
1245
  abortController = new AbortController();
1285
- context.requestOptions.signal = abortController.signal;
1246
+ context.request.signal = abortController.signal;
1286
1247
  const fetchPromise = (async () => {
1287
1248
  try {
1288
- const response = await fetchFn(context.requestOptions);
1289
- context.response = response;
1249
+ const response = await fetchFn(context.request);
1290
1250
  return response;
1291
1251
  } catch (err) {
1292
1252
  const errorResponse = {
@@ -1294,7 +1254,6 @@ function createOperationController(options) {
1294
1254
  error: err,
1295
1255
  data: void 0
1296
1256
  };
1297
- context.response = errorResponse;
1298
1257
  return errorResponse;
1299
1258
  }
1300
1259
  })();
@@ -1523,11 +1482,6 @@ function createInfiniteReadController(options) {
1523
1482
  );
1524
1483
  };
1525
1484
  const createContext = (pageKey) => {
1526
- const initialState = {
1527
- data: void 0,
1528
- error: void 0,
1529
- timestamp: 0
1530
- };
1531
1485
  return pluginExecutor.createContext({
1532
1486
  operationType: "infiniteRead",
1533
1487
  path,
@@ -1536,11 +1490,9 @@ function createInfiniteReadController(options) {
1536
1490
  tags,
1537
1491
  requestTimestamp: Date.now(),
1538
1492
  hookId,
1539
- requestOptions: {},
1540
- state: initialState,
1493
+ request: { headers: {} },
1541
1494
  metadata: /* @__PURE__ */ new Map(),
1542
1495
  pluginOptions,
1543
- abort: () => abortController?.abort(),
1544
1496
  stateManager,
1545
1497
  eventEmitter
1546
1498
  });
@@ -1567,7 +1519,6 @@ function createInfiniteReadController(options) {
1567
1519
  const fetchPromise = (async () => {
1568
1520
  try {
1569
1521
  const response = await fetchFn(mergedRequest, signal);
1570
- context.response = response;
1571
1522
  if (signal.aborted) {
1572
1523
  return {
1573
1524
  status: 0,
@@ -1589,7 +1540,6 @@ function createInfiniteReadController(options) {
1589
1540
  error: err,
1590
1541
  data: void 0
1591
1542
  };
1592
- context.response = errorResponse;
1593
1543
  latestError = err;
1594
1544
  return errorResponse;
1595
1545
  } finally {
@@ -1764,15 +1714,12 @@ export {
1764
1714
  HTTP_METHODS,
1765
1715
  Spoosh,
1766
1716
  __DEV__,
1767
- applyMiddlewares,
1768
1717
  buildUrl,
1769
- composeMiddlewares,
1770
1718
  containsFile,
1771
1719
  createClient,
1772
1720
  createEventEmitter,
1773
1721
  createInfiniteReadController,
1774
1722
  createInitialState,
1775
- createMiddleware,
1776
1723
  createOperationController,
1777
1724
  createPluginExecutor,
1778
1725
  createPluginRegistry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spoosh/core",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "license": "MIT",
5
5
  "description": "Type-safe API toolkit with plugin middleware system",
6
6
  "keywords": [