@asaidimu/utils-sync 2.3.0 → 2.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/index.d.ts CHANGED
@@ -1,43 +1,46 @@
1
+ import { SystemError } from "@asaidimu/utils-error";
2
+
3
+ //#region src/sync/debouncer.d.ts
1
4
  interface DebouncerOptions {
2
- /**
3
- * Quiet period in milliseconds. The last-enqueued function runs after
4
- * no new calls have arrived for this duration.
5
- * @default 300
6
- */
7
- delay?: number;
8
- /**
9
- * If true, also fires immediately on the leading edge (first call in a
10
- * quiet period), then debounces subsequent calls normally.
11
- *
12
- * Leading-edge semantics: the leading call fires immediately and clears
13
- * _pendingFn. Any calls arriving *while* the leading execution is in flight
14
- * register as a new trailing call and will be resolved by the trailing timer
15
- * when the quiet period expires. Calls arriving during the leading execution
16
- * are never lost.
17
- *
18
- * @default false
19
- */
20
- leading?: boolean;
5
+ /**
6
+ * Quiet period in milliseconds. The last-enqueued function runs after
7
+ * no new calls have arrived for this duration.
8
+ * @default 300
9
+ */
10
+ delay?: number;
11
+ /**
12
+ * If true, also fires immediately on the leading edge (first call in a
13
+ * quiet period), then debounces subsequent calls normally.
14
+ *
15
+ * Leading-edge semantics: the leading call fires immediately and clears
16
+ * _pendingFn. Any calls arriving *while* the leading execution is in flight
17
+ * register as a new trailing call and will be resolved by the trailing timer
18
+ * when the quiet period expires. Calls arriving during the leading execution
19
+ * are never lost.
20
+ *
21
+ * @default false
22
+ */
23
+ leading?: boolean;
21
24
  }
22
25
  /**
23
26
  * Result when the debounced function successfully executed.
24
27
  */
25
28
  type DebouncerOk<T> = {
26
- status: "ok";
27
- value: T;
29
+ status: "ok";
30
+ value: T;
28
31
  };
29
32
  /**
30
33
  * Result when the debounced function threw an error.
31
34
  */
32
35
  type DebouncerError = {
33
- status: "error";
34
- error: unknown;
36
+ status: "error";
37
+ error: unknown;
35
38
  };
36
39
  /**
37
40
  * Result when the debounced call was cancelled before execution.
38
41
  */
39
42
  type DebouncerCancelled = {
40
- status: "cancelled";
43
+ status: "cancelled";
41
44
  };
42
45
  /**
43
46
  * Discriminated union of all possible Debouncer outcomes.
@@ -67,220 +70,77 @@ type DebouncerResult<T> = DebouncerOk<T> | DebouncerError | DebouncerCancelled;
67
70
  * // b and c share the same result as a.
68
71
  */
