@mmstack/resource 19.3.1 → 19.3.2
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 +508 -73
- package/fesm2022/mmstack-resource.mjs +125 -42
- package/fesm2022/mmstack-resource.mjs.map +1 -1
- package/lib/query-resource.d.ts +21 -4
- package/lib/util/cache/cache.d.ts +18 -0
- package/lib/util/circuit-breaker.d.ts +38 -3
- package/lib/util/retry-on-error.d.ts +9 -1
- package/package.json +1 -1
package/lib/query-resource.d.ts
CHANGED
|
@@ -62,10 +62,16 @@ export type QueryResourceOptions<TResult, TRaw = TResult> = HttpResourceOptions<
|
|
|
62
62
|
*/
|
|
63
63
|
retry?: RetryOptions;
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
65
|
+
* Called on every failed attempt, including each retry.
|
|
66
|
+
*
|
|
67
|
+
* @param err - The error from the underlying HTTP request.
|
|
68
|
+
* @param retryCount - The number of retries that already happened before
|
|
69
|
+
* this error (`0` on the original failure, `1` after the first retry, etc.).
|
|
70
|
+
* @param isFinal - `true` when no further retry will be scheduled — either
|
|
71
|
+
* because retries are exhausted or `retry` was unset/0. Branch on this for
|
|
72
|
+
* "user actually needs to know" side effects (toasts, error reporting).
|
|
67
73
|
*/
|
|
68
|
-
onError?: (err: unknown) => void;
|
|
74
|
+
onError?: (err: unknown, retryCount: number, isFinal: boolean) => void;
|
|
69
75
|
/**
|
|
70
76
|
* Options for configuring a circuit breaker for the resource.
|
|
71
77
|
*/
|
|
@@ -80,6 +86,12 @@ export type QueryResourceOptions<TResult, TRaw = TResult> = HttpResourceOptions<
|
|
|
80
86
|
*/
|
|
81
87
|
triggerOnSameRequest?: boolean;
|
|
82
88
|
};
|
|
89
|
+
/**
|
|
90
|
+
* The reason a query resource is currently in the `disabled` state, or `null`
|
|
91
|
+
* if it is enabled. Useful for branching UI on cause (e.g. "offline" vs
|
|
92
|
+
* "circuit tripped" vs "nothing to fetch yet").
|
|
93
|
+
*/
|
|
94
|
+
export type DisabledReason = 'offline' | 'circuit-open' | 'no-request';
|
|
83
95
|
/**
|
|
84
96
|
* Represents a resource created by `queryResource`. Extends `HttpResourceRef` with additional properties.
|
|
85
97
|
*/
|
|
@@ -93,9 +105,14 @@ export type QueryResourceRef<TResult> = Omit<HttpResourceRef<TResult>, 'headers'
|
|
|
93
105
|
*/
|
|
94
106
|
readonly statusCode: WritableSignal<number | undefined>;
|
|
95
107
|
/**
|
|
96
|
-
* A signal indicating whether the resource is currently disabled (due to circuit breaker or undefined request).
|
|
108
|
+
* A signal indicating whether the resource is currently disabled (due to circuit breaker, offline, or undefined request).
|
|
97
109
|
*/
|
|
98
110
|
disabled: Signal<boolean>;
|
|
111
|
+
/**
|
|
112
|
+
* Why the resource is currently disabled, or `null` if it is enabled.
|
|
113
|
+
* Maps to one of: `'offline'`, `'circuit-open'`, `'no-request'`.
|
|
114
|
+
*/
|
|
115
|
+
disabledReason: Signal<DisabledReason | null>;
|
|
99
116
|
/**
|
|
100
117
|
* Prefetches data for the resource, populating the cache if caching is enabled. This can be
|
|
101
118
|
* used to proactively load data before it's needed. If a slow connection is detected, prefetching is skipped.
|
|
@@ -135,6 +135,24 @@ export declare class Cache<T> {
|
|
|
135
135
|
* @param key - The key of the entry to invalidate.
|
|
136
136
|
*/
|
|
137
137
|
invalidate(key: string): void;
|
|
138
|
+
/**
|
|
139
|
+
* Invalidates every cache entry whose key starts with `prefix`. Common after a
|
|
140
|
+
* list-mutating operation (e.g. invalidate every paginated `GET /api/posts*`
|
|
141
|
+
* after a POST). Returns the number of entries removed.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* cache.invalidatePrefix('GET https://api.example.com/posts');
|
|
145
|
+
*/
|
|
146
|
+
invalidatePrefix(prefix: string): number;
|
|
147
|
+
/**
|
|
148
|
+
* Invalidates every cache entry whose key matches the predicate. Use for
|
|
149
|
+
* arbitrary bulk invalidation that doesn't fit prefix matching (e.g.
|
|
150
|
+
* "everything containing `userId=42`"). Returns the number of entries removed.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* cache.invalidateWhere((key) => key.includes('/me/'));
|
|
154
|
+
*/
|
|
155
|
+
invalidateWhere(predicate: (key: string) => boolean): number;
|
|
138
156
|
private invalidateInternal;
|
|
139
157
|
/** @internal */
|
|
140
158
|
private cleanup;
|
|
@@ -37,6 +37,13 @@ export type CircuitBreaker = {
|
|
|
37
37
|
* to test if the underlying issue has been resolved after the circuit breaker has been open.
|
|
38
38
|
*/
|
|
39
39
|
halfOpen: () => void;
|
|
40
|
+
/**
|
|
41
|
+
* Fully resets the breaker state — clears the failure count, drops the half-open
|
|
42
|
+
* flag, and lifts a permanent open caused by `shouldFailForever`. Use after the
|
|
43
|
+
* underlying condition has been resolved (e.g. user re-authenticated after a
|
|
44
|
+
* 401-triggered permanent open).
|
|
45
|
+
*/
|
|
46
|
+
hardReset: () => void;
|
|
40
47
|
/**
|
|
41
48
|
* Destroys the circuit breaker & initiates related cleanup
|
|
42
49
|
*/
|
|
@@ -50,6 +57,10 @@ type CreateCircuitBreakerOptions = {
|
|
|
50
57
|
* The number of failures that will cause the circuit breaker to open.
|
|
51
58
|
* @default 5
|
|
52
59
|
*/
|
|
60
|
+
threshold?: number;
|
|
61
|
+
/**
|
|
62
|
+
* @deprecated Misspelled — use `threshold` instead. Kept for backwards compatibility; will be removed in a future major.
|
|
63
|
+
*/
|
|
53
64
|
treshold?: number;
|
|
54
65
|
/**
|
|
55
66
|
* The time in milliseconds after which the circuit breaker will reset and allow operations to proceed again.
|
|
@@ -63,6 +74,7 @@ type CreateCircuitBreakerOptions = {
|
|
|
63
74
|
shouldFail?: (err?: Error) => boolean;
|
|
64
75
|
/**
|
|
65
76
|
* A function that determines whether an error should cause the circuit breaker to be open forever.
|
|
77
|
+
* `hardReset()` is required to lift this state.
|
|
66
78
|
* @default Always returns false
|
|
67
79
|
*/
|
|
68
80
|
shouldFailForever?: (err?: Error) => boolean;
|
|
@@ -72,16 +84,39 @@ type CreateCircuitBreakerOptions = {
|
|
|
72
84
|
* - `false`: Disables circuit breaker functionality (always open).
|
|
73
85
|
* - true: Creates a new circuit breaker with default options.
|
|
74
86
|
* - `CircuitBreaker`: Provides an existing `CircuitBreaker` instance to use.
|
|
75
|
-
* - `{
|
|
87
|
+
* - `{ threshold?: number; timeout?: number; }`: Creates a new circuit breaker with the specified options.
|
|
76
88
|
*/
|
|
77
89
|
export type CircuitBreakerOptions = false | CircuitBreaker | CreateCircuitBreakerOptions;
|
|
90
|
+
/**
|
|
91
|
+
* Provides application-wide default options for {@link createCircuitBreaker}.
|
|
92
|
+
* Any `createCircuitBreaker()` call without explicit options (or with only
|
|
93
|
+
* partial options) merges these defaults in, so you can centralize threshold /
|
|
94
|
+
* timeout / failure-classifier behavior in one place.
|
|
95
|
+
*
|
|
96
|
+
* Per-call options always win over the provided defaults.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* bootstrapApplication(AppComponent, {
|
|
101
|
+
* providers: [
|
|
102
|
+
* provideCircuitBreakerDefaultOptions({
|
|
103
|
+
* threshold: 10,
|
|
104
|
+
* timeout: 60_000,
|
|
105
|
+
* shouldFailForever: (err) =>
|
|
106
|
+
* err instanceof HttpErrorResponse && [401, 403].includes(err.status),
|
|
107
|
+
* }),
|
|
108
|
+
* ],
|
|
109
|
+
* });
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
78
112
|
export declare function provideCircuitBreakerDefaultOptions(options: CircuitBreakerOptions): Provider;
|
|
79
113
|
/**
|
|
80
114
|
* Creates a circuit breaker instance.
|
|
81
115
|
*
|
|
82
116
|
* @param options - Configuration options for the circuit breaker. Can be:
|
|
83
|
-
* - `undefined`:
|
|
84
|
-
* - `
|
|
117
|
+
* - `undefined`: Uses defaults (threshold: 5, timeout: 30000ms) or provided defaults via {@link provideCircuitBreakerDefaultOptions}.
|
|
118
|
+
* - `false`: Creates a "no-op" circuit breaker that is always closed (never trips).
|
|
119
|
+
* - `true`: Creates a circuit breaker with default settings.
|
|
85
120
|
* - `CircuitBreaker`: Reuses an existing `CircuitBreaker` instance.
|
|
86
121
|
* - `{ threshold?: number; timeout?: number; }`: Creates a circuit breaker with the specified threshold and timeout.
|
|
87
122
|
*
|
|
@@ -3,4 +3,12 @@ export type RetryOptions = number | {
|
|
|
3
3
|
max?: number;
|
|
4
4
|
backoff?: number;
|
|
5
5
|
};
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Callback fired by the retry wrapper for every failed attempt.
|
|
8
|
+
* `retryCount` is the number of retries that already happened before this
|
|
9
|
+
* error (`0` on the original failure, `1` after the first retry, etc.).
|
|
10
|
+
* `isFinal` is `true` when no further retry will be scheduled — either because
|
|
11
|
+
* retries are exhausted or `retry` was unset/0.
|
|
12
|
+
*/
|
|
13
|
+
export type RetryErrorCallback<TError = unknown> = (err: TError, retryCount: number, isFinal: boolean) => void;
|
|
14
|
+
export declare function retryOnError<T>(res: HttpResourceRef<T>, opt?: RetryOptions, onError?: RetryErrorCallback): HttpResourceRef<T>;
|