@spoosh/core 0.9.0 → 0.9.1
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 +160 -5
- package/dist/index.d.ts +160 -5
- package/dist/index.js +115 -15
- package/dist/index.mjs +115 -15
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -124,7 +124,7 @@ type MethodOptionsMap<TQueryOptions = object, TMutationOptions = object> = {
|
|
|
124
124
|
DELETE: TMutationOptions;
|
|
125
125
|
};
|
|
126
126
|
type ExtractMethodOptions<TOptionsMap, TMethod extends HttpMethod> = TOptionsMap extends MethodOptionsMap<infer TQuery, infer TMutation> ? TMethod extends "GET" ? TQuery : TMutation : TOptionsMap;
|
|
127
|
-
type FetchExecutor<TOptions = SpooshOptions, TRequestOptions = AnyRequestOptions> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions, nextTags?: boolean) => Promise<SpooshResponse<TData, TError>>;
|
|
127
|
+
type FetchExecutor<TOptions = SpooshOptions, TRequestOptions = AnyRequestOptions> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions, nextTags?: boolean, tagPath?: string[]) => Promise<SpooshResponse<TData, TError>>;
|
|
128
128
|
type TypedParamsOption<TParamNames extends string> = [TParamNames] extends [
|
|
129
129
|
never
|
|
130
130
|
] ? object : {
|
|
@@ -251,6 +251,11 @@ type PluginContext<TData = unknown, TError = unknown> = {
|
|
|
251
251
|
readonly requestTimestamp: number;
|
|
252
252
|
/** Unique identifier for the hook instance. Persists across queryKey changes within the same hook. */
|
|
253
253
|
readonly hookId?: string;
|
|
254
|
+
/**
|
|
255
|
+
* Prefix to strip from tags. Configured at the Spoosh instance level.
|
|
256
|
+
* Plugins can use this to normalize tags before emitting events.
|
|
257
|
+
*/
|
|
258
|
+
readonly stripTagPrefix?: string;
|
|
254
259
|
requestOptions: AnyRequestOptions;
|
|
255
260
|
state: OperationState<TData, TError>;
|
|
256
261
|
response?: SpooshResponse<TData, TError>;
|
|
@@ -596,7 +601,10 @@ type PluginExecutor = {
|
|
|
596
601
|
/** Creates a full PluginContext with plugins accessor injected */
|
|
597
602
|
createContext: <TData, TError>(input: PluginContextInput<TData, TError>) => PluginContext<TData, TError>;
|
|
598
603
|
};
|
|
599
|
-
|
|
604
|
+
type PluginExecutorOptions = {
|
|
605
|
+
stripTagPrefix?: string;
|
|
606
|
+
};
|
|
607
|
+
declare function createPluginExecutor(initialPlugins?: SpooshPlugin[], options?: PluginExecutorOptions): PluginExecutor;
|
|
600
608
|
|
|
601
609
|
/**
|
|
602
610
|
* Resolves plugin option types based on the full context.
|
|
@@ -863,6 +871,33 @@ type HasReadMethod<TSchema, TPath extends string> = FindMatchingKey<TSchema, TPa
|
|
|
863
871
|
* Check if a schema path has any write methods.
|
|
864
872
|
*/
|
|
865
873
|
type HasWriteMethod<TSchema, TPath extends string> = FindMatchingKey<TSchema, TPath> extends infer TKey ? TKey extends keyof TSchema ? WriteMethod extends never ? false : Extract<keyof TSchema[TKey], WriteMethod> extends never ? false : true : false : false;
|
|
874
|
+
type NormalizePrefix<T extends string> = T extends `/${infer Rest}` ? NormalizePrefix<Rest> : T extends `${infer Rest}/` ? NormalizePrefix<Rest> : T;
|
|
875
|
+
type StripPrefixFromPath<TPath extends string, TPrefix extends string> = TPath extends TPrefix ? "" : TPath extends `${TPrefix}/${infer Rest}` ? Rest : TPath;
|
|
876
|
+
/**
|
|
877
|
+
* Strips a prefix from all path keys in a schema.
|
|
878
|
+
* Works with any schema (Elysia, Hono, or manual).
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```ts
|
|
882
|
+
* type FullSchema = {
|
|
883
|
+
* "api": { GET: { data: string } };
|
|
884
|
+
* "api/users": { GET: { data: User[] } };
|
|
885
|
+
* "api/posts/:id": { GET: { data: Post } };
|
|
886
|
+
* "health": { GET: { data: { status: string } } };
|
|
887
|
+
* };
|
|
888
|
+
*
|
|
889
|
+
* type ApiSchema = StripPrefix<FullSchema, "api">;
|
|
890
|
+
* // {
|
|
891
|
+
* // "": { GET: { data: string } };
|
|
892
|
+
* // "users": { GET: { data: User[] } };
|
|
893
|
+
* // "posts/:id": { GET: { data: Post } };
|
|
894
|
+
* // "health": { GET: { data: { status: string } } };
|
|
895
|
+
* // }
|
|
896
|
+
* ```
|
|
897
|
+
*/
|
|
898
|
+
type StripPrefix<TSchema, TPrefix extends string> = TPrefix extends "" ? TSchema : {
|
|
899
|
+
[K in keyof TSchema as K extends string ? StripPrefixFromPath<K, NormalizePrefix<TPrefix>> : K]: TSchema[K];
|
|
900
|
+
};
|
|
866
901
|
|
|
867
902
|
type IsNever<T> = [T] extends [never] ? true : false;
|
|
868
903
|
type EndpointRequestOptions<TEndpoint, TPath extends string> = (IsNever<ExtractBody$1<TEndpoint>> extends true ? object : {
|
|
@@ -1056,6 +1091,33 @@ type WritePathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchi
|
|
|
1056
1091
|
type WriteClient<TSchema, TDefaultError = unknown> = <TPath extends WritePaths<TSchema> | (string & {})>(path: TPath) => HasWriteMethod<TSchema, TPath> extends true ? WritePathMethods<TSchema, TPath, TDefaultError> : never;
|
|
1057
1092
|
|
|
1058
1093
|
type PluginArray = readonly SpooshPlugin<PluginTypeConfig>[];
|
|
1094
|
+
/**
|
|
1095
|
+
* Configuration options for Spoosh runtime behavior.
|
|
1096
|
+
*/
|
|
1097
|
+
type SpooshConfigOptions = {
|
|
1098
|
+
/**
|
|
1099
|
+
* Prefix to strip from tag generation.
|
|
1100
|
+
*
|
|
1101
|
+
* URL prefix stripping always auto-detects from baseUrl.
|
|
1102
|
+
* This option only affects tag generation for cache invalidation.
|
|
1103
|
+
*
|
|
1104
|
+
* - `undefined`: Auto-detect from baseUrl (default)
|
|
1105
|
+
* - `string`: Explicit prefix to strip from tags
|
|
1106
|
+
*
|
|
1107
|
+
* @example
|
|
1108
|
+
* ```ts
|
|
1109
|
+
* // Default: auto-detect from baseUrl
|
|
1110
|
+
* // baseUrl="/api", schema="api/posts" → tags: ["posts"]
|
|
1111
|
+
* new Spoosh<Schema>('https://localhost:3000/api')
|
|
1112
|
+
*
|
|
1113
|
+
* // Explicit prefix (when baseUrl doesn't have it)
|
|
1114
|
+
* // baseUrl="/", schema="api/v1/posts" → tags: ["posts"]
|
|
1115
|
+
* new Spoosh<Schema>('http://localhost:3000')
|
|
1116
|
+
* .configure({ stripTagPrefix: "api/v1" })
|
|
1117
|
+
* ```
|
|
1118
|
+
*/
|
|
1119
|
+
stripTagPrefix?: string;
|
|
1120
|
+
};
|
|
1059
1121
|
interface SpooshConfig<TPlugins extends PluginArray = PluginArray> {
|
|
1060
1122
|
baseUrl: string;
|
|
1061
1123
|
defaultOptions?: SpooshOptions;
|
|
@@ -1069,6 +1131,8 @@ type SpooshInstance<TSchema = unknown, TDefaultError = unknown, TPlugins extends
|
|
|
1069
1131
|
config: {
|
|
1070
1132
|
baseUrl: string;
|
|
1071
1133
|
defaultOptions: SpooshOptions;
|
|
1134
|
+
/** Resolved prefix to strip from tags (used for cache invalidation matching) */
|
|
1135
|
+
stripTagPrefix?: string;
|
|
1072
1136
|
};
|
|
1073
1137
|
_types: {
|
|
1074
1138
|
schema: TSchema;
|
|
@@ -1120,12 +1184,14 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1120
1184
|
private baseUrl;
|
|
1121
1185
|
private defaultOptions;
|
|
1122
1186
|
private _plugins;
|
|
1187
|
+
private _config;
|
|
1123
1188
|
/**
|
|
1124
1189
|
* Creates a new Spoosh instance.
|
|
1125
1190
|
*
|
|
1126
1191
|
* @param baseUrl - The base URL for all API requests (e.g., '/api' or 'https://api.example.com')
|
|
1127
1192
|
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, etc.)
|
|
1128
1193
|
* @param plugins - Internal parameter used by the `.use()` method. Do not pass directly.
|
|
1194
|
+
* @param configOptions - Internal parameter used by the `.config()` method. Do not pass directly.
|
|
1129
1195
|
*
|
|
1130
1196
|
* @example
|
|
1131
1197
|
* ```ts
|
|
@@ -1138,7 +1204,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1138
1204
|
* });
|
|
1139
1205
|
* ```
|
|
1140
1206
|
*/
|
|
1141
|
-
constructor(baseUrl: string, defaultOptions?: SpooshOptions, plugins?: TPlugins);
|
|
1207
|
+
constructor(baseUrl: string, defaultOptions?: SpooshOptions, plugins?: TPlugins, configOptions?: SpooshConfigOptions);
|
|
1142
1208
|
/**
|
|
1143
1209
|
* Adds plugins to the Spoosh instance.
|
|
1144
1210
|
*
|
|
@@ -1174,6 +1240,34 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1174
1240
|
* ```
|
|
1175
1241
|
*/
|
|
1176
1242
|
use<const TNewPlugins extends PluginArray>(plugins: TNewPlugins): Spoosh<TSchema, TError, TNewPlugins>;
|
|
1243
|
+
/**
|
|
1244
|
+
* Configures runtime options for the Spoosh instance.
|
|
1245
|
+
*
|
|
1246
|
+
* Returns a **new** Spoosh instance with the updated configuration (immutable pattern).
|
|
1247
|
+
* Configuration is preserved across `.use()` calls.
|
|
1248
|
+
*
|
|
1249
|
+
* URL prefix stripping always auto-detects from baseUrl.
|
|
1250
|
+
* Tag prefix stripping defaults to URL prefix but can be overridden.
|
|
1251
|
+
*
|
|
1252
|
+
* @param options - Configuration options
|
|
1253
|
+
* @returns A new Spoosh instance with the specified configuration
|
|
1254
|
+
*
|
|
1255
|
+
* @example Default behavior (auto-detect from baseUrl for both URL and tags)
|
|
1256
|
+
* ```ts
|
|
1257
|
+
* // baseUrl="/api", schema="api/posts"
|
|
1258
|
+
* // URL: /api/posts, Tags: ["posts"]
|
|
1259
|
+
* const client = new Spoosh<Schema, Error>('https://localhost:3000/api');
|
|
1260
|
+
* ```
|
|
1261
|
+
*
|
|
1262
|
+
* @example Override tag prefix (when baseUrl doesn't have the prefix you want to strip from tags)
|
|
1263
|
+
* ```ts
|
|
1264
|
+
* // baseUrl="/", schema="api/v1/posts"
|
|
1265
|
+
* // URL: /api/v1/posts, Tags: ["posts"] (strips "api/v1" from tags only)
|
|
1266
|
+
* const client = new Spoosh<Schema, Error>('http://localhost:3000')
|
|
1267
|
+
* .configure({ stripTagPrefix: "api/v1" });
|
|
1268
|
+
* ```
|
|
1269
|
+
*/
|
|
1270
|
+
configure(options: SpooshConfigOptions): Spoosh<TSchema, TError, TPlugins>;
|
|
1177
1271
|
/**
|
|
1178
1272
|
* Cached instance of the underlying SpooshInstance.
|
|
1179
1273
|
* Created lazily on first property access.
|
|
@@ -1273,6 +1367,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1273
1367
|
get config(): {
|
|
1274
1368
|
baseUrl: string;
|
|
1275
1369
|
defaultOptions: SpooshOptions;
|
|
1370
|
+
stripTagPrefix?: string;
|
|
1276
1371
|
};
|
|
1277
1372
|
/**
|
|
1278
1373
|
* Type information carrier for generic type inference.
|
|
@@ -1291,6 +1386,16 @@ type SpooshClientConfig = {
|
|
|
1291
1386
|
baseUrl: string;
|
|
1292
1387
|
defaultOptions?: SpooshOptions;
|
|
1293
1388
|
middlewares?: SpooshMiddleware[];
|
|
1389
|
+
/**
|
|
1390
|
+
* Prefix to strip from tag generation.
|
|
1391
|
+
*
|
|
1392
|
+
* URL prefix stripping always auto-detects from baseUrl.
|
|
1393
|
+
* This option only affects tag generation for cache invalidation.
|
|
1394
|
+
*
|
|
1395
|
+
* - `undefined`: Auto-detect from baseUrl (default, same as URL prefix)
|
|
1396
|
+
* - `string`: Explicit prefix to strip from tags
|
|
1397
|
+
*/
|
|
1398
|
+
stripTagPrefix?: string;
|
|
1294
1399
|
};
|
|
1295
1400
|
/**
|
|
1296
1401
|
* Creates a lightweight type-safe API client for vanilla JavaScript/TypeScript usage.
|
|
@@ -1384,11 +1489,61 @@ type TagOptions = {
|
|
|
1384
1489
|
declare function resolveTags(options: TagOptions | undefined, resolvedPath: string[]): string[];
|
|
1385
1490
|
declare function resolvePath(path: string[], params: Record<string, string | number> | undefined): string[];
|
|
1386
1491
|
|
|
1492
|
+
/**
|
|
1493
|
+
* Extracts the path prefix from a base URL.
|
|
1494
|
+
*
|
|
1495
|
+
* @param baseUrl - The base URL (absolute or relative)
|
|
1496
|
+
* @returns The path portion without leading/trailing slashes
|
|
1497
|
+
*
|
|
1498
|
+
* @example
|
|
1499
|
+
* ```ts
|
|
1500
|
+
* extractPrefixFromBaseUrl("https://localhost:3000/api"); // "api"
|
|
1501
|
+
* extractPrefixFromBaseUrl("/api/v1"); // "api/v1"
|
|
1502
|
+
* extractPrefixFromBaseUrl("api"); // "api"
|
|
1503
|
+
* ```
|
|
1504
|
+
*/
|
|
1505
|
+
declare function extractPrefixFromBaseUrl(baseUrl: string): string;
|
|
1506
|
+
/**
|
|
1507
|
+
* Strips a prefix from path segments if the path starts with that prefix.
|
|
1508
|
+
*
|
|
1509
|
+
* @param pathSegments - Array of path segments
|
|
1510
|
+
* @param prefix - Prefix to strip (e.g., "api" or "api/v1")
|
|
1511
|
+
* @returns Path segments with prefix removed
|
|
1512
|
+
*
|
|
1513
|
+
* @example
|
|
1514
|
+
* ```ts
|
|
1515
|
+
* stripPrefixFromPath(["api", "posts"], "api"); // ["posts"]
|
|
1516
|
+
* stripPrefixFromPath(["api", "v1", "users"], "api/v1"); // ["users"]
|
|
1517
|
+
* stripPrefixFromPath(["posts"], "api"); // ["posts"] (no match, unchanged)
|
|
1518
|
+
* ```
|
|
1519
|
+
*/
|
|
1520
|
+
declare function stripPrefixFromPath(pathSegments: string[], prefix: string): string[];
|
|
1521
|
+
/**
|
|
1522
|
+
* Resolves the strip prefix value based on configuration.
|
|
1523
|
+
*
|
|
1524
|
+
* @param stripPathPrefix - Configuration value (boolean, string, or undefined)
|
|
1525
|
+
* @param baseUrl - The base URL to extract prefix from when true
|
|
1526
|
+
* @returns The resolved prefix string to strip
|
|
1527
|
+
*
|
|
1528
|
+
* @example
|
|
1529
|
+
* ```ts
|
|
1530
|
+
* resolveStripPrefix(true, "https://localhost:3000/api"); // "api"
|
|
1531
|
+
* resolveStripPrefix("api/v1", "https://localhost:3000/api"); // "api/v1"
|
|
1532
|
+
* resolveStripPrefix(false, "https://localhost:3000/api"); // ""
|
|
1533
|
+
* resolveStripPrefix(undefined, "https://localhost:3000/api"); // ""
|
|
1534
|
+
* ```
|
|
1535
|
+
*/
|
|
1536
|
+
declare function resolveStripPrefix(stripPathPrefix: boolean | string | undefined, baseUrl: string): string;
|
|
1537
|
+
|
|
1387
1538
|
type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
1388
1539
|
baseUrl: string;
|
|
1389
1540
|
defaultOptions: TOptions;
|
|
1390
1541
|
fetchExecutor?: FetchExecutor<TOptions, AnyRequestOptions>;
|
|
1391
1542
|
nextTags?: boolean;
|
|
1543
|
+
/** Prefix to strip from URL path (auto-detected from baseUrl, always applied) */
|
|
1544
|
+
urlPrefix?: string;
|
|
1545
|
+
/** Prefix to strip from tag generation (defaults to urlPrefix) */
|
|
1546
|
+
tagPrefix?: string;
|
|
1392
1547
|
};
|
|
1393
1548
|
/**
|
|
1394
1549
|
* Creates an API client proxy that uses path strings instead of chained property access.
|
|
@@ -1515,7 +1670,7 @@ declare function extractPathFromSelector(fn: unknown): string;
|
|
|
1515
1670
|
*/
|
|
1516
1671
|
declare function extractMethodFromSelector(fn: unknown): string | undefined;
|
|
1517
1672
|
|
|
1518
|
-
declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions & SpooshOptionsExtra, requestOptions?: AnyRequestOptions, nextTags?: boolean): Promise<SpooshResponse<TData, TError>>;
|
|
1673
|
+
declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions & SpooshOptionsExtra, requestOptions?: AnyRequestOptions, nextTags?: boolean, tagPath?: string[]): Promise<SpooshResponse<TData, TError>>;
|
|
1519
1674
|
|
|
1520
1675
|
declare function createMiddleware<TData = unknown, TError = unknown>(name: string, phase: MiddlewarePhase, handler: SpooshMiddleware<TData, TError>["handler"]): SpooshMiddleware<TData, TError>;
|
|
1521
1676
|
declare function applyMiddlewares<TData = unknown, TError = unknown>(context: MiddlewareContext<TData, TError>, middlewares: SpooshMiddleware<TData, TError>[], phase: MiddlewarePhase): Promise<MiddlewareContext<TData, TError>>;
|
|
@@ -1609,4 +1764,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
|
|
|
1609
1764
|
};
|
|
1610
1765
|
declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
|
|
1611
1766
|
|
|
1612
|
-
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 RouteToPath, 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 SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type TagMode, type TagOptions, 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, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded };
|
|
1767
|
+
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 PluginExecutorOptions, 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 RouteToPath, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshConfigOptions, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, 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, extractPrefixFromBaseUrl, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveStripPrefix, resolveTags, setHeaders, sortObjectKeys, stripPrefixFromPath, urlencoded };
|
package/dist/index.d.ts
CHANGED
|
@@ -124,7 +124,7 @@ type MethodOptionsMap<TQueryOptions = object, TMutationOptions = object> = {
|
|
|
124
124
|
DELETE: TMutationOptions;
|
|
125
125
|
};
|
|
126
126
|
type ExtractMethodOptions<TOptionsMap, TMethod extends HttpMethod> = TOptionsMap extends MethodOptionsMap<infer TQuery, infer TMutation> ? TMethod extends "GET" ? TQuery : TMutation : TOptionsMap;
|
|
127
|
-
type FetchExecutor<TOptions = SpooshOptions, TRequestOptions = AnyRequestOptions> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions, nextTags?: boolean) => Promise<SpooshResponse<TData, TError>>;
|
|
127
|
+
type FetchExecutor<TOptions = SpooshOptions, TRequestOptions = AnyRequestOptions> = <TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: TOptions, requestOptions?: TRequestOptions, nextTags?: boolean, tagPath?: string[]) => Promise<SpooshResponse<TData, TError>>;
|
|
128
128
|
type TypedParamsOption<TParamNames extends string> = [TParamNames] extends [
|
|
129
129
|
never
|
|
130
130
|
] ? object : {
|
|
@@ -251,6 +251,11 @@ type PluginContext<TData = unknown, TError = unknown> = {
|
|
|
251
251
|
readonly requestTimestamp: number;
|
|
252
252
|
/** Unique identifier for the hook instance. Persists across queryKey changes within the same hook. */
|
|
253
253
|
readonly hookId?: string;
|
|
254
|
+
/**
|
|
255
|
+
* Prefix to strip from tags. Configured at the Spoosh instance level.
|
|
256
|
+
* Plugins can use this to normalize tags before emitting events.
|
|
257
|
+
*/
|
|
258
|
+
readonly stripTagPrefix?: string;
|
|
254
259
|
requestOptions: AnyRequestOptions;
|
|
255
260
|
state: OperationState<TData, TError>;
|
|
256
261
|
response?: SpooshResponse<TData, TError>;
|
|
@@ -596,7 +601,10 @@ type PluginExecutor = {
|
|
|
596
601
|
/** Creates a full PluginContext with plugins accessor injected */
|
|
597
602
|
createContext: <TData, TError>(input: PluginContextInput<TData, TError>) => PluginContext<TData, TError>;
|
|
598
603
|
};
|
|
599
|
-
|
|
604
|
+
type PluginExecutorOptions = {
|
|
605
|
+
stripTagPrefix?: string;
|
|
606
|
+
};
|
|
607
|
+
declare function createPluginExecutor(initialPlugins?: SpooshPlugin[], options?: PluginExecutorOptions): PluginExecutor;
|
|
600
608
|
|
|
601
609
|
/**
|
|
602
610
|
* Resolves plugin option types based on the full context.
|
|
@@ -863,6 +871,33 @@ type HasReadMethod<TSchema, TPath extends string> = FindMatchingKey<TSchema, TPa
|
|
|
863
871
|
* Check if a schema path has any write methods.
|
|
864
872
|
*/
|
|
865
873
|
type HasWriteMethod<TSchema, TPath extends string> = FindMatchingKey<TSchema, TPath> extends infer TKey ? TKey extends keyof TSchema ? WriteMethod extends never ? false : Extract<keyof TSchema[TKey], WriteMethod> extends never ? false : true : false : false;
|
|
874
|
+
type NormalizePrefix<T extends string> = T extends `/${infer Rest}` ? NormalizePrefix<Rest> : T extends `${infer Rest}/` ? NormalizePrefix<Rest> : T;
|
|
875
|
+
type StripPrefixFromPath<TPath extends string, TPrefix extends string> = TPath extends TPrefix ? "" : TPath extends `${TPrefix}/${infer Rest}` ? Rest : TPath;
|
|
876
|
+
/**
|
|
877
|
+
* Strips a prefix from all path keys in a schema.
|
|
878
|
+
* Works with any schema (Elysia, Hono, or manual).
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```ts
|
|
882
|
+
* type FullSchema = {
|
|
883
|
+
* "api": { GET: { data: string } };
|
|
884
|
+
* "api/users": { GET: { data: User[] } };
|
|
885
|
+
* "api/posts/:id": { GET: { data: Post } };
|
|
886
|
+
* "health": { GET: { data: { status: string } } };
|
|
887
|
+
* };
|
|
888
|
+
*
|
|
889
|
+
* type ApiSchema = StripPrefix<FullSchema, "api">;
|
|
890
|
+
* // {
|
|
891
|
+
* // "": { GET: { data: string } };
|
|
892
|
+
* // "users": { GET: { data: User[] } };
|
|
893
|
+
* // "posts/:id": { GET: { data: Post } };
|
|
894
|
+
* // "health": { GET: { data: { status: string } } };
|
|
895
|
+
* // }
|
|
896
|
+
* ```
|
|
897
|
+
*/
|
|
898
|
+
type StripPrefix<TSchema, TPrefix extends string> = TPrefix extends "" ? TSchema : {
|
|
899
|
+
[K in keyof TSchema as K extends string ? StripPrefixFromPath<K, NormalizePrefix<TPrefix>> : K]: TSchema[K];
|
|
900
|
+
};
|
|
866
901
|
|
|
867
902
|
type IsNever<T> = [T] extends [never] ? true : false;
|
|
868
903
|
type EndpointRequestOptions<TEndpoint, TPath extends string> = (IsNever<ExtractBody$1<TEndpoint>> extends true ? object : {
|
|
@@ -1056,6 +1091,33 @@ type WritePathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchi
|
|
|
1056
1091
|
type WriteClient<TSchema, TDefaultError = unknown> = <TPath extends WritePaths<TSchema> | (string & {})>(path: TPath) => HasWriteMethod<TSchema, TPath> extends true ? WritePathMethods<TSchema, TPath, TDefaultError> : never;
|
|
1057
1092
|
|
|
1058
1093
|
type PluginArray = readonly SpooshPlugin<PluginTypeConfig>[];
|
|
1094
|
+
/**
|
|
1095
|
+
* Configuration options for Spoosh runtime behavior.
|
|
1096
|
+
*/
|
|
1097
|
+
type SpooshConfigOptions = {
|
|
1098
|
+
/**
|
|
1099
|
+
* Prefix to strip from tag generation.
|
|
1100
|
+
*
|
|
1101
|
+
* URL prefix stripping always auto-detects from baseUrl.
|
|
1102
|
+
* This option only affects tag generation for cache invalidation.
|
|
1103
|
+
*
|
|
1104
|
+
* - `undefined`: Auto-detect from baseUrl (default)
|
|
1105
|
+
* - `string`: Explicit prefix to strip from tags
|
|
1106
|
+
*
|
|
1107
|
+
* @example
|
|
1108
|
+
* ```ts
|
|
1109
|
+
* // Default: auto-detect from baseUrl
|
|
1110
|
+
* // baseUrl="/api", schema="api/posts" → tags: ["posts"]
|
|
1111
|
+
* new Spoosh<Schema>('https://localhost:3000/api')
|
|
1112
|
+
*
|
|
1113
|
+
* // Explicit prefix (when baseUrl doesn't have it)
|
|
1114
|
+
* // baseUrl="/", schema="api/v1/posts" → tags: ["posts"]
|
|
1115
|
+
* new Spoosh<Schema>('http://localhost:3000')
|
|
1116
|
+
* .configure({ stripTagPrefix: "api/v1" })
|
|
1117
|
+
* ```
|
|
1118
|
+
*/
|
|
1119
|
+
stripTagPrefix?: string;
|
|
1120
|
+
};
|
|
1059
1121
|
interface SpooshConfig<TPlugins extends PluginArray = PluginArray> {
|
|
1060
1122
|
baseUrl: string;
|
|
1061
1123
|
defaultOptions?: SpooshOptions;
|
|
@@ -1069,6 +1131,8 @@ type SpooshInstance<TSchema = unknown, TDefaultError = unknown, TPlugins extends
|
|
|
1069
1131
|
config: {
|
|
1070
1132
|
baseUrl: string;
|
|
1071
1133
|
defaultOptions: SpooshOptions;
|
|
1134
|
+
/** Resolved prefix to strip from tags (used for cache invalidation matching) */
|
|
1135
|
+
stripTagPrefix?: string;
|
|
1072
1136
|
};
|
|
1073
1137
|
_types: {
|
|
1074
1138
|
schema: TSchema;
|
|
@@ -1120,12 +1184,14 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1120
1184
|
private baseUrl;
|
|
1121
1185
|
private defaultOptions;
|
|
1122
1186
|
private _plugins;
|
|
1187
|
+
private _config;
|
|
1123
1188
|
/**
|
|
1124
1189
|
* Creates a new Spoosh instance.
|
|
1125
1190
|
*
|
|
1126
1191
|
* @param baseUrl - The base URL for all API requests (e.g., '/api' or 'https://api.example.com')
|
|
1127
1192
|
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, etc.)
|
|
1128
1193
|
* @param plugins - Internal parameter used by the `.use()` method. Do not pass directly.
|
|
1194
|
+
* @param configOptions - Internal parameter used by the `.config()` method. Do not pass directly.
|
|
1129
1195
|
*
|
|
1130
1196
|
* @example
|
|
1131
1197
|
* ```ts
|
|
@@ -1138,7 +1204,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1138
1204
|
* });
|
|
1139
1205
|
* ```
|
|
1140
1206
|
*/
|
|
1141
|
-
constructor(baseUrl: string, defaultOptions?: SpooshOptions, plugins?: TPlugins);
|
|
1207
|
+
constructor(baseUrl: string, defaultOptions?: SpooshOptions, plugins?: TPlugins, configOptions?: SpooshConfigOptions);
|
|
1142
1208
|
/**
|
|
1143
1209
|
* Adds plugins to the Spoosh instance.
|
|
1144
1210
|
*
|
|
@@ -1174,6 +1240,34 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1174
1240
|
* ```
|
|
1175
1241
|
*/
|
|
1176
1242
|
use<const TNewPlugins extends PluginArray>(plugins: TNewPlugins): Spoosh<TSchema, TError, TNewPlugins>;
|
|
1243
|
+
/**
|
|
1244
|
+
* Configures runtime options for the Spoosh instance.
|
|
1245
|
+
*
|
|
1246
|
+
* Returns a **new** Spoosh instance with the updated configuration (immutable pattern).
|
|
1247
|
+
* Configuration is preserved across `.use()` calls.
|
|
1248
|
+
*
|
|
1249
|
+
* URL prefix stripping always auto-detects from baseUrl.
|
|
1250
|
+
* Tag prefix stripping defaults to URL prefix but can be overridden.
|
|
1251
|
+
*
|
|
1252
|
+
* @param options - Configuration options
|
|
1253
|
+
* @returns A new Spoosh instance with the specified configuration
|
|
1254
|
+
*
|
|
1255
|
+
* @example Default behavior (auto-detect from baseUrl for both URL and tags)
|
|
1256
|
+
* ```ts
|
|
1257
|
+
* // baseUrl="/api", schema="api/posts"
|
|
1258
|
+
* // URL: /api/posts, Tags: ["posts"]
|
|
1259
|
+
* const client = new Spoosh<Schema, Error>('https://localhost:3000/api');
|
|
1260
|
+
* ```
|
|
1261
|
+
*
|
|
1262
|
+
* @example Override tag prefix (when baseUrl doesn't have the prefix you want to strip from tags)
|
|
1263
|
+
* ```ts
|
|
1264
|
+
* // baseUrl="/", schema="api/v1/posts"
|
|
1265
|
+
* // URL: /api/v1/posts, Tags: ["posts"] (strips "api/v1" from tags only)
|
|
1266
|
+
* const client = new Spoosh<Schema, Error>('http://localhost:3000')
|
|
1267
|
+
* .configure({ stripTagPrefix: "api/v1" });
|
|
1268
|
+
* ```
|
|
1269
|
+
*/
|
|
1270
|
+
configure(options: SpooshConfigOptions): Spoosh<TSchema, TError, TPlugins>;
|
|
1177
1271
|
/**
|
|
1178
1272
|
* Cached instance of the underlying SpooshInstance.
|
|
1179
1273
|
* Created lazily on first property access.
|
|
@@ -1273,6 +1367,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1273
1367
|
get config(): {
|
|
1274
1368
|
baseUrl: string;
|
|
1275
1369
|
defaultOptions: SpooshOptions;
|
|
1370
|
+
stripTagPrefix?: string;
|
|
1276
1371
|
};
|
|
1277
1372
|
/**
|
|
1278
1373
|
* Type information carrier for generic type inference.
|
|
@@ -1291,6 +1386,16 @@ type SpooshClientConfig = {
|
|
|
1291
1386
|
baseUrl: string;
|
|
1292
1387
|
defaultOptions?: SpooshOptions;
|
|
1293
1388
|
middlewares?: SpooshMiddleware[];
|
|
1389
|
+
/**
|
|
1390
|
+
* Prefix to strip from tag generation.
|
|
1391
|
+
*
|
|
1392
|
+
* URL prefix stripping always auto-detects from baseUrl.
|
|
1393
|
+
* This option only affects tag generation for cache invalidation.
|
|
1394
|
+
*
|
|
1395
|
+
* - `undefined`: Auto-detect from baseUrl (default, same as URL prefix)
|
|
1396
|
+
* - `string`: Explicit prefix to strip from tags
|
|
1397
|
+
*/
|
|
1398
|
+
stripTagPrefix?: string;
|
|
1294
1399
|
};
|
|
1295
1400
|
/**
|
|
1296
1401
|
* Creates a lightweight type-safe API client for vanilla JavaScript/TypeScript usage.
|
|
@@ -1384,11 +1489,61 @@ type TagOptions = {
|
|
|
1384
1489
|
declare function resolveTags(options: TagOptions | undefined, resolvedPath: string[]): string[];
|
|
1385
1490
|
declare function resolvePath(path: string[], params: Record<string, string | number> | undefined): string[];
|
|
1386
1491
|
|
|
1492
|
+
/**
|
|
1493
|
+
* Extracts the path prefix from a base URL.
|
|
1494
|
+
*
|
|
1495
|
+
* @param baseUrl - The base URL (absolute or relative)
|
|
1496
|
+
* @returns The path portion without leading/trailing slashes
|
|
1497
|
+
*
|
|
1498
|
+
* @example
|
|
1499
|
+
* ```ts
|
|
1500
|
+
* extractPrefixFromBaseUrl("https://localhost:3000/api"); // "api"
|
|
1501
|
+
* extractPrefixFromBaseUrl("/api/v1"); // "api/v1"
|
|
1502
|
+
* extractPrefixFromBaseUrl("api"); // "api"
|
|
1503
|
+
* ```
|
|
1504
|
+
*/
|
|
1505
|
+
declare function extractPrefixFromBaseUrl(baseUrl: string): string;
|
|
1506
|
+
/**
|
|
1507
|
+
* Strips a prefix from path segments if the path starts with that prefix.
|
|
1508
|
+
*
|
|
1509
|
+
* @param pathSegments - Array of path segments
|
|
1510
|
+
* @param prefix - Prefix to strip (e.g., "api" or "api/v1")
|
|
1511
|
+
* @returns Path segments with prefix removed
|
|
1512
|
+
*
|
|
1513
|
+
* @example
|
|
1514
|
+
* ```ts
|
|
1515
|
+
* stripPrefixFromPath(["api", "posts"], "api"); // ["posts"]
|
|
1516
|
+
* stripPrefixFromPath(["api", "v1", "users"], "api/v1"); // ["users"]
|
|
1517
|
+
* stripPrefixFromPath(["posts"], "api"); // ["posts"] (no match, unchanged)
|
|
1518
|
+
* ```
|
|
1519
|
+
*/
|
|
1520
|
+
declare function stripPrefixFromPath(pathSegments: string[], prefix: string): string[];
|
|
1521
|
+
/**
|
|
1522
|
+
* Resolves the strip prefix value based on configuration.
|
|
1523
|
+
*
|
|
1524
|
+
* @param stripPathPrefix - Configuration value (boolean, string, or undefined)
|
|
1525
|
+
* @param baseUrl - The base URL to extract prefix from when true
|
|
1526
|
+
* @returns The resolved prefix string to strip
|
|
1527
|
+
*
|
|
1528
|
+
* @example
|
|
1529
|
+
* ```ts
|
|
1530
|
+
* resolveStripPrefix(true, "https://localhost:3000/api"); // "api"
|
|
1531
|
+
* resolveStripPrefix("api/v1", "https://localhost:3000/api"); // "api/v1"
|
|
1532
|
+
* resolveStripPrefix(false, "https://localhost:3000/api"); // ""
|
|
1533
|
+
* resolveStripPrefix(undefined, "https://localhost:3000/api"); // ""
|
|
1534
|
+
* ```
|
|
1535
|
+
*/
|
|
1536
|
+
declare function resolveStripPrefix(stripPathPrefix: boolean | string | undefined, baseUrl: string): string;
|
|
1537
|
+
|
|
1387
1538
|
type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
1388
1539
|
baseUrl: string;
|
|
1389
1540
|
defaultOptions: TOptions;
|
|
1390
1541
|
fetchExecutor?: FetchExecutor<TOptions, AnyRequestOptions>;
|
|
1391
1542
|
nextTags?: boolean;
|
|
1543
|
+
/** Prefix to strip from URL path (auto-detected from baseUrl, always applied) */
|
|
1544
|
+
urlPrefix?: string;
|
|
1545
|
+
/** Prefix to strip from tag generation (defaults to urlPrefix) */
|
|
1546
|
+
tagPrefix?: string;
|
|
1392
1547
|
};
|
|
1393
1548
|
/**
|
|
1394
1549
|
* Creates an API client proxy that uses path strings instead of chained property access.
|
|
@@ -1515,7 +1670,7 @@ declare function extractPathFromSelector(fn: unknown): string;
|
|
|
1515
1670
|
*/
|
|
1516
1671
|
declare function extractMethodFromSelector(fn: unknown): string | undefined;
|
|
1517
1672
|
|
|
1518
|
-
declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions & SpooshOptionsExtra, requestOptions?: AnyRequestOptions, nextTags?: boolean): Promise<SpooshResponse<TData, TError>>;
|
|
1673
|
+
declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions & SpooshOptionsExtra, requestOptions?: AnyRequestOptions, nextTags?: boolean, tagPath?: string[]): Promise<SpooshResponse<TData, TError>>;
|
|
1519
1674
|
|
|
1520
1675
|
declare function createMiddleware<TData = unknown, TError = unknown>(name: string, phase: MiddlewarePhase, handler: SpooshMiddleware<TData, TError>["handler"]): SpooshMiddleware<TData, TError>;
|
|
1521
1676
|
declare function applyMiddlewares<TData = unknown, TError = unknown>(context: MiddlewareContext<TData, TError>, middlewares: SpooshMiddleware<TData, TError>[], phase: MiddlewarePhase): Promise<MiddlewareContext<TData, TError>>;
|
|
@@ -1609,4 +1764,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
|
|
|
1609
1764
|
};
|
|
1610
1765
|
declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
|
|
1611
1766
|
|
|
1612
|
-
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 RouteToPath, 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 SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type TagMode, type TagOptions, 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, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded };
|
|
1767
|
+
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 PluginExecutorOptions, 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 RouteToPath, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshConfigOptions, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, 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, extractPrefixFromBaseUrl, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveStripPrefix, resolveTags, setHeaders, sortObjectKeys, stripPrefixFromPath, urlencoded };
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ __export(src_exports, {
|
|
|
41
41
|
executeFetch: () => executeFetch,
|
|
42
42
|
extractMethodFromSelector: () => extractMethodFromSelector,
|
|
43
43
|
extractPathFromSelector: () => extractPathFromSelector,
|
|
44
|
+
extractPrefixFromBaseUrl: () => extractPrefixFromBaseUrl,
|
|
44
45
|
form: () => form,
|
|
45
46
|
generateTags: () => generateTags,
|
|
46
47
|
getContentType: () => getContentType,
|
|
@@ -53,9 +54,11 @@ __export(src_exports, {
|
|
|
53
54
|
resolveHeadersToRecord: () => resolveHeadersToRecord,
|
|
54
55
|
resolvePath: () => resolvePath,
|
|
55
56
|
resolveRequestBody: () => resolveRequestBody,
|
|
57
|
+
resolveStripPrefix: () => resolveStripPrefix,
|
|
56
58
|
resolveTags: () => resolveTags,
|
|
57
59
|
setHeaders: () => setHeaders,
|
|
58
60
|
sortObjectKeys: () => sortObjectKeys,
|
|
61
|
+
stripPrefixFromPath: () => stripPrefixFromPath,
|
|
59
62
|
urlencoded: () => urlencoded
|
|
60
63
|
});
|
|
61
64
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -368,11 +371,41 @@ function resolvePath(path, params) {
|
|
|
368
371
|
});
|
|
369
372
|
}
|
|
370
373
|
|
|
374
|
+
// src/utils/stripPathPrefix.ts
|
|
375
|
+
function extractPrefixFromBaseUrl(baseUrl) {
|
|
376
|
+
const isAbsolute = /^https?:\/\//.test(baseUrl);
|
|
377
|
+
if (isAbsolute) {
|
|
378
|
+
try {
|
|
379
|
+
const url = new URL(baseUrl);
|
|
380
|
+
return url.pathname.replace(/^\/|\/$/g, "");
|
|
381
|
+
} catch {
|
|
382
|
+
return "";
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return baseUrl.replace(/^\/|\/$/g, "");
|
|
386
|
+
}
|
|
387
|
+
function stripPrefixFromPath(pathSegments, prefix) {
|
|
388
|
+
if (!prefix) return pathSegments;
|
|
389
|
+
const prefixSegments = prefix.split("/").filter(Boolean);
|
|
390
|
+
if (prefixSegments.length === 0) return pathSegments;
|
|
391
|
+
const startsWithPrefix = prefixSegments.every(
|
|
392
|
+
(seg, i) => pathSegments[i] === seg
|
|
393
|
+
);
|
|
394
|
+
return startsWithPrefix ? pathSegments.slice(prefixSegments.length) : pathSegments;
|
|
395
|
+
}
|
|
396
|
+
function resolveStripPrefix(stripPathPrefix, baseUrl) {
|
|
397
|
+
if (!stripPathPrefix) return "";
|
|
398
|
+
if (stripPathPrefix === true) {
|
|
399
|
+
return extractPrefixFromBaseUrl(baseUrl);
|
|
400
|
+
}
|
|
401
|
+
return stripPathPrefix.replace(/^\/|\/$/g, "");
|
|
402
|
+
}
|
|
403
|
+
|
|
371
404
|
// src/fetch.ts
|
|
372
405
|
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
373
406
|
var isNetworkError = (err) => err instanceof TypeError;
|
|
374
407
|
var isAbortError = (err) => err instanceof DOMException && err.name === "AbortError";
|
|
375
|
-
async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions, nextTags) {
|
|
408
|
+
async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions, nextTags, tagPath) {
|
|
376
409
|
const middlewares = defaultOptions.middlewares ?? [];
|
|
377
410
|
let context = {
|
|
378
411
|
baseUrl,
|
|
@@ -392,7 +425,8 @@ async function executeFetch(baseUrl, path, method, defaultOptions, requestOption
|
|
|
392
425
|
defaultOptions: context.defaultOptions,
|
|
393
426
|
requestOptions: context.requestOptions,
|
|
394
427
|
middlewareFetchInit: context.fetchInit,
|
|
395
|
-
nextTags
|
|
428
|
+
nextTags,
|
|
429
|
+
tagPath
|
|
396
430
|
});
|
|
397
431
|
context.response = response;
|
|
398
432
|
if (middlewares.length > 0) {
|
|
@@ -424,7 +458,8 @@ async function executeCoreFetch(config) {
|
|
|
424
458
|
defaultOptions,
|
|
425
459
|
requestOptions,
|
|
426
460
|
middlewareFetchInit,
|
|
427
|
-
nextTags
|
|
461
|
+
nextTags,
|
|
462
|
+
tagPath
|
|
428
463
|
} = config;
|
|
429
464
|
const {
|
|
430
465
|
middlewares: _,
|
|
@@ -449,7 +484,7 @@ async function executeCoreFetch(config) {
|
|
|
449
484
|
}
|
|
450
485
|
fetchInit.cache = requestOptions?.cache ?? fetchDefaults?.cache;
|
|
451
486
|
if (nextTags) {
|
|
452
|
-
const autoTags = generateTags(path);
|
|
487
|
+
const autoTags = generateTags(tagPath ?? path);
|
|
453
488
|
const userNext = requestOptions?.next;
|
|
454
489
|
fetchInit.next = {
|
|
455
490
|
tags: userNext?.tags ?? autoTags,
|
|
@@ -540,8 +575,11 @@ function createProxyHandler(config) {
|
|
|
540
575
|
baseUrl,
|
|
541
576
|
defaultOptions,
|
|
542
577
|
fetchExecutor = executeFetch,
|
|
543
|
-
nextTags
|
|
578
|
+
nextTags,
|
|
579
|
+
urlPrefix,
|
|
580
|
+
tagPrefix
|
|
544
581
|
} = config;
|
|
582
|
+
const effectiveTagPrefix = tagPrefix ?? urlPrefix;
|
|
545
583
|
return ((path) => {
|
|
546
584
|
return new Proxy(
|
|
547
585
|
{},
|
|
@@ -554,13 +592,16 @@ function createProxyHandler(config) {
|
|
|
554
592
|
}
|
|
555
593
|
return (options) => {
|
|
556
594
|
const resolvedPath = resolvePath2(path, options?.params);
|
|
595
|
+
const urlPath = urlPrefix ? stripPrefixFromPath(resolvedPath, urlPrefix) : resolvedPath;
|
|
596
|
+
const tagPath = effectiveTagPrefix ? stripPrefixFromPath(resolvedPath, effectiveTagPrefix) : resolvedPath;
|
|
557
597
|
return fetchExecutor(
|
|
558
598
|
baseUrl,
|
|
559
|
-
|
|
599
|
+
urlPath,
|
|
560
600
|
method,
|
|
561
601
|
defaultOptions,
|
|
562
602
|
options,
|
|
563
|
-
nextTags
|
|
603
|
+
nextTags,
|
|
604
|
+
tagPath
|
|
564
605
|
);
|
|
565
606
|
};
|
|
566
607
|
}
|
|
@@ -843,7 +884,8 @@ function sortByDependencies(plugins) {
|
|
|
843
884
|
}
|
|
844
885
|
return sorted;
|
|
845
886
|
}
|
|
846
|
-
function createPluginExecutor(initialPlugins = []) {
|
|
887
|
+
function createPluginExecutor(initialPlugins = [], options = {}) {
|
|
888
|
+
const { stripTagPrefix } = options;
|
|
847
889
|
validateDependencies(initialPlugins);
|
|
848
890
|
const plugins = sortByDependencies(initialPlugins);
|
|
849
891
|
const frozenPlugins = Object.freeze([...plugins]);
|
|
@@ -927,6 +969,9 @@ function createPluginExecutor(initialPlugins = []) {
|
|
|
927
969
|
ctx.headers = { ...ctx.headers, ...newHeaders };
|
|
928
970
|
ctx.requestOptions.headers = ctx.headers;
|
|
929
971
|
};
|
|
972
|
+
if (stripTagPrefix) {
|
|
973
|
+
ctx.stripTagPrefix = stripTagPrefix;
|
|
974
|
+
}
|
|
930
975
|
return ctx;
|
|
931
976
|
}
|
|
932
977
|
};
|
|
@@ -945,12 +990,14 @@ var Spoosh = class _Spoosh {
|
|
|
945
990
|
baseUrl;
|
|
946
991
|
defaultOptions;
|
|
947
992
|
_plugins;
|
|
993
|
+
_config;
|
|
948
994
|
/**
|
|
949
995
|
* Creates a new Spoosh instance.
|
|
950
996
|
*
|
|
951
997
|
* @param baseUrl - The base URL for all API requests (e.g., '/api' or 'https://api.example.com')
|
|
952
998
|
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, etc.)
|
|
953
999
|
* @param plugins - Internal parameter used by the `.use()` method. Do not pass directly.
|
|
1000
|
+
* @param configOptions - Internal parameter used by the `.config()` method. Do not pass directly.
|
|
954
1001
|
*
|
|
955
1002
|
* @example
|
|
956
1003
|
* ```ts
|
|
@@ -963,10 +1010,11 @@ var Spoosh = class _Spoosh {
|
|
|
963
1010
|
* });
|
|
964
1011
|
* ```
|
|
965
1012
|
*/
|
|
966
|
-
constructor(baseUrl, defaultOptions, plugins) {
|
|
1013
|
+
constructor(baseUrl, defaultOptions, plugins, configOptions) {
|
|
967
1014
|
this.baseUrl = baseUrl;
|
|
968
1015
|
this.defaultOptions = defaultOptions || {};
|
|
969
1016
|
this._plugins = plugins || [];
|
|
1017
|
+
this._config = configOptions || {};
|
|
970
1018
|
}
|
|
971
1019
|
/**
|
|
972
1020
|
* Adds plugins to the Spoosh instance.
|
|
@@ -1006,7 +1054,43 @@ var Spoosh = class _Spoosh {
|
|
|
1006
1054
|
return new _Spoosh(
|
|
1007
1055
|
this.baseUrl,
|
|
1008
1056
|
this.defaultOptions,
|
|
1009
|
-
plugins
|
|
1057
|
+
plugins,
|
|
1058
|
+
this._config
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Configures runtime options for the Spoosh instance.
|
|
1063
|
+
*
|
|
1064
|
+
* Returns a **new** Spoosh instance with the updated configuration (immutable pattern).
|
|
1065
|
+
* Configuration is preserved across `.use()` calls.
|
|
1066
|
+
*
|
|
1067
|
+
* URL prefix stripping always auto-detects from baseUrl.
|
|
1068
|
+
* Tag prefix stripping defaults to URL prefix but can be overridden.
|
|
1069
|
+
*
|
|
1070
|
+
* @param options - Configuration options
|
|
1071
|
+
* @returns A new Spoosh instance with the specified configuration
|
|
1072
|
+
*
|
|
1073
|
+
* @example Default behavior (auto-detect from baseUrl for both URL and tags)
|
|
1074
|
+
* ```ts
|
|
1075
|
+
* // baseUrl="/api", schema="api/posts"
|
|
1076
|
+
* // URL: /api/posts, Tags: ["posts"]
|
|
1077
|
+
* const client = new Spoosh<Schema, Error>('https://localhost:3000/api');
|
|
1078
|
+
* ```
|
|
1079
|
+
*
|
|
1080
|
+
* @example Override tag prefix (when baseUrl doesn't have the prefix you want to strip from tags)
|
|
1081
|
+
* ```ts
|
|
1082
|
+
* // baseUrl="/", schema="api/v1/posts"
|
|
1083
|
+
* // URL: /api/v1/posts, Tags: ["posts"] (strips "api/v1" from tags only)
|
|
1084
|
+
* const client = new Spoosh<Schema, Error>('http://localhost:3000')
|
|
1085
|
+
* .configure({ stripTagPrefix: "api/v1" });
|
|
1086
|
+
* ```
|
|
1087
|
+
*/
|
|
1088
|
+
configure(options) {
|
|
1089
|
+
return new _Spoosh(
|
|
1090
|
+
this.baseUrl,
|
|
1091
|
+
this.defaultOptions,
|
|
1092
|
+
this._plugins,
|
|
1093
|
+
{ ...this._config, ...options }
|
|
1010
1094
|
);
|
|
1011
1095
|
}
|
|
1012
1096
|
/**
|
|
@@ -1022,13 +1106,19 @@ var Spoosh = class _Spoosh {
|
|
|
1022
1106
|
*/
|
|
1023
1107
|
getInstance() {
|
|
1024
1108
|
if (!this._instance) {
|
|
1109
|
+
const urlPrefix = extractPrefixFromBaseUrl(this.baseUrl) || void 0;
|
|
1110
|
+
const tagPrefix = this._config.stripTagPrefix ?? urlPrefix;
|
|
1025
1111
|
const api = createProxyHandler({
|
|
1026
1112
|
baseUrl: this.baseUrl,
|
|
1027
|
-
defaultOptions: this.defaultOptions
|
|
1113
|
+
defaultOptions: this.defaultOptions,
|
|
1114
|
+
urlPrefix,
|
|
1115
|
+
tagPrefix
|
|
1028
1116
|
});
|
|
1029
1117
|
const stateManager = createStateManager();
|
|
1030
1118
|
const eventEmitter = createEventEmitter();
|
|
1031
|
-
const pluginExecutor = createPluginExecutor([...this._plugins]
|
|
1119
|
+
const pluginExecutor = createPluginExecutor([...this._plugins], {
|
|
1120
|
+
stripTagPrefix: tagPrefix
|
|
1121
|
+
});
|
|
1032
1122
|
this._instance = {
|
|
1033
1123
|
api,
|
|
1034
1124
|
stateManager,
|
|
@@ -1036,7 +1126,8 @@ var Spoosh = class _Spoosh {
|
|
|
1036
1126
|
pluginExecutor,
|
|
1037
1127
|
config: {
|
|
1038
1128
|
baseUrl: this.baseUrl,
|
|
1039
|
-
defaultOptions: this.defaultOptions
|
|
1129
|
+
defaultOptions: this.defaultOptions,
|
|
1130
|
+
stripTagPrefix: tagPrefix
|
|
1040
1131
|
},
|
|
1041
1132
|
_types: {
|
|
1042
1133
|
schema: void 0,
|
|
@@ -1155,12 +1246,21 @@ var Spoosh = class _Spoosh {
|
|
|
1155
1246
|
|
|
1156
1247
|
// src/createClient.ts
|
|
1157
1248
|
function createClient(config) {
|
|
1158
|
-
const {
|
|
1249
|
+
const {
|
|
1250
|
+
baseUrl,
|
|
1251
|
+
defaultOptions = {},
|
|
1252
|
+
middlewares = [],
|
|
1253
|
+
stripTagPrefix
|
|
1254
|
+
} = config;
|
|
1159
1255
|
const optionsWithMiddlewares = { ...defaultOptions, middlewares };
|
|
1256
|
+
const urlPrefix = extractPrefixFromBaseUrl(baseUrl) || void 0;
|
|
1257
|
+
const tagPrefix = stripTagPrefix ?? urlPrefix;
|
|
1160
1258
|
return createProxyHandler({
|
|
1161
1259
|
baseUrl,
|
|
1162
1260
|
defaultOptions: optionsWithMiddlewares,
|
|
1163
|
-
nextTags: true
|
|
1261
|
+
nextTags: true,
|
|
1262
|
+
urlPrefix,
|
|
1263
|
+
tagPrefix
|
|
1164
1264
|
});
|
|
1165
1265
|
}
|
|
1166
1266
|
|
package/dist/index.mjs
CHANGED
|
@@ -306,11 +306,41 @@ function resolvePath(path, params) {
|
|
|
306
306
|
});
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
+
// src/utils/stripPathPrefix.ts
|
|
310
|
+
function extractPrefixFromBaseUrl(baseUrl) {
|
|
311
|
+
const isAbsolute = /^https?:\/\//.test(baseUrl);
|
|
312
|
+
if (isAbsolute) {
|
|
313
|
+
try {
|
|
314
|
+
const url = new URL(baseUrl);
|
|
315
|
+
return url.pathname.replace(/^\/|\/$/g, "");
|
|
316
|
+
} catch {
|
|
317
|
+
return "";
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return baseUrl.replace(/^\/|\/$/g, "");
|
|
321
|
+
}
|
|
322
|
+
function stripPrefixFromPath(pathSegments, prefix) {
|
|
323
|
+
if (!prefix) return pathSegments;
|
|
324
|
+
const prefixSegments = prefix.split("/").filter(Boolean);
|
|
325
|
+
if (prefixSegments.length === 0) return pathSegments;
|
|
326
|
+
const startsWithPrefix = prefixSegments.every(
|
|
327
|
+
(seg, i) => pathSegments[i] === seg
|
|
328
|
+
);
|
|
329
|
+
return startsWithPrefix ? pathSegments.slice(prefixSegments.length) : pathSegments;
|
|
330
|
+
}
|
|
331
|
+
function resolveStripPrefix(stripPathPrefix, baseUrl) {
|
|
332
|
+
if (!stripPathPrefix) return "";
|
|
333
|
+
if (stripPathPrefix === true) {
|
|
334
|
+
return extractPrefixFromBaseUrl(baseUrl);
|
|
335
|
+
}
|
|
336
|
+
return stripPathPrefix.replace(/^\/|\/$/g, "");
|
|
337
|
+
}
|
|
338
|
+
|
|
309
339
|
// src/fetch.ts
|
|
310
340
|
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
311
341
|
var isNetworkError = (err) => err instanceof TypeError;
|
|
312
342
|
var isAbortError = (err) => err instanceof DOMException && err.name === "AbortError";
|
|
313
|
-
async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions, nextTags) {
|
|
343
|
+
async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions, nextTags, tagPath) {
|
|
314
344
|
const middlewares = defaultOptions.middlewares ?? [];
|
|
315
345
|
let context = {
|
|
316
346
|
baseUrl,
|
|
@@ -330,7 +360,8 @@ async function executeFetch(baseUrl, path, method, defaultOptions, requestOption
|
|
|
330
360
|
defaultOptions: context.defaultOptions,
|
|
331
361
|
requestOptions: context.requestOptions,
|
|
332
362
|
middlewareFetchInit: context.fetchInit,
|
|
333
|
-
nextTags
|
|
363
|
+
nextTags,
|
|
364
|
+
tagPath
|
|
334
365
|
});
|
|
335
366
|
context.response = response;
|
|
336
367
|
if (middlewares.length > 0) {
|
|
@@ -362,7 +393,8 @@ async function executeCoreFetch(config) {
|
|
|
362
393
|
defaultOptions,
|
|
363
394
|
requestOptions,
|
|
364
395
|
middlewareFetchInit,
|
|
365
|
-
nextTags
|
|
396
|
+
nextTags,
|
|
397
|
+
tagPath
|
|
366
398
|
} = config;
|
|
367
399
|
const {
|
|
368
400
|
middlewares: _,
|
|
@@ -387,7 +419,7 @@ async function executeCoreFetch(config) {
|
|
|
387
419
|
}
|
|
388
420
|
fetchInit.cache = requestOptions?.cache ?? fetchDefaults?.cache;
|
|
389
421
|
if (nextTags) {
|
|
390
|
-
const autoTags = generateTags(path);
|
|
422
|
+
const autoTags = generateTags(tagPath ?? path);
|
|
391
423
|
const userNext = requestOptions?.next;
|
|
392
424
|
fetchInit.next = {
|
|
393
425
|
tags: userNext?.tags ?? autoTags,
|
|
@@ -478,8 +510,11 @@ function createProxyHandler(config) {
|
|
|
478
510
|
baseUrl,
|
|
479
511
|
defaultOptions,
|
|
480
512
|
fetchExecutor = executeFetch,
|
|
481
|
-
nextTags
|
|
513
|
+
nextTags,
|
|
514
|
+
urlPrefix,
|
|
515
|
+
tagPrefix
|
|
482
516
|
} = config;
|
|
517
|
+
const effectiveTagPrefix = tagPrefix ?? urlPrefix;
|
|
483
518
|
return ((path) => {
|
|
484
519
|
return new Proxy(
|
|
485
520
|
{},
|
|
@@ -492,13 +527,16 @@ function createProxyHandler(config) {
|
|
|
492
527
|
}
|
|
493
528
|
return (options) => {
|
|
494
529
|
const resolvedPath = resolvePath2(path, options?.params);
|
|
530
|
+
const urlPath = urlPrefix ? stripPrefixFromPath(resolvedPath, urlPrefix) : resolvedPath;
|
|
531
|
+
const tagPath = effectiveTagPrefix ? stripPrefixFromPath(resolvedPath, effectiveTagPrefix) : resolvedPath;
|
|
495
532
|
return fetchExecutor(
|
|
496
533
|
baseUrl,
|
|
497
|
-
|
|
534
|
+
urlPath,
|
|
498
535
|
method,
|
|
499
536
|
defaultOptions,
|
|
500
537
|
options,
|
|
501
|
-
nextTags
|
|
538
|
+
nextTags,
|
|
539
|
+
tagPath
|
|
502
540
|
);
|
|
503
541
|
};
|
|
504
542
|
}
|
|
@@ -781,7 +819,8 @@ function sortByDependencies(plugins) {
|
|
|
781
819
|
}
|
|
782
820
|
return sorted;
|
|
783
821
|
}
|
|
784
|
-
function createPluginExecutor(initialPlugins = []) {
|
|
822
|
+
function createPluginExecutor(initialPlugins = [], options = {}) {
|
|
823
|
+
const { stripTagPrefix } = options;
|
|
785
824
|
validateDependencies(initialPlugins);
|
|
786
825
|
const plugins = sortByDependencies(initialPlugins);
|
|
787
826
|
const frozenPlugins = Object.freeze([...plugins]);
|
|
@@ -865,6 +904,9 @@ function createPluginExecutor(initialPlugins = []) {
|
|
|
865
904
|
ctx.headers = { ...ctx.headers, ...newHeaders };
|
|
866
905
|
ctx.requestOptions.headers = ctx.headers;
|
|
867
906
|
};
|
|
907
|
+
if (stripTagPrefix) {
|
|
908
|
+
ctx.stripTagPrefix = stripTagPrefix;
|
|
909
|
+
}
|
|
868
910
|
return ctx;
|
|
869
911
|
}
|
|
870
912
|
};
|
|
@@ -883,12 +925,14 @@ var Spoosh = class _Spoosh {
|
|
|
883
925
|
baseUrl;
|
|
884
926
|
defaultOptions;
|
|
885
927
|
_plugins;
|
|
928
|
+
_config;
|
|
886
929
|
/**
|
|
887
930
|
* Creates a new Spoosh instance.
|
|
888
931
|
*
|
|
889
932
|
* @param baseUrl - The base URL for all API requests (e.g., '/api' or 'https://api.example.com')
|
|
890
933
|
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, etc.)
|
|
891
934
|
* @param plugins - Internal parameter used by the `.use()` method. Do not pass directly.
|
|
935
|
+
* @param configOptions - Internal parameter used by the `.config()` method. Do not pass directly.
|
|
892
936
|
*
|
|
893
937
|
* @example
|
|
894
938
|
* ```ts
|
|
@@ -901,10 +945,11 @@ var Spoosh = class _Spoosh {
|
|
|
901
945
|
* });
|
|
902
946
|
* ```
|
|
903
947
|
*/
|
|
904
|
-
constructor(baseUrl, defaultOptions, plugins) {
|
|
948
|
+
constructor(baseUrl, defaultOptions, plugins, configOptions) {
|
|
905
949
|
this.baseUrl = baseUrl;
|
|
906
950
|
this.defaultOptions = defaultOptions || {};
|
|
907
951
|
this._plugins = plugins || [];
|
|
952
|
+
this._config = configOptions || {};
|
|
908
953
|
}
|
|
909
954
|
/**
|
|
910
955
|
* Adds plugins to the Spoosh instance.
|
|
@@ -944,7 +989,43 @@ var Spoosh = class _Spoosh {
|
|
|
944
989
|
return new _Spoosh(
|
|
945
990
|
this.baseUrl,
|
|
946
991
|
this.defaultOptions,
|
|
947
|
-
plugins
|
|
992
|
+
plugins,
|
|
993
|
+
this._config
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Configures runtime options for the Spoosh instance.
|
|
998
|
+
*
|
|
999
|
+
* Returns a **new** Spoosh instance with the updated configuration (immutable pattern).
|
|
1000
|
+
* Configuration is preserved across `.use()` calls.
|
|
1001
|
+
*
|
|
1002
|
+
* URL prefix stripping always auto-detects from baseUrl.
|
|
1003
|
+
* Tag prefix stripping defaults to URL prefix but can be overridden.
|
|
1004
|
+
*
|
|
1005
|
+
* @param options - Configuration options
|
|
1006
|
+
* @returns A new Spoosh instance with the specified configuration
|
|
1007
|
+
*
|
|
1008
|
+
* @example Default behavior (auto-detect from baseUrl for both URL and tags)
|
|
1009
|
+
* ```ts
|
|
1010
|
+
* // baseUrl="/api", schema="api/posts"
|
|
1011
|
+
* // URL: /api/posts, Tags: ["posts"]
|
|
1012
|
+
* const client = new Spoosh<Schema, Error>('https://localhost:3000/api');
|
|
1013
|
+
* ```
|
|
1014
|
+
*
|
|
1015
|
+
* @example Override tag prefix (when baseUrl doesn't have the prefix you want to strip from tags)
|
|
1016
|
+
* ```ts
|
|
1017
|
+
* // baseUrl="/", schema="api/v1/posts"
|
|
1018
|
+
* // URL: /api/v1/posts, Tags: ["posts"] (strips "api/v1" from tags only)
|
|
1019
|
+
* const client = new Spoosh<Schema, Error>('http://localhost:3000')
|
|
1020
|
+
* .configure({ stripTagPrefix: "api/v1" });
|
|
1021
|
+
* ```
|
|
1022
|
+
*/
|
|
1023
|
+
configure(options) {
|
|
1024
|
+
return new _Spoosh(
|
|
1025
|
+
this.baseUrl,
|
|
1026
|
+
this.defaultOptions,
|
|
1027
|
+
this._plugins,
|
|
1028
|
+
{ ...this._config, ...options }
|
|
948
1029
|
);
|
|
949
1030
|
}
|
|
950
1031
|
/**
|
|
@@ -960,13 +1041,19 @@ var Spoosh = class _Spoosh {
|
|
|
960
1041
|
*/
|
|
961
1042
|
getInstance() {
|
|
962
1043
|
if (!this._instance) {
|
|
1044
|
+
const urlPrefix = extractPrefixFromBaseUrl(this.baseUrl) || void 0;
|
|
1045
|
+
const tagPrefix = this._config.stripTagPrefix ?? urlPrefix;
|
|
963
1046
|
const api = createProxyHandler({
|
|
964
1047
|
baseUrl: this.baseUrl,
|
|
965
|
-
defaultOptions: this.defaultOptions
|
|
1048
|
+
defaultOptions: this.defaultOptions,
|
|
1049
|
+
urlPrefix,
|
|
1050
|
+
tagPrefix
|
|
966
1051
|
});
|
|
967
1052
|
const stateManager = createStateManager();
|
|
968
1053
|
const eventEmitter = createEventEmitter();
|
|
969
|
-
const pluginExecutor = createPluginExecutor([...this._plugins]
|
|
1054
|
+
const pluginExecutor = createPluginExecutor([...this._plugins], {
|
|
1055
|
+
stripTagPrefix: tagPrefix
|
|
1056
|
+
});
|
|
970
1057
|
this._instance = {
|
|
971
1058
|
api,
|
|
972
1059
|
stateManager,
|
|
@@ -974,7 +1061,8 @@ var Spoosh = class _Spoosh {
|
|
|
974
1061
|
pluginExecutor,
|
|
975
1062
|
config: {
|
|
976
1063
|
baseUrl: this.baseUrl,
|
|
977
|
-
defaultOptions: this.defaultOptions
|
|
1064
|
+
defaultOptions: this.defaultOptions,
|
|
1065
|
+
stripTagPrefix: tagPrefix
|
|
978
1066
|
},
|
|
979
1067
|
_types: {
|
|
980
1068
|
schema: void 0,
|
|
@@ -1093,12 +1181,21 @@ var Spoosh = class _Spoosh {
|
|
|
1093
1181
|
|
|
1094
1182
|
// src/createClient.ts
|
|
1095
1183
|
function createClient(config) {
|
|
1096
|
-
const {
|
|
1184
|
+
const {
|
|
1185
|
+
baseUrl,
|
|
1186
|
+
defaultOptions = {},
|
|
1187
|
+
middlewares = [],
|
|
1188
|
+
stripTagPrefix
|
|
1189
|
+
} = config;
|
|
1097
1190
|
const optionsWithMiddlewares = { ...defaultOptions, middlewares };
|
|
1191
|
+
const urlPrefix = extractPrefixFromBaseUrl(baseUrl) || void 0;
|
|
1192
|
+
const tagPrefix = stripTagPrefix ?? urlPrefix;
|
|
1098
1193
|
return createProxyHandler({
|
|
1099
1194
|
baseUrl,
|
|
1100
1195
|
defaultOptions: optionsWithMiddlewares,
|
|
1101
|
-
nextTags: true
|
|
1196
|
+
nextTags: true,
|
|
1197
|
+
urlPrefix,
|
|
1198
|
+
tagPrefix
|
|
1102
1199
|
});
|
|
1103
1200
|
}
|
|
1104
1201
|
|
|
@@ -1681,6 +1778,7 @@ export {
|
|
|
1681
1778
|
executeFetch,
|
|
1682
1779
|
extractMethodFromSelector,
|
|
1683
1780
|
extractPathFromSelector,
|
|
1781
|
+
extractPrefixFromBaseUrl,
|
|
1684
1782
|
form,
|
|
1685
1783
|
generateTags,
|
|
1686
1784
|
getContentType,
|
|
@@ -1693,8 +1791,10 @@ export {
|
|
|
1693
1791
|
resolveHeadersToRecord,
|
|
1694
1792
|
resolvePath,
|
|
1695
1793
|
resolveRequestBody,
|
|
1794
|
+
resolveStripPrefix,
|
|
1696
1795
|
resolveTags,
|
|
1697
1796
|
setHeaders,
|
|
1698
1797
|
sortObjectKeys,
|
|
1798
|
+
stripPrefixFromPath,
|
|
1699
1799
|
urlencoded
|
|
1700
1800
|
};
|