@spoosh/core 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -592,6 +592,7 @@ type PluginTypeConfig = {
592
592
  readOptions?: object;
593
593
  writeOptions?: object;
594
594
  infiniteReadOptions?: object;
595
+ writeTriggerOptions?: object;
595
596
  readResult?: object;
596
597
  writeResult?: object;
597
598
  instanceApi?: object;
@@ -1019,6 +1020,9 @@ type ExtractWriteOptions<T> = T extends SpooshPlugin<infer Types> ? Types extend
1019
1020
  type ExtractInfiniteReadOptions<T> = T extends SpooshPlugin<infer Types> ? Types extends {
1020
1021
  infiniteReadOptions: infer I;
1021
1022
  } ? I : object : object;
1023
+ type ExtractWriteTriggerOptions<T> = T extends SpooshPlugin<infer Types> ? Types extends {
1024
+ writeTriggerOptions: infer W;
1025
+ } ? W : object : object;
1022
1026
  type ExtractReadResult<T> = T extends SpooshPlugin<infer Types> ? Types extends {
1023
1027
  readResult: infer R;
1024
1028
  } ? R : object : object;
@@ -1033,6 +1037,7 @@ type MergePluginOptions<TPlugins extends readonly SpooshPlugin<PluginTypeConfig>
1033
1037
  read: UnionToIntersection<ExtractReadOptions<TPlugins[number]>>;
1034
1038
  write: UnionToIntersection<ExtractWriteOptions<TPlugins[number]>>;
1035
1039
  infiniteRead: UnionToIntersection<ExtractInfiniteReadOptions<TPlugins[number]>>;
1040
+ writeTrigger: UnionToIntersection<ExtractWriteTriggerOptions<TPlugins[number]>>;
1036
1041
  };
