@spoosh/core 0.12.0 → 0.12.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 +1 -1
- package/dist/index.d.mts +54 -44
- package/dist/index.d.ts +54 -44
- package/dist/index.js +53 -96
- package/dist/index.mjs +53 -96
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -164,7 +164,7 @@ import { Spoosh } from "@spoosh/core";
|
|
|
164
164
|
import { cachePlugin } from "@spoosh/plugin-cache";
|
|
165
165
|
import { retryPlugin } from "@spoosh/plugin-retry";
|
|
166
166
|
|
|
167
|
-
const
|
|
167
|
+
const spoosh = new Spoosh<ApiSchema, Error>("/api", {
|
|
168
168
|
headers: { Authorization: "Bearer token" },
|
|
169
169
|
}).use([cachePlugin({ staleTime: 5000 }), retryPlugin({ retries: 3 })]);
|
|
170
170
|
|
package/dist/index.d.mts
CHANGED
|
@@ -82,10 +82,6 @@ interface TransportOptionsMap {
|
|
|
82
82
|
fetch: never;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
type RetryConfig = {
|
|
86
|
-
retries?: number | false;
|
|
87
|
-
retryDelay?: number;
|
|
88
|
-
};
|
|
89
85
|
type HeadersInitOrGetter = HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
|
|
90
86
|
type SpooshOptions = Omit<RequestInit, "method" | "body" | "headers"> & {
|
|
91
87
|
headers?: HeadersInitOrGetter;
|
|
@@ -134,7 +130,7 @@ type AnyRequestOptions = BaseRequestOptions$1 & {
|
|
|
134
130
|
transport?: TransportOption;
|
|
135
131
|
/** Transport-specific options passed through to the transport function. */
|
|
136
132
|
transportOptions?: unknown;
|
|
137
|
-
}
|
|
133
|
+
};
|
|
138
134
|
type DynamicParamsOption = {
|
|
139
135
|
params?: Record<string, string | number>;
|
|
140
136
|
};
|
|
@@ -270,7 +266,7 @@ type CacheEntry<TData = unknown, TError = unknown> = {
|
|
|
270
266
|
stale?: boolean;
|
|
271
267
|
};
|
|
272
268
|
/** RequestOptions in plugin context have headers already resolved to Record */
|
|
273
|
-
type PluginRequestOptions = Omit<AnyRequestOptions, "headers"> & {
|
|
269
|
+
type PluginRequestOptions = Omit<AnyRequestOptions, "headers" | "cache"> & {
|
|
274
270
|
headers: Record<string, string>;
|
|
275
271
|
};
|
|
276
272
|
type PluginContext = {
|
|
@@ -318,12 +314,9 @@ type PluginContextInput = Omit<PluginContext, "plugins">;
|
|
|
318
314
|
* return next();
|
|
319
315
|
* }
|
|
320
316
|
*
|
|
321
|
-
* //
|
|
317
|
+
* // Auth middleware - add authentication headers
|
|
322
318
|
* middleware: async (context, next) => {
|
|
323
|
-
*
|
|
324
|
-
* const result = await next();
|
|
325
|
-
* if (!result.error) return result;
|
|
326
|
-
* }
|
|
319
|
+
* context.request.headers['Authorization'] = `Bearer ${getToken()}`;
|
|
327
320
|
* return next();
|
|
328
321
|
* }
|
|
329
322
|
* ```
|
|
@@ -379,7 +372,7 @@ type PluginTypeConfig = {
|
|
|
379
372
|
* Base interface for Spoosh plugins.
|
|
380
373
|
*
|
|
381
374
|
* Plugins can implement:
|
|
382
|
-
* - `middleware`: Wraps the fetch flow for full control (intercept,
|
|
375
|
+
* - `middleware`: Wraps the fetch flow for full control (intercept, transform, modify)
|
|
383
376
|
* - `afterResponse`: Called after every response, regardless of early returns
|
|
384
377
|
* - `lifecycle`: Component lifecycle hooks (onMount, onUpdate, onUnmount)
|
|
385
378
|
* - `exports`: Functions/variables accessible to other plugins
|
|
@@ -427,7 +420,7 @@ interface SpooshPlugin<T extends PluginTypeConfig = PluginTypeConfig> {
|
|
|
427
420
|
/** Expose functions/variables for other plugins to access via `context.plugins.get(name)` */
|
|
428
421
|
exports?: (context: PluginContext) => object;
|
|
429
422
|
/**
|
|
430
|
-
* Expose functions/properties on the framework adapter return value (e.g.,
|
|
423
|
+
* Expose functions/properties on the framework adapter return value (e.g., create).
|
|
431
424
|
* Unlike `exports`, these are accessible directly from the instance, not just within plugin context.
|
|
432
425
|
*
|
|
433
426
|
* @example
|
|
@@ -441,7 +434,37 @@ interface SpooshPlugin<T extends PluginTypeConfig = PluginTypeConfig> {
|
|
|
441
434
|
instanceApi?: (context: InstanceApiContext) => T extends {
|
|
442
435
|
instanceApi: infer A;
|
|
443
436
|
} ? A : object;
|
|
444
|
-
/**
|
|
437
|
+
/**
|
|
438
|
+
* Plugin execution priority. Lower numbers run first, higher numbers run last.
|
|
439
|
+
* Default: 0
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```ts
|
|
443
|
+
* // Cache plugin runs early (checks cache before other plugins)
|
|
444
|
+
* { name: "cache", priority: -10, ... }
|
|
445
|
+
*
|
|
446
|
+
* // Throttle plugin runs last (blocks all requests including force fetches)
|
|
447
|
+
* { name: "throttle", priority: 100, ... }
|
|
448
|
+
*
|
|
449
|
+
* // Most plugins use default priority
|
|
450
|
+
* { name: "retry", priority: 0, ... } // or omit priority
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
priority?: number;
|
|
454
|
+
/**
|
|
455
|
+
* List of plugin names that this plugin depends on.
|
|
456
|
+
* Used to validate that required plugins are registered.
|
|
457
|
+
* Does not affect execution order - use `priority` for that.
|
|
458
|
+
*
|
|
459
|
+
* @example
|
|
460
|
+
* ```ts
|
|
461
|
+
* {
|
|
462
|
+
* name: "spoosh:optimistic",
|
|
463
|
+
* dependencies: ["spoosh:invalidation"],
|
|
464
|
+
* // ...
|
|
465
|
+
* }
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
445
468
|
dependencies?: string[];
|
|
446
469
|
/** @internal Type carrier for inference - do not use directly */
|
|
447
470
|
readonly _types?: T;
|
|
@@ -1132,8 +1155,8 @@ type SpooshInstance<TSchema = unknown, TDefaultError = unknown, TPlugins extends
|
|
|
1132
1155
|
*
|
|
1133
1156
|
* @example Basic usage
|
|
1134
1157
|
* ```ts
|
|
1135
|
-
* const
|
|
1136
|
-
* .use([cachePlugin(),
|
|
1158
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api')
|
|
1159
|
+
* .use([cachePlugin(), debugPlugin()]);
|
|
1137
1160
|
*
|
|
1138
1161
|
* const { api } = client;
|
|
1139
1162
|
* const response = await api("posts").GET();
|
|
@@ -1141,19 +1164,19 @@ type SpooshInstance<TSchema = unknown, TDefaultError = unknown, TPlugins extends
|
|
|
1141
1164
|
*
|
|
1142
1165
|
* @example With default options
|
|
1143
1166
|
* ```ts
|
|
1144
|
-
* const
|
|
1167
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
1145
1168
|
* headers: { 'Authorization': 'Bearer token' }
|
|
1146
1169
|
* }).use([cachePlugin()]);
|
|
1147
1170
|
* ```
|
|
1148
1171
|
*
|
|
1149
1172
|
* @example With React hooks
|
|
1150
1173
|
* ```ts
|
|
1151
|
-
* import {
|
|
1174
|
+
* import { create } from '@spoosh/react';
|
|
1152
1175
|
*
|
|
1153
|
-
* const
|
|
1154
|
-
* .use([cachePlugin(),
|
|
1176
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api')
|
|
1177
|
+
* .use([cachePlugin(), prefetchPlugin()]);
|
|
1155
1178
|
*
|
|
1156
|
-
* const { useRead, useWrite } =
|
|
1179
|
+
* const { useRead, useWrite } = create(client);
|
|
1157
1180
|
*
|
|
1158
1181
|
* // In component
|
|
1159
1182
|
* const { data } = useRead((api) => api("posts").GET());
|
|
@@ -1176,15 +1199,15 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1176
1199
|
* @example
|
|
1177
1200
|
* ```ts
|
|
1178
1201
|
* // Simple usage
|
|
1179
|
-
* const
|
|
1202
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api');
|
|
1180
1203
|
*
|
|
1181
1204
|
* // With default headers
|
|
1182
|
-
* const
|
|
1205
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
1183
1206
|
* headers: { 'X-API-Key': 'secret' }
|
|
1184
1207
|
* });
|
|
1185
1208
|
*
|
|
1186
1209
|
* // With XHR transport (narrows available options to XHR-compatible fields)
|
|
1187
|
-
* const
|
|
1210
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
1188
1211
|
* transport: 'xhr',
|
|
1189
1212
|
* credentials: 'include',
|
|
1190
1213
|
* });
|
|
@@ -1201,26 +1224,10 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1201
1224
|
* @param plugins - Array of plugin instances to use
|
|
1202
1225
|
* @returns A new Spoosh instance with the specified plugins
|
|
1203
1226
|
*
|
|
1204
|
-
* @example Single use() call
|
|
1205
|
-
* ```ts
|
|
1206
|
-
* const client = new Spoosh<Schema, Error>('/api')
|
|
1207
|
-
* .use([cachePlugin(), retryPlugin(), debouncePlugin()]);
|
|
1208
|
-
* ```
|
|
1209
|
-
*
|
|
1210
|
-
* @example Chaining use() calls (replaces plugins)
|
|
1211
|
-
* ```ts
|
|
1212
|
-
* const client1 = new Spoosh<Schema, Error>('/api')
|
|
1213
|
-
* .use([cachePlugin()]);
|
|
1214
|
-
*
|
|
1215
|
-
* // This replaces cachePlugin with retryPlugin
|
|
1216
|
-
* const client2 = client1.use([retryPlugin()]);
|
|
1217
|
-
* ```
|
|
1218
|
-
*
|
|
1219
|
-
* @example With plugin configuration
|
|
1220
1227
|
* ```ts
|
|
1221
|
-
* const
|
|
1228
|
+
* const spoosh = new Spoosh<Schema, Error>('/api').use([
|
|
1222
1229
|
* cachePlugin({ staleTime: 5000 }),
|
|
1223
|
-
*
|
|
1230
|
+
* invalidationPlugin(),
|
|
1224
1231
|
* prefetchPlugin(),
|
|
1225
1232
|
* ]);
|
|
1226
1233
|
* ```
|
|
@@ -1245,7 +1252,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1245
1252
|
*
|
|
1246
1253
|
* @example
|
|
1247
1254
|
* ```ts
|
|
1248
|
-
* const
|
|
1255
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api').use([...]);
|
|
1249
1256
|
* const { api } = client;
|
|
1250
1257
|
*
|
|
1251
1258
|
* // GET request
|
|
@@ -1436,6 +1443,9 @@ type TagOptions = {
|
|
|
1436
1443
|
declare function resolveTags(options: TagOptions | undefined, resolvedPath: string[]): string[];
|
|
1437
1444
|
declare function resolvePath(path: string[], params: Record<string, string | number> | undefined): string[];
|
|
1438
1445
|
|
|
1446
|
+
declare const isNetworkError: (err: unknown) => boolean;
|
|
1447
|
+
declare const isAbortError: (err: unknown) => boolean;
|
|
1448
|
+
|
|
1439
1449
|
type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
1440
1450
|
baseUrl: string;
|
|
1441
1451
|
defaultOptions: TOptions;
|
|
@@ -1670,4 +1680,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
|
|
|
1670
1680
|
};
|
|
1671
1681
|
declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
|
|
1672
1682
|
|
|
1673
|
-
export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginRequestOptions, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type
|
|
1683
|
+
export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginRequestOptions, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshOptions, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, buildUrl, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isAbortError, isJsonBody, isNetworkError, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
|
package/dist/index.d.ts
CHANGED
|
@@ -82,10 +82,6 @@ interface TransportOptionsMap {
|
|
|
82
82
|
fetch: never;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
type RetryConfig = {
|
|
86
|
-
retries?: number | false;
|
|
87
|
-
retryDelay?: number;
|
|
88
|
-
};
|
|
89
85
|
type HeadersInitOrGetter = HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
|
|
90
86
|
type SpooshOptions = Omit<RequestInit, "method" | "body" | "headers"> & {
|
|
91
87
|
headers?: HeadersInitOrGetter;
|
|
@@ -134,7 +130,7 @@ type AnyRequestOptions = BaseRequestOptions$1 & {
|
|
|
134
130
|
transport?: TransportOption;
|
|
135
131
|
/** Transport-specific options passed through to the transport function. */
|
|
136
132
|
transportOptions?: unknown;
|
|
137
|
-
}
|
|
133
|
+
};
|
|
138
134
|
type DynamicParamsOption = {
|
|
139
135
|
params?: Record<string, string | number>;
|
|
140
136
|
};
|
|
@@ -270,7 +266,7 @@ type CacheEntry<TData = unknown, TError = unknown> = {
|
|
|
270
266
|
stale?: boolean;
|
|
271
267
|
};
|
|
272
268
|
/** RequestOptions in plugin context have headers already resolved to Record */
|
|
273
|
-
type PluginRequestOptions = Omit<AnyRequestOptions, "headers"> & {
|
|
269
|
+
type PluginRequestOptions = Omit<AnyRequestOptions, "headers" | "cache"> & {
|
|
274
270
|
headers: Record<string, string>;
|
|
275
271
|
};
|
|
276
272
|
type PluginContext = {
|
|
@@ -318,12 +314,9 @@ type PluginContextInput = Omit<PluginContext, "plugins">;
|
|
|
318
314
|
* return next();
|
|
319
315
|
* }
|
|
320
316
|
*
|
|
321
|
-
* //
|
|
317
|
+
* // Auth middleware - add authentication headers
|
|
322
318
|
* middleware: async (context, next) => {
|
|
323
|
-
*
|
|
324
|
-
* const result = await next();
|
|
325
|
-
* if (!result.error) return result;
|
|
326
|
-
* }
|
|
319
|
+
* context.request.headers['Authorization'] = `Bearer ${getToken()}`;
|
|
327
320
|
* return next();
|
|
328
321
|
* }
|
|
329
322
|
* ```
|
|
@@ -379,7 +372,7 @@ type PluginTypeConfig = {
|
|
|
379
372
|
* Base interface for Spoosh plugins.
|
|
380
373
|
*
|
|
381
374
|
* Plugins can implement:
|
|
382
|
-
* - `middleware`: Wraps the fetch flow for full control (intercept,
|
|
375
|
+
* - `middleware`: Wraps the fetch flow for full control (intercept, transform, modify)
|
|
383
376
|
* - `afterResponse`: Called after every response, regardless of early returns
|
|
384
377
|
* - `lifecycle`: Component lifecycle hooks (onMount, onUpdate, onUnmount)
|
|
385
378
|
* - `exports`: Functions/variables accessible to other plugins
|
|
@@ -427,7 +420,7 @@ interface SpooshPlugin<T extends PluginTypeConfig = PluginTypeConfig> {
|
|
|
427
420
|
/** Expose functions/variables for other plugins to access via `context.plugins.get(name)` */
|
|
428
421
|
exports?: (context: PluginContext) => object;
|
|
429
422
|
/**
|
|
430
|
-
* Expose functions/properties on the framework adapter return value (e.g.,
|
|
423
|
+
* Expose functions/properties on the framework adapter return value (e.g., create).
|
|
431
424
|
* Unlike `exports`, these are accessible directly from the instance, not just within plugin context.
|
|
432
425
|
*
|
|
433
426
|
* @example
|
|
@@ -441,7 +434,37 @@ interface SpooshPlugin<T extends PluginTypeConfig = PluginTypeConfig> {
|
|
|
441
434
|
instanceApi?: (context: InstanceApiContext) => T extends {
|
|
442
435
|
instanceApi: infer A;
|
|
443
436
|
} ? A : object;
|
|
444
|
-
/**
|
|
437
|
+
/**
|
|
438
|
+
* Plugin execution priority. Lower numbers run first, higher numbers run last.
|
|
439
|
+
* Default: 0
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```ts
|
|
443
|
+
* // Cache plugin runs early (checks cache before other plugins)
|
|
444
|
+
* { name: "cache", priority: -10, ... }
|
|
445
|
+
*
|
|
446
|
+
* // Throttle plugin runs last (blocks all requests including force fetches)
|
|
447
|
+
* { name: "throttle", priority: 100, ... }
|
|
448
|
+
*
|
|
449
|
+
* // Most plugins use default priority
|
|
450
|
+
* { name: "retry", priority: 0, ... } // or omit priority
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
priority?: number;
|
|
454
|
+
/**
|
|
455
|
+
* List of plugin names that this plugin depends on.
|
|
456
|
+
* Used to validate that required plugins are registered.
|
|
457
|
+
* Does not affect execution order - use `priority` for that.
|
|
458
|
+
*
|
|
459
|
+
* @example
|
|
460
|
+
* ```ts
|
|
461
|
+
* {
|
|
462
|
+
* name: "spoosh:optimistic",
|
|
463
|
+
* dependencies: ["spoosh:invalidation"],
|
|
464
|
+
* // ...
|
|
465
|
+
* }
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
445
468
|
dependencies?: string[];
|
|
446
469
|
/** @internal Type carrier for inference - do not use directly */
|
|
447
470
|
readonly _types?: T;
|
|
@@ -1132,8 +1155,8 @@ type SpooshInstance<TSchema = unknown, TDefaultError = unknown, TPlugins extends
|
|
|
1132
1155
|
*
|
|
1133
1156
|
* @example Basic usage
|
|
1134
1157
|
* ```ts
|
|
1135
|
-
* const
|
|
1136
|
-
* .use([cachePlugin(),
|
|
1158
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api')
|
|
1159
|
+
* .use([cachePlugin(), debugPlugin()]);
|
|
1137
1160
|
*
|
|
1138
1161
|
* const { api } = client;
|
|
1139
1162
|
* const response = await api("posts").GET();
|
|
@@ -1141,19 +1164,19 @@ type SpooshInstance<TSchema = unknown, TDefaultError = unknown, TPlugins extends
|
|
|
1141
1164
|
*
|
|
1142
1165
|
* @example With default options
|
|
1143
1166
|
* ```ts
|
|
1144
|
-
* const
|
|
1167
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
1145
1168
|
* headers: { 'Authorization': 'Bearer token' }
|
|
1146
1169
|
* }).use([cachePlugin()]);
|
|
1147
1170
|
* ```
|
|
1148
1171
|
*
|
|
1149
1172
|
* @example With React hooks
|
|
1150
1173
|
* ```ts
|
|
1151
|
-
* import {
|
|
1174
|
+
* import { create } from '@spoosh/react';
|
|
1152
1175
|
*
|
|
1153
|
-
* const
|
|
1154
|
-
* .use([cachePlugin(),
|
|
1176
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api')
|
|
1177
|
+
* .use([cachePlugin(), prefetchPlugin()]);
|
|
1155
1178
|
*
|
|
1156
|
-
* const { useRead, useWrite } =
|
|
1179
|
+
* const { useRead, useWrite } = create(client);
|
|
1157
1180
|
*
|
|
1158
1181
|
* // In component
|
|
1159
1182
|
* const { data } = useRead((api) => api("posts").GET());
|
|
@@ -1176,15 +1199,15 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1176
1199
|
* @example
|
|
1177
1200
|
* ```ts
|
|
1178
1201
|
* // Simple usage
|
|
1179
|
-
* const
|
|
1202
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api');
|
|
1180
1203
|
*
|
|
1181
1204
|
* // With default headers
|
|
1182
|
-
* const
|
|
1205
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
1183
1206
|
* headers: { 'X-API-Key': 'secret' }
|
|
1184
1207
|
* });
|
|
1185
1208
|
*
|
|
1186
1209
|
* // With XHR transport (narrows available options to XHR-compatible fields)
|
|
1187
|
-
* const
|
|
1210
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
1188
1211
|
* transport: 'xhr',
|
|
1189
1212
|
* credentials: 'include',
|
|
1190
1213
|
* });
|
|
@@ -1201,26 +1224,10 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1201
1224
|
* @param plugins - Array of plugin instances to use
|
|
1202
1225
|
* @returns A new Spoosh instance with the specified plugins
|
|
1203
1226
|
*
|
|
1204
|
-
* @example Single use() call
|
|
1205
|
-
* ```ts
|
|
1206
|
-
* const client = new Spoosh<Schema, Error>('/api')
|
|
1207
|
-
* .use([cachePlugin(), retryPlugin(), debouncePlugin()]);
|
|
1208
|
-
* ```
|
|
1209
|
-
*
|
|
1210
|
-
* @example Chaining use() calls (replaces plugins)
|
|
1211
|
-
* ```ts
|
|
1212
|
-
* const client1 = new Spoosh<Schema, Error>('/api')
|
|
1213
|
-
* .use([cachePlugin()]);
|
|
1214
|
-
*
|
|
1215
|
-
* // This replaces cachePlugin with retryPlugin
|
|
1216
|
-
* const client2 = client1.use([retryPlugin()]);
|
|
1217
|
-
* ```
|
|
1218
|
-
*
|
|
1219
|
-
* @example With plugin configuration
|
|
1220
1227
|
* ```ts
|
|
1221
|
-
* const
|
|
1228
|
+
* const spoosh = new Spoosh<Schema, Error>('/api').use([
|
|
1222
1229
|
* cachePlugin({ staleTime: 5000 }),
|
|
1223
|
-
*
|
|
1230
|
+
* invalidationPlugin(),
|
|
1224
1231
|
* prefetchPlugin(),
|
|
1225
1232
|
* ]);
|
|
1226
1233
|
* ```
|
|
@@ -1245,7 +1252,7 @@ declare class Spoosh<TSchema = unknown, TError = unknown, TPlugins extends Plugi
|
|
|
1245
1252
|
*
|
|
1246
1253
|
* @example
|
|
1247
1254
|
* ```ts
|
|
1248
|
-
* const
|
|
1255
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api').use([...]);
|
|
1249
1256
|
* const { api } = client;
|
|
1250
1257
|
*
|
|
1251
1258
|
* // GET request
|
|
@@ -1436,6 +1443,9 @@ type TagOptions = {
|
|
|
1436
1443
|
declare function resolveTags(options: TagOptions | undefined, resolvedPath: string[]): string[];
|
|
1437
1444
|
declare function resolvePath(path: string[], params: Record<string, string | number> | undefined): string[];
|
|
1438
1445
|
|
|
1446
|
+
declare const isNetworkError: (err: unknown) => boolean;
|
|
1447
|
+
declare const isAbortError: (err: unknown) => boolean;
|
|
1448
|
+
|
|
1439
1449
|
type ProxyHandlerConfig<TOptions = SpooshOptions> = {
|
|
1440
1450
|
baseUrl: string;
|
|
1441
1451
|
defaultOptions: TOptions;
|
|
@@ -1670,4 +1680,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
|
|
|
1670
1680
|
};
|
|
1671
1681
|
declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
|
|
1672
1682
|
|
|
1673
|
-
export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginRequestOptions, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type
|
|
1683
|
+
export { type AnyRequestOptions, type ApiSchema, type BuiltInEvents, type CacheEntry, type CacheEntryWithKey, type CapturedCall, type ComputeRequestOptions, type CoreRequestOptionsBase, type CreateInfiniteReadOptions, type CreateOperationOptions, type DataAwareCallback, type DataAwareTransform, type EventEmitter, type ExtractBody$1 as ExtractBody, type ExtractData, type ExtractError, type ExtractMethodOptions, type ExtractParamNames, type ExtractQuery$1 as ExtractQuery, type FetchDirection, type FetchExecutor, type FindMatchingKey, HTTP_METHODS, type HasParams, type HasReadMethod, type HasWriteMethod, type HeadersInitOrGetter, type HttpMethod, type HttpMethodKey, type InfiniteReadController, type InfiniteReadState, type InfiniteRequestOptions, type InstanceApiContext, type InstanceApiResolvers, type InstancePluginExecutor, type LifecyclePhase, type MergePluginInstanceApi, type MergePluginOptions, type MergePluginResults, type MethodOptionsMap, type OperationController, type OperationState, type OperationType, type PageContext, type PluginAccessor, type PluginArray, type PluginContext, type PluginContextInput, type PluginExecutor, type PluginExportsRegistry, type PluginFactory, type PluginHandler, type PluginLifecycle, type PluginMiddleware, type PluginRegistry, type PluginRequestOptions, type PluginResolvers, type PluginResponseHandler, type PluginResultResolvers, type PluginTypeConfig, type PluginUpdateHandler, type ReadClient, type ReadPaths, type ReadSchemaHelper, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, type Simplify, Spoosh, type SpooshBody, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshOptions, type SpooshOptionsInput, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type StripPrefix, type TagMode, type TagOptions, type Transport, type TransportOption, type TransportOptionsMap, type TransportResponse, type WriteClient, type WriteMethod, type WritePaths, type WriteSchemaHelper, __DEV__, buildUrl, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, fetchTransport, form, generateTags, getContentType, isAbortError, isJsonBody, isNetworkError, isSpooshBody, json, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveRequestBody, resolveTags, setHeaders, sortObjectKeys, urlencoded, xhrTransport };
|
package/dist/index.js
CHANGED
|
@@ -42,7 +42,9 @@ __export(src_exports, {
|
|
|
42
42
|
form: () => form,
|
|
43
43
|
generateTags: () => generateTags,
|
|
44
44
|
getContentType: () => getContentType,
|
|
45
|
+
isAbortError: () => isAbortError,
|
|
45
46
|
isJsonBody: () => isJsonBody,
|
|
47
|
+
isNetworkError: () => isNetworkError,
|
|
46
48
|
isSpooshBody: () => isSpooshBody,
|
|
47
49
|
json: () => json,
|
|
48
50
|
mergeHeaders: () => mergeHeaders,
|
|
@@ -352,6 +354,10 @@ function resolvePath(path, params) {
|
|
|
352
354
|
});
|
|
353
355
|
}
|
|
354
356
|
|
|
357
|
+
// src/utils/errors.ts
|
|
358
|
+
var isNetworkError = (err) => err instanceof TypeError;
|
|
359
|
+
var isAbortError = (err) => err instanceof DOMException && err.name === "AbortError";
|
|
360
|
+
|
|
355
361
|
// src/transport/fetch.ts
|
|
356
362
|
var fetchTransport = async (url, init) => {
|
|
357
363
|
const res = await fetch(url, init);
|
|
@@ -434,9 +440,6 @@ var xhrTransport = (url, init, options) => {
|
|
|
434
440
|
};
|
|
435
441
|
|
|
436
442
|
// src/fetch.ts
|
|
437
|
-
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
438
|
-
var isNetworkError = (err) => err instanceof TypeError;
|
|
439
|
-
var isAbortError = (err) => err instanceof DOMException && err.name === "AbortError";
|
|
440
443
|
async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions, nextTags) {
|
|
441
444
|
return executeCoreFetch({
|
|
442
445
|
baseUrl,
|
|
@@ -486,9 +489,6 @@ async function executeCoreFetch(config) {
|
|
|
486
489
|
...fetchDefaults
|
|
487
490
|
} = defaultOptions;
|
|
488
491
|
const inputFields = buildInputFields(requestOptions);
|
|
489
|
-
const maxRetries = requestOptions?.retries ?? 3;
|
|
490
|
-
const baseDelay = requestOptions?.retryDelay ?? 1e3;
|
|
491
|
-
const retryCount = maxRetries === false ? 0 : maxRetries;
|
|
492
492
|
const finalPath = path;
|
|
493
493
|
const url = buildUrl(baseUrl, finalPath, requestOptions?.query);
|
|
494
494
|
let headers = await mergeHeaders(defaultHeaders, requestOptions?.headers);
|
|
@@ -529,50 +529,40 @@ async function executeCoreFetch(config) {
|
|
|
529
529
|
const resolvedTransport = resolveTransport(
|
|
530
530
|
requestOptions?.transport ?? defaultTransport
|
|
531
531
|
);
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
);
|
|
540
|
-
if (result.ok) {
|
|
541
|
-
return {
|
|
542
|
-
status: result.status,
|
|
543
|
-
data: result.data,
|
|
544
|
-
headers: result.headers,
|
|
545
|
-
error: void 0,
|
|
546
|
-
...inputFields
|
|
547
|
-
};
|
|
548
|
-
}
|
|
532
|
+
try {
|
|
533
|
+
const result = await resolvedTransport(
|
|
534
|
+
url,
|
|
535
|
+
fetchInit,
|
|
536
|
+
requestOptions?.transportOptions
|
|
537
|
+
);
|
|
538
|
+
if (result.ok) {
|
|
549
539
|
return {
|
|
550
540
|
status: result.status,
|
|
551
|
-
|
|
541
|
+
data: result.data,
|
|
552
542
|
headers: result.headers,
|
|
543
|
+
error: void 0,
|
|
544
|
+
...inputFields
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
return {
|
|
548
|
+
status: result.status,
|
|
549
|
+
error: result.data,
|
|
550
|
+
headers: result.headers,
|
|
551
|
+
data: void 0,
|
|
552
|
+
...inputFields
|
|
553
|
+
};
|
|
554
|
+
} catch (err) {
|
|
555
|
+
if (isAbortError(err)) {
|
|
556
|
+
return {
|
|
557
|
+
status: 0,
|
|
558
|
+
error: err,
|
|
553
559
|
data: void 0,
|
|
560
|
+
aborted: true,
|
|
554
561
|
...inputFields
|
|
555
562
|
};
|
|
556
|
-
} catch (err) {
|
|
557
|
-
if (isAbortError(err)) {
|
|
558
|
-
return {
|
|
559
|
-
status: 0,
|
|
560
|
-
error: err,
|
|
561
|
-
data: void 0,
|
|
562
|
-
aborted: true,
|
|
563
|
-
...inputFields
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
lastError = err;
|
|
567
|
-
if (isNetworkError(err) && attempt < retryCount) {
|
|
568
|
-
const delayMs = baseDelay * Math.pow(2, attempt);
|
|
569
|
-
await delay(delayMs);
|
|
570
|
-
continue;
|
|
571
|
-
}
|
|
572
|
-
return { status: 0, error: lastError, data: void 0, ...inputFields };
|
|
573
563
|
}
|
|
564
|
+
return { status: 0, error: err, data: void 0, ...inputFields };
|
|
574
565
|
}
|
|
575
|
-
return { status: 0, error: lastError, data: void 0, ...inputFields };
|
|
576
566
|
}
|
|
577
567
|
|
|
578
568
|
// src/proxy/handler.ts
|
|
@@ -865,46 +855,29 @@ function createEventEmitter() {
|
|
|
865
855
|
|
|
866
856
|
// src/plugins/executor.ts
|
|
867
857
|
function validateDependencies(plugins) {
|
|
868
|
-
const
|
|
858
|
+
const pluginNames = new Set(plugins.map((p) => p.name));
|
|
869
859
|
for (const plugin of plugins) {
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
860
|
+
if (plugin.dependencies) {
|
|
861
|
+
for (const dep of plugin.dependencies) {
|
|
862
|
+
if (!pluginNames.has(dep)) {
|
|
863
|
+
throw new Error(
|
|
864
|
+
`Plugin "${plugin.name}" depends on "${dep}", but "${dep}" is not registered.`
|
|
865
|
+
);
|
|
866
|
+
}
|
|
875
867
|
}
|
|
876
868
|
}
|
|
877
869
|
}
|
|
878
870
|
}
|
|
879
|
-
function
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
if (visited.has(plugin.name)) return;
|
|
886
|
-
if (visiting.has(plugin.name)) {
|
|
887
|
-
throw new Error(
|
|
888
|
-
`Circular dependency detected involving "${plugin.name}"`
|
|
889
|
-
);
|
|
890
|
-
}
|
|
891
|
-
visiting.add(plugin.name);
|
|
892
|
-
for (const dep of plugin.dependencies ?? []) {
|
|
893
|
-
const depPlugin = pluginMap.get(dep);
|
|
894
|
-
if (depPlugin) visit(depPlugin);
|
|
895
|
-
}
|
|
896
|
-
visiting.delete(plugin.name);
|
|
897
|
-
visited.add(plugin.name);
|
|
898
|
-
sorted.push(plugin);
|
|
899
|
-
}
|
|
900
|
-
for (const plugin of plugins) {
|
|
901
|
-
visit(plugin);
|
|
902
|
-
}
|
|
903
|
-
return sorted;
|
|
871
|
+
function sortByPriority(plugins) {
|
|
872
|
+
return [...plugins].sort((a, b) => {
|
|
873
|
+
const priorityA = a.priority ?? 0;
|
|
874
|
+
const priorityB = b.priority ?? 0;
|
|
875
|
+
return priorityA - priorityB;
|
|
876
|
+
});
|
|
904
877
|
}
|
|
905
878
|
function createPluginExecutor(initialPlugins = []) {
|
|
906
879
|
validateDependencies(initialPlugins);
|
|
907
|
-
const plugins =
|
|
880
|
+
const plugins = sortByPriority(initialPlugins);
|
|
908
881
|
const frozenPlugins = Object.freeze([...plugins]);
|
|
909
882
|
const createPluginAccessor = (context) => ({
|
|
910
883
|
get(name) {
|
|
@@ -1007,15 +980,15 @@ var Spoosh = class _Spoosh {
|
|
|
1007
980
|
* @example
|
|
1008
981
|
* ```ts
|
|
1009
982
|
* // Simple usage
|
|
1010
|
-
* const
|
|
983
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api');
|
|
1011
984
|
*
|
|
1012
985
|
* // With default headers
|
|
1013
|
-
* const
|
|
986
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
1014
987
|
* headers: { 'X-API-Key': 'secret' }
|
|
1015
988
|
* });
|
|
1016
989
|
*
|
|
1017
990
|
* // With XHR transport (narrows available options to XHR-compatible fields)
|
|
1018
|
-
* const
|
|
991
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
1019
992
|
* transport: 'xhr',
|
|
1020
993
|
* credentials: 'include',
|
|
1021
994
|
* });
|
|
@@ -1036,26 +1009,10 @@ var Spoosh = class _Spoosh {
|
|
|
1036
1009
|
* @param plugins - Array of plugin instances to use
|
|
1037
1010
|
* @returns A new Spoosh instance with the specified plugins
|
|
1038
1011
|
*
|
|
1039
|
-
* @example Single use() call
|
|
1040
|
-
* ```ts
|
|
1041
|
-
* const client = new Spoosh<Schema, Error>('/api')
|
|
1042
|
-
* .use([cachePlugin(), retryPlugin(), debouncePlugin()]);
|
|
1043
|
-
* ```
|
|
1044
|
-
*
|
|
1045
|
-
* @example Chaining use() calls (replaces plugins)
|
|
1046
|
-
* ```ts
|
|
1047
|
-
* const client1 = new Spoosh<Schema, Error>('/api')
|
|
1048
|
-
* .use([cachePlugin()]);
|
|
1049
|
-
*
|
|
1050
|
-
* // This replaces cachePlugin with retryPlugin
|
|
1051
|
-
* const client2 = client1.use([retryPlugin()]);
|
|
1052
|
-
* ```
|
|
1053
|
-
*
|
|
1054
|
-
* @example With plugin configuration
|
|
1055
1012
|
* ```ts
|
|
1056
|
-
* const
|
|
1013
|
+
* const spoosh = new Spoosh<Schema, Error>('/api').use([
|
|
1057
1014
|
* cachePlugin({ staleTime: 5000 }),
|
|
1058
|
-
*
|
|
1015
|
+
* invalidationPlugin(),
|
|
1059
1016
|
* prefetchPlugin(),
|
|
1060
1017
|
* ]);
|
|
1061
1018
|
* ```
|
|
@@ -1112,7 +1069,7 @@ var Spoosh = class _Spoosh {
|
|
|
1112
1069
|
*
|
|
1113
1070
|
* @example
|
|
1114
1071
|
* ```ts
|
|
1115
|
-
* const
|
|
1072
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api').use([...]);
|
|
1116
1073
|
* const { api } = client;
|
|
1117
1074
|
*
|
|
1118
1075
|
* // GET request
|
package/dist/index.mjs
CHANGED
|
@@ -291,6 +291,10 @@ function resolvePath(path, params) {
|
|
|
291
291
|
});
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
// src/utils/errors.ts
|
|
295
|
+
var isNetworkError = (err) => err instanceof TypeError;
|
|
296
|
+
var isAbortError = (err) => err instanceof DOMException && err.name === "AbortError";
|
|
297
|
+
|
|
294
298
|
// src/transport/fetch.ts
|
|
295
299
|
var fetchTransport = async (url, init) => {
|
|
296
300
|
const res = await fetch(url, init);
|
|
@@ -373,9 +377,6 @@ var xhrTransport = (url, init, options) => {
|
|
|
373
377
|
};
|
|
374
378
|
|
|
375
379
|
// src/fetch.ts
|
|
376
|
-
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
377
|
-
var isNetworkError = (err) => err instanceof TypeError;
|
|
378
|
-
var isAbortError = (err) => err instanceof DOMException && err.name === "AbortError";
|
|
379
380
|
async function executeFetch(baseUrl, path, method, defaultOptions, requestOptions, nextTags) {
|
|
380
381
|
return executeCoreFetch({
|
|
381
382
|
baseUrl,
|
|
@@ -425,9 +426,6 @@ async function executeCoreFetch(config) {
|
|
|
425
426
|
...fetchDefaults
|
|
426
427
|
} = defaultOptions;
|
|
427
428
|
const inputFields = buildInputFields(requestOptions);
|
|
428
|
-
const maxRetries = requestOptions?.retries ?? 3;
|
|
429
|
-
const baseDelay = requestOptions?.retryDelay ?? 1e3;
|
|
430
|
-
const retryCount = maxRetries === false ? 0 : maxRetries;
|
|
431
429
|
const finalPath = path;
|
|
432
430
|
const url = buildUrl(baseUrl, finalPath, requestOptions?.query);
|
|
433
431
|
let headers = await mergeHeaders(defaultHeaders, requestOptions?.headers);
|
|
@@ -468,50 +466,40 @@ async function executeCoreFetch(config) {
|
|
|
468
466
|
const resolvedTransport = resolveTransport(
|
|
469
467
|
requestOptions?.transport ?? defaultTransport
|
|
470
468
|
);
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
);
|
|
479
|
-
if (result.ok) {
|
|
480
|
-
return {
|
|
481
|
-
status: result.status,
|
|
482
|
-
data: result.data,
|
|
483
|
-
headers: result.headers,
|
|
484
|
-
error: void 0,
|
|
485
|
-
...inputFields
|
|
486
|
-
};
|
|
487
|
-
}
|
|
469
|
+
try {
|
|
470
|
+
const result = await resolvedTransport(
|
|
471
|
+
url,
|
|
472
|
+
fetchInit,
|
|
473
|
+
requestOptions?.transportOptions
|
|
474
|
+
);
|
|
475
|
+
if (result.ok) {
|
|
488
476
|
return {
|
|
489
477
|
status: result.status,
|
|
490
|
-
|
|
478
|
+
data: result.data,
|
|
491
479
|
headers: result.headers,
|
|
480
|
+
error: void 0,
|
|
481
|
+
...inputFields
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
status: result.status,
|
|
486
|
+
error: result.data,
|
|
487
|
+
headers: result.headers,
|
|
488
|
+
data: void 0,
|
|
489
|
+
...inputFields
|
|
490
|
+
};
|
|
491
|
+
} catch (err) {
|
|
492
|
+
if (isAbortError(err)) {
|
|
493
|
+
return {
|
|
494
|
+
status: 0,
|
|
495
|
+
error: err,
|
|
492
496
|
data: void 0,
|
|
497
|
+
aborted: true,
|
|
493
498
|
...inputFields
|
|
494
499
|
};
|
|
495
|
-
} catch (err) {
|
|
496
|
-
if (isAbortError(err)) {
|
|
497
|
-
return {
|
|
498
|
-
status: 0,
|
|
499
|
-
error: err,
|
|
500
|
-
data: void 0,
|
|
501
|
-
aborted: true,
|
|
502
|
-
...inputFields
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
lastError = err;
|
|
506
|
-
if (isNetworkError(err) && attempt < retryCount) {
|
|
507
|
-
const delayMs = baseDelay * Math.pow(2, attempt);
|
|
508
|
-
await delay(delayMs);
|
|
509
|
-
continue;
|
|
510
|
-
}
|
|
511
|
-
return { status: 0, error: lastError, data: void 0, ...inputFields };
|
|
512
500
|
}
|
|
501
|
+
return { status: 0, error: err, data: void 0, ...inputFields };
|
|
513
502
|
}
|
|
514
|
-
return { status: 0, error: lastError, data: void 0, ...inputFields };
|
|
515
503
|
}
|
|
516
504
|
|
|
517
505
|
// src/proxy/handler.ts
|
|
@@ -804,46 +792,29 @@ function createEventEmitter() {
|
|
|
804
792
|
|
|
805
793
|
// src/plugins/executor.ts
|
|
806
794
|
function validateDependencies(plugins) {
|
|
807
|
-
const
|
|
795
|
+
const pluginNames = new Set(plugins.map((p) => p.name));
|
|
808
796
|
for (const plugin of plugins) {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
797
|
+
if (plugin.dependencies) {
|
|
798
|
+
for (const dep of plugin.dependencies) {
|
|
799
|
+
if (!pluginNames.has(dep)) {
|
|
800
|
+
throw new Error(
|
|
801
|
+
`Plugin "${plugin.name}" depends on "${dep}", but "${dep}" is not registered.`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
814
804
|
}
|
|
815
805
|
}
|
|
816
806
|
}
|
|
817
807
|
}
|
|
818
|
-
function
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
if (visited.has(plugin.name)) return;
|
|
825
|
-
if (visiting.has(plugin.name)) {
|
|
826
|
-
throw new Error(
|
|
827
|
-
`Circular dependency detected involving "${plugin.name}"`
|
|
828
|
-
);
|
|
829
|
-
}
|
|
830
|
-
visiting.add(plugin.name);
|
|
831
|
-
for (const dep of plugin.dependencies ?? []) {
|
|
832
|
-
const depPlugin = pluginMap.get(dep);
|
|
833
|
-
if (depPlugin) visit(depPlugin);
|
|
834
|
-
}
|
|
835
|
-
visiting.delete(plugin.name);
|
|
836
|
-
visited.add(plugin.name);
|
|
837
|
-
sorted.push(plugin);
|
|
838
|
-
}
|
|
839
|
-
for (const plugin of plugins) {
|
|
840
|
-
visit(plugin);
|
|
841
|
-
}
|
|
842
|
-
return sorted;
|
|
808
|
+
function sortByPriority(plugins) {
|
|
809
|
+
return [...plugins].sort((a, b) => {
|
|
810
|
+
const priorityA = a.priority ?? 0;
|
|
811
|
+
const priorityB = b.priority ?? 0;
|
|
812
|
+
return priorityA - priorityB;
|
|
813
|
+
});
|
|
843
814
|
}
|
|
844
815
|
function createPluginExecutor(initialPlugins = []) {
|
|
845
816
|
validateDependencies(initialPlugins);
|
|
846
|
-
const plugins =
|
|
817
|
+
const plugins = sortByPriority(initialPlugins);
|
|
847
818
|
const frozenPlugins = Object.freeze([...plugins]);
|
|
848
819
|
const createPluginAccessor = (context) => ({
|
|
849
820
|
get(name) {
|
|
@@ -946,15 +917,15 @@ var Spoosh = class _Spoosh {
|
|
|
946
917
|
* @example
|
|
947
918
|
* ```ts
|
|
948
919
|
* // Simple usage
|
|
949
|
-
* const
|
|
920
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api');
|
|
950
921
|
*
|
|
951
922
|
* // With default headers
|
|
952
|
-
* const
|
|
923
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
953
924
|
* headers: { 'X-API-Key': 'secret' }
|
|
954
925
|
* });
|
|
955
926
|
*
|
|
956
927
|
* // With XHR transport (narrows available options to XHR-compatible fields)
|
|
957
|
-
* const
|
|
928
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api', {
|
|
958
929
|
* transport: 'xhr',
|
|
959
930
|
* credentials: 'include',
|
|
960
931
|
* });
|
|
@@ -975,26 +946,10 @@ var Spoosh = class _Spoosh {
|
|
|
975
946
|
* @param plugins - Array of plugin instances to use
|
|
976
947
|
* @returns A new Spoosh instance with the specified plugins
|
|
977
948
|
*
|
|
978
|
-
* @example Single use() call
|
|
979
|
-
* ```ts
|
|
980
|
-
* const client = new Spoosh<Schema, Error>('/api')
|
|
981
|
-
* .use([cachePlugin(), retryPlugin(), debouncePlugin()]);
|
|
982
|
-
* ```
|
|
983
|
-
*
|
|
984
|
-
* @example Chaining use() calls (replaces plugins)
|
|
985
|
-
* ```ts
|
|
986
|
-
* const client1 = new Spoosh<Schema, Error>('/api')
|
|
987
|
-
* .use([cachePlugin()]);
|
|
988
|
-
*
|
|
989
|
-
* // This replaces cachePlugin with retryPlugin
|
|
990
|
-
* const client2 = client1.use([retryPlugin()]);
|
|
991
|
-
* ```
|
|
992
|
-
*
|
|
993
|
-
* @example With plugin configuration
|
|
994
949
|
* ```ts
|
|
995
|
-
* const
|
|
950
|
+
* const spoosh = new Spoosh<Schema, Error>('/api').use([
|
|
996
951
|
* cachePlugin({ staleTime: 5000 }),
|
|
997
|
-
*
|
|
952
|
+
* invalidationPlugin(),
|
|
998
953
|
* prefetchPlugin(),
|
|
999
954
|
* ]);
|
|
1000
955
|
* ```
|
|
@@ -1051,7 +1006,7 @@ var Spoosh = class _Spoosh {
|
|
|
1051
1006
|
*
|
|
1052
1007
|
* @example
|
|
1053
1008
|
* ```ts
|
|
1054
|
-
* const
|
|
1009
|
+
* const spoosh = new Spoosh<ApiSchema, Error>('/api').use([...]);
|
|
1055
1010
|
* const { api } = client;
|
|
1056
1011
|
*
|
|
1057
1012
|
* // GET request
|
|
@@ -1733,7 +1688,9 @@ export {
|
|
|
1733
1688
|
form,
|
|
1734
1689
|
generateTags,
|
|
1735
1690
|
getContentType,
|
|
1691
|
+
isAbortError,
|
|
1736
1692
|
isJsonBody,
|
|
1693
|
+
isNetworkError,
|
|
1737
1694
|
isSpooshBody,
|
|
1738
1695
|
json,
|
|
1739
1696
|
mergeHeaders,
|