69
72
  declare class Debouncer<T = void> {
70
- private _delay;
71
- private _leading;
72
- private _timer;
73
- private _pendingFn;
74
- private _pendingResolvers;
75
- private _leadingFired;
76
- constructor(options?: DebouncerOptions);
77
- /**
78
- * Enqueues a function and returns a promise that resolves with its result.
79
- *
80
- * All callers within the same quiet window share the same result — the
81
- * function that actually runs is the *last* one enqueued before the delay
82
- * expires. Use this when you need the return value of the debounced call.
83
- *
84
- * For fire-and-forget use (void fns, event handlers), prefer `fire()` to
85
- * avoid unhandled-promise-rejection warnings and make intent explicit.
86
- *
87
- * @param fn - The function to debounce.
88
- * @returns A promise resolving with a DebouncerResult discriminated union.
89
- */
90
- do(fn: () => Promise<T> | T): Promise<DebouncerResult<T>>;
91
- /**
92
- * Enqueues a function without returning a promise (fire-and-forget).
93
- *
94
- * Identical debounce semantics to `do()` — the last-enqueued function runs
95
- * after the quiet period. Use this for void callbacks, DOM event handlers,
96
- * or any caller that doesn't care about the result. No dangling promise,
97
- * no unhandled-rejection risk.
98
- *
99
- * @param fn - The function to debounce.
100
- */
101
- fire(fn: () => Promise<T> | T): void;
102
- /**
103
- * Shared enqueue logic for both `do()` and `fire()`.
104
- *
105
- */
106
- private _enqueue;
107
- /**
108
- * Immediately cancels any pending debounced execution.
109
- * All waiting callers resolve with `{ status: "cancelled" }`.
110
- */
111
- cancel(): void;
112
- /**
113
- * Immediately executes the pending function (if any), bypassing the remaining delay.
114
- * All waiting callers receive the result.
115
- */
116
- flush(): Promise<DebouncerResult<T> | null>;
117
- /** Returns true if a debounced call is currently pending. */
118
- pending(): boolean;
119
- /**
120
- * Executes the latest pending function and resolves all waiting callers.
121
- */
122
- private _fire;
123
- }
124
-
125
- /**
126
- * Severity levels for structured issues and errors.
127
- */
128
- type Severity = "error" | "warning" | "info";
129
- /**
130
- * Represents a detailed validation or operational problem.
131
- * Designed to be machine-readable while remaining human-friendly.
132
- */
133
- interface Issue {
134
- /** Machine-readable identifier (e.g., `"REQUIRED_FIELD_MISSING"`). */
135
- readonly code: string;
136
- /** Human-readable description of the problem. */
137
- readonly message: string;
138
- /** Field path in a document or state (e.g., `"users.0.email"`). */
139
- readonly path?: string;
140
- /** Optional array index when the issue relates to a specific element. */
141
- readonly index?: number;
142
- /** Seriousness of the issue. Defaults to `"error"`. */
143
- readonly severity?: Severity;
144
- /** Optional detailed explanation, can be multi-line. */
145
- readonly description?: string;
146
- /** Nested issues that caused this one. */
147
- readonly cause?: readonly Issue[];
148
- }
149
- /**
150
- * Standardized error code format: `CATEGORY-XXX[-SUFFIX]`
151
- *
152
- * @example
153
- * ```typescript
154
- * // VAL-001 (Validation: required field missing)
155
- * // DB-001-NF (Database: not found)
156
- * // AUTH-001-DENIED (Auth: permission denied)
157
- * ```
158
- */
159
- interface ErrorCodeMetadata {
160
- /** The formal unique error code identifier (e.g., `"VAL-001"`). */
161
- readonly code: string;
162
- /** Human-readable uppercase name for the error type (e.g., `"VALIDATION_FAILED"`). */
163
- readonly name: string;
164
- /** Detailed description of when and why this error occurs. */
165
- readonly description: string;
166
- /** Suggested immediate action for engineers or systems to handle or resolve the error. */
167
- readonly action?: string;
168
- /** HTTP status code mapping for upstream REST APIs (e.g., `404`, `500`). */
169
- readonly httpStatus?: number;
170
- /** Operational category this error belongs to. */
171
- readonly category: ErrorCategory;
172
- /** Recommended logging level threshold (`"debug" | "info" | "warn" | "error"`). */
173
- readonly logLevel?: "debug" | "info" | "warn" | "error";
174
- }
175
- /**
176
- * Categories classifying the origin and nature of an error.
177
- */
178
- type ErrorCategory = "validation" | "database" | "auth" | "business" | "system" | "network" | "concurrency" | "custom";
179
- /**
180
- * Configuration options container used when constructing a new `SystemError`.
181
- */
182
- interface SystemErrorParams {
183
- /** The unique registered error code identifier (e.g., `ErrorCodes.INTERNAL_ERROR.code`). */
184
- code: string;
185
- /** Explicit error message overrides. If omitted, defaults to the error code's description. */
186
- message?: string;
187
- /** Operational severity tier. Defaults to `"error"`. */
188
- severity?: Severity;
189
- /** State path or key where the operational failure occurred (e.g., `"users.0.email"`). */
190
- path?: string;
191
- /** The function block, track component, or routing process executing during failure. */
192
- operation?: string;
193
- /** Flat array of precise granular input issues or schema violations. */
194
- issues?: readonly Issue[];
195
- /** The underlying trigger cause. Accepts native `Error` instances, custom errors, or concurrent `Error[]` arrays. */
196
- cause?: unknown | unknown[];
197
- /** Ad-hoc modifications overriding base properties mapped from the code registry. */
198
- metadata?: Partial<Omit<ErrorCodeMetadata, "code" | "category">>;
199
- }
200
- /**
201
- * Primary domain operational error wrapper.
202
- * Unfolds historical deep error hierarchies into flat, parallel-aware trace timelines.
203
- */
204
- declare class SystemError extends Error {
205
- /** The associated error identifier code (e.g., `"SYS-001"`). */
206
- readonly code: string;
207
- /** Static documentation mapping and registration rules bound to this code type. */
208
- readonly codeMetadata: ErrorCodeMetadata;
209
- /** Severity tier grading the operational consequence of this failure. */
210
- readonly severity: Severity;
211
- /** Target state or document path where processing failed. */
212
- readonly path?: string;
213
- /** Active system segment, workflow node, or function context executing during failure. */
214
- readonly operation?: string;
215
- /** Detailed sub-layer list representing structural data validation failures. */
216
- readonly issues: readonly Issue[];
217
- /** Raw backing trigger, array of concurrent branches, or source error instance. */
218
- readonly cause?: unknown | unknown[];
219
- /** Exact moment this error was instantiated (ISO string). */
220
- readonly timestamp: string;
221
- /**
222
- * Instantiate a structurally traceable `SystemError`.
223
- * @param params - Configuration parameters outlining the error's state context.
224
- */
225
- constructor(params: SystemErrorParams);
226
- /**
227
- * Generates a separate immutable clone assigning a target file, state, or field path.
228
- */
229
- withPath(path: string): SystemError;
230
- /**
231
- * Generates a separate immutable clone assigning a specific execution block or track tracking context.
232
- */
233
- withOperation(operation: string): SystemError;
234
- /**
235
- * Appends an operational validation structural issue to a fresh clone of this error.
236
- */
237
- withIssue(issue: Issue): SystemError;
238
- /**
239
- * Appends multiple validation issues onto a fresh clone of this error instance.
240
- */
241
- withIssues(issues: readonly Issue[]): SystemError;
242
- /**
243
- * Maps a backing raw trigger or concurrent array tracking set to a fresh clone of this error instance.
244
- */
245
- withCause(cause: unknown | unknown[]): SystemError;
246
- /**
247
- * Retrieves the recommended HTTP Status code mapped to this error's code metadata registry.
248
- */
249
- getHttpStatus(): number | undefined;
250
- /**
251
- * Unrolls execution hierarchies chronologically, isolating parallel pipelines into clear trace frames.
252
- * Always returns an array of trace nodes.
253
- */
254
- private extractChronologicalTrace;
255
- /**
256
- * Transforms the error payload into a flattened transmission format.
257
- * Strips recursive depth in favor of an array-mapped chronological failure `trace`.
258
- */
259
- toJSON(): Record<string, unknown>;
260
- /**
261
- * Generates a flat payload matching `toJSON` format requirements for centralized platform ingestion engines.
262
- */
263
- toLogEntry(): Record<string, unknown>;
264
- /**
265
- * Terminal terminal dump representation formatting details cleanly into plain strings.
266
- */
267
- toString(): string;
73
+ private _delay;
74
+ private _leading;
75
+ private _timer;
76
+ private _pendingFn;
77
+ private _pendingResolvers;
78
+ private _leadingFired;
79
+ constructor(options?: DebouncerOptions);
80
+ /**
81
+ * Enqueues a function and returns a promise that resolves with its result.
82
+ *
83
+ * All callers within the same quiet window share the same result — the
84
+ * function that actually runs is the *last* one enqueued before the delay
85
+ * expires. Use this when you need the return value of the debounced call.
86
+ *
87
+ * For fire-and-forget use (void fns, event handlers), prefer `fire()` to
88
+ * avoid unhandled-promise-rejection warnings and make intent explicit.
89
+ *
90
+ * @param fn - The function to debounce.
91
+ * @returns A promise resolving with a DebouncerResult discriminated union.
92
+ */
93
+ do(fn: () => Promise<T> | T): Promise<DebouncerResult<T>>;
94
+ /**
95
+ * Enqueues a function without returning a promise (fire-and-forget).
96
+ *
97
+ * Identical debounce semantics to `do()` — the last-enqueued function runs
98
+ * after the quiet period. Use this for void callbacks, DOM event handlers,
99
+ * or any caller that doesn't care about the result. No dangling promise,
100
+ * no unhandled-rejection risk.
101
+ *
102
+ * @param fn - The function to debounce.
103
+ */
104
+ fire(fn: () => Promise<T> | T): void;
105
+ /**
106
+ * Shared enqueue logic for both `do()` and `fire()`.
107
+ *
108
+ */
109
+ private _enqueue;
110
+ /**
111
+ * Immediately cancels any pending debounced execution.
112
+ * All waiting callers resolve with `{ status: "cancelled" }`.
113
+ */
114
+ cancel(): void;
115
+ /**
116
+ * Immediately executes the pending function (if any), bypassing the remaining delay.
117
+ * All waiting callers receive the result.
118
+ */
119
+ flush(): Promise<DebouncerResult<T> | null>;
120
+ /** Returns true if a debounced call is currently pending. */
121
+ pending(): boolean;
122
+ /**
123
+ * Executes the latest pending function and resolves all waiting callers.
124
+ */
125
+ private _fire;
268
126
  }
