@spoosh/core 0.9.3 → 0.10.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/README.md +9 -9
- package/dist/index.d.mts +80 -14
- package/dist/index.d.ts +80 -14
- package/dist/index.js +120 -17
- package/dist/index.mjs +120 -17
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @spoosh/core
|
|
2
2
|
|
|
3
|
-
Core client and plugin system for Spoosh - a type-safe API
|
|
3
|
+
Core client and plugin system for Spoosh - a type-safe API toolkit.
|
|
4
4
|
|
|
5
|
-
**[Documentation](https://spoosh.dev/docs)** · **Requirements:** TypeScript >= 5.0
|
|
5
|
+
**[Documentation](https://spoosh.dev/docs/react)** · **Requirements:** TypeScript >= 5.0
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -188,7 +188,7 @@ Path parameters are defined using `:param` syntax in the path key (e.g., `"users
|
|
|
188
188
|
|
|
189
189
|
### createClient(config)
|
|
190
190
|
|
|
191
|
-
Creates a lightweight type-safe API
|
|
191
|
+
Creates a lightweight type-safe API instance.
|
|
192
192
|
|
|
193
193
|
| Option | Type | Description |
|
|
194
194
|
| ---------------- | -------------------- | -------------------------------------------------- |
|
|
@@ -228,12 +228,12 @@ const { data } = await api("users").GET();
|
|
|
228
228
|
|
|
229
229
|
**Properties:**
|
|
230
230
|
|
|
231
|
-
| Property | Description
|
|
232
|
-
| ----------------- |
|
|
233
|
-
| `.api` | Type-safe API
|
|
234
|
-
| `.stateManager` | Cache and state management
|
|
235
|
-
| `.eventEmitter` | Event system for refetch/invalidation
|
|
236
|
-
| `.pluginExecutor` | Plugin lifecycle management
|
|
231
|
+
| Property | Description |
|
|
232
|
+
| ----------------- | ------------------------------------------- |
|
|
233
|
+
| `.api` | Type-safe API interface for making requests |
|
|
234
|
+
| `.stateManager` | Cache and state management |
|
|
235
|
+
| `.eventEmitter` | Event system for refetch/invalidation |
|
|
236
|
+
| `.pluginExecutor` | Plugin lifecycle management |
|
|
237
237
|
|
|
238
238
|
## Creating Plugins
|
|
239
239
|
|
package/dist/index.d.mts
CHANGED
|
@@ -72,6 +72,24 @@ declare function resolveRequestBody(rawBody: unknown): {
|
|
|
72
72
|
headers?: Record<string, string>;
|
|
73
73
|
} | undefined;
|
|
74
74
|
|
|
75
|
+
interface TransportResponse {
|
|
76
|
+
ok: boolean;
|
|
77
|
+
status: number;
|
|
78
|
+
headers: Headers;
|
|
79
|
+
data: unknown;
|
|
80
|
+
}
|
|
81
|
+
type Transport<TOptions = unknown> = (url: string, init: RequestInit, options?: TOptions) => Promise<TransportResponse>;
|
|
82
|
+
/**
|
|
83
|
+
* Transport layer used for requests.
|
|
84
|
+
*
|
|
85
|
+
* - `"fetch"` — Uses the Fetch API (default).
|
|
86
|
+
* - `"xhr"` — Uses XMLHttpRequest. Required for upload/download progress tracking.
|
|
87
|
+
*/
|
|
88
|
+
type TransportOption = "fetch" | "xhr";
|
|
89
|
+
interface TransportOptionsMap {
|
|
90
|
+
fetch: never;
|
|
91
|
+
}
|
|
92
|
+
|
|
75
93
|
type RetryConfig = {
|
|
76
94
|
retries?: number | false;
|
|
77
95
|
retryDelay?: number;
|
|
@@ -79,7 +97,26 @@ type RetryConfig = {
|
|
|
79
97
|
type HeadersInitOrGetter = HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
|
|
80
98
|
type SpooshOptions = Omit<RequestInit, "method" | "body" | "headers"> & {
|
|
81
99
|
headers?: HeadersInitOrGetter;
|
|
100
|
+
/** Default transport for all requests. */
|
|
101
|
+
transport?: TransportOption;
|
|
82
102
|
};
|
|
103
|
+
type FetchOnlyInitKeys = "mode" | "cache" | "integrity" | "keepalive" | "next" | "priority" | "redirect" | "referrer" | "referrerPolicy" | "window";
|
|
104
|
+
type SharedSpooshOptions = Omit<RequestInit, "signal" | "method" | "body" | "headers" | FetchOnlyInitKeys> & {
|
|
105
|
+
headers?: HeadersInitOrGetter;
|
|
106
|
+
};
|
|
107
|
+
type SpooshFetchOptions = SharedSpooshOptions & Pick<RequestInit, Extract<keyof RequestInit, FetchOnlyInitKeys>> & {
|
|
108
|
+
transport?: "fetch";
|
|
109
|
+
};
|
|
110
|
+
type SpooshXhrOptions = SharedSpooshOptions & {
|
|
111
|
+
transport: "xhr";
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Constructor-level options with transport-aware type narrowing.
|
|
115
|
+
*
|
|
116
|
+
* When `transport` is `"xhr"`, fetch-only fields (e.g. `mode`, `cache`, `redirect`) are
|
|
117
|
+
* excluded from autocomplete since they have no effect on XMLHttpRequest.
|
|
118
|
+
*/
|
|
119
|
+
type SpooshOptionsInput = SpooshFetchOptions | SpooshXhrOptions;
|
|
83
120
|
type BaseRequestOptions$1 = {
|
|
84
121
|
headers?: HeadersInitOrGetter;
|
|
85
122
|
cache?: RequestCache;
|
|
@@ -101,6 +138,10 @@ type AnyRequestOptions = BaseRequestOptions$1 & {
|
|
|
101
138
|
query?: Record<string, string | number | boolean | undefined>;
|
|
102
139
|
params?: Record<string, string | number>;
|
|
103
140
|
signal?: AbortSignal;
|
|
141
|
+
/** Per-request transport override. */
|
|
142
|
+
transport?: TransportOption;
|
|
143
|
+
/** Transport-specific options passed through to the transport function. */
|
|
144
|
+
transportOptions?: unknown;
|
|
104
145
|
} & Partial<RetryConfig>;
|
|
105
146
|
type DynamicParamsOption = {
|
|
106
147
|
params?: Record<string, string | number>;
|
|
@@ -728,7 +769,7 @@ declare function createPluginRegistry<TPlugins extends SpooshPlugin<PluginTypeCo
|
|
|
728
769
|
type ApiSchema = {
|
|
729
770
|
[path: string]: {
|
|
730
771
|
[method in HttpMethod]?: {
|
|
731
|
-
data
|
|
772
|
+
data?: unknown;
|
|
732
773
|
body?: unknown;
|
|
733
774
|
query?: unknown;
|
|
734
775
|
error?: unknown;
|
|
@@ -740,7 +781,7 @@ type ApiSchema = {
|
|
|
740
781
|
*/
|
|
741
782
|
type ExtractData<T> = T extends {
|
|
742
783
|
data: infer D;
|
|
743
|
-
} ? D :
|
|
784
|
+
} ? D : void;
|
|
744
785
|
/**
|
|
745
786
|
* Extract body type from an endpoint.
|
|
746
787
|
*/
|
|
@@ -1019,7 +1060,7 @@ type PathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchingKey
|
|
|
1019
1060
|
*/
|
|
1020
1061
|
type SchemaPaths<TSchema> = keyof TSchema & string;
|
|
1021
1062
|
/**
|
|
1022
|
-
* An API
|
|
1063
|
+
* An API interface that uses path strings instead of chained property access.
|
|
1023
1064
|
* Methods use HTTP names directly: GET, POST, PUT, PATCH, DELETE.
|
|
1024
1065
|
*
|
|
1025
1066
|
* @example
|
|
@@ -1050,7 +1091,7 @@ type ReadPathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchin
|
|
|
1050
1091
|
GET: MethodFn<TSchema[TKey]["GET"], TDefaultError, TPath>;
|
|
1051
1092
|
}> : never : never : never;
|
|
1052
1093
|
/**
|
|
1053
|
-
* A read-only API
|
|
1094
|
+
* A read-only API interface that only exposes GET methods.
|
|
1054
1095
|
* Used by useRead and injectRead hooks.
|
|
1055
1096
|
*/
|
|
1056
1097
|
type ReadClient<TSchema, TDefaultError = unknown> = <TPath extends ReadPaths<TSchema> | (string & {})>(path: TPath) => HasReadMethod<TSchema, TPath> extends true ? ReadPathMethods<TSchema, TPath, TDefaultError> : never;
|
|
@@ -1062,7 +1103,7 @@ type WritePathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchi
|
|
|
1062
1103
|
[M in WriteMethod as M extends keyof TSchema[TKey] ? M : never]: M extends keyof TSchema[TKey] ? MethodFn<TSchema[TKey][M], TDefaultError, TPath> : never;
|
|
1063
1104
|
}> : never : never;
|
|
1064
1105
|
/**
|
|
1065
|
-
* A write-only API
|
|
1106
|
+
* A write-only API interface that only exposes mutation methods (POST, PUT, PATCH, DELETE).
|
|
1066
1107
|
* Used by useWrite and injectWrite hooks.
|
|
1067
1108
|
*/
|
|
1068
1109
|
type WriteClient<TSchema, TDefaultError = unknown> = <TPath extends WritePaths<TSchema> | (string & {})>(path: TPath) => HasWriteMethod<TSchema, TPath> extends true ? WritePathMethods<TSchema, TPath, TDefaultError> : never;
|
|
@@ -1136,7 +1177,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1136
1177
|
* Creates a new Spoosh instance.
|
|
1137
1178
|
*
|
|
1138
1179
|
* @param baseUrl - The base URL for all API requests (e.g., '/api' or 'https://api.example.com')
|
|
1139
|
-
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, etc.)
|
|
1180
|
+
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, transport, etc.)
|
|
1140
1181
|
* @param plugins - Internal parameter used by the `.use()` method. Do not pass directly.
|
|
1141
1182
|
*
|
|
1142
1183
|
* @example
|
|
@@ -1148,9 +1189,15 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1148
1189
|
* const client = new Spoosh<ApiSchema, Error>('/api', {
|
|
1149
1190
|
* headers: { 'X-API-Key': 'secret' }
|
|
1150
1191
|
* });
|
|
1192
|
+
*
|
|
1193
|
+
* // With XHR transport (narrows available options to XHR-compatible fields)
|
|
1194
|
+
* const client = new Spoosh<ApiSchema, Error>('/api', {
|
|
1195
|
+
* transport: 'xhr',
|
|
1196
|
+
* credentials: 'include',
|
|
1197
|
+
* });
|
|
1151
1198
|
* ```
|
|
1152
1199
|
*/
|
|
1153
|
-
constructor(baseUrl: string, defaultOptions?:
|
|
1200
|
+
constructor(baseUrl: string, defaultOptions?: SpooshOptionsInput, plugins?: TPlugins);
|
|
1154
1201
|
/**
|
|
1155
1202
|
* Adds plugins to the Spoosh instance.
|
|
1156
1203
|
*
|
|
@@ -1199,7 +1246,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1199
1246
|
*/
|
|
1200
1247
|
private getInstance;
|
|
1201
1248
|
/**
|
|
1202
|
-
* The type-safe API
|
|
1249
|
+
* The type-safe API interface for making requests.
|
|
1203
1250
|
*
|
|
1204
1251
|
* Provides a proxy-based interface for accessing endpoints defined in your schema.
|
|
1205
1252
|
*
|
|
@@ -1301,17 +1348,17 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1301
1348
|
|
|
1302
1349
|
type SpooshClientConfig = {
|
|
1303
1350
|
baseUrl: string;
|
|
1304
|
-
defaultOptions?:
|
|
1351
|
+
defaultOptions?: SpooshOptionsInput;
|
|
1305
1352
|
middlewares?: SpooshMiddleware[];
|
|
1306
1353
|
};
|
|
1307
1354
|
/**
|
|
1308
|
-
* Creates a lightweight type-safe API
|
|
1355
|
+
* Creates a lightweight type-safe API instance for vanilla JavaScript/TypeScript usage.
|
|
1309
1356
|
*
|
|
1310
1357
|
* This is a simpler alternative to `Spoosh` for users who don't need
|
|
1311
1358
|
* the full plugin system, state management, or React integration.
|
|
1312
1359
|
*
|
|
1313
1360
|
* @param config - Client configuration
|
|
1314
|
-
* @returns Type-safe API
|
|
1361
|
+
* @returns Type-safe API instance
|
|
1315
1362
|
*
|
|
1316
1363
|
* @example
|
|
1317
1364
|
* ```ts
|
|
@@ -1339,6 +1386,12 @@ type SpooshClientConfig = {
|
|
|
1339
1386
|
* const { data } = await api("posts").GET();
|
|
1340
1387
|
* const { data: post } = await api("posts/123").GET();
|
|
1341
1388
|
* await api("posts/:id").GET({ params: { id: 123 } });
|
|
1389
|
+
*
|
|
1390
|
+
* // With XHR transport
|
|
1391
|
+
* const api = createClient<ApiSchema, ApiError>({
|
|
1392
|
+
* baseUrl: "/api",
|
|
1393
|
+
* defaultOptions: { transport: "xhr" },
|
|
1394
|
+
* });
|
|
1342
1395
|
* ```
|
|
1343
1396
|
*/
|
|
1344
1397
|
declare function createClient<TSchema, TDefaultError = unknown>(config: SpooshClientConfig): SpooshClient<TSchema, TDefaultError>;
|
|
@@ -1403,7 +1456,7 @@ type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
|
1403
1456
|
nextTags?: boolean;
|
|
1404
1457
|
};
|
|
1405
1458
|
/**
|
|
1406
|
-
* Creates an API
|
|
1459
|
+
* Creates an API proxy that uses path strings instead of chained property access.
|
|
1407
1460
|
* Methods use HTTP names directly: GET, POST, PUT, PATCH, DELETE.
|
|
1408
1461
|
*
|
|
1409
1462
|
* @param config - Proxy handler configuration
|
|
@@ -1421,7 +1474,7 @@ type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
|
1421
1474
|
*/
|
|
1422
1475
|
declare function createProxyHandler<TSchema, TDefaultError = unknown, TOptions = SpooshOptions>(config: ProxyHandlerConfig<TOptions>): SpooshClient<TSchema, TDefaultError>;
|
|
1423
1476
|
|
|
1424
|
-
/** All supported HTTP method keys used in the API
|
|
1477
|
+
/** All supported HTTP method keys used in the API toolkit */
|
|
1425
1478
|
declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
1426
1479
|
/** Union type of all HTTP method keys */
|
|
1427
1480
|
type HttpMethodKey = (typeof HTTP_METHODS)[number];
|
|
@@ -1527,6 +1580,19 @@ declare function extractPathFromSelector(fn: unknown): string;
|
|
|
1527
1580
|
*/
|
|
1528
1581
|
declare function extractMethodFromSelector(fn: unknown): string | undefined;
|
|
1529
1582
|
|
|
1583
|
+
declare const fetchTransport: Transport;
|
|
1584
|
+
|
|
1585
|
+
interface XhrTransportOptions {
|
|
1586
|
+
/** Called on upload and download progress events. */
|
|
1587
|
+
onProgress?: (event: ProgressEvent, xhr: XMLHttpRequest) => void;
|
|
1588
|
+
}
|
|
1589
|
+
declare module "./types" {
|
|
1590
|
+
interface TransportOptionsMap {
|
|
1591
|
+
xhr: XhrTransportOptions;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
declare const xhrTransport: Transport<XhrTransportOptions>;
|
|
1595
|
+
|
|
1530
1596
|
declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions & SpooshOptionsExtra, requestOptions?: AnyRequestOptions, nextTags?: boolean): Promise<SpooshResponse<TData, TError>>;
|
|
1531
1597
|
|
|
1532
1598
|
declare function createMiddleware<TData = unknown, TError = unknown>(name: string, phase: MiddlewarePhase, handler: SpooshMiddleware<TData, TError>["handler"]): SpooshMiddleware<TData, TError>;
|
|
@@ -1621,4 +1687,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
|
|
|
1621
1687
|
};
|
|
1622
1688
|
declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
|
|
1623
1689
|
|
|
1624
|
-
export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type SpooshClientConfig as ClientConfig, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type MiddlewareContext, type MiddlewareHandler, type MiddlewarePhase, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type 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, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded };
|
|
1690
|
+
export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type SpooshClientConfig as ClientConfig, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type MiddlewareContext, type MiddlewareHandler, type MiddlewarePhase, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, applyMiddlewares, buildUrl, composeMiddlewares, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createMiddleware, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
|
package/dist/index.d.ts
CHANGED
|
@@ -72,6 +72,24 @@ declare function resolveRequestBody(rawBody: unknown): {
|
|
|
72
72
|
headers?: Record<string, string>;
|
|
73
73
|
} | undefined;
|
|
74
74
|
|
|
75
|
+
interface TransportResponse {
|
|
76
|
+
ok: boolean;
|
|
77
|
+
status: number;
|
|
78
|
+
headers: Headers;
|
|
79
|
+
data: unknown;
|
|
80
|
+
}
|
|
81
|
+
type Transport<TOptions = unknown> = (url: string, init: RequestInit, options?: TOptions) => Promise<TransportResponse>;
|
|
82
|
+
/**
|
|
83
|
+
* Transport layer used for requests.
|
|
84
|
+
*
|
|
85
|
+
* - `"fetch"` — Uses the Fetch API (default).
|
|
86
|
+
* - `"xhr"` — Uses XMLHttpRequest. Required for upload/download progress tracking.
|
|
87
|
+
*/
|
|
88
|
+
type TransportOption = "fetch" | "xhr";
|
|
89
|
+
interface TransportOptionsMap {
|
|
90
|
+
fetch: never;
|
|
91
|
+
}
|
|
92
|
+
|
|
75
93
|
type RetryConfig = {
|
|
76
94
|
retries?: number | false;
|
|
77
95
|
retryDelay?: number;
|
|
@@ -79,7 +97,26 @@ type RetryConfig = {
|
|
|
79
97
|
type HeadersInitOrGetter = HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
|
|
80
98
|
type SpooshOptions = Omit<RequestInit, "method" | "body" | "headers"> & {
|
|
81
99
|
headers?: HeadersInitOrGetter;
|
|
100
|
+
/** Default transport for all requests. */
|
|
101
|
+
transport?: TransportOption;
|
|
82
102
|
};
|
|
103
|
+
type FetchOnlyInitKeys = "mode" | "cache" | "integrity" | "keepalive" | "next" | "priority" | "redirect" | "referrer" | "referrerPolicy" | "window";
|
|
104
|
+
type SharedSpooshOptions = Omit<RequestInit, "signal" | "method" | "body" | "headers" | FetchOnlyInitKeys> & {
|
|
105
|
+
headers?: HeadersInitOrGetter;
|
|
106
|
+
};
|
|
107
|
+
type SpooshFetchOptions = SharedSpooshOptions & Pick<RequestInit, Extract<keyof RequestInit, FetchOnlyInitKeys>> & {
|
|
108
|
+
transport?: "fetch";
|
|
109
|
+
};
|
|
110
|
+
type SpooshXhrOptions = SharedSpooshOptions & {
|
|
111
|
+
transport: "xhr";
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Constructor-level options with transport-aware type narrowing.
|
|
115
|
+
*
|
|
116
|
+
* When `transport` is `"xhr"`, fetch-only fields (e.g. `mode`, `cache`, `redirect`) are
|
|
117
|
+
* excluded from autocomplete since they have no effect on XMLHttpRequest.
|
|
118
|
+
*/
|
|
119
|
+
type SpooshOptionsInput = SpooshFetchOptions | SpooshXhrOptions;
|
|
83
120
|
type BaseRequestOptions$1 = {
|
|
84
121
|
headers?: HeadersInitOrGetter;
|
|
85
122
|
cache?: RequestCache;
|
|
@@ -101,6 +138,10 @@ type AnyRequestOptions = BaseRequestOptions$1 & {
|
|
|
101
138
|
query?: Record<string, string | number | boolean | undefined>;
|
|
102
139
|
params?: Record<string, string | number>;
|
|
103
140
|
signal?: AbortSignal;
|
|
141
|
+
/** Per-request transport override. */
|
|
142
|
+
transport?: TransportOption;
|
|
143
|
+
/** Transport-specific options passed through to the transport function. */
|
|
144
|
+
transportOptions?: unknown;
|
|
104
145
|
} & Partial<RetryConfig>;
|
|
105
146
|
type DynamicParamsOption = {
|
|
106
147
|
params?: Record<string, string | number>;
|
|
@@ -728,7 +769,7 @@ declare function createPluginRegistry<TPlugins extends SpooshPlugin<PluginTypeCo
|
|
|
728
769
|
type ApiSchema = {
|
|
729
770
|
[path: string]: {
|
|
730
771
|
[method in HttpMethod]?: {
|
|
731
|
-
data
|
|
772
|
+
data?: unknown;
|
|
732
773
|
body?: unknown;
|
|
733
774
|
query?: unknown;
|
|
734
775
|
error?: unknown;
|
|
@@ -740,7 +781,7 @@ type ApiSchema = {
|
|
|
740
781
|
*/
|
|
741
782
|
type ExtractData<T> = T extends {
|
|
742
783
|
data: infer D;
|
|
743
|
-
} ? D :
|
|
784
|
+
} ? D : void;
|
|
744
785
|
/**
|
|
745
786
|
* Extract body type from an endpoint.
|
|
746
787
|
*/
|
|
@@ -1019,7 +1060,7 @@ type PathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchingKey
|
|
|
1019
1060
|
*/
|
|
1020
1061
|
type SchemaPaths<TSchema> = keyof TSchema & string;
|
|
1021
1062
|
/**
|
|
1022
|
-
* An API
|
|
1063
|
+
* An API interface that uses path strings instead of chained property access.
|
|
1023
1064
|
* Methods use HTTP names directly: GET, POST, PUT, PATCH, DELETE.
|
|
1024
1065
|
*
|
|
1025
1066
|
* @example
|
|
@@ -1050,7 +1091,7 @@ type ReadPathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchin
|
|
|
1050
1091
|
GET: MethodFn<TSchema[TKey]["GET"], TDefaultError, TPath>;
|
|
1051
1092
|
}> : never : never : never;
|
|
1052
1093
|
/**
|
|
1053
|
-
* A read-only API
|
|
1094
|
+
* A read-only API interface that only exposes GET methods.
|
|
1054
1095
|
* Used by useRead and injectRead hooks.
|
|
1055
1096
|
*/
|
|
1056
1097
|
type ReadClient<TSchema, TDefaultError = unknown> = <TPath extends ReadPaths<TSchema> | (string & {})>(path: TPath) => HasReadMethod<TSchema, TPath> extends true ? ReadPathMethods<TSchema, TPath, TDefaultError> : never;
|
|
@@ -1062,7 +1103,7 @@ type WritePathMethods<TSchema, TPath extends string, TDefaultError> = FindMatchi
|
|
|
1062
1103
|
[M in WriteMethod as M extends keyof TSchema[TKey] ? M : never]: M extends keyof TSchema[TKey] ? MethodFn<TSchema[TKey][M], TDefaultError, TPath> : never;
|
|
1063
1104
|
}> : never : never;
|
|
1064
1105
|
/**
|
|
1065
|
-
* A write-only API
|
|
1106
|
+
* A write-only API interface that only exposes mutation methods (POST, PUT, PATCH, DELETE).
|
|
1066
1107
|
* Used by useWrite and injectWrite hooks.
|
|
1067
1108
|
*/
|
|
1068
1109
|
type WriteClient<TSchema, TDefaultError = unknown> = <TPath extends WritePaths<TSchema> | (string & {})>(path: TPath) => HasWriteMethod<TSchema, TPath> extends true ? WritePathMethods<TSchema, TPath, TDefaultError> : never;
|
|
@@ -1136,7 +1177,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1136
1177
|
* Creates a new Spoosh instance.
|
|
1137
1178
|
*
|
|
1138
1179
|
* @param baseUrl - The base URL for all API requests (e.g., '/api' or 'https://api.example.com')
|
|
1139
|
-
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, etc.)
|
|
1180
|
+
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, transport, etc.)
|
|
1140
1181
|
* @param plugins - Internal parameter used by the `.use()` method. Do not pass directly.
|
|
1141
1182
|
*
|
|
1142
1183
|
* @example
|
|
@@ -1148,9 +1189,15 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1148
1189
|
* const client = new Spoosh<ApiSchema, Error>('/api', {
|
|
1149
1190
|
* headers: { 'X-API-Key': 'secret' }
|
|
1150
1191
|
* });
|
|
1192
|
+
*
|
|
1193
|
+
* // With XHR transport (narrows available options to XHR-compatible fields)
|
|
1194
|
+
* const client = new Spoosh<ApiSchema, Error>('/api', {
|
|
1195
|
+
* transport: 'xhr',
|
|
1196
|
+
* credentials: 'include',
|
|
1197
|
+
* });
|
|
1151
1198
|
* ```
|
|
1152
1199
|
*/
|
|
1153
|
-
constructor(baseUrl: string, defaultOptions?:
|
|
1200
|
+
constructor(baseUrl: string, defaultOptions?: SpooshOptionsInput, plugins?: TPlugins);
|
|
1154
1201
|
/**
|
|
1155
1202
|
* Adds plugins to the Spoosh instance.
|
|
1156
1203
|
*
|
|
@@ -1199,7 +1246,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1199
1246
|
*/
|
|
1200
1247
|
private getInstance;
|
|
1201
1248
|
/**
|
|
1202
|
-
* The type-safe API
|
|
1249
|
+
* The type-safe API interface for making requests.
|
|
1203
1250
|
*
|
|
1204
1251
|
* Provides a proxy-based interface for accessing endpoints defined in your schema.
|
|
1205
1252
|
*
|
|
@@ -1301,17 +1348,17 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1301
1348
|
|
|
1302
1349
|
type SpooshClientConfig = {
|
|
1303
1350
|
baseUrl: string;
|
|
1304
|
-
defaultOptions?:
|
|
1351
|
+
defaultOptions?: SpooshOptionsInput;
|
|
1305
1352
|
middlewares?: SpooshMiddleware[];
|
|
1306
1353
|
};
|
|
1307
1354
|
/**
|
|
1308
|
-
* Creates a lightweight type-safe API
|
|
1355
|
+
* Creates a lightweight type-safe API instance for vanilla JavaScript/TypeScript usage.
|
|
1309
1356
|
*
|
|
1310
1357
|
* This is a simpler alternative to `Spoosh` for users who don't need
|
|
1311
1358
|
* the full plugin system, state management, or React integration.
|
|
1312
1359
|
*
|
|
1313
1360
|
* @param config - Client configuration
|
|
1314
|
-
* @returns Type-safe API
|
|
1361
|
+
* @returns Type-safe API instance
|
|
1315
1362
|
*
|
|
1316
1363
|
* @example
|
|
1317
1364
|
* ```ts
|
|
@@ -1339,6 +1386,12 @@ type SpooshClientConfig = {
|
|
|
1339
1386
|
* const { data } = await api("posts").GET();
|
|
1340
1387
|
* const { data: post } = await api("posts/123").GET();
|
|
1341
1388
|
* await api("posts/:id").GET({ params: { id: 123 } });
|
|
1389
|
+
*
|
|
1390
|
+
* // With XHR transport
|
|
1391
|
+
* const api = createClient<ApiSchema, ApiError>({
|
|
1392
|
+
* baseUrl: "/api",
|
|
1393
|
+
* defaultOptions: { transport: "xhr" },
|
|
1394
|
+
* });
|
|
1342
1395
|
* ```
|
|
1343
1396
|
*/
|
|
1344
1397
|
declare function createClient<TSchema, TDefaultError = unknown>(config: SpooshClientConfig): SpooshClient<TSchema, TDefaultError>;
|
|
@@ -1403,7 +1456,7 @@ type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
|
1403
1456
|
nextTags?: boolean;
|
|
1404
1457
|
};
|
|
1405
1458
|
/**
|
|
1406
|
-
* Creates an API
|
|
1459
|
+
* Creates an API proxy that uses path strings instead of chained property access.
|
|
1407
1460
|
* Methods use HTTP names directly: GET, POST, PUT, PATCH, DELETE.
|
|
1408
1461
|
*
|
|
1409
1462
|
* @param config - Proxy handler configuration
|
|
@@ -1421,7 +1474,7 @@ type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
|
1421
1474
|
*/
|
|
1422
1475
|
declare function createProxyHandler<TSchema, TDefaultError = unknown, TOptions = SpooshOptions>(config: ProxyHandlerConfig<TOptions>): SpooshClient<TSchema, TDefaultError>;
|
|
1423
1476
|
|
|
1424
|
-
/** All supported HTTP method keys used in the API
|
|
1477
|
+
/** All supported HTTP method keys used in the API toolkit */
|
|
1425
1478
|
declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
1426
1479
|
/** Union type of all HTTP method keys */
|
|
1427
1480
|
type HttpMethodKey = (typeof HTTP_METHODS)[number];
|
|
@@ -1527,6 +1580,19 @@ declare function extractPathFromSelector(fn: unknown): string;
|
|
|
1527
1580
|
*/
|
|
1528
1581
|
declare function extractMethodFromSelector(fn: unknown): string | undefined;
|
|
1529
1582
|
|
|
1583
|
+
declare const fetchTransport: Transport;
|
|
1584
|
+
|
|
1585
|
+
interface XhrTransportOptions {
|
|
1586
|
+
/** Called on upload and download progress events. */
|
|
1587
|
+
onProgress?: (event: ProgressEvent, xhr: XMLHttpRequest) => void;
|
|
1588
|
+
}
|
|
1589
|
+
declare module "./types" {
|
|
1590
|
+
interface TransportOptionsMap {
|
|
1591
|
+
xhr: XhrTransportOptions;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
declare const xhrTransport: Transport<XhrTransportOptions>;
|
|
1595
|
+
|
|
1530
1596
|
declare function executeFetch<TData, TError>(baseUrl: string, path: string[], method: HttpMethod, defaultOptions: SpooshOptions & SpooshOptionsExtra, requestOptions?: AnyRequestOptions, nextTags?: boolean): Promise<SpooshResponse<TData, TError>>;
|
|
1531
1597
|
|
|
1532
1598
|
declare function createMiddleware<TData = unknown, TError = unknown>(name: string, phase: MiddlewarePhase, handler: SpooshMiddleware<TData, TError>["handler"]): SpooshMiddleware<TData, TError>;
|
|
@@ -1621,4 +1687,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
|
|
|
1621
1687
|
};
|
|
1622
1688
|
declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
|
|
1623
1689
|
|
|
1624
|
-
export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type SpooshClientConfig as ClientConfig, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type MiddlewareContext, type MiddlewareHandler, type MiddlewarePhase, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type 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, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded };
|
|
1690
|
+
export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type SpooshClientConfig as ClientConfig, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type MiddlewareContext, type MiddlewareHandler, type MiddlewarePhase, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, applyMiddlewares, buildUrl, composeMiddlewares, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createMiddleware, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isJsonBody, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
|
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
|
+
fetchTransport: () => fetchTransport,
|
|
44
45
|
form: () => form,
|
|
45
46
|
generateTags: () => generateTags,
|
|
46
47
|
getContentType: () => getContentType,
|
|
@@ -56,7 +57,8 @@ __export(src_exports, {
|
|
|
56
57
|
resolveTags: () => resolveTags,
|
|
57
58
|
setHeaders: () => setHeaders,
|
|
58
59
|
sortObjectKeys: () => sortObjectKeys,
|
|
59
|
-
urlencoded: () => urlencoded
|
|
60
|
+
urlencoded: () => urlencoded,
|
|
61
|
+
xhrTransport: () => xhrTransport
|
|
60
62
|
});
|
|
61
63
|
module.exports = __toCommonJS(src_exports);
|
|
62
64
|
|
|
@@ -368,6 +370,87 @@ function resolvePath(path, params) {
|
|
|
368
370
|
});
|
|
369
371
|
}
|
|
370
372
|
|
|
373
|
+
// src/transport/fetch.ts
|
|
374
|
+
var fetchTransport = async (url, init) => {
|
|
375
|
+
const res = await fetch(url, init);
|
|
376
|
+
const contentType = res.headers.get("content-type");
|
|
377
|
+
const isJson = contentType?.includes("application/json");
|
|
378
|
+
const data = isJson ? await res.json() : res;
|
|
379
|
+
return { ok: res.ok, status: res.status, headers: res.headers, data };
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// src/transport/xhr.ts
|
|
383
|
+
var xhrTransport = (url, init, options) => {
|
|
384
|
+
return new Promise((resolve, reject) => {
|
|
385
|
+
const xhr = new XMLHttpRequest();
|
|
386
|
+
xhr.open(init.method ?? "GET", url);
|
|
387
|
+
if (init.headers) {
|
|
388
|
+
const headers = init.headers instanceof Headers ? init.headers : new Headers(init.headers);
|
|
389
|
+
headers.forEach((value, key) => {
|
|
390
|
+
xhr.setRequestHeader(key, value);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
if (init.credentials === "include") {
|
|
394
|
+
xhr.withCredentials = true;
|
|
395
|
+
}
|
|
396
|
+
const onAbort = () => xhr.abort();
|
|
397
|
+
if (init.signal) {
|
|
398
|
+
if (init.signal.aborted) {
|
|
399
|
+
xhr.abort();
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
init.signal.addEventListener("abort", onAbort);
|
|
403
|
+
}
|
|
404
|
+
const cleanup = () => {
|
|
405
|
+
init.signal?.removeEventListener("abort", onAbort);
|
|
406
|
+
};
|
|
407
|
+
if (options?.onProgress) {
|
|
408
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
409
|
+
options.onProgress(event, xhr);
|
|
410
|
+
});
|
|
411
|
+
xhr.addEventListener("progress", (event) => {
|
|
412
|
+
options.onProgress(event, xhr);
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
xhr.addEventListener("load", () => {
|
|
416
|
+
cleanup();
|
|
417
|
+
const status = xhr.status;
|
|
418
|
+
const ok = status >= 200 && status < 300;
|
|
419
|
+
const responseHeaders = new Headers();
|
|
420
|
+
const rawHeaders = xhr.getAllResponseHeaders().trim();
|
|
421
|
+
if (rawHeaders) {
|
|
422
|
+
rawHeaders.split("\r\n").forEach((line) => {
|
|
423
|
+
const idx = line.indexOf(": ");
|
|
424
|
+
if (idx > 0) {
|
|
425
|
+
responseHeaders.append(
|
|
426
|
+
line.substring(0, idx),
|
|
427
|
+
line.substring(idx + 2)
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
const contentType = responseHeaders.get("content-type");
|
|
433
|
+
const isJson = contentType?.includes("application/json");
|
|
434
|
+
let data;
|
|
435
|
+
try {
|
|
436
|
+
data = isJson ? JSON.parse(xhr.responseText) : xhr.responseText;
|
|
437
|
+
} catch {
|
|
438
|
+
data = xhr.responseText;
|
|
439
|
+
}
|
|
440
|
+
resolve({ ok, status, headers: responseHeaders, data });
|
|
441
|
+
});
|
|
442
|
+
xhr.addEventListener("error", () => {
|
|
443
|
+
cleanup();
|
|
444
|
+
reject(new TypeError("Network request failed"));
|
|
445
|
+
});
|
|
446
|
+
xhr.addEventListener("abort", () => {
|
|
447
|
+
cleanup();
|
|
448
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
449
|
+
});
|
|
450
|
+
xhr.send(init.body);
|
|
451
|
+
});
|
|
452
|
+
};
|
|
453
|
+
|
|
371
454
|
// src/fetch.ts
|
|
372
455
|
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
373
456
|
var isNetworkError = (err) => err instanceof TypeError;
|
|
@@ -416,6 +499,12 @@ function buildInputFields(requestOptions) {
|
|
|
416
499
|
}
|
|
417
500
|
return { input: fields };
|
|
418
501
|
}
|
|
502
|
+
function resolveTransport(option) {
|
|
503
|
+
if (option === "xhr" && typeof XMLHttpRequest !== "undefined") {
|
|
504
|
+
return xhrTransport;
|
|
505
|
+
}
|
|
506
|
+
return fetchTransport;
|
|
507
|
+
}
|
|
419
508
|
async function executeCoreFetch(config) {
|
|
420
509
|
const {
|
|
421
510
|
baseUrl,
|
|
@@ -429,6 +518,7 @@ async function executeCoreFetch(config) {
|
|
|
429
518
|
const {
|
|
430
519
|
middlewares: _,
|
|
431
520
|
headers: defaultHeaders,
|
|
521
|
+
transport: defaultTransport,
|
|
432
522
|
...fetchDefaults
|
|
433
523
|
} = defaultOptions;
|
|
434
524
|
void _;
|
|
@@ -473,28 +563,30 @@ async function executeCoreFetch(config) {
|
|
|
473
563
|
}
|
|
474
564
|
}
|
|
475
565
|
}
|
|
566
|
+
const resolvedTransport = resolveTransport(
|
|
567
|
+
requestOptions?.transport ?? defaultTransport
|
|
568
|
+
);
|
|
476
569
|
let lastError;
|
|
477
570
|
for (let attempt = 0; attempt <= retryCount; attempt++) {
|
|
478
571
|
try {
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
if (res.ok) {
|
|
572
|
+
const result = await resolvedTransport(
|
|
573
|
+
url,
|
|
574
|
+
fetchInit,
|
|
575
|
+
requestOptions?.transportOptions
|
|
576
|
+
);
|
|
577
|
+
if (result.ok) {
|
|
486
578
|
return {
|
|
487
|
-
status,
|
|
488
|
-
data:
|
|
489
|
-
headers:
|
|
579
|
+
status: result.status,
|
|
580
|
+
data: result.data,
|
|
581
|
+
headers: result.headers,
|
|
490
582
|
error: void 0,
|
|
491
583
|
...inputFields
|
|
492
584
|
};
|
|
493
585
|
}
|
|
494
586
|
return {
|
|
495
|
-
status,
|
|
496
|
-
error:
|
|
497
|
-
headers:
|
|
587
|
+
status: result.status,
|
|
588
|
+
error: result.data,
|
|
589
|
+
headers: result.headers,
|
|
498
590
|
data: void 0,
|
|
499
591
|
...inputFields
|
|
500
592
|
};
|
|
@@ -738,6 +830,7 @@ function createStateManager() {
|
|
|
738
830
|
for (const [name, value] of Object.entries(data)) {
|
|
739
831
|
entry.meta.set(name, value);
|
|
740
832
|
}
|
|
833
|
+
entry.state = { ...entry.state };
|
|
741
834
|
notifySubscribers(key);
|
|
742
835
|
},
|
|
743
836
|
markStale(tags) {
|
|
@@ -949,7 +1042,7 @@ var Spoosh = class _Spoosh {
|
|
|
949
1042
|
* Creates a new Spoosh instance.
|
|
950
1043
|
*
|
|
951
1044
|
* @param baseUrl - The base URL for all API requests (e.g., '/api' or 'https://api.example.com')
|
|
952
|
-
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, etc.)
|
|
1045
|
+
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, transport, etc.)
|
|
953
1046
|
* @param plugins - Internal parameter used by the `.use()` method. Do not pass directly.
|
|
954
1047
|
*
|
|
955
1048
|
* @example
|
|
@@ -961,6 +1054,12 @@ var Spoosh = class _Spoosh {
|
|
|
961
1054
|
* const client = new Spoosh<ApiSchema, Error>('/api', {
|
|
962
1055
|
* headers: { 'X-API-Key': 'secret' }
|
|
963
1056
|
* });
|
|
1057
|
+
*
|
|
1058
|
+
* // With XHR transport (narrows available options to XHR-compatible fields)
|
|
1059
|
+
* const client = new Spoosh<ApiSchema, Error>('/api', {
|
|
1060
|
+
* transport: 'xhr',
|
|
1061
|
+
* credentials: 'include',
|
|
1062
|
+
* });
|
|
964
1063
|
* ```
|
|
965
1064
|
*/
|
|
966
1065
|
constructor(baseUrl, defaultOptions, plugins) {
|
|
@@ -1048,7 +1147,7 @@ var Spoosh = class _Spoosh {
|
|
|
1048
1147
|
return this._instance;
|
|
1049
1148
|
}
|
|
1050
1149
|
/**
|
|
1051
|
-
* The type-safe API
|
|
1150
|
+
* The type-safe API interface for making requests.
|
|
1052
1151
|
*
|
|
1053
1152
|
* Provides a proxy-based interface for accessing endpoints defined in your schema.
|
|
1054
1153
|
*
|
|
@@ -1156,7 +1255,10 @@ var Spoosh = class _Spoosh {
|
|
|
1156
1255
|
// src/createClient.ts
|
|
1157
1256
|
function createClient(config) {
|
|
1158
1257
|
const { baseUrl, defaultOptions = {}, middlewares = [] } = config;
|
|
1159
|
-
const optionsWithMiddlewares = {
|
|
1258
|
+
const optionsWithMiddlewares = {
|
|
1259
|
+
...defaultOptions,
|
|
1260
|
+
middlewares
|
|
1261
|
+
};
|
|
1160
1262
|
return createProxyHandler({
|
|
1161
1263
|
baseUrl,
|
|
1162
1264
|
defaultOptions: optionsWithMiddlewares,
|
|
@@ -1216,6 +1318,7 @@ function createOperationController(options) {
|
|
|
1216
1318
|
if (cached) {
|
|
1217
1319
|
stateManager.setCache(queryKey, {
|
|
1218
1320
|
state: { ...cached.state, ...updater },
|
|
1321
|
+
tags,
|
|
1219
1322
|
stale: false
|
|
1220
1323
|
});
|
|
1221
1324
|
} else {
|
package/dist/index.mjs
CHANGED
|
@@ -306,6 +306,87 @@ function resolvePath(path, params) {
|
|
|
306
306
|
});
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
+
// src/transport/fetch.ts
|
|
310
|
+
var fetchTransport = async (url, init) => {
|
|
311
|
+
const res = await fetch(url, init);
|
|
312
|
+
const contentType = res.headers.get("content-type");
|
|
313
|
+
const isJson = contentType?.includes("application/json");
|
|
314
|
+
const data = isJson ? await res.json() : res;
|
|
315
|
+
return { ok: res.ok, status: res.status, headers: res.headers, data };
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// src/transport/xhr.ts
|
|
319
|
+
var xhrTransport = (url, init, options) => {
|
|
320
|
+
return new Promise((resolve, reject) => {
|
|
321
|
+
const xhr = new XMLHttpRequest();
|
|
322
|
+
xhr.open(init.method ?? "GET", url);
|
|
323
|
+
if (init.headers) {
|
|
324
|
+
const headers = init.headers instanceof Headers ? init.headers : new Headers(init.headers);
|
|
325
|
+
headers.forEach((value, key) => {
|
|
326
|
+
xhr.setRequestHeader(key, value);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
if (init.credentials === "include") {
|
|
330
|
+
xhr.withCredentials = true;
|
|
331
|
+
}
|
|
332
|
+
const onAbort = () => xhr.abort();
|
|
333
|
+
if (init.signal) {
|
|
334
|
+
if (init.signal.aborted) {
|
|
335
|
+
xhr.abort();
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
init.signal.addEventListener("abort", onAbort);
|
|
339
|
+
}
|
|
340
|
+
const cleanup = () => {
|
|
341
|
+
init.signal?.removeEventListener("abort", onAbort);
|
|
342
|
+
};
|
|
343
|
+
if (options?.onProgress) {
|
|
344
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
345
|
+
options.onProgress(event, xhr);
|
|
346
|
+
});
|
|
347
|
+
xhr.addEventListener("progress", (event) => {
|
|
348
|
+
options.onProgress(event, xhr);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
xhr.addEventListener("load", () => {
|
|
352
|
+
cleanup();
|
|
353
|
+
const status = xhr.status;
|
|
354
|
+
const ok = status >= 200 && status < 300;
|
|
355
|
+
const responseHeaders = new Headers();
|
|
356
|
+
const rawHeaders = xhr.getAllResponseHeaders().trim();
|
|
357
|
+
if (rawHeaders) {
|
|
358
|
+
rawHeaders.split("\r\n").forEach((line) => {
|
|
359
|
+
const idx = line.indexOf(": ");
|
|
360
|
+
if (idx > 0) {
|
|
361
|
+
responseHeaders.append(
|
|
362
|
+
line.substring(0, idx),
|
|
363
|
+
line.substring(idx + 2)
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
const contentType = responseHeaders.get("content-type");
|
|
369
|
+
const isJson = contentType?.includes("application/json");
|
|
370
|
+
let data;
|
|
371
|
+
try {
|
|
372
|
+
data = isJson ? JSON.parse(xhr.responseText) : xhr.responseText;
|
|
373
|
+
} catch {
|
|
374
|
+
data = xhr.responseText;
|
|
375
|
+
}
|
|
376
|
+
resolve({ ok, status, headers: responseHeaders, data });
|
|
377
|
+
});
|
|
378
|
+
xhr.addEventListener("error", () => {
|
|
379
|
+
cleanup();
|
|
380
|
+
reject(new TypeError("Network request failed"));
|
|
381
|
+
});
|
|
382
|
+
xhr.addEventListener("abort", () => {
|
|
383
|
+
cleanup();
|
|
384
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
385
|
+
});
|
|
386
|
+
xhr.send(init.body);
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
|
|
309
390
|
// src/fetch.ts
|
|
310
391
|
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
311
392
|
var isNetworkError = (err) => err instanceof TypeError;
|
|
@@ -354,6 +435,12 @@ function buildInputFields(requestOptions) {
|
|
|
354
435
|
}
|
|
355
436
|
return { input: fields };
|
|
356
437
|
}
|
|
438
|
+
function resolveTransport(option) {
|
|
439
|
+
if (option === "xhr" && typeof XMLHttpRequest !== "undefined") {
|
|
440
|
+
return xhrTransport;
|
|
441
|
+
}
|
|
442
|
+
return fetchTransport;
|
|
443
|
+
}
|
|
357
444
|
async function executeCoreFetch(config) {
|
|
358
445
|
const {
|
|
359
446
|
baseUrl,
|
|
@@ -367,6 +454,7 @@ async function executeCoreFetch(config) {
|
|
|
367
454
|
const {
|
|
368
455
|
middlewares: _,
|
|
369
456
|
headers: defaultHeaders,
|
|
457
|
+
transport: defaultTransport,
|
|
370
458
|
...fetchDefaults
|
|
371
459
|
} = defaultOptions;
|
|
372
460
|
void _;
|
|
@@ -411,28 +499,30 @@ async function executeCoreFetch(config) {
|
|
|
411
499
|
}
|
|
412
500
|
}
|
|
413
501
|
}
|
|
502
|
+
const resolvedTransport = resolveTransport(
|
|
503
|
+
requestOptions?.transport ?? defaultTransport
|
|
504
|
+
);
|
|
414
505
|
let lastError;
|
|
415
506
|
for (let attempt = 0; attempt <= retryCount; attempt++) {
|
|
416
507
|
try {
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if (res.ok) {
|
|
508
|
+
const result = await resolvedTransport(
|
|
509
|
+
url,
|
|
510
|
+
fetchInit,
|
|
511
|
+
requestOptions?.transportOptions
|
|
512
|
+
);
|
|
513
|
+
if (result.ok) {
|
|
424
514
|
return {
|
|
425
|
-
status,
|
|
426
|
-
data:
|
|
427
|
-
headers:
|
|
515
|
+
status: result.status,
|
|
516
|
+
data: result.data,
|
|
517
|
+
headers: result.headers,
|
|
428
518
|
error: void 0,
|
|
429
519
|
...inputFields
|
|
430
520
|
};
|
|
431
521
|
}
|
|
432
522
|
return {
|
|
433
|
-
status,
|
|
434
|
-
error:
|
|
435
|
-
headers:
|
|
523
|
+
status: result.status,
|
|
524
|
+
error: result.data,
|
|
525
|
+
headers: result.headers,
|
|
436
526
|
data: void 0,
|
|
437
527
|
...inputFields
|
|
438
528
|
};
|
|
@@ -676,6 +766,7 @@ function createStateManager() {
|
|
|
676
766
|
for (const [name, value] of Object.entries(data)) {
|
|
677
767
|
entry.meta.set(name, value);
|
|
678
768
|
}
|
|
769
|
+
entry.state = { ...entry.state };
|
|
679
770
|
notifySubscribers(key);
|
|
680
771
|
},
|
|
681
772
|
markStale(tags) {
|
|
@@ -887,7 +978,7 @@ var Spoosh = class _Spoosh {
|
|
|
887
978
|
* Creates a new Spoosh instance.
|
|
888
979
|
*
|
|
889
980
|
* @param baseUrl - The base URL for all API requests (e.g., '/api' or 'https://api.example.com')
|
|
890
|
-
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, etc.)
|
|
981
|
+
* @param defaultOptions - Optional default options applied to all requests (headers, credentials, transport, etc.)
|
|
891
982
|
* @param plugins - Internal parameter used by the `.use()` method. Do not pass directly.
|
|
892
983
|
*
|
|
893
984
|
* @example
|
|
@@ -899,6 +990,12 @@ var Spoosh = class _Spoosh {
|
|
|
899
990
|
* const client = new Spoosh<ApiSchema, Error>('/api', {
|
|
900
991
|
* headers: { 'X-API-Key': 'secret' }
|
|
901
992
|
* });
|
|
993
|
+
*
|
|
994
|
+
* // With XHR transport (narrows available options to XHR-compatible fields)
|
|
995
|
+
* const client = new Spoosh<ApiSchema, Error>('/api', {
|
|
996
|
+
* transport: 'xhr',
|
|
997
|
+
* credentials: 'include',
|
|
998
|
+
* });
|
|
902
999
|
* ```
|
|
903
1000
|
*/
|
|
904
1001
|
constructor(baseUrl, defaultOptions, plugins) {
|
|
@@ -986,7 +1083,7 @@ var Spoosh = class _Spoosh {
|
|
|
986
1083
|
return this._instance;
|
|
987
1084
|
}
|
|
988
1085
|
/**
|
|
989
|
-
* The type-safe API
|
|
1086
|
+
* The type-safe API interface for making requests.
|
|
990
1087
|
*
|
|
991
1088
|
* Provides a proxy-based interface for accessing endpoints defined in your schema.
|
|
992
1089
|
*
|
|
@@ -1094,7 +1191,10 @@ var Spoosh = class _Spoosh {
|
|
|
1094
1191
|
// src/createClient.ts
|
|
1095
1192
|
function createClient(config) {
|
|
1096
1193
|
const { baseUrl, defaultOptions = {}, middlewares = [] } = config;
|
|
1097
|
-
const optionsWithMiddlewares = {
|
|
1194
|
+
const optionsWithMiddlewares = {
|
|
1195
|
+
...defaultOptions,
|
|
1196
|
+
middlewares
|
|
1197
|
+
};
|
|
1098
1198
|
return createProxyHandler({
|
|
1099
1199
|
baseUrl,
|
|
1100
1200
|
defaultOptions: optionsWithMiddlewares,
|
|
@@ -1154,6 +1254,7 @@ function createOperationController(options) {
|
|
|
1154
1254
|
if (cached) {
|
|
1155
1255
|
stateManager.setCache(queryKey, {
|
|
1156
1256
|
state: { ...cached.state, ...updater },
|
|
1257
|
+
tags,
|
|
1157
1258
|
stale: false
|
|
1158
1259
|
});
|
|
1159
1260
|
} else {
|
|
@@ -1681,6 +1782,7 @@ export {
|
|
|
1681
1782
|
executeFetch,
|
|
1682
1783
|
extractMethodFromSelector,
|
|
1683
1784
|
extractPathFromSelector,
|
|
1785
|
+
fetchTransport,
|
|
1684
1786
|
form,
|
|
1685
1787
|
generateTags,
|
|
1686
1788
|
getContentType,
|
|
@@ -1696,5 +1798,6 @@ export {
|
|
|
1696
1798
|
resolveTags,
|
|
1697
1799
|
setHeaders,
|
|
1698
1800
|
sortObjectKeys,
|
|
1699
|
-
urlencoded
|
|
1801
|
+
urlencoded,
|
|
1802
|
+
xhrTransport
|
|
1700
1803
|
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spoosh/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"description": "Type-safe API
|
|
5
|
+
"description": "Type-safe API toolkit with plugin middleware system",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"spoosh",
|
|
8
|
-
"api-
|
|
8
|
+
"api-toolkit",
|
|
9
9
|
"fetch",
|
|
10
10
|
"typescript",
|
|
11
11
|
"middleware",
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
],
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/
|
|
17
|
+
"url": "git+https://github.com/spooshdev/spoosh.git",
|
|
18
18
|
"directory": "packages/core"
|
|
19
19
|
},
|
|
20
20
|
"bugs": {
|
|
21
|
-
"url": "https://github.com/
|
|
21
|
+
"url": "https://github.com/spooshdev/spoosh/issues"
|
|
22
22
|
},
|
|
23
|
-
"homepage": "https://spoosh.dev/react/
|
|
23
|
+
"homepage": "https://spoosh.dev/docs/react/core",
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|