@spoosh/core 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -287,9 +287,10 @@ type PluginHandler<TData = unknown, TError = unknown> = (context: PluginContext<
287
287
  type PluginUpdateHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, previousContext: PluginContext<TData, TError>) => void | Promise<void>;
288
288
  /**
289
289
  * Handler called after every response, regardless of early returns from middleware.
290
- * Use this for post-response logic like scheduling polls or emitting events.
290
+ * Can return a new response to transform it, or void for side effects only.
291
+ * Returned responses are chained through plugins in order.
291
292
  */
292
- type PluginResponseHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, response: SpooshResponse<TData, TError>) => void | Promise<void>;
293
+ type PluginResponseHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, response: SpooshResponse<TData, TError>) => SpooshResponse<TData, TError> | void | Promise<SpooshResponse<TData, TError> | void>;
293
294
  type PluginLifecycle<TData = unknown, TError = unknown> = {
294
295
  /** Called on component mount */
295
296
  onMount?: PluginHandler<TData, TError>;
@@ -333,7 +334,7 @@ type PluginTypeConfig = {
333
334
  *
334
335
  * Plugins can implement:
335
336
  * - `middleware`: Wraps the fetch flow for full control (intercept, retry, transform)
336
- * - `onResponse`: Called after every response, regardless of early returns
337
+ * - `afterResponse`: Called after every response, regardless of early returns
337
338
  * - `lifecycle`: Component lifecycle hooks (onMount, onUpdate, onUnmount)
338
339
  * - `exports`: Functions/variables accessible to other plugins
339
340
  *
@@ -353,7 +354,7 @@ type PluginTypeConfig = {
353
354
  * const result = await next();
354
355
  * return result;
355
356
  * },
356
- * onResponse(context, response) {
357
+ * afterResponse(context, response) {
357
358
  * // Always runs after response
358
359
  * },
359
360
  * lifecycle: {
@@ -370,8 +371,11 @@ interface SpooshPlugin<T extends PluginTypeConfig = PluginTypeConfig> {
370
371
  operations: OperationType[];
371
372
  /** Middleware for controlling the fetch flow. Called in plugin order, composing a chain. */
372
373
  middleware?: PluginMiddleware;
373
- /** Called after every response, regardless of early returns from middleware. */
374
- onResponse?: PluginResponseHandler;
374
+ /**
375
+ * Called after middleware chain completes, regardless of early returns.
376
+ * Return a new response to transform it, or void for side effects only.
377
+ */
378
+ afterResponse?: PluginResponseHandler;
375
379
  /** Component lifecycle hooks (setup, cleanup, option changes) */
376
380
  lifecycle?: PluginLifecycle;
377
381
  /** Expose functions/variables for other plugins to access via `context.plugins.get(name)` */
@@ -568,7 +572,7 @@ type PluginExecutor = {
568
572
  executeLifecycle: <TData, TError>(phase: "onMount" | "onUnmount", operationType: OperationType, context: PluginContext<TData, TError>) => Promise<void>;
569
573
  /** Execute onUpdate lifecycle with previous context */
570
574
  executeUpdateLifecycle: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, previousContext: PluginContext<TData, TError>) => Promise<void>;
571
- /** Execute middleware chain with a core fetch function, then run onResponse handlers */
575
+ /** Execute middleware chain with a core fetch function, then run afterResponse handlers */
572
576
  executeMiddleware: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, coreFetch: () => Promise<SpooshResponse<TData, TError>>) => Promise<SpooshResponse<TData, TError>>;
573
577
  getPlugins: () => readonly SpooshPlugin[];
574
578
  /** Creates a full PluginContext with plugins accessor injected */
@@ -1360,14 +1364,21 @@ declare function objectToUrlEncoded(obj: Record<string, unknown>): string;
1360
1364
 
1361
1365
  declare function sortObjectKeys(obj: unknown, seen?: WeakSet<object>): unknown;
1362
1366
 
1367
+ type TagMode = "all" | "self" | "none";
1368
+ type TagModeInArray = "all" | "self";
1363
1369
  /**
1364
1370
  * Common tag options used across plugins and operations.
1365
1371
  */
1366
1372
  type TagOptions = {
1367
- /** Custom tags to use instead of auto-generated path-based tags */
1368
- tags?: string[];
1369
- /** Additional tags to append to auto-generated or custom tags */
1370
- additionalTags?: string[];
1373
+ /**
1374
+ * Unified tag option (follows invalidation pattern)
1375
+ * - String: mode only ('all' | 'self' | 'none')
1376
+ * - Array: custom tags only OR [mode keyword mixed with custom tags]
1377
+ * - If array contains 'all' or 'self', it's treated as mode + tags
1378
+ * - Otherwise, it's custom tags only (replaces auto-generated tags)
1379
+ * - 'none' keyword should NOT be used in arrays (use string 'none' instead)
1380
+ */
1381
+ tags?: TagMode | (TagModeInArray | (string & {}))[];
1371
1382
  };
1372
1383
  declare function resolveTags(options: TagOptions | undefined, resolvedPath: string[]): string[];
1373
1384
  declare function resolvePath(path: string[], params: Record<string, string | number> | undefined): string[];
@@ -1597,4 +1608,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
1597
1608
  };
1598
1609
  declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
1599
1610
 
1600
- 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 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 MutationSchemaHelper, 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 QuerySchemaHelper, type ReadClient, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type RouteToPath, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, Spoosh, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type TagOptions, type WriteClient, applyMiddlewares, buildUrl, composeMiddlewares, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createMiddleware, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, generateTags, getContentType, isJsonBody, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveTags, setHeaders, sortObjectKeys };
1611
+ 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 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 MutationSchemaHelper, 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 QuerySchemaHelper, type ReadClient, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type RouteToPath, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, Spoosh, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type TagMode, type TagOptions, type WriteClient, applyMiddlewares, buildUrl, composeMiddlewares, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createMiddleware, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, generateTags, getContentType, isJsonBody, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveTags, setHeaders, sortObjectKeys };
package/dist/index.d.ts CHANGED
@@ -287,9 +287,10 @@ type PluginHandler<TData = unknown, TError = unknown> = (context: PluginContext<
287
287
  type PluginUpdateHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, previousContext: PluginContext<TData, TError>) => void | Promise<void>;
288
288
  /**
289
289
  * Handler called after every response, regardless of early returns from middleware.
290
- * Use this for post-response logic like scheduling polls or emitting events.
290
+ * Can return a new response to transform it, or void for side effects only.
291
+ * Returned responses are chained through plugins in order.
291
292
  */
292
- type PluginResponseHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, response: SpooshResponse<TData, TError>) => void | Promise<void>;
293
+ type PluginResponseHandler<TData = unknown, TError = unknown> = (context: PluginContext<TData, TError>, response: SpooshResponse<TData, TError>) => SpooshResponse<TData, TError> | void | Promise<SpooshResponse<TData, TError> | void>;
293
294
  type PluginLifecycle<TData = unknown, TError = unknown> = {
294
295
  /** Called on component mount */
295
296
  onMount?: PluginHandler<TData, TError>;
@@ -333,7 +334,7 @@ type PluginTypeConfig = {
333
334
  *
334
335
  * Plugins can implement:
335
336
  * - `middleware`: Wraps the fetch flow for full control (intercept, retry, transform)
336
- * - `onResponse`: Called after every response, regardless of early returns
337
+ * - `afterResponse`: Called after every response, regardless of early returns
337
338
  * - `lifecycle`: Component lifecycle hooks (onMount, onUpdate, onUnmount)
338
339
  * - `exports`: Functions/variables accessible to other plugins
339
340
  *
@@ -353,7 +354,7 @@ type PluginTypeConfig = {
353
354
  * const result = await next();
354
355
  * return result;
355
356
  * },
356
- * onResponse(context, response) {
357
+ * afterResponse(context, response) {
357
358
  * // Always runs after response
358
359
  * },
359
360
  * lifecycle: {
@@ -370,8 +371,11 @@ interface SpooshPlugin<T extends PluginTypeConfig = PluginTypeConfig> {
370
371
  operations: OperationType[];
371
372
  /** Middleware for controlling the fetch flow. Called in plugin order, composing a chain. */
372
373
  middleware?: PluginMiddleware;
373
- /** Called after every response, regardless of early returns from middleware. */
374
- onResponse?: PluginResponseHandler;
374
+ /**
375
+ * Called after middleware chain completes, regardless of early returns.
376
+ * Return a new response to transform it, or void for side effects only.
377
+ */
378
+ afterResponse?: PluginResponseHandler;
375
379
  /** Component lifecycle hooks (setup, cleanup, option changes) */
376
380
  lifecycle?: PluginLifecycle;
377
381
  /** Expose functions/variables for other plugins to access via `context.plugins.get(name)` */
@@ -568,7 +572,7 @@ type PluginExecutor = {
568
572
  executeLifecycle: <TData, TError>(phase: "onMount" | "onUnmount", operationType: OperationType, context: PluginContext<TData, TError>) => Promise<void>;
569
573
  /** Execute onUpdate lifecycle with previous context */
570
574
  executeUpdateLifecycle: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, previousContext: PluginContext<TData, TError>) => Promise<void>;
571
- /** Execute middleware chain with a core fetch function, then run onResponse handlers */
575
+ /** Execute middleware chain with a core fetch function, then run afterResponse handlers */
572
576
  executeMiddleware: <TData, TError>(operationType: OperationType, context: PluginContext<TData, TError>, coreFetch: () => Promise<SpooshResponse<TData, TError>>) => Promise<SpooshResponse<TData, TError>>;
573
577
  getPlugins: () => readonly SpooshPlugin[];
574
578
  /** Creates a full PluginContext with plugins accessor injected */
@@ -1360,14 +1364,21 @@ declare function objectToUrlEncoded(obj: Record<string, unknown>): string;
1360
1364
 
1361
1365
  declare function sortObjectKeys(obj: unknown, seen?: WeakSet<object>): unknown;
1362
1366
 
1367
+ type TagMode = "all" | "self" | "none";
1368
+ type TagModeInArray = "all" | "self";
1363
1369
  /**
1364
1370
  * Common tag options used across plugins and operations.
1365
1371
  */
1366
1372
  type TagOptions = {
1367
- /** Custom tags to use instead of auto-generated path-based tags */
1368
- tags?: string[];
1369
- /** Additional tags to append to auto-generated or custom tags */
1370
- additionalTags?: string[];
1373
+ /**
1374
+ * Unified tag option (follows invalidation pattern)
1375
+ * - String: mode only ('all' | 'self' | 'none')
1376
+ * - Array: custom tags only OR [mode keyword mixed with custom tags]
1377
+ * - If array contains 'all' or 'self', it's treated as mode + tags
1378
+ * - Otherwise, it's custom tags only (replaces auto-generated tags)
1379
+ * - 'none' keyword should NOT be used in arrays (use string 'none' instead)
1380
+ */
1381
+ tags?: TagMode | (TagModeInArray | (string & {}))[];
1371
1382
  };
1372
1383
  declare function resolveTags(options: TagOptions | undefined, resolvedPath: string[]): string[];
1373
1384
  declare function resolvePath(path: string[], params: Record<string, string | number> | undefined): string[];
@@ -1597,4 +1608,4 @@ type CreateInfiniteReadOptions<TData, TItem, TError, TRequest> = {
1597
1608
  };
1598
1609
  declare function createInfiniteReadController<TData, TItem, TError, TRequest extends InfiniteRequestOptions = InfiniteRequestOptions>(options: CreateInfiniteReadOptions<TData, TItem, TError, TRequest>): InfiniteReadController<TData, TItem, TError>;
1599
1610
 
1600
- 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 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 MutationSchemaHelper, 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 QuerySchemaHelper, type ReadClient, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type RouteToPath, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, Spoosh, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type TagOptions, type WriteClient, applyMiddlewares, buildUrl, composeMiddlewares, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createMiddleware, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, generateTags, getContentType, isJsonBody, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveTags, setHeaders, sortObjectKeys };
1611
+ 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 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 MutationSchemaHelper, 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 QuerySchemaHelper, type ReadClient, type RefetchEvent, type RequestOptions$1 as RequestOptions, type ResolveInstanceApi, type ResolveResultTypes, type ResolveSchemaTypes, type ResolveTypes, type ResolverContext, type RetryConfig, type RouteToPath, type SchemaPaths, type SelectedEndpoint, type SelectorFunction, type SelectorResult, Spoosh, type SpooshClient, type SpooshConfig, type SpooshInstance, type SpooshMiddleware, type SpooshOptions, type SpooshOptionsExtra, type SpooshPlugin, type SpooshResponse, type SpooshSchema, type StateManager, type TagMode, type TagOptions, type WriteClient, applyMiddlewares, buildUrl, composeMiddlewares, containsFile, createClient, createEventEmitter, createInfiniteReadController, createInitialState, createMiddleware, createOperationController, createPluginExecutor, createPluginRegistry, createProxyHandler, createSelectorProxy, createStateManager, executeFetch, extractMethodFromSelector, extractPathFromSelector, generateTags, getContentType, isJsonBody, mergeHeaders, objectToFormData, objectToUrlEncoded, resolveHeadersToRecord, resolvePath, resolveTags, setHeaders, sortObjectKeys };
package/dist/index.js CHANGED
@@ -247,11 +247,40 @@ function sortObjectKeys(obj, seen = /* @__PURE__ */ new WeakSet()) {
247
247
  }
248
248
 
249
249
  // src/utils/path-utils.ts
250
+ function resolveTagMode(mode, path) {
251
+ switch (mode) {
252
+ case "all":
253
+ return generateTags(path);
254
+ case "self":
255
+ return [path.join("/")];
256
+ case "none":
257
+ return [];
258
+ }
259
+ }
250
260
  function resolveTags(options, resolvedPath) {
251
- const customTags = options?.tags;
252
- const additionalTags = options?.additionalTags ?? [];
253
- const baseTags = customTags ?? generateTags(resolvedPath);
254
- return [...baseTags, ...additionalTags];
261
+ const tagsOption = options?.tags;
262
+ if (!tagsOption) {
263
+ return generateTags(resolvedPath);
264
+ }
265
+ if (typeof tagsOption === "string") {
266
+ return resolveTagMode(tagsOption, resolvedPath);
267
+ }
268
+ if (Array.isArray(tagsOption)) {
269
+ const tags = [];
270
+ let mode = null;
271
+ for (const item of tagsOption) {
272
+ if (item === "all" || item === "self") {
273
+ mode = item;
274
+ } else if (typeof item === "string") {
275
+ tags.push(item);
276
+ }
277
+ }
278
+ if (mode) {
279
+ tags.push(...resolveTagMode(mode, resolvedPath));
280
+ }
281
+ return [...new Set(tags)];
282
+ }
283
+ return generateTags(resolvedPath);
255
284
  }
256
285
  function resolvePath(path, params) {
257
286
  if (!params) return path;
@@ -807,11 +836,14 @@ function createPluginExecutor(initialPlugins = []) {
807
836
  response = await chain();
808
837
  }
809
838
  for (const plugin of applicablePlugins) {
810
- if (plugin.onResponse) {
811
- await plugin.onResponse(
839
+ if (plugin.afterResponse) {
840
+ const newResponse = await plugin.afterResponse(
812
841
  context,
813
842
  response
814
843
  );
844
+ if (newResponse) {
845
+ response = newResponse;
846
+ }
815
847
  }
816
848
  }
817
849
  return response;
@@ -1146,13 +1178,6 @@ function createOperationController(options) {
1146
1178
  try {
1147
1179
  const response = await fetchFn(context.requestOptions);
1148
1180
  context.response = response;
1149
- if (response.data !== void 0 && !response.error) {
1150
- updateState({
1151
- data: response.data,
1152
- error: void 0,
1153
- timestamp: Date.now()
1154
- });
1155
- }
1156
1181
  return response;
1157
1182
  } catch (err) {
1158
1183
  const errorResponse = {
@@ -1170,11 +1195,19 @@ function createOperationController(options) {
1170
1195
  });
1171
1196
  return fetchPromise;
1172
1197
  };
1173
- return pluginExecutor.executeMiddleware(
1198
+ const finalResponse = await pluginExecutor.executeMiddleware(
1174
1199
  operationType,
1175
1200
  context,
1176
1201
  coreFetch
1177
1202
  );
1203
+ if (finalResponse.data !== void 0 && !finalResponse.error) {
1204
+ updateState({
1205
+ data: finalResponse.data,
1206
+ error: void 0,
1207
+ timestamp: Date.now()
1208
+ });
1209
+ }
1210
+ return finalResponse;
1178
1211
  },
1179
1212
  getState() {
1180
1213
  const cached = stateManager.getCache(queryKey);
@@ -1433,34 +1466,6 @@ function createInfiniteReadController(options) {
1433
1466
  aborted: true
1434
1467
  };
1435
1468
  }
1436
- if (response.data !== void 0 && !response.error) {
1437
- pageRequests.set(pageKey, mergedRequest);
1438
- if (direction === "next") {
1439
- if (!pageKeys.includes(pageKey)) {
1440
- pageKeys = [...pageKeys, pageKey];
1441
- }
1442
- } else {
1443
- if (!pageKeys.includes(pageKey)) {
1444
- pageKeys = [pageKey, ...pageKeys];
1445
- }
1446
- }
1447
- saveToTracker();
1448
- subscribeToPages();
1449
- stateManager.setCache(pageKey, {
1450
- state: {
1451
- data: response.data,
1452
- error: void 0,
1453
- timestamp: Date.now()
1454
- },
1455
- tags,
1456
- stale: false
1457
- });
1458
- }
1459
- if (response.data !== void 0 && !response.error) {
1460
- latestError = void 0;
1461
- } else if (response.error) {
1462
- latestError = response.error;
1463
- }
1464
1469
  return response;
1465
1470
  } catch (err) {
1466
1471
  if (signal.aborted) {
@@ -1488,7 +1493,37 @@ function createInfiniteReadController(options) {
1488
1493
  stateManager.setPendingPromise(pageKey, fetchPromise);
1489
1494
  return fetchPromise;
1490
1495
  };
1491
- await pluginExecutor.executeMiddleware("infiniteRead", context, coreFetch);
1496
+ const finalResponse = await pluginExecutor.executeMiddleware(
1497
+ "infiniteRead",
1498
+ context,
1499
+ coreFetch
1500
+ );
1501
+ if (finalResponse.data !== void 0 && !finalResponse.error) {
1502
+ pageRequests.set(pageKey, mergedRequest);
1503
+ if (direction === "next") {
1504
+ if (!pageKeys.includes(pageKey)) {
1505
+ pageKeys = [...pageKeys, pageKey];
1506
+ }
1507
+ } else {
1508
+ if (!pageKeys.includes(pageKey)) {
1509
+ pageKeys = [pageKey, ...pageKeys];
1510
+ }
1511
+ }
1512
+ saveToTracker();
1513
+ subscribeToPages();
1514
+ stateManager.setCache(pageKey, {
1515
+ state: {
1516
+ data: finalResponse.data,
1517
+ error: void 0,
1518
+ timestamp: Date.now()
1519
+ },
1520
+ tags,
1521
+ stale: false
1522
+ });
1523
+ latestError = void 0;
1524
+ } else if (finalResponse.error) {
1525
+ latestError = finalResponse.error;
1526
+ }
1492
1527
  };
1493
1528
  const controller = {
1494
1529
  getState() {
package/dist/index.mjs CHANGED
@@ -191,11 +191,40 @@ function sortObjectKeys(obj, seen = /* @__PURE__ */ new WeakSet()) {
191
191
  }
192
192
 
193
193
  // src/utils/path-utils.ts
194
+ function resolveTagMode(mode, path) {
195
+ switch (mode) {
196
+ case "all":
197
+ return generateTags(path);
198
+ case "self":
199
+ return [path.join("/")];
200
+ case "none":
201
+ return [];
202
+ }
203
+ }
194
204
  function resolveTags(options, resolvedPath) {
195
- const customTags = options?.tags;
196
- const additionalTags = options?.additionalTags ?? [];
197
- const baseTags = customTags ?? generateTags(resolvedPath);
198
- return [...baseTags, ...additionalTags];
205
+ const tagsOption = options?.tags;
206
+ if (!tagsOption) {
207
+ return generateTags(resolvedPath);
208
+ }
209
+ if (typeof tagsOption === "string") {
210
+ return resolveTagMode(tagsOption, resolvedPath);
211
+ }
212
+ if (Array.isArray(tagsOption)) {
213
+ const tags = [];
214
+ let mode = null;
215
+ for (const item of tagsOption) {
216
+ if (item === "all" || item === "self") {
217
+ mode = item;
218
+ } else if (typeof item === "string") {
219
+ tags.push(item);
220
+ }
221
+ }
222
+ if (mode) {
223
+ tags.push(...resolveTagMode(mode, resolvedPath));
224
+ }
225
+ return [...new Set(tags)];
226
+ }
227
+ return generateTags(resolvedPath);
199
228
  }
200
229
  function resolvePath(path, params) {
201
230
  if (!params) return path;
@@ -751,11 +780,14 @@ function createPluginExecutor(initialPlugins = []) {
751
780
  response = await chain();
752
781
  }
753
782
  for (const plugin of applicablePlugins) {
754
- if (plugin.onResponse) {
755
- await plugin.onResponse(
783
+ if (plugin.afterResponse) {
784
+ const newResponse = await plugin.afterResponse(
756
785
  context,
757
786
  response
758
787
  );
788
+ if (newResponse) {
789
+ response = newResponse;
790
+ }
759
791
  }
760
792
  }
761
793
  return response;
@@ -1090,13 +1122,6 @@ function createOperationController(options) {
1090
1122
  try {
1091
1123
  const response = await fetchFn(context.requestOptions);
1092
1124
  context.response = response;
1093
- if (response.data !== void 0 && !response.error) {
1094
- updateState({
1095
- data: response.data,
1096
- error: void 0,
1097
- timestamp: Date.now()
1098
- });
1099
- }
1100
1125
  return response;
1101
1126
  } catch (err) {
1102
1127
  const errorResponse = {
@@ -1114,11 +1139,19 @@ function createOperationController(options) {
1114
1139
  });
1115
1140
  return fetchPromise;
1116
1141
  };
1117
- return pluginExecutor.executeMiddleware(
1142
+ const finalResponse = await pluginExecutor.executeMiddleware(
1118
1143
  operationType,
1119
1144
  context,
1120
1145
  coreFetch
1121
1146
  );
1147
+ if (finalResponse.data !== void 0 && !finalResponse.error) {
1148
+ updateState({
1149
+ data: finalResponse.data,
1150
+ error: void 0,
1151
+ timestamp: Date.now()
1152
+ });
1153
+ }
1154
+ return finalResponse;
1122
1155
  },
1123
1156
  getState() {
1124
1157
  const cached = stateManager.getCache(queryKey);
@@ -1377,34 +1410,6 @@ function createInfiniteReadController(options) {
1377
1410
  aborted: true
1378
1411
  };
1379
1412
  }
1380
- if (response.data !== void 0 && !response.error) {
1381
- pageRequests.set(pageKey, mergedRequest);
1382
- if (direction === "next") {
1383
- if (!pageKeys.includes(pageKey)) {
1384
- pageKeys = [...pageKeys, pageKey];
1385
- }
1386
- } else {
1387
- if (!pageKeys.includes(pageKey)) {
1388
- pageKeys = [pageKey, ...pageKeys];
1389
- }
1390
- }
1391
- saveToTracker();
1392
- subscribeToPages();
1393
- stateManager.setCache(pageKey, {
1394
- state: {
1395
- data: response.data,
1396
- error: void 0,
1397
- timestamp: Date.now()
1398
- },
1399
- tags,
1400
- stale: false
1401
- });
1402
- }
1403
- if (response.data !== void 0 && !response.error) {
1404
- latestError = void 0;
1405
- } else if (response.error) {
1406
- latestError = response.error;
1407
- }
1408
1413
  return response;
1409
1414
  } catch (err) {
1410
1415
  if (signal.aborted) {
@@ -1432,7 +1437,37 @@ function createInfiniteReadController(options) {
1432
1437
  stateManager.setPendingPromise(pageKey, fetchPromise);
1433
1438
  return fetchPromise;
1434
1439
  };
1435
- await pluginExecutor.executeMiddleware("infiniteRead", context, coreFetch);
1440
+ const finalResponse = await pluginExecutor.executeMiddleware(
1441
+ "infiniteRead",
1442
+ context,
1443
+ coreFetch
1444
+ );
1445
+ if (finalResponse.data !== void 0 && !finalResponse.error) {
1446
+ pageRequests.set(pageKey, mergedRequest);
1447
+ if (direction === "next") {
1448
+ if (!pageKeys.includes(pageKey)) {
1449
+ pageKeys = [...pageKeys, pageKey];
1450
+ }
1451
+ } else {
1452
+ if (!pageKeys.includes(pageKey)) {
1453
+ pageKeys = [pageKey, ...pageKeys];
1454
+ }
1455
+ }
1456
+ saveToTracker();
1457
+ subscribeToPages();
1458
+ stateManager.setCache(pageKey, {
1459
+ state: {
1460
+ data: finalResponse.data,
1461
+ error: void 0,
1462
+ timestamp: Date.now()
1463
+ },
1464
+ tags,
1465
+ stale: false
1466
+ });
1467
+ latestError = void 0;
1468
+ } else if (finalResponse.error) {
1469
+ latestError = finalResponse.error;
1470
+ }
1436
1471
  };
1437
1472
  const controller = {
1438
1473
  getState() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spoosh/core",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "description": "Type-safe API client with plugin middleware system",
6
6
  "keywords": [