269
-
127
+ //#endregion
128
+ //#region src/sync/errors.d.ts
270
129
  /** Represents all errors within the sync system */
271
130
  declare class SyncError extends SystemError {
272
- constructor(message: string, cause?: unknown);
131
+ constructor(message: string, cause?: unknown);
273
132
  }
274
133
  declare class TimeoutError extends SyncError {
275
- constructor(message?: string);
134
+ constructor(message?: string);
276
135
  }
277
136
  declare class OnceExecutionConflict extends SyncError {
278
- constructor(cause?: unknown);
137
+ constructor(cause?: unknown);
279
138
  }
280
139
  declare class SerializerExecutionDone extends SyncError {
281
- constructor(cause?: unknown);
140
+ constructor(cause?: unknown);
282
141
  }
283
-
142
+ //#endregion
143
+ //#region src/sync/latch.d.ts
284
144
  /**
285
145
  * A one-shot gate that starts closed and opens exactly once.
286
146
  *
@@ -299,52 +159,53 @@ declare class SerializerExecutionDone extends SyncError {
299
159
  * ready.open();
300
160
  */
301
161
  declare class Latch {
302
- private _open;
303
- private _resolve;
304
- private _promise;
305
- constructor();
306
- /**
307
- * Opens the latch. All current and future waiters resolve immediately.
308
- * Calling open() more than once is a no-op.
309
- */
310
- open(): void;
311
- /**
312
- * Returns a promise that resolves when the latch is opened.
313
- * If already open, resolves on the next microtask checkpoint.
314
- *
315
- * @param timeout - Optional maximum wait time in milliseconds.
316
- * @throws {TimeoutError} If the latch does not open within the timeout.
317
- */
318
- wait(timeout?: number): Promise<void>;
319
- /**
320
- * Synchronously checks whether the latch has been opened.
321
- */
322
- isOpen(): boolean;
162
+ private _open;
163
+ private _resolve;
164
+ private _promise;
165
+ constructor();
166
+ /**
167
+ * Opens the latch. All current and future waiters resolve immediately.
168
+ * Calling open() more than once is a no-op.
169
+ */
170
+ open(): void;
171
+ /**
172
+ * Returns a promise that resolves when the latch is opened.
173
+ * If already open, resolves on the next microtask checkpoint.
174
+ *
175
+ * @param timeout - Optional maximum wait time in milliseconds.
176
+ * @throws {TimeoutError} If the latch does not open within the timeout.
177
+ */
178
+ wait(timeout?: number): Promise<void>;
179
+ /**
180
+ * Synchronously checks whether the latch has been opened.
181
+ */
182
+ isOpen(): boolean;
323
183
  }
324
-
184
+ //#endregion
185
+ //#region src/sync/mutex.d.ts
325
186
  interface MutexOptions {
326
- /**
327
- * Maximum number of pending requests allowed in the queue.
328
- * If exceeded, tryLock/lock will fail.
329
- * @default Infinity
330
- */
331
- capacity?: number;
332
- /**
333
- * Controls how lock handoff is scheduled when a waiter is unblocked.
334
- *
335
- * - `"macrotask"` (default): Uses setTimeout(fn, 0) to yield to the event
336
- * loop between handoffs. Prevents microtask starvation under heavy
337
- * contention — I/O, rendering, and other macrotasks can run between
338
- * lock acquisitions. Use for Serializer and other coarse-grained
339
- * serializers.
340
- *
341
- * - `"microtask"`: Uses queueMicrotask(fn) for handoff. Near-zero latency
342
- * between acquisitions — no macrotask delay. Safe when you need
343
- * back-to-back operations to complete as fast as possible and starvation
344
- * is not a concern (e.g. Once, Semaphore where builds are infrequent
345
- * and latency matters more than fairness).
346
- */
347
- yieldMode?: "macrotask" | "microtask";
187
+ /**
188
+ * Maximum number of pending requests allowed in the queue.
189
+ * If exceeded, tryLock/lock will fail.
190
+ * @default Infinity
191
+ */
192
+ capacity?: number;
193
+ /**
194
+ * Controls how lock handoff is scheduled when a waiter is unblocked.
195
+ *
196
+ * - `"macrotask"` (default): Uses setTimeout(fn, 0) to yield to the event
197
+ * loop between handoffs. Prevents microtask starvation under heavy
198
+ * contention — I/O, rendering, and other macrotasks can run between
199
+ * lock acquisitions. Use for Serializer and other coarse-grained
200
+ * serializers.
201
+ *
202
+ * - `"microtask"`: Uses queueMicrotask(fn) for handoff. Near-zero latency
203
+ * between acquisitions — no macrotask delay. Safe when you need
204
+ * back-to-back operations to complete as fast as possible and starvation
205
+ * is not a concern (e.g. Once, Semaphore where builds are infrequent
206
+ * and latency matters more than fairness).
207
+ */
208
+ yieldMode?: "macrotask" | "microtask";
348
209
  }
349
210
  /**
350
211
  * A mutual exclusion lock.
@@ -355,43 +216,44 @@ interface MutexOptions {
355
216
  * - "microtask": zero-delay handoff for latency-sensitive paths.
356
217
  */
