@mmstack/resource 20.8.2 → 20.8.3
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/fesm2022/mmstack-resource.mjs +109 -10
- package/fesm2022/mmstack-resource.mjs.map +1 -1
- package/index.d.ts +83 -56
- package/package.json +1 -1
|
@@ -874,13 +874,46 @@ const SAFE_RAW_HEADERS = new Set([
|
|
|
874
874
|
'content-language',
|
|
875
875
|
'content-type',
|
|
876
876
|
]);
|
|
877
|
+
const UNSAFE_HEADER_MESSAGES = new Map([
|
|
878
|
+
[
|
|
879
|
+
'cookie',
|
|
880
|
+
"[@mmstack/resource]: varyHeaders includes 'cookie'. Browser-attached cookies never appear on the request object (so this usually partitions nothing), and manually-set cookie values often rotate per-request (shredding the hit rate). The header IS still honored (digested) — but prefer varying on 'Authorization' or a tenant header.",
|
|
881
|
+
],
|
|
882
|
+
[
|
|
883
|
+
'set-cookie',
|
|
884
|
+
"[@mmstack/resource]: varyHeaders includes 'set-cookie'. Browser-attached cookies never appear on the request object (so this usually partitions nothing), and manually-set cookie values often rotate per-request (shredding the hit rate). The header IS still honored (digested) — but prefer varying on 'Authorization' or a tenant header.",
|
|
885
|
+
],
|
|
886
|
+
[
|
|
887
|
+
'authorization',
|
|
888
|
+
"[@mmstack/resource]: varyHeaders includes 'Authorization'. If your token rotates frequently (e.g., short-lived JWTs), this will cause 100% cache churn on refresh. Consider adding a namespace prefix with the users sub, not using it as a cache-key or using a custom 'cache.hash' function with a stable session/user ID instead.",
|
|
889
|
+
],
|
|
890
|
+
[
|
|
891
|
+
'x-request-id',
|
|
892
|
+
"[@mmstack/resource]: varyHeaders includes 'X-Request-ID'. This header is often set to a unique value per-request, which will cause 100% cache churn. Consider removing it from varyHeaders or using a custom 'cache.hash' function that ignores it.",
|
|
893
|
+
],
|
|
894
|
+
[
|
|
895
|
+
'x-correlation-id',
|
|
896
|
+
"[@mmstack/resource]: varyHeaders includes 'X-Correlation-ID'. This header is often set to a unique value per-request, which will cause 100% cache churn. Consider removing it from varyHeaders or using a custom 'cache.hash' function that ignores it.",
|
|
897
|
+
],
|
|
898
|
+
[
|
|
899
|
+
'if-none-match',
|
|
900
|
+
"[@mmstack/resource]: varyHeaders includes 'If-None-Match'. This header contains ETags that change whenever the server's resource version changes, which will cause cache misses on every update. Consider removing it from varyHeaders or using a custom 'cache.hash' function that ignores it.",
|
|
901
|
+
],
|
|
902
|
+
[
|
|
903
|
+
'if-modified-since',
|
|
904
|
+
"[@mmstack/resource]: varyHeaders includes 'If-Modified-Since'. This header contains timestamps that change whenever the server's resource version changes, which will cause cache misses on every update. Consider removing it from varyHeaders or using a custom 'cache.hash' function that ignores it.",
|
|
905
|
+
],
|
|
906
|
+
]);
|
|
877
907
|
function normalizeVaryHeaders(headers, names) {
|
|
908
|
+
const isDev = isDevMode();
|
|
878
909
|
return names
|
|
879
910
|
.map((n) => n.toLowerCase())
|
|
880
911
|
.toSorted()
|
|
881
912
|
.map((name) => {
|
|
882
|
-
if (
|
|
883
|
-
|
|
913
|
+
if (isDev) {
|
|
914
|
+
const warning = UNSAFE_HEADER_MESSAGES.get(name);
|
|
915
|
+
if (warning)
|
|
916
|
+
console.warn(warning);
|
|
884
917
|
}
|
|
885
918
|
const value = readHeader(headers, name);
|
|
886
919
|
if (value === null)
|
|
@@ -894,13 +927,17 @@ function normalizeVaryHeaders(headers, names) {
|
|
|
894
927
|
.join('&');
|
|
895
928
|
}
|
|
896
929
|
function normalizeParams(params) {
|
|
897
|
-
const p = params instanceof HttpParams
|
|
930
|
+
const p = params instanceof HttpParams
|
|
931
|
+
? params
|
|
932
|
+
: new HttpParams({ fromObject: params });
|
|
898
933
|
return p
|
|
899
934
|
.keys()
|
|
900
935
|
.toSorted()
|
|
901
936
|
.map((key) => {
|
|
902
937
|
const encodedKey = encodeURIComponent(key);
|
|
903
|
-
return (p.getAll(key) ?? [])
|
|
938
|
+
return (p.getAll(key) ?? [])
|
|
939
|
+
.map((v) => `${encodedKey}=${encodeURIComponent(v)}`)
|
|
940
|
+
.join('&');
|
|
904
941
|
})
|
|
905
942
|
.join('&');
|
|
906
943
|
}
|
|
@@ -920,7 +957,8 @@ function hashBody(body) {
|
|
|
920
957
|
entries.sort(([ak, av], [bk, bv]) => ak.localeCompare(bk) || av.localeCompare(bv));
|
|
921
958
|
return `FormData:${entries.map(([k, v]) => `${k}=${v}`).join('&')}`;
|
|
922
959
|
}
|
|
923
|
-
if (typeof URLSearchParams !== 'undefined' &&
|
|
960
|
+
if (typeof URLSearchParams !== 'undefined' &&
|
|
961
|
+
body instanceof URLSearchParams) {
|
|
924
962
|
const sp = new URLSearchParams(body);
|
|
925
963
|
sp.sort();
|
|
926
964
|
return `URLSearchParams:${sp.toString()}`;
|
|
@@ -1666,6 +1704,57 @@ function hasSlowConnection() {
|
|
|
1666
1704
|
return false;
|
|
1667
1705
|
}
|
|
1668
1706
|
|
|
1707
|
+
/**
|
|
1708
|
+
* Deep merges multiple circuit breaker options.
|
|
1709
|
+
* The latter options override the former.
|
|
1710
|
+
*/
|
|
1711
|
+
function mergeCircuitBreakerOptions(global, query, local) {
|
|
1712
|
+
if (!global && !query && !local)
|
|
1713
|
+
return undefined;
|
|
1714
|
+
return {
|
|
1715
|
+
...(global === true ? {} : global),
|
|
1716
|
+
...(query === true ? {} : query),
|
|
1717
|
+
...(local === true ? {} : local),
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Deep merges multiple retry options.
|
|
1722
|
+
* The latter options override the former.
|
|
1723
|
+
*/
|
|
1724
|
+
function mergeRetryOptions(global, query, local) {
|
|
1725
|
+
if (global === undefined && query === undefined && local === undefined)
|
|
1726
|
+
return undefined;
|
|
1727
|
+
return {
|
|
1728
|
+
...(typeof global === 'number' ? { max: global } : global),
|
|
1729
|
+
...(typeof query === 'number' ? { max: query } : query),
|
|
1730
|
+
...(typeof local === 'number' ? { max: local } : local),
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Deep merges multiple cache options.
|
|
1735
|
+
* The latter options override the former.
|
|
1736
|
+
*/
|
|
1737
|
+
function mergeCacheOptions(query, local) {
|
|
1738
|
+
if (query === undefined && local === undefined)
|
|
1739
|
+
return undefined;
|
|
1740
|
+
return {
|
|
1741
|
+
...(query === true ? {} : query),
|
|
1742
|
+
...(local === true ? {} : local),
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Deep merges multiple refresh options.
|
|
1747
|
+
* The latter options override the former.
|
|
1748
|
+
*/
|
|
1749
|
+
function mergeRefreshOptions(query, local) {
|
|
1750
|
+
if (query === undefined && local === undefined)
|
|
1751
|
+
return undefined;
|
|
1752
|
+
return {
|
|
1753
|
+
...(typeof query === 'number' ? { interval: query } : query),
|
|
1754
|
+
...(typeof local === 'number' ? { interval: local } : local),
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1669
1758
|
function persistResourceValues(resource, shouldPersist = false, equal) {
|
|
1670
1759
|
if (!shouldPersist)
|
|
1671
1760
|
return resource;
|
|
@@ -1847,10 +1936,16 @@ function injectQueryResourceOptions(injector) {
|
|
|
1847
1936
|
const PAUSED = Symbol('@mmstack/resource:paused');
|
|
1848
1937
|
function queryResource(request, options0) {
|
|
1849
1938
|
// Two-layer option injection: per-call > provideQueryResourceOptions > provideResourceOptions.
|
|
1939
|
+
const globalOpts = injectResourceOptions(options0?.injector);
|
|
1940
|
+
const queryOpts = injectQueryResourceOptions(options0?.injector);
|
|
1850
1941
|
const options = {
|
|
1851
|
-
...
|
|
1852
|
-
...
|
|
1942
|
+
...globalOpts,
|
|
1943
|
+
...queryOpts,
|
|
1853
1944
|
...options0,
|
|
1945
|
+
cache: mergeCacheOptions(queryOpts.cache, options0?.cache),
|
|
1946
|
+
circuitBreaker: mergeCircuitBreakerOptions(globalOpts.circuitBreaker, queryOpts.circuitBreaker, options0?.circuitBreaker),
|
|
1947
|
+
retry: mergeRetryOptions(globalOpts.retry, queryOpts.retry, options0?.retry),
|
|
1948
|
+
refresh: mergeRefreshOptions(queryOpts.refresh, options0?.refresh),
|
|
1854
1949
|
};
|
|
1855
1950
|
const cache = injectQueryCache(options?.injector);
|
|
1856
1951
|
const destroyRef = options?.injector
|
|
@@ -2369,10 +2464,14 @@ function injectMutationResourceOptions(injector) {
|
|
|
2369
2464
|
*/
|
|
2370
2465
|
function mutationResource(request, options0 = {}) {
|
|
2371
2466
|
// Two-layer option injection: per-call > provideMutationResourceOptions > provideResourceOptions.
|
|
2467
|
+
const globalOpts = injectResourceOptions(options0.injector);
|
|
2468
|
+
const mutOpts = injectMutationResourceOptions(options0.injector);
|
|
2372
2469
|
const options = {
|
|
2373
|
-
...
|
|
2374
|
-
...
|
|
2470
|
+
...globalOpts,
|
|
2471
|
+
...mutOpts,
|
|
2375
2472
|
...options0,
|
|
2473
|
+
circuitBreaker: mergeCircuitBreakerOptions(globalOpts.circuitBreaker, mutOpts.circuitBreaker, options0?.circuitBreaker),
|
|
2474
|
+
retry: mergeRetryOptions(globalOpts.retry, mutOpts.retry, options0?.retry),
|
|
2376
2475
|
};
|
|
2377
2476
|
// `register` is pulled out (and forced off on the inner query below) so the mutation ref is
|
|
2378
2477
|
// the only thing registered into the transition scope, not its internal query resource.
|
|
@@ -2588,5 +2687,5 @@ function mutationResource(request, options0 = {}) {
|
|
|
2588
2687
|
* Generated bundle index. Do not edit.
|
|
2589
2688
|
*/
|
|
2590
2689
|
|
|
2591
|
-
export { Cache, PAUSED, applyResourceRegistration, createCacheInterceptor, createCircuitBreaker, createDedupeRequestsInterceptor, infiniteQueryResource, injectQueryCache, injectResourceOptions, manualQueryResource, mutationResource, noDedupe, provideCircuitBreakerDefaultOptions, provideMutationResourceOptions, provideQueryCache, provideQueryResourceOptions, provideResourceOptions, provideTypedResourceOptions, queryResource };
|
|
2690
|
+
export { Cache, PAUSED, applyResourceRegistration, createCacheInterceptor, createCircuitBreaker, createDedupeRequestsInterceptor, hashRequest, infiniteQueryResource, injectQueryCache, injectResourceOptions, manualQueryResource, mutationResource, noDedupe, provideCircuitBreakerDefaultOptions, provideMutationResourceOptions, provideQueryCache, provideQueryResourceOptions, provideResourceOptions, provideTypedResourceOptions, queryResource };
|
|
2592
2691
|
//# sourceMappingURL=mmstack-resource.mjs.map
|