@orkestrel/pool 0.0.13 → 0.0.15

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.
@@ -11,12 +11,15 @@ import type { EmitterInterface } from '@orkestrel/emitter';
11
11
  * request order. `clear` owns its idle snapshot; `destroy` returns one stable barrier and
12
12
  * waits for every in-flight create, validation, and destroy attempt before destroying the
13
13
  * emitter last.
14
+ * Set `min` with its required `restarts` bound and call `start()` to warm resources and
15
+ * refill losses independently of queued acquires. `watch` observes loss and its signal
16
+ * aborts at disposal; a token can declare loss through `destroy()`.
14
17
  *
15
18
  * @typeParam T - The pooled resource type
16
- * @param options - Lifecycle hooks, optional positive safe `max`, and observation hooks
19
+ * @param options - Lifecycle hooks, capacity, optional bounded warm floor, and observation hooks
17
20
  * @returns A working {@link PoolInterface}
18
- * @throws {@link PoolError} Thrown when `options.max` is present and is not a positive safe
19
- * integer, with `code: 'invalid'`. Construction validates it synchronously, before the pool exists.
21
+ * @throws {@link PoolError} Thrown with `code: 'invalid'` when capacity, restart bounds, or
22
+ * the watch hook are invalid. Construction validates them synchronously, before the pool exists.
20
23
  *
21
24
  * @example Create a pool