357
218
  declare class Mutex {
358
- private _locked;
359
- private _capacity;
360
- private _yieldMode;
361
- private waiters;
362
- constructor(options?: MutexOptions);
363
- /**
364
- * Acquires the lock. If already held, waits until released or timeout reached.
365
- *
366
- * @param timeout - Optional maximum wait time in milliseconds.
367
- * @throws {TimeoutError} If the lock cannot be acquired within the timeout.
368
- * @throws {Error} If the wait queue is full (backpressure).
369
- */
370
- lock(timeout?: number): Promise<void>;
371
- /**
372
- * Attempts to acquire the lock without waiting.
373
- * @returns `true` if the lock was acquired, `false` otherwise.
374
- */
375
- tryLock(): boolean;
376
- /**
377
- * Releases the lock, scheduling the next waiter according to yieldMode.
378
- *
379
- * When a waiter exists, `_locked` intentionally remains `true` — ownership
380
- * transfers directly to the next waiter without ever clearing the flag.
381
- * Only when the queue is empty is `_locked` set to false.
382
- *
383
- * @throws {Error} If the mutex is not currently locked.
384
- */
385
- unlock(): void;
386
- /** Returns true if the mutex is currently locked. */
387
- locked(): boolean;
388
- /** Returns the number of operations waiting for the lock. */
389
- pending(): number;
219
+ private _locked;
220
+ private _capacity;
221
+ private _yieldMode;
222
+ private waiters;
223
+ constructor(options?: MutexOptions);
224
+ /**
225
+ * Acquires the lock. If already held, waits until released or timeout reached.
226
+ *
227
+ * @param timeout - Optional maximum wait time in milliseconds.
228
+ * @throws {TimeoutError} If the lock cannot be acquired within the timeout.
229
+ * @throws {Error} If the wait queue is full (backpressure).
230
+ */
231
+ lock(timeout?: number): Promise<void>;
232
+ /**
233
+ * Attempts to acquire the lock without waiting.
234
+ * @returns `true` if the lock was acquired, `false` otherwise.
235
+ */
236
+ tryLock(): boolean;
237
+ /**
238
+ * Releases the lock, scheduling the next waiter according to yieldMode.
239
+ *
240
+ * When a waiter exists, `_locked` intentionally remains `true` — ownership
241
+ * transfers directly to the next waiter without ever clearing the flag.
242
+ * Only when the queue is empty is `_locked` set to false.
243
+ *
244
+ * @throws {Error} If the mutex is not currently locked.
245
+ */
246
+ unlock(): void;
247
+ /** Returns true if the mutex is currently locked. */
248
+ locked(): boolean;
249
+ /** Returns the number of operations waiting for the lock. */
250
+ pending(): number;
390
251
  }
391
-
252
+ //#endregion
253
+ //#region src/sync/once.d.ts
392
254
  type OnceResult<T> = {
393
- value: T | null;
394
- error?: unknown;
255
+ value: T | null;
256
+ error?: unknown;
395
257
  };
396
258
  /**
397
259
  * Ensures a specific task is executed exactly once, regardless of concurrent callers.
@@ -403,97 +265,101 @@ type OnceResult<T> = {
403
265
  * @template T - The type of value returned by the execution.
404
266
  */
405
267
  declare class Once<T = void> {
406
- private mutex;
407
- private promise;
408
- private _value;
409
- private _error;
410
- private _done;
411
- private retry;
412
- private throws;
413
- constructor({ retry, throws }?: {
414
- retry?: boolean;
415
- throws?: boolean;
416
- });
417
- /**
418
- * Manually sets a value, marking the operation as done.
419
- * Throws if the operation is already done or currently running.
420
- *
421
- * @param value - The value to resolve to.
422
- */
423
- resolve(value: T): void;
424
- /**
425
- * Execute the function if it hasn't been executed yet.
426
- * Subsequent calls return the result of the first execution.
427
- *
428
- * @param fn - The function to execute.
429
- * @param timeout - Max wait time in ms (includes lock wait + execution time).
430
- */
431
- do(fn: () => Promise<T> | T, timeout?: number): Promise<OnceResult<T>>;
432
- /**
433
- * Executes the function synchronously if it hasn't been executed yet.
434
- * If an async operation is pending or the lock is contended, it will fail immediately.
435
- * The failure is returned as an error state, OR thrown if `throws: true` is configured.
436
- *
437
- * @param fn - The synchronous function to execute.
438
- * @returns The result of the execution.
439
- */
440
- doSync(fn: () => T): OnceResult<T>;
441
- /**
442
- * Returns true if the operation is currently executing.
443
- *
444
- * Uses ready() as the canonical check: running is its logical complement
445
- * when a promise is in flight. This correctly handles the brief window where
446
- * _done=true but the promise hasn't been cleared yet in finally.
447
- */
448
- running(): boolean;
449
- /**
450
- * Returns the current state without waiting.
451
- */
452
- peek(): OnceResult<T>;
453
- /**
454
- * Returns the stored value if successful, otherwise throws the stored error.
455
- * Throws if the operation is not yet complete.
456
- */
457
- get(): T | null;
458
- /**
459
- * Resets the instance, allowing the action to run again on the next call.
460
- *
461
- * @throws {Error} If called while an operation is currently executing,
462
- * as this would leave the in-flight promise in an inconsistent state.
463
- */
464
- reset(): void;
465
- /**
466
- * Returns true if the operation has finished (success or final failure).
467
- */
468
- done(): boolean;
469
- /**
470
- * Returns the in-flight execution promise if one is currently running, null otherwise.
471
- *
472
- * Use this to join an in-progress operation without triggering a new one.
473
- */
474
- current(): Promise<OnceResult<T>> | null;
475
- /**
476
- * Awaits a promise with an optional timeout.
477
- *
478
- * We clear the timer on the winning branch, consistent with the
479
- * Mutex/Semaphore/Latch timeout pattern throughout this module.
480
- */
481
- private _awaitWithTimeout;
268
+ private mutex;
269
+ private promise;
270
+ private _value;
271
+ private _error;
272
+ private _done;
273
+ private retry;
274
+ private throws;
275
+ constructor({
276
+ retry,
277
+ throws
278
+ }?: {
279
+ retry?: boolean;
280
+ throws?: boolean;
281
+ });
282
+ /**
283
+ * Manually sets a value, marking the operation as done.
284
+ * Throws if the operation is already done or currently running.
285
+ *
286
+ * @param value - The value to resolve to.
287
+ */
288
+ resolve(value: T): void;
289
+ /**
290
+ * Execute the function if it hasn't been executed yet.
291
+ * Subsequent calls return the result of the first execution.
292
+ *
293
+ * @param fn - The function to execute.
294
+ * @param timeout - Max wait time in ms (includes lock wait + execution time).
295
+ */
296
+ do(fn: () => Promise<T> | T, timeout?: number): Promise<OnceResult<T>>;
297
+ /**
298
+ * Executes the function synchronously if it hasn't been executed yet.
299
+ * If an async operation is pending or the lock is contended, it will fail immediately.
300
+ * The failure is returned as an error state, OR thrown if `throws: true` is configured.
301
+ *
302
+ * @param fn - The synchronous function to execute.
303
+ * @returns The result of the execution.
304
+ */
305
+ doSync(fn: () => T): OnceResult<T>;
306
+ /**
307
+ * Returns true if the operation is currently executing.
308
+ *
309
+ * Uses ready() as the canonical check: running is its logical complement
310
+ * when a promise is in flight. This correctly handles the brief window where
311
+ * _done=true but the promise hasn't been cleared yet in finally.
312
+ */
313
+ running(): boolean;
314
+ /**
315
+ * Returns the current state without waiting.
316
+ */
317
+ peek(): OnceResult<T>;
318
+ /**
319
+ * Returns the stored value if successful, otherwise throws the stored error.
320
+ * Throws if the operation is not yet complete.
321
+ */
322
+ get(): T | null;
323
+ /**
324
+ * Resets the instance, allowing the action to run again on the next call.
325
+ *
326
+ * @throws {Error} If called while an operation is currently executing,
327
+ * as this would leave the in-flight promise in an inconsistent state.
328
+ */
329
+ reset(): void;
330
+ /**
331
+ * Returns true if the operation has finished (success or final failure).
332
+ */
333
+ done(): boolean;
334
+ /**
335
+ * Returns the in-flight execution promise if one is currently running, null otherwise.
336
+ *
337
+ * Use this to join an in-progress operation without triggering a new one.
338
+ */
339
+ current(): Promise<OnceResult<T>> | null;
340
+ /**
341
+ * Awaits a promise with an optional timeout.
342
+ *
343
+ * We clear the timer on the winning branch, consistent with the
344
+ * Mutex/Semaphore/Latch timeout pattern throughout this module.
345
+ */
346
+ private _awaitWithTimeout;
482
347
  }
