@mmstack/resource 22.1.5 → 22.2.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/README.md +56 -11
- package/fesm2022/mmstack-resource.mjs +1097 -1013
- package/fesm2022/mmstack-resource.mjs.map +1 -1
- package/package.json +1 -1
- package/types/mmstack-resource.d.ts +88 -17
package/package.json
CHANGED
|
@@ -80,6 +80,8 @@ declare class Cache<T> {
|
|
|
80
80
|
private hydrated;
|
|
81
81
|
/** Keys invalidated while hydration was still in flight — must not be resurrected by it. */
|
|
82
82
|
private readonly hydrationTombstones;
|
|
83
|
+
/** Dev-only: ensures the "foreign keys, no matcher" hint in invalidateUrlPrefix fires at most once. */
|
|
84
|
+
private warnedForeignKeys;
|
|
83
85
|
private readonly hitCount;
|
|
84
86
|
private readonly missCount;
|
|
85
87
|
/**
|
|
@@ -189,6 +191,35 @@ declare class Cache<T> {
|
|
|
189
191
|
* cache.invalidatePrefix('GET https://api.example.com/posts');
|
|
190
192
|
*/
|
|
191
193
|
invalidatePrefix(prefix: string): number;
|
|
194
|
+
/**
|
|
195
|
+
* Invalidates every cache entry whose *request URL* starts with `urlPrefix`,
|
|
196
|
+
* regardless of HTTP method. This is the engine behind `mutationResource`'s
|
|
197
|
+
* `invalidates` option: `'/api/posts'` clears `/api/posts` with any query
|
|
198
|
+
* params, subpaths like `/api/posts/123`, and all `varyHeaders` variants —
|
|
199
|
+
* across GET/HEAD/OPTIONS/POST or any other cached method. Returns the number
|
|
200
|
+
* of entries removed.
|
|
201
|
+
*
|
|
202
|
+
* Unlike {@link invalidatePrefix} (which matches the raw key from its start),
|
|
203
|
+
* this extracts the URL field from the auto-generated key shape, so it is not
|
|
204
|
+
* fooled by the leading method token nor by a namespace a custom `cache.hash`
|
|
205
|
+
* prepends (e.g. `tenant:…`). Plain prefix matching still catches siblings
|
|
206
|
+
* sharing the prefix (`/api/posts-archive`) — pass `'/api/posts/'` to narrow.
|
|
207
|
+
*
|
|
208
|
+
* Keys produced by a custom `hash` that don't follow the auto shape won't be
|
|
209
|
+
* matched by the default; pass `match` to describe how a URL prefix maps onto
|
|
210
|
+
* your key format. In dev mode, if a default-matcher call removes nothing and
|
|
211
|
+
* every cached key is foreign-shaped, this logs a one-time hint pointing at the
|
|
212
|
+
* `match` escape hatch (a likely sign of a custom `hash` with no matcher wired up).
|
|
213
|
+
*
|
|
214
|
+
* @param urlPrefix - URL prefix to match.
|
|
215
|
+
* @param match - Optional custom matcher: given the prefix, returns a key predicate.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* cache.invalidateUrlPrefix('/api/posts');
|
|
219
|
+
* // custom key scheme:
|
|
220
|
+
* cache.invalidateUrlPrefix('/api/posts', (p) => (k) => k.includes(`|url=${p}`));
|
|
221
|
+
*/
|
|
222
|
+
invalidateUrlPrefix(urlPrefix: string, match?: (urlPrefix: string) => (key: string) => boolean): number;
|
|
192
223
|
/**
|
|
193
224
|
* Invalidates every cache entry whose key matches the predicate. Use for
|
|
194
225
|
* arbitrary bulk invalidation that doesn't fit prefix matching (e.g.
|
|
@@ -530,7 +561,9 @@ type HashableRequest = {
|
|
|
530
561
|
* Builds a stable cache/dedupe key from an HTTP request shape (accepts both
|
|
531
562
|
* `HttpRequest` and `HttpResourceRequest`).
|
|
532
563
|
*
|
|
533
|
-
* Key composition: `${method}
|
|
564
|
+
* Key composition: `${method}␟${url}␟${responseType}[␟${params}][␟${body}][␟${vary}]`,
|
|
565
|
+
* where `␟` is {@link KEY_DELIMITER} (ASCII Unit Separator) — a content-rare top-level
|
|
566
|
+
* separator. Sub-fields inside `params`/`vary` keep their own `&`/`=` delimiters.
|
|
534
567
|
* - `method` defaults to `'GET'`, `responseType` to `'json'` (Angular defaults).
|
|
535
568
|
* - Query params are sorted alphabetically and URL-encoded for stability.
|
|
536
569
|
* - Body hashing handles `File`/`Blob`/`FormData`/`URLSearchParams`/`ArrayBuffer`
|
|
@@ -1049,15 +1082,28 @@ declare function manualQueryResource<TResult, TRaw = TResult>(request: () => Htt
|
|
|
1049
1082
|
declare function manualQueryResource<TResult, TRaw = TResult>(request: () => HttpResourceRequest | string | undefined | void, options?: QueryResourceOptions<TResult, TRaw>): ManualQueryResourceRef<TResult | undefined>;
|
|
1050
1083
|
|
|
1051
1084
|
/**
|
|
1052
|
-
* @
|
|
1053
|
-
*
|
|
1085
|
+
* Why a {@link MutationResourceRef.mutateAsync} promise was cancelled — a closed
|
|
1086
|
+
* set so consumers can branch on the cause without parsing the message:
|
|
1087
|
+
* - `'superseded'`: a newer mutation replaced it (latest-wins).
|
|
1088
|
+
* - `'queue-cleared'`: dropped from the queue by `clearQueue()`.
|
|
1089
|
+
* - `'queue-key-changed'`: dropped from the queue by a reactive `key` change.
|
|
1090
|
+
* - `'destroyed'`: the resource was destroyed while it was pending/in flight.
|
|
1091
|
+
* - `'no-request'`: `request()` returned `undefined`, so nothing was sent.
|
|
1054
1092
|
*/
|
|
1055
|
-
type
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1093
|
+
type MutationCancellationReason = 'superseded' | 'queue-cleared' | 'queue-key-changed' | 'destroyed' | 'no-request';
|
|
1094
|
+
/**
|
|
1095
|
+
* Rejection reason for a {@link MutationResourceRef.mutateAsync} promise whose
|
|
1096
|
+
* mutation never completed. The {@link MutationCancelledError.type} discriminant
|
|
1097
|
+
* carries the cause ({@link MutationCancellationReason}); the message is a
|
|
1098
|
+
* human-readable elaboration of it.
|
|
1099
|
+
*
|
|
1100
|
+
* Only `mutateAsync` promises reject with this; plain `mutate()` calls have no
|
|
1101
|
+
* promise and so produce no (potentially unhandled) rejection.
|
|
1102
|
+
*/
|
|
1103
|
+
declare class MutationCancelledError extends Error {
|
|
1104
|
+
readonly type: MutationCancellationReason;
|
|
1105
|
+
constructor(type: MutationCancellationReason, message: string);
|
|
1106
|
+
}
|
|
1061
1107
|
/**
|
|
1062
1108
|
* Object form of the `queue` option. Enabling the queue serializes mutations
|
|
1063
1109
|
* into a FIFO that runs one-at-a-time.
|
|
@@ -1131,14 +1177,17 @@ type MutationResourceOptions<TResult, TRaw = TResult, TMutation = TResult, TCTX
|
|
|
1131
1177
|
* Cache entries to invalidate after a SUCCESSFUL mutation — the declarative
|
|
1132
1178
|
* alternative to calling `injectQueryCache().invalidatePrefix(...)` in `onSuccess`.
|
|
1133
1179
|
*
|
|
1134
|
-
* Each string is a URL prefix matched against
|
|
1135
|
-
*
|
|
1136
|
-
* plus subpaths like `/api/posts/123` — and all `varyHeaders`
|
|
1180
|
+
* Each string is a URL prefix matched against the request URL of every cached
|
|
1181
|
+
* entry, regardless of HTTP method: `'/api/posts'` invalidates `/api/posts` with
|
|
1182
|
+
* any query params, plus subpaths like `/api/posts/123` — and all `varyHeaders`
|
|
1183
|
+
* variants of each — across GET/HEAD/OPTIONS/POST or whatever methods you cache.
|
|
1137
1184
|
* Note that plain prefix matching also catches sibling paths sharing the prefix
|
|
1138
1185
|
* (`/api/posts-archive`); pass `'/api/posts/'` or the exact URL to narrow.
|
|
1139
1186
|
*
|
|
1140
|
-
*
|
|
1141
|
-
*
|
|
1187
|
+
* Keys built by a custom `cache.hash` that merely *prepends* a namespace (e.g. a
|
|
1188
|
+
* tenant/`sub` for per-user persistent caches) are still matched — the URL is
|
|
1189
|
+
* recovered structurally. Keys that abandon the auto shape entirely need an
|
|
1190
|
+
* a custom invalidateMatcher (or manual `injectQueryCache().invalidateWhere`).
|
|
1142
1191
|
*
|
|
1143
1192
|
* The function form receives the mutation result and the mutated value:
|
|
1144
1193
|
* ```ts
|
|
@@ -1146,6 +1195,11 @@ type MutationResourceOptions<TResult, TRaw = TResult, TMutation = TResult, TCTX
|
|
|
1146
1195
|
* ```
|
|
1147
1196
|
*/
|
|
1148
1197
|
invalidates?: string[] | ((value: NoInfer<TResult>, mutation: NoInfer<TMutation>) => string[]);
|
|
1198
|
+
/**
|
|
1199
|
+
* override for how {@link MutationResourceOptions.invalidates} URL
|
|
1200
|
+
* prefixes map onto cache keys — given a prefix, return a key predicate.
|
|
1201
|
+
*/
|
|
1202
|
+
invalidateMatcher?: (urlPrefix: string) => (key: string) => boolean;
|
|
1149
1203
|
equal?: ValueEqualityFn<TMutation>;
|
|
1150
1204
|
};
|
|
1151
1205
|
/**
|
|
@@ -1181,6 +1235,18 @@ type MutationResourceRef<TResult, TMutation = TResult, TICTX = void> = Omit<Quer
|
|
|
1181
1235
|
* @param ctx An optional initial context value that will be passed to the `onMutate` callback.
|
|
1182
1236
|
*/
|
|
1183
1237
|
mutate: (value: TMutation, ctx?: TICTX) => void;
|
|
1238
|
+
/**
|
|
1239
|
+
* Executes the mutation and returns a `Promise`
|
|
1240
|
+
*
|
|
1241
|
+
* If the mutation never completes — superseded by a newer `mutate`/`mutateAsync`
|
|
1242
|
+
* (latest-wins), dropped from the queue (`clearQueue` / queue `key` change),
|
|
1243
|
+
* abandoned on `destroy()`, or its `request()` returned `undefined` — the
|
|
1244
|
+
* promise rejects with a {@link MutationCancelledError}.
|
|
1245
|
+
*
|
|
1246
|
+
* @param value The mutation value (usually the request body).
|
|
1247
|
+
* @param ctx An optional initial context value that will be passed to the `onMutate` callback.
|
|
1248
|
+
*/
|
|
1249
|
+
mutateAsync: (value: TMutation, ctx?: TICTX) => Promise<TResult>;
|
|
1184
1250
|
/**
|
|
1185
1251
|
* A signal that holds the current mutation request, or `null` if no mutation is in progress.
|
|
1186
1252
|
* This can be useful for tracking the state of the mutation or for displaying loading indicators.
|
|
@@ -1241,7 +1307,12 @@ type MutationResourceRef<TResult, TMutation = TResult, TICTX = void> = Omit<Quer
|
|
|
1241
1307
|
* );
|
|
1242
1308
|
* ```
|
|
1243
1309
|
*/
|
|
1244
|
-
declare function mutationResource<TResult, TRaw = TResult, TMutation = TResult, TCTX = void, TICTX = TCTX
|
|
1310
|
+
declare function mutationResource<TResult, TRaw = TResult, TMutation = TResult, TCTX = void, TICTX = TCTX>(request: (params: TMutation) => (Omit<HttpResourceRequest, 'body' | 'method'> & {
|
|
1311
|
+
method: 'DELETE' | 'delete';
|
|
1312
|
+
}) | undefined | void, options0?: MutationResourceOptions<TResult, TRaw, TMutation, TCTX, TICTX>): MutationResourceRef<TResult, TMutation, TICTX>;
|
|
1313
|
+
declare function mutationResource<TResult, TRaw = TResult, TMutation = TResult, TCTX = void, TICTX = TCTX>(request: (params: TMutation) => (Omit<HttpResourceRequest, 'body'> & {
|
|
1314
|
+
body: TMutation;
|
|
1315
|
+
}) | undefined | void, options0?: MutationResourceOptions<TResult, TRaw, TMutation, TCTX, TICTX>): MutationResourceRef<TResult, TMutation, TICTX>;
|
|
1245
1316
|
|
|
1246
|
-
export { Cache, PAUSED, applyResourceRegistration, createCacheInterceptor, createCircuitBreaker, createDedupeRequestsInterceptor, hashRequest, infiniteQueryResource, injectQueryCache, injectResourceOptions, manualQueryResource, mutationResource, noDedupe, provideCircuitBreakerDefaultOptions, provideMutationResourceOptions, provideQueryCache, provideQueryResourceOptions, provideResourceOptions, provideTypedResourceOptions, queryResource };
|
|
1247
|
-
export type { CacheEntry, CleanupType, CommonResourceOptions, DisabledReason, InfiniteQueryResourceOptions, InfiniteQueryResourceRef, InfiniteRequestContext, ManualQueryResourceRef, MutationQueueOptions, MutationResourceOptions, MutationResourceRef, QueryResourceOptions, QueryResourceRef, RefreshOptions, RequestContext, ResourceCacheOptions, ResourceRequestFn, TransitionRegistration };
|
|
1317
|
+
export { Cache, MutationCancelledError, PAUSED, applyResourceRegistration, createCacheInterceptor, createCircuitBreaker, createDedupeRequestsInterceptor, hashRequest, infiniteQueryResource, injectQueryCache, injectResourceOptions, manualQueryResource, mutationResource, noDedupe, provideCircuitBreakerDefaultOptions, provideMutationResourceOptions, provideQueryCache, provideQueryResourceOptions, provideResourceOptions, provideTypedResourceOptions, queryResource };
|
|
1318
|
+
export type { CacheEntry, CleanupType, CommonResourceOptions, DisabledReason, InfiniteQueryResourceOptions, InfiniteQueryResourceRef, InfiniteRequestContext, ManualQueryResourceRef, MutationCancellationReason, MutationQueueOptions, MutationResourceOptions, MutationResourceRef, QueryResourceOptions, QueryResourceRef, RefreshOptions, RequestContext, ResourceCacheOptions, ResourceRequestFn, TransitionRegistration };
|