22
25
  * ```ts
@@ -83,9 +86,9 @@ import type { EmitterInterface } from '@orkestrel/emitter';
83
86
  export declare function isPoolSignal(value: unknown): value is AbortSignal;
84
87
 
85
88
  /**
86
- * Represents a capacity-aware resource pool whose opaque ownership records preserve FIFO
87
- * settlement, cancellation, exact lease release, and deterministic teardown under concurrent
88
- * hooks.
89
+ * Represents a resource pool with optional bounded capacity and a warm floor, whose opaque
90
+ * ownership records preserve FIFO settlement, cancellation, exact lease release, and
91
+ * deterministic teardown under concurrent hooks.
89
92
  *
90
93
  * @typeParam T - The pooled resource value
91
94
  *
@@ -108,205 +111,250 @@ import type { EmitterInterface } from '@orkestrel/emitter';
108
111
  /**
109
112
  * Constructs a pool and synchronously validates its capacity contract.
110
113
  *
111
- * @param options - Resource hooks, observation hooks, and optional positive safe `max`
112
- * @throws {@link PoolError} Thrown when `options.max` is present and is not a positive safe
113
- * integer, with `code: 'invalid'`.
114
+ * @param options - Resource hooks, observation hooks, capacity, and optional bounded warm floor
115
+ * @throws {@link PoolError} Thrown with `code: 'invalid'` when `min` or `max` is not a positive
116
+ * safe integer, their explicit values differ, `restarts` is absent with `min`, present without
117
+ * `min`, or not a non-negative safe integer, or `watch` is not callable.
114
118
  */
115
119
  constructor(options: PoolOptions<T>);
116
120
  /** Holds the typed synchronous lifecycle observation surface. */
117
121
  get emitter(): EmitterInterface<PoolEventMap>;
118
- /** Counts all owned records, including records validating or destroying. */
122
+ /** Counts all owned records, including records validating, destroying, or retained after failed cleanup. */
119
123
  get size(): number;
120
124
  /** Counts the records immediately available without validation work. */
121
125
  get idle(): number;
122
126
  /** Counts the records represented by unsettled released-once lease tokens. */
123
127
  get active(): number;
124
128
  /**
125
- * Queues and leases one resource in FIFO settlement order.
129
+ * Fills the warm floor and resets the strikes of a spent floor; without a floor, resolves immediately.
126
130
  *
127
- * @param signal - Optional native cancellation signal
128
- * @returns A promise for the unique resource lease
129
- * @throws {@link PoolError} Thrown when `signal` is present and is not a native `AbortSignal`,
130
- * with `code: 'invalid'`. This throw is synchronous rather than a rejected promise, so a caller
131
- * that handles failures with `.catch()` alone misses it.
132
- * @throws {@link PoolError} Thrown as a rejection when `destroy()` has already begun, with
133
- * `code: 'destroyed'`; when the create hook fails, with `code: 'create'` and the hook's thrown
134
- * value as `cause`; and when an invalid record's cleanup fails, with `code: 'cleanup'`. A
135
- * `signal` that aborts rejects with the caller's exact `signal.reason` instead.
136
- */
137
- acquire(signal?: AbortSignal): Promise<PoolToken<T>>;
138
- /**
139
- * Destroys the records that are idle at this call's synchronous snapshot.
140
- *
141
- * @returns A promise that settles after every snapshot cleanup attempt
142
- * @throws {@link PoolError} Thrown when `destroy()` has already begun, with `code: 'destroyed'`.
143
- * @throws {@link PoolError} Thrown when a claimed record's destroy hook fails, with
144
- * `code: 'cleanup'` and every distinct failure in `context.failures`. Each arrives as a rejected
145
- * promise rather than a synchronous throw.
146
- */
147
- clear(): Promise<void>;
148
- /**
149
- * Tears down the pool permanently and returns its stable completion barrier.
150
- *
151
- * @returns The exact promise shared by every destroy call
152
- * @throws {@link PoolError} Thrown when a destroy hook failed during teardown, with
153
- * `code: 'cleanup'` and every distinct failure in `context.failures`. The barrier rejects; it
154
- * never throws synchronously, and a repeat call receives the same rejected promise.
131
+ * @returns A promise that resolves when the floor owns `min` live records
132
+ * @throws {@link PoolError} Thrown as a rejection with `code: 'create'` and the last cause
133
+ * when the bound is spent, `code: 'cleanup'` when retained failed cleanup prevents filling,
134
+ * or `code: 'destroyed'` when teardown begins.
135
+ */
136
+ start(): Promise<void>;
137
+ /**
138
+ * Queues the caller in FIFO order, validates an idle record or waits for a floor refill, and
139
+ * creates on demand only without a floor.
140
+ *
141
+ * @param signal - Optional native cancellation signal
142
+ * @returns A promise for the unique resource lease
143
+ * @throws {@link PoolError} Thrown when `signal` is present and is not a native `AbortSignal`,
144
+ * with `code: 'invalid'`. This throw is synchronous rather than a rejected promise, so a caller
145
+ * that handles failures with `.catch()` alone misses it.
146
+ * @throws {@link PoolError} Thrown as a rejection when `destroy()` has already begun, with
147
+ * `code: 'destroyed'`; when the create hook fails or the floor is spent without an idle record,
148
+ * with `code: 'create'` and the last cause; and when an invalid record's cleanup fails or
149
+ * a floor retains a record, owns `min` records, has nothing idle, and has no refill or disposal
150
+ * pending, even with live leases, with `code: 'cleanup'` and the retained cleanup failure as cause.
151
+ * A `signal` that aborts rejects with the caller's exact `signal.reason` instead.
152
+ */
153
+ acquire(signal?: AbortSignal): Promise<PoolToken<T>>;
154
+ /**
155
+ * Destroys the records that are idle at this call's synchronous snapshot and restores a started floor.
156
+ *
157
+ * @returns A promise that settles after every snapshot cleanup attempt
158
+ * @throws {@link PoolError} Thrown when `destroy()` has already begun, with `code: 'destroyed'`.
159
+ * @throws {@link PoolError} Thrown when a claimed record's destroy hook fails, with
160
+ * `code: 'cleanup'` and every distinct failure in `context.failures`. Each arrives as a rejected
161
+ * promise rather than a synchronous throw.
155
162
  */
156
- destroy(): Promise<void>;
157
- }
163
+ clear(): Promise<void>;
164
+ /**
165
+ * Tears down the pool permanently and returns its stable completion barrier.
166
+ *
167
+ * @returns The exact promise shared by every destroy call
168
+ * @throws {@link PoolError} Thrown when a destroy hook failure was retained or occurred during teardown, with
169
+ * `code: 'cleanup'` and every distinct failure in `context.failures`. The barrier rejects; it
170
+ * never throws synchronously, and a repeat call receives the same rejected promise.
171
+ */
172
+ destroy(): Promise<void>;
173
+ }
158
174
 
159
- /**
160
- * Names the machine-readable failure codes produced by {@link PoolError}.
161
- */
162
- export declare type PoolCode = 'invalid' | 'destroyed' | 'create' | 'cleanup';
175
+ /**
176
+ * Names the machine-readable failure codes produced by {@link PoolError}.
177
+ */
178
+ export declare type PoolCode = 'invalid' | 'destroyed' | 'create' | 'cleanup';
163
179
 
164
- /**
165
- * Represents the structured context attached to a {@link PoolError}: the rejected input, or the
166
- * distinct destroy-hook failures an aggregate cleanup collected.
167
- */
168
- export declare interface PoolContext {
169
- /** Holds the rejected public input, when the failure is an input-validation error. */
170
- readonly value?: unknown;
171
- /** Holds distinct destroy-hook failures collected by `clear()` or `destroy()`. */
172
- readonly failures?: readonly unknown[];
173
- }
180
+ /**
181
+ * Represents the structured context attached to a {@link PoolError}: the rejected input, or the
182
+ * distinct destroy-hook failures an aggregate cleanup collected.
183
+ */
184
+ export declare interface PoolContext {
185
+ /** Holds the rejected public input, when the failure is an input-validation error. */
186
+ readonly value?: unknown;
187
+ /** Holds distinct destroy-hook failures collected by `clear()` or `destroy()`. */
188
+ readonly failures?: readonly unknown[];
189
+ }
174
190
 
175
- /**
176
- * Represents a stable, machine-readable pool failure that retains the original thrown value as
177
- * its cause without unsafe coercion, alongside structured context.
178
- *
179
- * @example
180
- * ```ts
181
- * import { PoolError, isPoolError } from '@orkestrel/pool'
182
- *
183
- * try {
184
- * await pool.acquire()
185
- * } catch (error: unknown) {
186
- * if (isPoolError(error)) console.error(error.code, error.cause)
187
- * }
188
- * ```
189
- */
190
- export declare class PoolError extends Error {
191
- /** Holds the stable machine-readable failure category. */
192
- readonly code: PoolCode;
193
- /** Holds optional structured input or aggregate destroy-hook failure details. */
194
- readonly context: PoolContext | undefined;
195
- /**
196
- * Creates a pool failure without coercing a hostile thrown value.
197
- *
198
- * @param options - Stable code plus optional cause and structured context
199
- */
200
- constructor(options: PoolErrorOptions);
201
- }
202
-
203
- /**
204
- * Represents the construction options for {@link PoolError}: the stable code, an optional cause,
205
- * and optional structured context.
206
- */
207
- export declare interface PoolErrorOptions {
208
- /** Holds the stable machine-readable failure category. */
209
- readonly code: PoolCode;
210
- /** Holds the original thrown value, retained without unsafe string coercion. */
211
- readonly cause?: unknown;
212
- /** Holds optional structured failure details. */
213
- readonly context?: PoolContext;
214
- }
191
+ /**
192
+ * Represents a stable, machine-readable pool failure that retains the original thrown value as
193
+ * its cause without unsafe coercion, alongside structured context.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * import { PoolError, isPoolError } from '@orkestrel/pool'
198
+ *
199
+ * try {
200
+ * await pool.acquire()
201
+ * } catch (error: unknown) {
202
+ * if (isPoolError(error)) console.error(error.code, error.cause)
203
+ * }
204
+ * ```
205
+ */
206
+ export declare class PoolError extends Error {
207
+ /** Holds the stable machine-readable failure category. */
208
+ readonly code: PoolCode;
209
+ /** Holds optional structured input or aggregate destroy-hook failure details. */
210
+ readonly context: PoolContext | undefined;
211
+ /**
212
+ * Creates a pool failure without coercing a hostile thrown value.
213
+ *
214
+ * @param options - Stable code plus optional cause and structured context
215
+ */
216
+ constructor(options: PoolErrorOptions);
217
+ }
215
218
 