483
-
348
+ //#endregion
349
+ //#region src/sync/rwmutex.d.ts
484
350
  interface RWMutexOptions {
485
- /**
486
- * Controls how lock handoff is scheduled when a waiter is unblocked.
487
- *
488
- * - `"microtask"` (default): Uses queueMicrotask(fn). Near-zero latency
489
- * between handoffs. Appropriate for RWMutex because readers are woken in
490
- * bulk and writers are rarely contended enough to cause starvation.
491
- *
492
- * - `"macrotask"`: Uses setTimeout(fn, 0) to yield to the event loop between
493
- * handoffs. Use if you observe microtask starvation under extreme write
494
- * contention on this specific lock.
495
- */
496
- yieldMode?: "macrotask" | "microtask";
351
+ /**
352
+ * Controls how lock handoff is scheduled when a waiter is unblocked.
353
+ *
354
+ * - `"microtask"` (default): Uses queueMicrotask(fn). Near-zero latency
355
+ * between handoffs. Appropriate for RWMutex because readers are woken in
356
+ * bulk and writers are rarely contended enough to cause starvation.
357
+ *
358
+ * - `"macrotask"`: Uses setTimeout(fn, 0) to yield to the event loop between
359
+ * handoffs. Use if you observe microtask starvation under extreme write
360
+ * contention on this specific lock.
361
+ */
362
+ yieldMode?: "macrotask" | "microtask";
497
363
  }
498
364
  /**
499
365
  * A read-write mutex with writer preference.
@@ -508,80 +374,81 @@ interface RWMutexOptions {
508
374
  * Prefer the `read()` and `write()` convenience wrappers to avoid lock leaks.
509
375
  */
510
376
  declare class RWMutex {
511
- private _readers;
512
- private _writeLocked;
513
- private _pendingWriters;
514
- private _yieldMode;
515
- private readerWaiters;
516
- private writerWaiters;
517
- constructor(options?: RWMutexOptions);
518
- /**
519
- * Acquires a read lock. Blocks if a writer holds or is waiting for the lock.
520
- *
521
- * @param timeout - Optional maximum wait time in milliseconds.
522
- * @throws {TimeoutError} If the read lock cannot be acquired within the timeout.
523
- */
524
- rlock(timeout?: number): Promise<void>;
525
- /**
526
- * Releases a read lock.
527
- * @throws {Error} If no read lock is currently held.
528
- */
529
- runlock(): void;
530
- /**
531
- * Acquires the write lock. Blocks until all current readers and any prior
532
- * writers have finished.
533
- *
534
- * @param timeout - Optional maximum wait time in milliseconds.
535
- * @throws {TimeoutError} If the write lock cannot be acquired within the timeout.
536
- */
537
- lock(timeout?: number): Promise<void>;
538
- /**
539
- * Releases the write lock.
540
- * Wakes the next pending writer if one exists; otherwise wakes all pending readers.
541
- *
542
- * @throws {Error} If no write lock is currently held.
543
- */
544
- unlock(): void;
545
- /**
546
- * Runs a function under a read lock, releasing it when done.
547
- * Prefer this over manual rlock/runlock to avoid lock leaks.
548
- */
549
- read<T>(fn: () => Promise<T> | T, timeout?: number): Promise<T>;
550
- /**
551
- * Runs a function under the write lock, releasing it when done.
552
- * Prefer this over manual lock/unlock to avoid lock leaks.
553
- */
554
- write<T>(fn: () => Promise<T> | T, timeout?: number): Promise<T>;
555
- /** Returns true if the write lock is currently held. */
556
- writeLocked(): boolean;
557
- /** Returns the number of active read lock holders. */
558
- readers(): number;
559
- /** Returns the number of writers currently waiting for the lock. */
560
- pendingWriters(): number;
561
- /** Returns the number of readers currently waiting for the lock. */
562
- pendingReaders(): number;
563
- /**
564
- * Wakes the next writer if the lock is free and a writer is waiting.
565
- * Returns true if a writer was woken, false otherwise.
566
- */
567
- private _tryWakeWriter;
568
- /**
569
- * Wakes all pending readers (called when a writer releases and no writers are queued).
570
- */
571
- private _wakeAllReaders;
572
- /**
573
- * Schedules a waiter callback according to the configured yieldMode.
574
- */
575
- private _schedule;
377
+ private _readers;
378
+ private _writeLocked;
379
+ private _pendingWriters;
380
+ private _yieldMode;
381
+ private readerWaiters;
382
+ private writerWaiters;
383
+ constructor(options?: RWMutexOptions);
384
+ /**
385
+ * Acquires a read lock. Blocks if a writer holds or is waiting for the lock.
386
+ *
387
+ * @param timeout - Optional maximum wait time in milliseconds.
388
+ * @throws {TimeoutError} If the read lock cannot be acquired within the timeout.
389
+ */
390
+ rlock(timeout?: number): Promise<void>;
391
+ /**
392
+ * Releases a read lock.
393
+ * @throws {Error} If no read lock is currently held.
394
+ */
395
+ runlock(): void;
396
+ /**
397
+ * Acquires the write lock. Blocks until all current readers and any prior
398
+ * writers have finished.
399
+ *
400
+ * @param timeout - Optional maximum wait time in milliseconds.
401
+ * @throws {TimeoutError} If the write lock cannot be acquired within the timeout.
402
+ */
403
+ lock(timeout?: number): Promise<void>;
404
+ /**
405
+ * Releases the write lock.
406
+ * Wakes the next pending writer if one exists; otherwise wakes all pending readers.
407
+ *
408
+ * @throws {Error} If no write lock is currently held.
409
+ */
410
+ unlock(): void;
411
+ /**
412
+ * Runs a function under a read lock, releasing it when done.
413
+ * Prefer this over manual rlock/runlock to avoid lock leaks.
414
+ */
415
+ read<T>(fn: () => Promise<T> | T, timeout?: number): Promise<T>;
416
+ /**
417
+ * Runs a function under the write lock, releasing it when done.
418
+ * Prefer this over manual lock/unlock to avoid lock leaks.
419
+ */
420
+ write<T>(fn: () => Promise<T> | T, timeout?: number): Promise<T>;
421
+ /** Returns true if the write lock is currently held. */
422
+ writeLocked(): boolean;
423
+ /** Returns the number of active read lock holders. */
424
+ readers(): number;
425
+ /** Returns the number of writers currently waiting for the lock. */
426
+ pendingWriters(): number;
427
+ /** Returns the number of readers currently waiting for the lock. */
428
+ pendingReaders(): number;
429
+ /**
430
+ * Wakes the next writer if the lock is free and a writer is waiting.
431
+ * Returns true if a writer was woken, false otherwise.
432
+ */
433
+ private _tryWakeWriter;
434
+ /**
435
+ * Wakes all pending readers (called when a writer releases and no writers are queued).
436
+ */
437
+ private _wakeAllReaders;
438
+ /**
439
+ * Schedules a waiter callback according to the configured yieldMode.
440
+ */
441
+ private _schedule;
576
442
  }
