shelving 1.196.0 → 1.196.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.196.0",
3
+ "version": "1.196.2",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -5,8 +5,8 @@ import { NULLABLE } from "./NullableSchema.js";
5
5
  import { StringSchema } from "./StringSchema.js";
6
6
  export { formatAddress };
7
7
  const ADDRESS_PROPS = {
8
- address1: new StringSchema({ title: "Address 1", max: 60, min: 1 }),
9
- address2: new StringSchema({ title: "Address 2", max: 60, min: 0 }),
8
+ address1: new StringSchema({ title: "Address", max: 60, min: 1 }),
9
+ address2: new StringSchema({ title: undefined, max: 60, min: 0 }),
10
10
  city: new StringSchema({ title: "City", min: 1, max: 60 }),
11
11
  state: new StringSchema({ title: "State", min: 0, max: 60 }),
12
12
  postcode: new StringSchema({ title: "Postcode", min: 1, max: 12, case: "upper" }),
@@ -16,6 +16,6 @@ export declare class PayloadFetchStore<P, R> extends FetchStore<R> {
16
16
  * - New payloads can be set using `this.payload.value`
17
17
  */
18
18
  readonly payload: Store<P>;
19
- constructor(payload: P, value: R | typeof NONE, callback?: PayloadFetchCallback<P, R>, debounce?: number);
19
+ constructor(payload: P | typeof NONE, value: R | typeof NONE, callback?: PayloadFetchCallback<P, R>, debounce?: number);
20
20
  [Symbol.asyncDispose](): Promise<void>;
21
21
  }
@@ -1,4 +1,4 @@
1
- import { awaitAbort, getDelay } from "../util/async.js";
1
+ import { awaitAbort, awaitRace, getDelay } from "../util/async.js";
2
2
  import { awaitDispose } from "../util/dispose.js";
3
3
  import { FetchStore } from "./FetchStore.js";
4
4
  import { Store } from "./Store.js";
@@ -20,13 +20,12 @@ export class PayloadFetchStore extends FetchStore {
20
20
  constructor(payload, value, callback, debounce = 0) {
21
21
  const payloadStore = new Store(payload);
22
22
  const fetch = callback &&
23
- (debounce > 0
24
- ? async (signal) => {
25
- const snap = payloadStore.value;
26
- await Promise.race([getDelay(debounce), awaitAbort(signal)]);
27
- return callback(snap, signal);
28
- }
29
- : (signal) => callback(payloadStore.value, signal));
23
+ (async (signal) => {
24
+ if (debounce > 0)
25
+ await awaitRace(getDelay(debounce), awaitAbort(signal));
26
+ const value = payloadStore.loading ? await awaitRace(payloadStore.next, awaitAbort(signal)) : payloadStore.value;
27
+ return callback(value, signal);
28
+ });
30
29
  super(value, fetch);
31
30
  this.payload = payloadStore;
32
31
  void _iterate(this);
package/util/async.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ImmutableArray } from "./array.js";
2
- import type { ErrorCallback, ValueCallback } from "./function.js";
2
+ import { type ErrorCallback, type ValueCallback } from "./function.js";
3
3
  /** Is a value an asynchronous value implementing a `then()` function. */
4
4
  export declare function isAsync<T>(value: PromiseLike<T> | T): value is PromiseLike<T>;
5
5
  /** Is a value a synchronous value. */
@@ -65,8 +65,17 @@ export declare function getDelay(ms: number): Promise<void>;
65
65
  /**
66
66
  * Get a promise that rejects with the signal's reason when an `AbortSignal` fires.
67
67
  * - Rejects immediately if the signal is already aborted.
68
- * - Use with `Promise.race()` to cancel a concurrent operation when a signal fires.
68
+ * - Use with `awaitRace()` to cancel a concurrent operation when a signal fires.
69
69
  *
70
- * @example await Promise.race([getDelay(300), awaitAbort(signal)]);
70
+ * @example await awaitRace(getDelay(300), awaitAbort(signal));
71
71
  */
72
72
  export declare function awaitAbort(signal: AbortSignal): Promise<never>;
73
+ /**
74
+ * Race promises like `Promise.race()` but silently swallow rejections from the losers.
75
+ * - Returns a promise that settles with the first input to settle, exactly like `Promise.race()`.
76
+ * - The losing inputs keep running (Promises cannot be cancelled), but their eventual rejection — if any — is silently absorbed instead of bubbling up as an unhandled rejection.
77
+ * - Built for cancellation/timeout patterns, where the loser's eventual fate is genuinely uninteresting once another arm has settled. Do not use when both arms might surface meaningful errors that the caller should see.
78
+ *
79
+ * @example await awaitRace(getDelay(300), awaitAbort(signal)); // delay or abort, no leaked ABORT rejection if delay wins
80
+ */
81
+ export declare function awaitRace<T>(...promises: Promise<T>[]): Promise<T>;
package/util/async.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Errors } from "../error/Errors.js";
2
2
  import { RequiredError } from "../error/RequiredError.js";
3
+ import { BLACKHOLE } from "./function.js";
3
4
  /** Is a value an asynchronous value implementing a `then()` function. */
4
5
  export function isAsync(value) {
5
6
  return typeof value === "object" && value !== null && typeof value.then === "function";
@@ -113,15 +114,30 @@ export function getDelay(ms) {
113
114
  /**
114
115
  * Get a promise that rejects with the signal's reason when an `AbortSignal` fires.
115
116
  * - Rejects immediately if the signal is already aborted.
116
- * - Use with `Promise.race()` to cancel a concurrent operation when a signal fires.
117
+ * - Use with `awaitRace()` to cancel a concurrent operation when a signal fires.
117
118
  *
118
- * @example await Promise.race([getDelay(300), awaitAbort(signal)]);
119
+ * @example await awaitRace(getDelay(300), awaitAbort(signal));
119
120
  */
120
121
  export function awaitAbort(signal) {
121
- return new Promise((_, reject) => {
122
+ const promise = new Promise((_, reject) => {
122
123
  if (signal.aborted)
123
124
  reject(signal.reason);
124
125
  else
125
126
  signal.addEventListener("abort", () => reject(signal.reason), { once: true });
126
127
  });
128
+ promise.catch(BLACKHOLE);
129
+ return promise;
130
+ }
131
+ /**
132
+ * Race promises like `Promise.race()` but silently swallow rejections from the losers.
133
+ * - Returns a promise that settles with the first input to settle, exactly like `Promise.race()`.
134
+ * - The losing inputs keep running (Promises cannot be cancelled), but their eventual rejection — if any — is silently absorbed instead of bubbling up as an unhandled rejection.
135
+ * - Built for cancellation/timeout patterns, where the loser's eventual fate is genuinely uninteresting once another arm has settled. Do not use when both arms might surface meaningful errors that the caller should see.
136
+ *
137
+ * @example await awaitRace(getDelay(300), awaitAbort(signal)); // delay or abort, no leaked ABORT rejection if delay wins
138
+ */
139
+ export function awaitRace(...promises) {
140
+ for (const promise of promises)
141
+ promise.catch(BLACKHOLE);
142
+ return Promise.race(promises);
127
143
  }