1037
1042
  type MergePluginResults<TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = {
1038
1043
  read: UnionToIntersection<ExtractReadResult<TPlugins[number]>>;
@@ -1408,6 +1413,22 @@ type WritePathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchi
1408
1413
  * Used by useWrite and injectWrite hooks.
1409
1414
  */
1410
1415
  type WriteClient<TSchema, TDefaultError = unknown> = <TPath extends WritePaths<TSchema> | (string & {})>(path: TPath) => HasWriteMethod<TSchema, TPath> extends true ? WritePathMethods<TSchema, TPath, TDefaultError> : never;
1416
+ /**
1417
+ * Method function type for write selectors - accepts no arguments.
1418
+ * All input (body, query, params) is passed to trigger() instead.
1419
+ */
1420
+ type WriteSelectorMethodFn<TMethodConfig, TDefaultError, TUserPath extends string> = () => Promise<MethodResponse<TMethodConfig, TDefaultError, TUserPath>>;
1421
+ /**
1422
+ * Write selector path methods - methods accept no arguments.
1423
+ */
1424
+ type WriteSelectorPathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchingKey<TSchema, TPath> extends infer TKey ? TKey extends keyof TSchema ? Simplify<{
1425
+ [M in WriteMethod as M extends keyof TSchema[TKey] ? M : never]: M extends keyof TSchema[TKey] ? WriteSelectorMethodFn<TSchema[TKey][M], TDefaultError, TPath> : never;
1426
+ }> : never : never;
1427
+ /**
1428
+ * Write selector client - methods accept no arguments.
1429
+ * Used by useWrite for selecting endpoints. All input goes to trigger().
1430
+ */
1431
+ type WriteSelectorClient<TSchema, TDefaultError = unknown> = <TPath extends WritePaths<TSchema> | (string & {})>(path: TPath) => HasWriteMethod<TSchema, TPath> extends true ? WriteSelectorPathMethods<TSchema, TPath, TDefaultError> : never;
1411
1432
 
1412
1433
  type PluginArray = readonly SpooshPlugin<PluginTypeConfig>[];
1413
1434
  interface SpooshConfig<TPlugins extends PluginArray = PluginArray> {
@@ -1465,7 +1486,7 @@ type SpooshInstance<TSchema = unknown, TDefaultError = unknown, TPlugins extends
1465
1486
  *
1466
1487
  * // In component
1467
1488
  * const { data } = useRead((api) => api("posts").GET());
1468
- * const { trigger } = useWrite((api) => api("posts").POST);
1489
+ * const { trigger } = useWrite((api) => api("posts").POST());
1469
1490
  * ```
1470
1491
  *
1471
1492
  * @since 0.1.0
@@ -1727,6 +1748,21 @@ type TagOptions = {
1727
1748
  };
1728
1749
  declare function resolveTags(options: TagOptions | undefined, resolvedPath: string[]): string[];
1729
1750
  declare function resolvePath(path: string[], params: Record<string, string | number> | undefined): string[];
1751
+ /**
1752
+ * Resolves dynamic path parameters in a string path.
1753
+ * Unlike `resolvePath`, this works with string paths and doesn't throw on missing params.
1754
+ *
1755
+ * @param path - The path string with dynamic segments (e.g., "products/:id/comments")
1756
+ * @param params - The params object containing values to substitute
1757
+ * @returns The resolved path string (e.g., "products/1/comments")
1758
+ *
1759
+ * @example
1760
+ * ```ts
1761
+ * resolvePathString("products/:id/comments", { id: 1 })
1762
+ * // => "products/1/comments"
1763
+ * ```
1764
+ */
1765
+ declare function resolvePathString(path: string, params: Record<string, string | number> | undefined): string;
1730
1766
 
1731
1767
  declare const isNetworkError: (err: unknown) => boolean;
1732
1768
  declare const isAbortError: (err: unknown) => boolean;
@@ -1872,7 +1908,7 @@ declare function extractPathFromSelector(fn: unknown): string;
1872
1908
  * @example
1873
1909
  * ```ts
1874
1910
  * const proxy = createSelectorProxy<ApiSchema>();
1875
- * const method = extractMethodFromSelector(proxy("posts").POST);
1911
+ * const method = extractMethodFromSelector(proxy("posts").POST());
1876
1912
  * // method = 'POST'
1877
1913
  * ```
1878
1914
  */
@@ -1981,4 +2017,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
1981
2017
  };
1982
2018
  declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
1983
2019
 
1984
- 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 DevtoolEvents, type EventEmitter, type EventListener, type EventOptions, type EventTracer, 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 PluginContextBase, type PluginContextExtensions, 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 RequestCompleteEvent, type RequestOptions$1 as RequestOptions, type RequestTracer, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type SetupContext, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshOptions, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StandaloneEvent, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Trace, type TraceColor, type TraceEvent, type TraceInfo, type TraceListener, type TraceOptions, type TraceStage, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, buildUrl, clone, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, createTracer, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isAbortError, isJsonBody, isNetworkError, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
2020
+ 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 DevtoolEvents, type EventEmitter, type EventListener, type EventOptions, type EventTracer, 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 PluginContextBase, type PluginContextExtensions, 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 RequestCompleteEvent, type RequestOptions$1 as RequestOptions, type RequestTracer, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type SetupContext, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshOptions, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StandaloneEvent, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Trace, type TraceColor, type TraceEvent, type TraceInfo, type TraceListener, type TraceOptions, type TraceStage, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, type WriteSelectorClient, __DEV__, buildUrl, clone, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, createTracer, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isAbortError, isJsonBody, isNetworkError, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolvePathString, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
package/dist/index.d.ts CHANGED
@@ -592,6 +592,7 @@ type PluginTypeConfig = {
592
592
  readOptions?: object;
593
593
  writeOptions?: object;
594
594
  infiniteReadOptions?: object;
595
+ writeTriggerOptions?: object;
595
596
  readResult?: object;
596
597
  writeResult?: object;
597
598
  instanceApi?: object;
@@ -1019,6 +1020,9 @@ type ExtractWriteOptions<T> = T extends SpooshPlugin<infer Types> ? Types extend
1019
1020
  type ExtractInfiniteReadOptions<T> = T extends SpooshPlugin<infer Types> ? Types extends {
1020
1021
  infiniteReadOptions: infer I;
1021
1022
  } ? I : object : object;
1023
+ type ExtractWriteTriggerOptions<T> = T extends SpooshPlugin<infer Types> ? Types extends {
1024
+ writeTriggerOptions: infer W;
1025
+ } ? W : object : object;
1022
1026
  type ExtractReadResult<T> = T extends SpooshPlugin<infer Types> ? Types extends {
1023
1027
  readResult: infer R;
1024
1028
  } ? R : object : object;
@@ -1033,6 +1037,7 @@ type MergePluginOptions<TPlugins extends readonly SpooshPlugin<PluginTypeConfig>
1033
1037
  read: UnionToIntersection<ExtractReadOptions<TPlugins[number]>>;
1034
1038
  write: UnionToIntersection<ExtractWriteOptions<TPlugins[number]>>;
1035
1039
  infiniteRead: UnionToIntersection<ExtractInfiniteReadOptions<TPlugins[number]>>;
1040
+ writeTrigger: UnionToIntersection<ExtractWriteTriggerOptions<TPlugins[number]>>;
1036
1041
  };
1037
1042
  type MergePluginResults<TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = {
1038
1043
  read: UnionToIntersection<ExtractReadResult<TPlugins[number]>>;
@@ -1408,6 +1413,22 @@ type WritePathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchi
1408
1413
  * Used by useWrite and injectWrite hooks.
1409
1414
  */
1410
1415
  type WriteClient<TSchema, TDefaultError = unknown> = <TPath extends WritePaths<TSchema> | (string & {})>(path: TPath) => HasWriteMethod<TSchema, TPath> extends true ? WritePathMethods<TSchema, TPath, TDefaultError> : never;
1416
+ /**
1417
+ * Method function type for write selectors - accepts no arguments.
1418
+ * All input (body, query, params) is passed to trigger() instead.
1419
+ */
1420
+ type WriteSelectorMethodFn<TMethodConfig, TDefaultError, TUserPath extends string> = () => Promise<MethodResponse<TMethodConfig, TDefaultError, TUserPath>>;
1421
+ /**
1422
+ * Write selector path methods - methods accept no arguments.
1423
+ */
1424
+ type WriteSelectorPathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchingKey<TSchema, TPath> extends infer TKey ? TKey extends keyof TSchema ? Simplify<{
1425
+ [M in WriteMethod as M extends keyof TSchema[TKey] ? M : never]: M extends keyof TSchema[TKey] ? WriteSelectorMethodFn<TSchema[TKey][M], TDefaultError, TPath> : never;
1426
+ }> : never : never;
1427
+ /**
1428
+ * Write selector client - methods accept no arguments.
1429
+ * Used by useWrite for selecting endpoints. All input goes to trigger().
1430
+ */
1431
+ type WriteSelectorClient<TSchema, TDefaultError = unknown> = <TPath extends WritePaths<TSchema> | (string & {})>(path: TPath) => HasWriteMethod<TSchema, TPath> extends true ? WriteSelectorPathMethods<TSchema, TPath, TDefaultError> : never;
1411
1432
 
1412
1433
  type PluginArray = readonly SpooshPlugin<PluginTypeConfig>[];
1413
1434
  interface SpooshConfig<TPlugins extends PluginArray = PluginArray> {
@@ -1465,7 +1486,7 @@ type SpooshInstance<TSchema = unknown, TDefaultError = unknown, TPlugins extends
1465
1486
  *
1466
1487
  * // In component
1467
1488
  * const { data } = useRead((api) => api("posts").GET());
1468
- * const { trigger } = useWrite((api) => api("posts").POST);
1489
+ * const { trigger } = useWrite((api) => api("posts").POST());
1469
1490
  * ```
1470
1491
  *
1471
1492
  * @since 0.1.0
@@ -1727,6 +1748,21 @@ type TagOptions = {
1727
1748
  };
1728
1749
  declare function resolveTags(options: TagOptions | undefined, resolvedPath: string[]): string[];
1729
1750
  declare function resolvePath(path: string[], params: Record<string, string | number> | undefined): string[];
1751
+ /**
1752
+ * Resolves dynamic path parameters in a string path.
1753
+ * Unlike `resolvePath`, this works with string paths and doesn't throw on missing params.
1754
+ *
1755
+ * @param path - The path string with dynamic segments (e.g., "products/:id/comments")
1756
+ * @param params - The params object containing values to substitute
1757
+ * @returns The resolved path string (e.g., "products/1/comments")
1758
+ *
1759
+ * @example
1760
+ * ```ts
1761
+ * resolvePathString("products/:id/comments", { id: 1 })
1762
+ * // => "products/1/comments"
1763
+ * ```
1764
+ */
1765
+ declare function resolvePathString(path: string, params: Record<string, string | number> | undefined): string;
1730
1766
 
1731
1767
  declare const isNetworkError: (err: unknown) => boolean;
1732
1768
  declare const isAbortError: (err: unknown) => boolean;
@@ -1872,7 +1908,7 @@ declare function extractPathFromSelector(fn: unknown): string;
1872
1908
  * @example
1873
1909
  * ```ts
1874
1910
  * const proxy = createSelectorProxy<ApiSchema>();
1875
- * const method = extractMethodFromSelector(proxy("posts").POST);
1911
+ * const method = extractMethodFromSelector(proxy("posts").POST());
1876
1912
  * // method = 'POST'
1877
1913
  * ```
1878
1914
  */
@@ -1981,4 +2017,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
1981
2017
  };
1982
2018
  declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
1983
2019
 
1984
- 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 DevtoolEvents, type EventEmitter, type EventListener, type EventOptions, type EventTracer, 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 PluginContextBase, type PluginContextExtensions, 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 RequestCompleteEvent, type RequestOptions$1 as RequestOptions, type RequestTracer, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type SetupContext, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshOptions, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StandaloneEvent, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Trace, type TraceColor, type TraceEvent, type TraceInfo, type TraceListener, type TraceOptions, type TraceStage, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, buildUrl, clone, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, createTracer, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isAbortError, isJsonBody, isNetworkError, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
2020
+ 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 DevtoolEvents, type EventEmitter, type EventListener, type EventOptions, type EventTracer, 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 PluginContextBase, type PluginContextExtensions, 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 RequestCompleteEvent, type RequestOptions$1 as RequestOptions, type RequestTracer, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type SetupContext, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshOptions, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StandaloneEvent, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Trace, type TraceColor, type TraceEvent, type TraceInfo, type TraceListener, type TraceOptions, type TraceStage, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, type WriteSelectorClient, __DEV__, buildUrl, clone, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, createTracer, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isAbortError, isJsonBody, isNetworkError, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolvePathString, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
package/dist/index.js CHANGED
@@ -54,6 +54,7 @@ __export(src_exports, {
54
54
  objectToUrlEncoded: () => objectToUrlEncoded,
55
55
  resolveHeadersToRecord: () => resolveHeadersToRecord,
56
56
  resolvePath: () => resolvePath,
57
+ resolvePathString: () => resolvePathString,
57
58
  resolveRequestBody: () => resolveRequestBody,
58
59
  resolveTags: () => resolveTags,
59
60
  setHeaders: () => setHeaders,
@@ -355,6 +356,17 @@ function resolvePath(path, params) {
355
356
  return segment;
356
357
  });
357
358
  }
359
+ function resolvePathString(path, params) {
360
+ if (!params) return path;
361
+ return path.split("/").map((segment) => {
362
+ if (segment.startsWith(":")) {
363
+ const paramName = segment.slice(1);
364
+ const value = params[paramName];
365
+ return value !== void 0 ? String(value) : segment;
366
+ }
367
+ return segment;
368
+ }).join("/");
369
+ }
358
370
 
359
371
  // src/utils/errors.ts
360
372
  var isNetworkError = (err) => err instanceof TypeError;
@@ -415,7 +427,15 @@ var fetchTransport = async (url, init) => {
415
427
  const res = await fetch(url, init);
416
428
  const contentType = res.headers.get("content-type");
417
429
  const isJson = contentType?.includes("application/json");
418
- const data = isJson ? await res.json() : res;
430
+ const isText = contentType?.includes("text/") || contentType?.includes("application/xml");
431
+ let data;
432
+ if (isJson) {
433
+ data = await res.json();
434
+ } else if (isText) {
435
+ data = await res.text();
436
+ } else {
437
+ data = void 0;
438
+ }
419
439
  return { ok: res.ok, status: res.status, headers: res.headers, data };
420
440
  };
421
441
 
@@ -1325,30 +1345,26 @@ function createOperationController(options) {
1325
1345
  const coreFetch = async () => {
1326
1346
  abortController = new AbortController();
1327
1347
  context.request.signal = abortController.signal;
1328
- const fetchPromise = (async () => {
1329
- try {
1330
- const response = await fetchFn(context.request);
1331
- return response;
1332
- } catch (err) {
1333
- const errorResponse = {
1334
- status: 0,
1335
- error: err,
1336
- data: void 0
1337
- };
1338
- return errorResponse;
1339
- }
1340
- })();
1341
- stateManager.setPendingPromise(queryKey, fetchPromise);
1342
- fetchPromise.finally(() => {
1343
- stateManager.setPendingPromise(queryKey, void 0);
1344
- });
1345
- return fetchPromise;
1348
+ try {
1349
+ const response = await fetchFn(context.request);
1350
+ return response;
1351
+ } catch (err) {
1352
+ const errorResponse = {
1353
+ status: 0,
1354
+ error: err,
1355
+ data: void 0
1356
+ };
1357
+ return errorResponse;
1358
+ }
1346
1359
  };
1347
- const finalResponse = await pluginExecutor.executeMiddleware(
1360
+ const middlewarePromise = pluginExecutor.executeMiddleware(
1348
1361
  operationType,
1349
1362
  context,
1350
1363
  coreFetch
1351
1364
  );
1365
+ stateManager.setPendingPromise(queryKey, middlewarePromise);
1366
+ const finalResponse = await middlewarePromise;
1367
+ stateManager.setPendingPromise(queryKey, void 0);
1352
1368
  if (finalResponse.data !== void 0 && !finalResponse.error) {
1353
1369
  updateState({
1354
1370
  data: finalResponse.data,
@@ -1483,6 +1499,7 @@ function createInfiniteReadController(options) {
1483
1499
  let cachedState = createInitialInfiniteState();
1484
1500
  const trackerKey = createTrackerKey(path, method, baseOptionsForKey);
1485
1501
  let pageSubscriptions = [];
1502
+ let trackerSubscription = null;
1486
1503
  let refetchUnsubscribe = null;
1487
1504
  const loadFromTracker = () => {
1488
1505
  const cached = stateManager.getCache(trackerKey);
@@ -1597,52 +1614,50 @@ function createInfiniteReadController(options) {
1597
1614
  }
1598
1615
  pendingFetches.add(pageKey);
1599
1616
  fetchingDirection = direction;
1617
+ latestError = void 0;
1600
1618
  notify();
1601
1619
  abortController = new AbortController();
1602
1620
  const signal = abortController.signal;
1603
1621
  const context = createContext(pageKey, mergedRequest);
1604
1622
  const coreFetch = async () => {
1605
- const fetchPromise = (async () => {
1606
- try {
1607
- const response = await fetchFn(mergedRequest, signal);
1608
- if (signal.aborted) {
1609
- return {
1610
- status: 0,
1611
- data: void 0,
1612
- aborted: true
1613
- };
1614
- }
1615
- return response;
1616
- } catch (err) {
1617
- if (signal.aborted) {
1618
- return {
1619
- status: 0,
1620
- data: void 0,
1621
- aborted: true
1622
- };
1623
- }
1624
- const errorResponse = {
1623
+ try {
1624
+ const response = await fetchFn(mergedRequest, signal);
1625
+ if (signal.aborted) {
1626
+ return {
1625
1627
  status: 0,
1626
- error: err,
1627
- data: void 0
1628
+ data: void 0,
1629
+ aborted: true
1630
+ };
1631
+ }
1632
+ return response;
1633
+ } catch (err) {
1634
+ if (signal.aborted) {
1635
+ return {
1636
+ status: 0,
1637
+ data: void 0,
1638
+ aborted: true
1628
1639
  };
1629
- latestError = err;
1630
- return errorResponse;
1631
- } finally {
1632
- pendingFetches.delete(pageKey);
1633
- fetchingDirection = null;
1634
- stateManager.setPendingPromise(pageKey, void 0);
1635
- notify();
1636
1640
  }
1637
- })();
1638
- stateManager.setPendingPromise(pageKey, fetchPromise);
1639
- return fetchPromise;
1641
+ const errorResponse = {
1642
+ status: 0,
1643
+ error: err,
1644
+ data: void 0
1645
+ };
1646
+ latestError = err;
1647
+ return errorResponse;
1648
+ }
1640
1649
  };
1641
- const finalResponse = await pluginExecutor.executeMiddleware(
1650
+ const middlewarePromise = pluginExecutor.executeMiddleware(
1642
1651
  "infiniteRead",
1643
1652
  context,
1644
1653
  coreFetch
1645
1654
  );
1655
+ stateManager.setPendingPromise(pageKey, middlewarePromise);
1656
+ const finalResponse = await middlewarePromise;
1657
+ pendingFetches.delete(pageKey);
1658
+ fetchingDirection = null;
1659
+ stateManager.setPendingPromise(pageKey, void 0);
1660
+ notify();
1646
1661
  if (finalResponse.data !== void 0 && !finalResponse.error) {
1647
1662
  pageRequests.set(pageKey, mergedRequest);
1648
1663
  if (direction === "next") {
@@ -1755,6 +1770,7 @@ function createInfiniteReadController(options) {
1755
1770
  loadFromTracker();
1756
1771
  cachedState = computeState();
1757
1772
  subscribeToPages();
1773
+ trackerSubscription = stateManager.subscribeCache(trackerKey, notify);
1758
1774
  const context = createContext(trackerKey, initialRequest);
1759
1775
  pluginExecutor.executeLifecycle("onMount", "infiniteRead", context);
1760
1776
  refetchUnsubscribe = eventEmitter.on("refetch", (event) => {
@@ -1776,6 +1792,8 @@ function createInfiniteReadController(options) {
1776
1792
  pluginExecutor.executeLifecycle("onUnmount", "infiniteRead", context);
1777
1793
  pageSubscriptions.forEach((unsub) => unsub());
1778
1794
  pageSubscriptions = [];
1795
+ trackerSubscription?.();
1796
+ trackerSubscription = null;
1779
1797
  refetchUnsubscribe?.();
1780
1798
  refetchUnsubscribe = null;
1781
1799
  },
package/dist/index.mjs CHANGED
@@ -290,6 +290,17 @@ function resolvePath(path, params) {
290
290
  return segment;
291
291
  });
292
292
  }
293
+ function resolvePathString(path, params) {
294
+ if (!params) return path;
295
+ return path.split("/").map((segment) => {
296
+ if (segment.startsWith(":")) {
297
+ const paramName = segment.slice(1);
298
+ const value = params[paramName];
299
+ return value !== void 0 ? String(value) : segment;
300
+ }
301
+ return segment;
302
+ }).join("/");
303
+ }
293
304
 
294
305
  // src/utils/errors.ts
295
306
  var isNetworkError = (err) => err instanceof TypeError;
@@ -350,7 +361,15 @@ var fetchTransport = async (url, init) => {
350
361
  const res = await fetch(url, init);
351
362
  const contentType = res.headers.get("content-type");
352
363
  const isJson = contentType?.includes("application/json");
353
- const data = isJson ? await res.json() : res;
364
+ const isText = contentType?.includes("text/") || contentType?.includes("application/xml");
365
+ let data;
366
+ if (isJson) {
367
+ data = await res.json();
368
+ } else if (isText) {
369
+ data = await res.text();
370
+ } else {
371
+ data = void 0;
372
+ }
354
373
  return { ok: res.ok, status: res.status, headers: res.headers, data };
355
374
  };
356
375
 
@@ -1260,30 +1279,26 @@ function createOperationController(options) {
1260
1279
  const coreFetch = async () => {
1261
1280
  abortController = new AbortController();
1262
1281
  context.request.signal = abortController.signal;
1263
- const fetchPromise = (async () => {
1264
- try {
1265
- const response = await fetchFn(context.request);
1266
- return response;
1267
- } catch (err) {
1268
- const errorResponse = {
1269
- status: 0,
1270
- error: err,
1271
- data: void 0
1272
- };
1273
- return errorResponse;
1274
- }
1275
- })();
1276
- stateManager.setPendingPromise(queryKey, fetchPromise);
1277
- fetchPromise.finally(() => {
1278
- stateManager.setPendingPromise(queryKey, void 0);
1279
- });
1280
- return fetchPromise;
1282
+ try {
1283
+ const response = await fetchFn(context.request);
1284
+ return response;
1285
+ } catch (err) {
1286
+ const errorResponse = {
1287
+ status: 0,
1288
+ error: err,
1289
+ data: void 0
1290
+ };
1291
+ return errorResponse;
1292
+ }
1281
1293
  };
1282
- const finalResponse = await pluginExecutor.executeMiddleware(
1294
+ const middlewarePromise = pluginExecutor.executeMiddleware(
1283
1295
  operationType,
1284
1296
  context,
1285
1297
  coreFetch
1286
1298
  );
1299
+ stateManager.setPendingPromise(queryKey, middlewarePromise);
1300
+ const finalResponse = await middlewarePromise;
1301
+ stateManager.setPendingPromise(queryKey, void 0);
1287
1302
  if (finalResponse.data !== void 0 && !finalResponse.error) {
1288
1303
  updateState({
1289
1304
  data: finalResponse.data,
@@ -1418,6 +1433,7 @@ function createInfiniteReadController(options) {
1418
1433
  let cachedState = createInitialInfiniteState();
1419
1434
  const trackerKey = createTrackerKey(path, method, baseOptionsForKey);
1420
1435
  let pageSubscriptions = [];
1436
+ let trackerSubscription = null;
1421
1437
  let refetchUnsubscribe = null;
1422
1438
  const loadFromTracker = () => {
1423
1439
  const cached = stateManager.getCache(trackerKey);
@@ -1532,52 +1548,50 @@ function createInfiniteReadController(options) {
1532
1548
  }
1533
1549
  pendingFetches.add(pageKey);
1534
1550
  fetchingDirection = direction;
1551
+ latestError = void 0;
1535
1552
  notify();
1536
1553
  abortController = new AbortController();
1537
1554
  const signal = abortController.signal;
1538
1555
  const context = createContext(pageKey, mergedRequest);
1539
1556
  const coreFetch = async () => {
1540
- const fetchPromise = (async () => {
1541
- try {
1542
- const response = await fetchFn(mergedRequest, signal);
1543
- if (signal.aborted) {
1544
- return {
1545
- status: 0,
1546
- data: void 0,
1547
- aborted: true
1548
- };
1549
- }
1550
- return response;
1551
- } catch (err) {
1552
- if (signal.aborted) {
1553
- return {
1554
- status: 0,
1555
- data: void 0,
1556
- aborted: true
1557
- };
1558
- }
1559
- const errorResponse = {
1557
+ try {
1558
+ const response = await fetchFn(mergedRequest, signal);
1559
+ if (signal.aborted) {
1560
+ return {
1560
1561
  status: 0,
1561
- error: err,
1562
- data: void 0
1562
+ data: void 0,
1563
+ aborted: true
1564
+ };
1565
+ }
1566
+ return response;
1567
+ } catch (err) {
1568
+ if (signal.aborted) {
1569
+ return {
1570
+ status: 0,
1571
+ data: void 0,
1572
+ aborted: true
1563
1573
  };
1564
- latestError = err;
1565
- return errorResponse;
1566
- } finally {
1567
- pendingFetches.delete(pageKey);
1568
- fetchingDirection = null;
1569
- stateManager.setPendingPromise(pageKey, void 0);
1570
- notify();
1571
1574
  }
1572
- })();
1573
- stateManager.setPendingPromise(pageKey, fetchPromise);
1574
- return fetchPromise;
1575
+ const errorResponse = {
1576
+ status: 0,
1577
+ error: err,
1578
+ data: void 0
1579
+ };
1580
+ latestError = err;
1581
+ return errorResponse;
1582
+ }
1575
1583
  };
1576
- const finalResponse = await pluginExecutor.executeMiddleware(
1584
+ const middlewarePromise = pluginExecutor.executeMiddleware(
1577
1585
  "infiniteRead",
1578
1586
  context,
1579
1587
  coreFetch
1580
1588
  );
1589
+ stateManager.setPendingPromise(pageKey, middlewarePromise);
1590
+ const finalResponse = await middlewarePromise;
1591
+ pendingFetches.delete(pageKey);
1592
+ fetchingDirection = null;
1593
+ stateManager.setPendingPromise(pageKey, void 0);
1594
+ notify();
1581
1595
  if (finalResponse.data !== void 0 && !finalResponse.error) {
1582
1596
  pageRequests.set(pageKey, mergedRequest);
1583
1597
  if (direction === "next") {
@@ -1690,6 +1704,7 @@ function createInfiniteReadController(options) {
1690
1704
  loadFromTracker();
1691
1705
  cachedState = computeState();
1692
1706
  subscribeToPages();
1707
+ trackerSubscription = stateManager.subscribeCache(trackerKey, notify);
1693
1708
  const context = createContext(trackerKey, initialRequest);
1694
1709
  pluginExecutor.executeLifecycle("onMount", "infiniteRead", context);
1695
1710
  refetchUnsubscribe = eventEmitter.on("refetch", (event) => {
@@ -1711,6 +1726,8 @@ function createInfiniteReadController(options) {
1711
1726
  pluginExecutor.executeLifecycle("onUnmount", "infiniteRead", context);
1712
1727
  pageSubscriptions.forEach((unsub) => unsub());
1713
1728
  pageSubscriptions = [];
1729
+ trackerSubscription?.();
1730
+ trackerSubscription = null;
1714
1731
  refetchUnsubscribe?.();
1715
1732
  refetchUnsubscribe = null;
1716
1733
  },
@@ -1766,6 +1783,7 @@ export {
1766
1783
  objectToUrlEncoded,
1767
1784
  resolveHeadersToRecord,
1768
1785
  resolvePath,
1786
+ resolvePathString,
1769
1787
  resolveRequestBody,
1770
1788
  resolveTags,
1771
1789
  setHeaders,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spoosh/core",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "license": "MIT",
5
5
  "description": "Type-safe API toolkit with plugin middleware system",
6
6
  "keywords": [