577
-
443
+ //#endregion
444
+ //#region src/sync/semaphore.d.ts
578
445
  interface SemaphoreOptions extends MutexOptions {
579
- /**
580
- * Number of concurrent holders allowed.
581
- * Inherits `capacity` and `yieldMode` from MutexOptions.
582
- * @default 1 (equivalent to a Mutex)
583
- */
584
- slots?: number;
446
+ /**
447
+ * Number of concurrent holders allowed.
448
+ * Inherits `capacity` and `yieldMode` from MutexOptions.
449
+ * @default 1 (equivalent to a Mutex)
450
+ */
451
+ slots?: number;
585
452
  }
586
453
  /**
587
454
  * A counting semaphore — generalises Mutex to allow up to N concurrent holders.
@@ -592,114 +459,137 @@ interface SemaphoreOptions extends MutexOptions {
592
459
  * Accepts the same `capacity` and `yieldMode` options as Mutex, plus `slots`.
593
460
  */
594
461
  declare class Semaphore {
595
- private _slots;
596
- private _available;
597
- private _capacity;
598
- private _yieldMode;
599
- private waiters;
600
- constructor(options?: SemaphoreOptions);
601
- /**
602
- * Acquires one slot. If all slots are taken, waits until one is released.
603
- *
604
- * @param timeout - Optional maximum wait time in milliseconds.
605
- * @throws {TimeoutError} If a slot cannot be acquired within the timeout.
606
- * @throws {Error} If the wait queue is full.
607
- */
608
- acquire(timeout?: number): Promise<void>;
609
- /**
610
- * Attempts to acquire a slot without waiting.
611
- * @returns `true` if a slot was acquired, `false` otherwise.
612
- */
613
- tryAcquire(): boolean;
614
- /**
615
- * Releases one slot. If waiters are queued, the next one is scheduled
616
- * according to yieldMode.
617
- *
618
- * @throws {Error} If no slot is currently held (release without acquire).
619
- */
620
- release(): void;
621
- /**
622
- * Runs a function with one acquired slot, releasing it when done.
623
- * Convenience wrapper — prefer this over manual acquire/release.
624
- */
625
- run<T>(fn: () => Promise<T> | T, timeout?: number): Promise<T>;
626
- /** Number of slots currently free. */
627
- available(): number;
628
- /** Number of callers waiting for a slot. */
629
- pending(): number;
630
- /** Total slot count (as configured). */
631
- slots(): number;
462
+ private _slots;
463
+ private _available;
464
+ private _capacity;
465
+ private _yieldMode;
466
+ private waiters;
467
+ constructor(options?: SemaphoreOptions);
468
+ /**
469
+ * Acquires one slot. If all slots are taken, waits until one is released.
470
+ *
471
+ * @param timeout - Optional maximum wait time in milliseconds.
472
+ * @throws {TimeoutError} If a slot cannot be acquired within the timeout.
473
+ * @throws {Error} If the wait queue is full.
474
+ */
475
+ acquire(timeout?: number): Promise<void>;
476
+ /**
477
+ * Attempts to acquire a slot without waiting.
478
+ * @returns `true` if a slot was acquired, `false` otherwise.
479
+ */
480
+ tryAcquire(): boolean;
481
+ /**
482
+ * Releases one slot. If waiters are queued, the next one is scheduled
483
+ * according to yieldMode.
484
+ *
485
+ * @throws {Error} If no slot is currently held (release without acquire).
486
+ */
487
+ release(): void;
488
+ /**
489
+ * Runs a function with one acquired slot, releasing it when done.
490
+ * Convenience wrapper — prefer this over manual acquire/release.
491
+ */
492
+ run<T>(fn: () => Promise<T> | T, timeout?: number): Promise<T>;
493
+ /** Number of slots currently free. */
494
+ available(): number;
495
+ /** Number of callers waiting for a slot. */
496
+ pending(): number;
497
+ /** Total slot count (as configured). */
498
+ slots(): number;
632
499
  }
633
-
500
+ //#endregion
501
+ //#region src/sync/serializer.d.ts
634
502
  type SerializerResult<T> = {
635
- value: T | null;
636
- error?: unknown;
503
+ value: T | null;
504
+ error?: unknown;
637
505
  };
638
506
  interface SerializerOptions {
639
- /**
640
- * Max items in queue. If full, .do() returns an error immediately.
641
- * @default 1000
642
- */
643
- capacity?: number;
644
- /**
645
- * Yield mode for the internal mutex. Defaults to "macrotask" for
646
- * coarse-grained serializers. Use "microtask" for fine-grained
647
- * latency-sensitive serializers.
648
- */
649
- yieldMode?: "macrotask" | "microtask";
507
+ /**
508
+ * Max items in queue. If full, .do() returns an error immediately.
509
+ * @default 1000
510
+ */
511
+ capacity?: number;
512
+ /**
513
+ * Yield mode for the internal mutex. Defaults to "macrotask" for
514
+ * coarse-grained serializers. Use "microtask" for fine-grained
515
+ * latency-sensitive serializers.
516
+ */
517
+ yieldMode?: "macrotask" | "microtask";
650
518
  }
651
519
  /**
652
520
  * Ensures tasks are executed sequentially (FIFO).
653
521
  * Maintains the result of the last successful execution.
654
522
  * Includes backpressure protection via configurable queue capacity.
655
523
  */