216
- /**
217
- * Represents the observable resource lifecycle events emitted by a {@link PoolInterface}.
218
- */
219
- export declare type PoolEventMap = {
220
- /** Signals that a created resource entered pool ownership. */
221
- readonly create: readonly [];
222
- /** Signals that a token settled successfully and its exact resource became leased. */
223
- readonly acquire: readonly [];
224
- /** Signals that a released resource became immediately idle. */
225
- readonly release: readonly [];
226
- /** Signals that a resource destroy hook completed or was attempted when absent. */
227
- readonly destroy: readonly [];
228
- };
219
+ /**
220
+ * Represents the construction options for {@link PoolError}: the stable code, an optional cause,
221
+ * and optional structured context.
222
+ */
223
+ export declare interface PoolErrorOptions {
224
+ /** Holds the stable machine-readable failure category. */
225
+ readonly code: PoolCode;
226
+ /** Holds the original thrown value, retained without unsafe string coercion. */
227
+ readonly cause?: unknown;
228
+ /** Holds optional structured failure details. */
229
+ readonly context?: PoolContext;
230
+ }
229
231
 
230
- /**
231
- * Represents a FIFO resource pool with optional bounded capacity and deterministic teardown,
232
- * exposing its record counts and a typed lifecycle emitter.
233
- */
234
- export declare interface PoolInterface<T> {
235
- /** Holds the typed synchronous lifecycle observation surface. */
236
- readonly emitter: EmitterInterface<PoolEventMap>;
237
- /** Counts all owned records, including records validating or destroying. */
238
- readonly size: number;
239
- /** Counts the records immediately available without validation work. */
240
- readonly idle: number;
241
- /** Counts the records represented by unsettled released-once lease tokens. */
242
- readonly active: number;
243
- /**
244
- * Queues the caller in FIFO order, validates an idle record or creates one, and settles queued
245
- * acquires in that same order.
246
- *
247
- * @param signal - Optional native cancellation signal
248
- * @returns A promise for the unique resource lease
249
- * @throws {@link PoolError} Thrown when `signal` is present and is not a native `AbortSignal`,
250
- * with `code: 'invalid'`. This throw is synchronous rather than a rejected promise, so a caller
251
- * that handles failures with `.catch()` alone misses it.
252
- * @throws {@link PoolError} Thrown as a rejection when `destroy()` has already begun, with
253
- * `code: 'destroyed'`; when the create hook fails, with `code: 'create'` and the hook's thrown
254
- * value as `cause`; and when an invalid record's cleanup fails, with `code: 'cleanup'`. A
255
- * `signal` that aborts rejects with the caller's exact `signal.reason` instead.
256
- */
257
- acquire(signal?: AbortSignal): Promise<PoolToken<T>>;
258
- /**
259
- * Destroys the records that are idle at this call's synchronous snapshot.
260
- *
261
- * @returns A promise that settles after every snapshot cleanup attempt
262
- * @throws {@link PoolError} Thrown when `destroy()` has already begun, with `code: 'destroyed'`.
263
- * @throws {@link PoolError} Thrown when a claimed record's destroy hook fails, with
264
- * `code: 'cleanup'` and every distinct failure in `context.failures`. Each arrives as a rejected
265
- * promise rather than a synchronous throw.
266
- */
267
- clear(): Promise<void>;
268
- /**
269
- * Tears down the pool permanently and returns its stable completion barrier.
270
- *
271
- * @returns The exact promise shared by every destroy call
272
- * @throws {@link PoolError} Thrown when a destroy hook failed during teardown, with
273
- * `code: 'cleanup'` and every distinct failure in `context.failures`. The barrier rejects; it
274
- * never throws synchronously, and a repeat call receives the same rejected promise.
275
- */
276
- destroy(): Promise<void>;
277
- }
232
+ /**
233
+ * Represents the observable resource lifecycle events emitted by a {@link PoolInterface}.
234
+ */
235
+ export declare type PoolEventMap = {
236
+ /** Signals that a created resource entered pool ownership. */
237
+ readonly create: readonly [];
238
+ /** Signals that a token settled successfully and its exact resource became leased. */
239
+ readonly acquire: readonly [];
240
+ /** Signals that a released, orphaned, or refilled resource became immediately idle. */
241
+ readonly release: readonly [];
242
+ /** Signals that a resource destroy hook completed or was attempted when absent. */
243
+ readonly destroy: readonly [];
244
+ };
278
245
 
