@mmstack/resource 21.1.1 → 21.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/resource",
3
- "version": "21.1.1",
3
+ "version": "21.2.0",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "signals",
@@ -37,5 +37,6 @@
37
37
  "types": "./types/mmstack-resource.d.ts",
38
38
  "default": "./fesm2022/mmstack-resource.mjs"
39
39
  }
40
- }
40
+ },
41
+ "type": "module"
41
42
  }
@@ -1,4 +1,4 @@
1
- import { HttpResponse, HttpInterceptorFn, HttpContext, HttpResourceRef, HttpHeaders, HttpResourceRequest, HttpResourceOptions } from '@angular/common/http';
1
+ import { HttpResponse, HttpInterceptorFn, HttpRequest, HttpContext, HttpResourceRef, HttpHeaders, HttpResourceRequest, HttpResourceOptions } from '@angular/common/http';
2
2
  import { Signal, Injector, Provider, WritableSignal, ValueEqualityFn } from '@angular/core';
3
3
 
4
4
  type StoredEntry<T> = Omit<CacheEntry<T>, 'timeout'>;
@@ -142,6 +142,24 @@ declare class Cache<T> {
142
142
  * @param key - The key of the entry to invalidate.
143
143
  */
144
144
  invalidate(key: string): void;
145
+ /**
146
+ * Invalidates every cache entry whose key starts with `prefix`. Common after a
147
+ * list-mutating operation (e.g. invalidate every paginated `GET /api/posts*`
148
+ * after a POST). Returns the number of entries removed.
149
+ *
150
+ * @example
151
+ * cache.invalidatePrefix('GET https://api.example.com/posts');
152
+ */
153
+ invalidatePrefix(prefix: string): number;
154
+ /**
155
+ * Invalidates every cache entry whose key matches the predicate. Use for
156
+ * arbitrary bulk invalidation that doesn't fit prefix matching (e.g.
157
+ * "everything containing `userId=42`"). Returns the number of entries removed.
158
+ *
159
+ * @example
160
+ * cache.invalidateWhere((key) => key.includes('/me/'));
161
+ */
162
+ invalidateWhere(predicate: (key: string) => boolean): number;
145
163
  private invalidateInternal;
146
164
  /** @internal */
147
165
  private cleanup;
@@ -295,6 +313,13 @@ type CircuitBreaker = {
295
313
  * to test if the underlying issue has been resolved after the circuit breaker has been open.
296
314
  */
297
315
  halfOpen: () => void;
316
+ /**
317
+ * Fully resets the breaker state — clears the failure count, drops the half-open
318
+ * flag, and lifts a permanent open caused by `shouldFailForever`. Use after the
319
+ * underlying condition has been resolved (e.g. user re-authenticated after a
320
+ * 401-triggered permanent open).
321
+ */
322
+ hardReset: () => void;
298
323
  /**
299
324
  * Destroys the circuit breaker & initiates related cleanup
300
325
  */
@@ -308,6 +333,10 @@ type CreateCircuitBreakerOptions = {
308
333
  * The number of failures that will cause the circuit breaker to open.
309
334
  * @default 5
310
335
  */
336
+ threshold?: number;
337
+ /**
338
+ * @deprecated Misspelled — use `threshold` instead. Kept for backwards compatibility; will be removed in a future major.
339
+ */
311
340
  treshold?: number;
312
341
  /**
313
342
  * The time in milliseconds after which the circuit breaker will reset and allow operations to proceed again.
@@ -321,6 +350,7 @@ type CreateCircuitBreakerOptions = {
321
350
  shouldFail?: (err?: Error) => boolean;
322
351
  /**
323
352
  * A function that determines whether an error should cause the circuit breaker to be open forever.
353
+ * `hardReset()` is required to lift this state.
324
354
  * @default Always returns false
325
355
  */
326
356
  shouldFailForever?: (err?: Error) => boolean;
@@ -330,16 +360,39 @@ type CreateCircuitBreakerOptions = {
330
360
  * - `false`: Disables circuit breaker functionality (always open).
331
361
  * - true: Creates a new circuit breaker with default options.
332
362
  * - `CircuitBreaker`: Provides an existing `CircuitBreaker` instance to use.
333
- * - `{ treshold?: number; timeout?: number; }`: Creates a new circuit breaker with the specified options.
363
+ * - `{ threshold?: number; timeout?: number; }`: Creates a new circuit breaker with the specified options.
334
364
  */
335
365
  type CircuitBreakerOptions = false | CircuitBreaker | CreateCircuitBreakerOptions;
366
+ /**
367
+ * Provides application-wide default options for {@link createCircuitBreaker}.
368
+ * Any `createCircuitBreaker()` call without explicit options (or with only
369
+ * partial options) merges these defaults in, so you can centralize threshold /
370
+ * timeout / failure-classifier behavior in one place.
371
+ *
372
+ * Per-call options always win over the provided defaults.
373
+ *
374
+ * @example
375
+ * ```ts
376
+ * bootstrapApplication(AppComponent, {
377
+ * providers: [
378
+ * provideCircuitBreakerDefaultOptions({
379
+ * threshold: 10,
380
+ * timeout: 60_000,
381
+ * shouldFailForever: (err) =>
382
+ * err instanceof HttpErrorResponse && [401, 403].includes(err.status),
383
+ * }),
384
+ * ],
385
+ * });
386
+ * ```
387
+ */
336
388
  declare function provideCircuitBreakerDefaultOptions(options: CircuitBreakerOptions): Provider;
337
389
  /**
338
390
  * Creates a circuit breaker instance.
339
391
  *
340
392
  * @param options - Configuration options for the circuit breaker. Can be:
341
- * - `undefined`: Creates a "no-op" circuit breaker that is always open (never trips).
342
- * - `true`: Creates a circuit breaker with default settings (threshold: 5, timeout: 30000ms).
393
+ * - `undefined`: Uses defaults (threshold: 5, timeout: 30000ms) or provided defaults via {@link provideCircuitBreakerDefaultOptions}.
394
+ * - `false`: Creates a "no-op" circuit breaker that is always closed (never trips).
395
+ * - `true`: Creates a circuit breaker with default settings.
343
396
  * - `CircuitBreaker`: Reuses an existing `CircuitBreaker` instance.
344
397
  * - `{ threshold?: number; timeout?: number; }`: Creates a circuit breaker with the specified threshold and timeout.
345
398
  *
@@ -384,6 +437,9 @@ declare function noDedupe(ctx?: HttpContext): HttpContext;
384
437
  *
385
438
  * @param allowed - An array of HTTP methods for which deduplication should be enabled.
386
439
  * Defaults to `['GET', 'DELETE', 'HEAD', 'OPTIONS']`.
440
+ * @param keyFn - Optional function to compute the dedupe key from a request.
441
+ * Defaults to `hashRequest`, which includes method, URL,
442
+ * response type, params, and body.
387
443
  *
388
444
  * @returns An `HttpInterceptorFn` that implements the request deduplication logic.
389
445
  *
@@ -407,7 +463,7 @@ declare function noDedupe(ctx?: HttpContext): HttpContext;
407
463
  * ],
408
464
  * };
409
465
  */
410
- declare function createDedupeRequestsInterceptor(allowed?: string[]): HttpInterceptorFn;
466
+ declare function createDedupeRequestsInterceptor(allowed?: string[], keyFn?: (req: HttpRequest<unknown>) => string): HttpInterceptorFn;
411
467
 
412
468
  type RetryOptions = number | {
413
469
  max?: number;
@@ -475,10 +531,16 @@ type QueryResourceOptions<TResult, TRaw = TResult> = HttpResourceOptions<TResult
475
531
  */
476
532
  retry?: RetryOptions;
477
533
  /**
478
- * An optional error handler callback. This function will be called whenever the
479
- * underlying HTTP request fails. Useful for displaying toasts or other error messages.
534
+ * Called on every failed attempt, including each retry.
535
+ *
536
+ * @param err - The error from the underlying HTTP request.
537
+ * @param retryCount - The number of retries that already happened before
538
+ * this error (`0` on the original failure, `1` after the first retry, etc.).
539
+ * @param isFinal - `true` when no further retry will be scheduled — either
540
+ * because retries are exhausted or `retry` was unset/0. Branch on this for
541
+ * "user actually needs to know" side effects (toasts, error reporting).
480
542
  */
481
- onError?: (err: unknown) => void;
543
+ onError?: (err: unknown, retryCount: number, isFinal: boolean) => void;
482
544
  /**
483
545
  * Options for configuring a circuit breaker for the resource.
484
546
  */
@@ -493,6 +555,12 @@ type QueryResourceOptions<TResult, TRaw = TResult> = HttpResourceOptions<TResult
493
555
  */
494
556
  triggerOnSameRequest?: boolean;
495
557
  };
558
+ /**
559
+ * The reason a query resource is currently in the `disabled` state, or `null`
560
+ * if it is enabled. Useful for branching UI on cause (e.g. "offline" vs
561
+ * "circuit tripped" vs "nothing to fetch yet").
562
+ */
563
+ type DisabledReason = 'offline' | 'circuit-open' | 'no-request';
496
564
  /**
497
565
  * Represents a resource created by `queryResource`. Extends `HttpResourceRef` with additional properties.
498
566
  */
@@ -506,9 +574,14 @@ type QueryResourceRef<TResult> = Omit<HttpResourceRef<TResult>, 'headers' | 'sta
506
574
  */
507
575
  readonly statusCode: WritableSignal<number | undefined>;
508
576
  /**
509
- * A signal indicating whether the resource is currently disabled (due to circuit breaker or undefined request).
577
+ * A signal indicating whether the resource is currently disabled (due to circuit breaker, offline, or undefined request).
510
578
  */
511
579
  disabled: Signal<boolean>;
580
+ /**
581
+ * Why the resource is currently disabled, or `null` if it is enabled.
582
+ * Maps to one of: `'offline'`, `'circuit-open'`, `'no-request'`.
583
+ */
584
+ disabledReason: Signal<DisabledReason | null>;
512
585
  /**
513
586
  * Prefetches data for the resource, populating the cache if caching is enabled. This can be
514
587
  * used to proactively load data before it's needed. If a slow connection is detected, prefetching is skipped.
@@ -673,4 +746,4 @@ type MutationResourceRef<TResult, TMutation = TResult, TICTX = void> = Omit<Quer
673
746
  declare function mutationResource<TResult, TRaw = TResult, TMutation = TResult, TCTX = void, TICTX = TCTX, TMethod extends HttpResourceRequest['method'] = HttpResourceRequest['method']>(request: (params: TMutation) => Omit<NextRequest<TMethod, TMutation>, 'body'> | undefined | void, options?: MutationResourceOptions<TResult, TRaw, TMutation, TCTX, TICTX>): MutationResourceRef<TResult, TMutation, TICTX>;
674
747
 
675
748
  export { Cache, createCacheInterceptor, createCircuitBreaker, createDedupeRequestsInterceptor, injectQueryCache, manualQueryResource, mutationResource, noDedupe, provideCircuitBreakerDefaultOptions, provideQueryCache, queryResource };
676
- export type { ManualQueryResourceRef, MutationResourceOptions, MutationResourceRef, QueryResourceOptions, QueryResourceRef };
749
+ export type { DisabledReason, ManualQueryResourceRef, MutationResourceOptions, MutationResourceRef, QueryResourceOptions, QueryResourceRef };