656
- declare class Serializer<T = void> {
657
- private mutex;
658
- private _done;
659
- private _lastValue;
660
- private _lastError;
661
- private _hasRun;
662
- constructor(options?: SerializerOptions);
663
- /**
664
- * Enqueue a function to be executed after all previous tasks complete.
665
- *
666
- * @param fn - The function to execute.
667
- * @param timeout - Max time to wait to acquire the lock.
668
- * @returns Object containing the value or error.
669
- */
670
- do(fn: () => Promise<T> | T, timeout?: number): Promise<SerializerResult<T | null>>;
671
- /**
672
- * Returns the result of the last execution.
673
- *
674
- * Returns `{ value: null, error: undefined }` both when the serializer has
675
- * never run and when it ran and returned null — use `hasRun()` to distinguish
676
- * these two cases.
677
- */
678
- peek(): SerializerResult<T | null>;
679
- /**
680
- * Returns true if at least one task has been executed (successfully or not).
681
- */
682
- hasRun(): boolean;
683
- /**
684
- * Permanently closes the serializer.
685
- * Subsequent calls to `do()` will fail immediately with SerializerExecutionDone.
686
- */
687
- close(): void;
688
- /** Returns the number of tasks currently waiting. */
689
- pending(): number;
690
- /** Returns true if a task is currently executing. */
691
- running(): boolean;
524
+ declare class Serializer<T = any> {
525
+ private mutex;
526
+ private _closed;
527
+ private _lastValue;
528
+ private _lastError;
529
+ private _hasRun;
530
+ private _closeOnce;
531
+ constructor(options?: SerializerOptions);
532
+ /**
533
+ * Enqueue a function to be executed after all previous tasks complete.
534
+ *
535
+ * @param fn - The function to execute.
536
+ * @param timeout - Max time to wait to acquire the lock.
537
+ * @returns Object containing the value or error.
538
+ */
539
+ do(fn: () => Promise<T> | T, timeout?: number): Promise<SerializerResult<T | null>>;
540
+ /**
541
+ * Permanently closes the serializer, preventing new tasks from being enqueued,
542
+ * and returns a promise that resolves once all previously enqueued tasks have
543
+ * finished executing.
544
+ *
545
+ * - The seal (`_closed = true`) happens synchronously on the first call, so any
546
+ * concurrent or subsequent `.do()` calls are rejected immediately.
547
+ * - The drain sentinel is enqueued exactly once via `Once`, so concurrent
548
+ * `close()` callers all join the same promise rather than each enqueuing
549
+ * their own sentinel.
550
+ *
551
+ * @returns A promise that resolves when the serializer is fully drained.
552
+ */
553
+ close(): Promise<void>;
554
+ /**
555
+ * Returns true if the serializer has been closed and fully drained —
556
+ * i.e., close() has been called and the drain sentinel has completed.
557
+ */
558
+ done(): boolean;
559
+ /**
560
+ * Returns the result of the last execution.
561
+ *
562
+ * Returns `{ value: null, error: undefined }` both when the serializer has
563
+ * never run and when it ran and returned null — use `hasRun()` to distinguish
564
+ * these two cases.
565
+ */
566
+ peek(): SerializerResult<T | null>;
567
+ /**
568
+ * Returns true if at least one task has been executed (successfully or not).
569
+ */
570
+ hasRun(): boolean;
571
+ /** Returns the number of tasks currently waiting. */
572
+ pending(): number;
573
+ /** Returns true if a task is currently executing. */
574
+ running(): boolean;
575
+ /**
576
+ * Raw lock → execute → unlock path. No `_closed` guard.
577
+ * Used by both `do()` (after the guard passes) and the drain sentinel
578
+ * inside `close()`.
579
+ */
580
+ private _enqueue;
692
581
  }
693
-
582
+ //#endregion
583
+ //#region src/sync/shared-resource.d.ts
694
584
  interface SharedResourceOptions {
695
- /**
696
- * How long to wait after the last subscriber leaves before cleaning up.
697
- * - "microtask": (Default) Waits until the end of the current synchronous execution tick.
698
- * Perfect for React StrictMode or concurrent rendering.
699
- * - number: Waits for the specified milliseconds. Useful for debouncing rapid reconnects.
700
- * - "sync": Cleans up immediately (bypasses the grace period entirely).
701
- */
702
- gracePeriod?: "microtask" | "sync" | number;
585
+ /**
586
+ * How long to wait after the last subscriber leaves before cleaning up.
587
+ * - "microtask": (Default) Waits until the end of the current synchronous execution tick.
588
+ * Perfect for React StrictMode or concurrent rendering.
589
+ * - number: Waits for the specified milliseconds. Useful for debouncing rapid reconnects.
590
+ * - "sync": Cleans up immediately (bypasses the grace period entirely).
591
+ */
592
+ gracePeriod?: "microtask" | "sync" | number;
703
593
  }
704
594
  /**
705
595
  * Manages the lifecycle of a reference-counted shared resource.
@@ -707,103 +597,104 @@ interface SharedResourceOptions {
707
597
  * and cleaned up only when the last subscriber releases it (after an optional grace period).
708
598
  */
709
599
  declare class SharedResource<T> {
710
- private readonly factory;
711
- private readonly onCleanup;
712
- private readonly options;
713
- private _count;
714
- private readonly init;
715
- private pendingMicrotask;
716
- private cleanupTimer?;
717
- constructor(factory: () => Promise<T> | T, onCleanup: (value: T | null) => void | Promise<void>, options?: SharedResourceOptions);
718
- /**
719
- * The current number of active subscribers.
720
- */
721
- get subscribers(): number;
722
- /**
723
- * Increments the reference count and ensures the resource is initialized.
724
- * If a cleanup was scheduled but hasn't executed yet, it is cancelled.
725
- *
726
- * @returns The initialized resource.
727
- */
728
- acquire(): Promise<T>;
729
- /**
730
- * Decrements the reference count.
731
- * If the count reaches zero, the cleanup process is scheduled according to the grace period.
732
- */
733
- release(): void;
734
- /**
735
- * Returns the current value synchronously if it has been successfully initialized.
736
- * Returns null if initialization is pending, failed, or hasn't started.
737
- */
738
- peek(): T | null;
739
- /**
740
- * Forcibly tears down the resource, ignoring the reference count and grace period.
741
- * Useful for emergency teardowns (e.g., container disposal).
742
- */
743
- forceCleanup(): void;
744
- /**
745
- * Cancels any scheduled cleanup tasks.
746
- * This is the core mechanism that prevents React StrictMode from destroying the resource.
747
- */
748
- private cancelPendingCleanup;
749
- /**
750
- * Determines how to schedule the cleanup based on the configured options.
751
- */
752
- private scheduleCleanup;
753
- /**
754
- * Performs the actual teardown of the resource and resets the initialization state.
755
- */
756
- private executeCleanup;
600
+ private readonly factory;
601
+ private readonly onCleanup;
602
+ private readonly options;
603
+ private _count;
604
+ private readonly init;
605
+ private pendingMicrotask;
606
+ private cleanupTimer?;
607
+ constructor(factory: () => Promise<T> | T, onCleanup: (value: T | null) => void | Promise<void>, options?: SharedResourceOptions);
608
+ /**
609
+ * The current number of active subscribers.
610
+ */
611
+ get subscribers(): number;
612
+ /**
613
+ * Increments the reference count and ensures the resource is initialized.
614
+ * If a cleanup was scheduled but hasn't executed yet, it is cancelled.
615
+ *
616
+ * @returns The initialized resource.
617
+ */
618
+ acquire(): Promise<T>;
619
+ /**
620
+ * Decrements the reference count.
621
+ * If the count reaches zero, the cleanup process is scheduled according to the grace period.
622
+ */
623
+ release(): void;
624
+ /**
625
+ * Returns the current value synchronously if it has been successfully initialized.
626
+ * Returns null if initialization is pending, failed, or hasn't started.
627
+ */
628
+ peek(): T | null;
629
+ /**
630
+ * Forcibly tears down the resource, ignoring the reference count and grace period.
631
+ * Useful for emergency teardowns (e.g., container disposal).
632
+ */
633
+ forceCleanup(): void;
634
+ /**
635
+ * Cancels any scheduled cleanup tasks.
636
+ * This is the core mechanism that prevents React StrictMode from destroying the resource.
637
+ */
638
+ private cancelPendingCleanup;
639
+ /**
640
+ * Determines how to schedule the cleanup based on the configured options.
641
+ */
642
+ private scheduleCleanup;
643
+ /**
644
+ * Performs the actual teardown of the resource and resets the initialization state.
645
+ */
646
+ private executeCleanup;
757
647
  }
758
-
648
+ //#endregion
649
+ //#region src/sync/registry.d.ts
759
650
  interface ResourceRegistryOptions<T, K, O> {
760
- /**
761
- * Async factory: receives the key and per-call options.
762
- * Only invoked when a resource does not already exist.
763
- */
764
- onCreate?: (key: K, options: O) => Promise<T>;
765
- /**
766
- * Sync factory: receives the key and per-call options.
767
- * Only invoked when a resource does not already exist.
768
- */
769
- onCreateSync?: (key: K, options: O) => T;
770
- /**
771
- * Optional disposer for explicit cleanup (release/clear).
772
- */
773
- onDispose?: (resource: T) => void | Promise<void>;
774
- /**
775
- * Optional callback fired when the resource is GC'd.
776
- */
777
- onEvict?: (key: K) => void;
651
+ /**
652
+ * Async factory: receives the key and per-call options.
653
+ * Only invoked when a resource does not already exist.
654
+ */
655
+ onCreate?: (key: K, options: O) => Promise<T>;
656
+ /**
657
+ * Sync factory: receives the key and per-call options.
658
+ * Only invoked when a resource does not already exist.
659
+ */
660
+ onCreateSync?: (key: K, options: O) => T;
661
+ /**
662
+ * Optional disposer for explicit cleanup (release/clear).
663
+ */
664
+ onDispose?: (resource: T) => void | Promise<void>;
665
+ /**
666
+ * Optional callback fired when the resource is GC'd.
667
+ */
668
+ onEvict?: (key: K) => void;
778
669
  }
779
670
  declare class ResourceRegistry<R extends object, K extends string | number = string, O = void> {
780
- private readonly entries;
781
- private readonly resourceToOnce;
782
- private readonly finalizer;
783
- private readonly onCreate?;
784
- private readonly onCreateSync?;
785
- private readonly onDispose?;
786
- private readonly onEvict?;
787
- constructor(options: ResourceRegistryOptions<R, K, O>);
788
- /**
789
- * Get or create a resource.
790
- *
791
- * IMPORTANT: The `options` are ONLY used if the resource does NOT exist yet.
792
- * If the resource already exists (or is being created by another concurrent call),
793
- * the `options` are ignored and the existing instance is returned.
794
- *
795
- * This matches the original `registry.ts` behavior.
796
- */
797
- get(key: K, options: O, timeout?: number): Promise<R>;
798
- /**
799
- * Synchronous version of `get`.
800
- * Options are only used on the first creation.
801
- */
802
- getSync(key: K, options: O): R;
803
- release(key: K): Promise<boolean>;
804
- clear(): Promise<void>;
805
- has(key: K): boolean;
806
- get size(): number;
671
+ private readonly entries;
672
+ private readonly resourceToOnce;
673
+ private readonly finalizer;
674
+ private readonly onCreate?;
675
+ private readonly onCreateSync?;
676
+ private readonly onDispose?;
677
+ private readonly onEvict?;
678
+ constructor(options: ResourceRegistryOptions<R, K, O>);
679
+ /**
680
+ * Get or create a resource.
681
+ *
682
+ * IMPORTANT: The `options` are ONLY used if the resource does NOT exist yet.
683
+ * If the resource already exists (or is being created by another concurrent call),
684
+ * the `options` are ignored and the existing instance is returned.
685
+ *
686
+ * This matches the original `registry.ts` behavior.
687
+ */
688
+ get(key: K, options: O, timeout?: number): Promise<R>;
689
+ /**
690
+ * Synchronous version of `get`.
691
+ * Options are only used on the first creation.
692
+ */
693
+ getSync(key: K, options: O): R;
694
+ release(key: K): Promise<boolean>;
695
+ clear(): Promise<void>;
696
+ has(key: K): boolean;
697
+ get size(): number;
807
698
  }
808
-
809
- export { Debouncer, type DebouncerCancelled, type DebouncerError, type DebouncerOk, type DebouncerOptions, type DebouncerResult, Latch, Mutex, type MutexOptions, Once, OnceExecutionConflict, type OnceResult, RWMutex, type RWMutexOptions, ResourceRegistry, type ResourceRegistryOptions, Semaphore, type SemaphoreOptions, Serializer, SerializerExecutionDone, type SerializerOptions, type SerializerResult, SharedResource, type SharedResourceOptions, SyncError, TimeoutError };
699
+ //#endregion
700
+ export { Debouncer, DebouncerCancelled, DebouncerError, DebouncerOk, DebouncerOptions, DebouncerResult, Latch, Mutex, MutexOptions, Once, OnceExecutionConflict, OnceResult, RWMutex, RWMutexOptions, ResourceRegistry, ResourceRegistryOptions, Semaphore, SemaphoreOptions, Serializer, SerializerExecutionDone, SerializerOptions, SerializerResult, SharedResource, SharedResourceOptions, SyncError, TimeoutError };