279
- /**
280
- * Represents the resource lifecycle options for {@link Pool} and `createPool`: creation,
281
- * destruction, validation, capacity, and observation.
282
- *
283
- * @remarks
284
- * `create` lazily produces resources. `destroy` tears down a claimed resource.
285
- * `validate` checks a previously owned resource before reuse. `max` is a positive
286
- * safe integer; omission is the only unbounded form. `on` installs initial emitter
287
- * listeners and `error` receives isolated listener failures.
246
+ /**
247
+ * Represents a FIFO resource pool with optional bounded capacity, a warm floor, loss recovery,
248
+ * and deterministic teardown, exposing its record counts and a typed lifecycle emitter.
249
+ */
250
+ export declare interface PoolInterface<T> {
251
+ /** Holds the typed synchronous lifecycle observation surface. */
252
+ readonly emitter: EmitterInterface<PoolEventMap>;
253
+ /** Counts all owned records, including records validating, destroying, or retained after failed cleanup. */
254
+ readonly size: number;
255
+ /** Counts the records immediately available without validation work. */
256
+ readonly idle: number;
257
+ /** Counts the records represented by unsettled released-once lease tokens. */
258
+ readonly active: number;
259
+ /**
260
+ * Fills the warm floor and resets the strikes of a spent floor; without a floor, resolves immediately.
261
+ *
262
+ * @returns A promise that resolves when the floor owns `min` live records
263
+ * @throws {@link PoolError} Thrown as a rejection with `code: 'create'` and the last cause
264
+ * when the bound is spent, `code: 'cleanup'` when retained failed cleanup prevents filling,
265
+ * or `code: 'destroyed'` when teardown begins.
266
+ */
267
+ start(): Promise<void>;
268
+ /**
269
+ * Queues the caller in FIFO order, validates an idle record or waits for a floor refill, and
270
+ * creates on demand only without a floor.
271
+ *
272
+ * @param signal - Optional native cancellation signal
273
+ * @returns A promise for the unique resource lease
274
+ * @throws {@link PoolError} Thrown when `signal` is present and is not a native `AbortSignal`,
275
+ * with `code: 'invalid'`. This throw is synchronous rather than a rejected promise, so a caller
276
+ * that handles failures with `.catch()` alone misses it.
277
+ * @throws {@link PoolError} Thrown as a rejection when `destroy()` has already begun, with
278
+ * `code: 'destroyed'`; when the create hook fails or the floor is spent without an idle record,
279
+ * with `code: 'create'` and the last cause; and when an invalid record's cleanup fails or
280
+ * a floor retains a record, owns `min` records, has nothing idle, and has no refill or disposal
281
+ * pending, even with live leases, with `code: 'cleanup'` and the retained cleanup failure as cause.
282
+ * A `signal` that aborts rejects with the caller's exact `signal.reason` instead.
288
283
  */
289
- export declare interface PoolOptions<T> {
290
- readonly on?: EmitterHooks<PoolEventMap>;
291
- readonly error?: EmitterErrorHandler;
292
- readonly create: () => Promise<T> | T;
293
- readonly destroy?: (value: T) => Promise<void> | void;
294
- readonly validate?: (value: T) => Promise<boolean> | boolean;
295
- readonly max?: number;
296
- }
284
+ acquire(signal?: AbortSignal): Promise<PoolToken<T>>;
285
+ /**
286
+ * Destroys the records that are idle at this call's synchronous snapshot and restores a started floor.
287
+ *
288
+ * @returns A promise that settles after every snapshot cleanup attempt
289
+ * @throws {@link PoolError} Thrown when `destroy()` has already begun, with `code: 'destroyed'`.
290
+ * @throws {@link PoolError} Thrown when a claimed record's destroy hook fails, with
291
+ * `code: 'cleanup'` and every distinct failure in `context.failures`. Each arrives as a rejected
292
+ * promise rather than a synchronous throw.
293
+ */
294
+ clear(): Promise<void>;
295
+ /**
296
+ * Tears down the pool permanently and returns its stable completion barrier.
297
+ *
298
+ * @returns The exact promise shared by every destroy call
299
+ * @throws {@link PoolError} Thrown when a destroy hook failure was retained or occurred during teardown, with
300
+ * `code: 'cleanup'` and every distinct failure in `context.failures`. The barrier rejects; it
301
+ * never throws synchronously, and a repeat call receives the same rejected promise.
302
+ */
303
+ destroy(): Promise<void>;
304
+ }
297
305
 
298
- /**
299
- * Represents a unique lease over one pool-owned resource record, exposing that record as a readonly
300
- * `value` and returning it through an idempotent `release`.
301
- */
302
- export declare interface PoolToken<T> {
303
- /** Holds the leased value. Duplicate values still belong to independent records. */
304
- readonly value: T;
305
- /**
306
- * Gives this exact record back to the pool once; a repeat call, and a call after teardown took
307
- * ownership, are no-ops.
308
- */
309
- release(): void;
310
- }
306
+ /**
307
+ * Represents the resource lifecycle options for {@link Pool} and `createPool`: creation,
308
+ * destruction, validation, capacity, a bounded warm floor, and loss observation.
309
+ *
310
+ * @remarks
311
+ * `create` produces resources on demand, or only to restore `min` after `start()`.
312
+ * `destroy` tears down a claimed resource. `validate` checks an owned resource before reuse.
313
+ * `min` and `max` are positive safe integers and must be equal when both are set; `max`
314
+ * defaults to `min`. Omitting both leaves capacity unbounded. `restarts` is required with
315
+ * `min`, is refused without `min`, has no default, and is a non-negative safe integer.
316
+ * A failed refill or loss of a never-leased record adds a strike. A lease grant resets the
317
+ * strikes only when its record was created after the last strike; creation alone does not.
318
+ * Exceeding `restarts` stops ordinary refills until `start()` or a qualifying grant.
319
+ * Each lost leased record earns one refill attempt after successful disposal, even when
320
+ * the bound is spent. That credit does not reset strikes; a failed attempt adds a strike.
321
+ * `watch` settles on loss and receives a signal aborted when disposal begins. Its rejection
322
+ * reaches `error` with event `watch` only while the record is live. Any settlement after
323
+ * the signal aborts is ignored. `on` installs initial emitter listeners; `error`
324
+ * also receives isolated listener failures.
325
+ */
326
+ export declare interface PoolOptions<T> {
327
+ readonly on?: EmitterHooks<PoolEventMap>;
328
+ readonly error?: EmitterErrorHandler;
329
+ readonly create: () => Promise<T> | T;
330
+ readonly destroy?: (value: T) => Promise<void> | void;
331
+ readonly validate?: (value: T) => Promise<boolean> | boolean;
332
+ readonly watch?: (value: T, signal: AbortSignal) => Promise<unknown>;
333
+ readonly max?: number;
334
+ readonly min?: number;
335
+ readonly restarts?: number;
336
+ }
337
+
338
+ /**
339
+ * Represents a unique lease over one pool-owned resource record, exposing that record as a readonly
340
+ * `value` and ending it through an idempotent `release` or `destroy`.
341
+ */
342
+ export declare interface PoolToken<T> {
343
+ /** Holds the leased value. Duplicate values still belong to independent records. */
344
+ readonly value: T;
345
+ /**
346
+ * Gives this exact record back to the pool once; a repeat call, and a call after loss or teardown
347
+ * took ownership, are no-ops.
348
+ */
349
+ release(): void;
350
+ /**
351
+ * Destroys this exact record instead of returning it; a repeat call, and a call after release,
352
+ * are no-ops.
353
+ *
354
+ * @returns A promise for this record's cleanup attempt, including an attempt already in progress
355
+ * @throws {@link PoolError} Thrown as a rejection with `code: 'cleanup'` when disposal fails.
356
+ */
357
+ destroy(): Promise<void>;
358
+ }
311
359
 
312
- export { }
360
+ export { }